diff --git a/.github/workflows/ci-llzk.yml b/.github/workflows/ci-llzk.yml new file mode 100644 index 000000000..bfe05b05c --- /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@v4 + 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@v4 + 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/Cargo.lock b/Cargo.lock index bf95bb67d..3cb5f6fc6 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,13 +272,22 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", "vec_map", ] +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "code_producers" version = "2.2.2" @@ -176,6 +329,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 +382,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 +400,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 +452,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 +501,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 +522,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 +574,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 +630,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", + "thiserror 1.0.31", ] [[package]] @@ -392,6 +654,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 +699,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", + "rustix 0.37.19", "windows-sys 0.48.0", ] @@ -461,7 +739,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax", + "regex-syntax 0.6.27", "string_cache", "term", "tiny-keccak", @@ -479,9 +757,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 +772,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 = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libloading" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] [[package]] name = "libm" @@ -510,6 +798,77 @@ 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.1.0" +source = "git+https://github.com/project-llzk/llzk-rs#eaa5d0dc35d522757fcbd2027d8940d4b908ca1b" +dependencies = [ + "llzk-macro", + "llzk-sys", + "log", + "melior", + "melior-macro", + "mlir-sys", + "paste", +] + +[[package]] +name = "llzk-macro" +version = "0.1.0" +source = "git+https://github.com/project-llzk/llzk-rs#eaa5d0dc35d522757fcbd2027d8940d4b908ca1b" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.106", + "thiserror 2.0.17", +] + +[[package]] +name = "llzk-sys" +version = "0.1.0" +source = "git+https://github.com/project-llzk/llzk-rs#eaa5d0dc35d522757fcbd2027d8940d4b908ca1b" +dependencies = [ + "anyhow", + "llzk-sys-build-support", + "mlir-sys", +] + +[[package]] +name = "llzk-sys-build-support" +version = "0.1.0" +source = "git+https://github.com/project-llzk/llzk-rs#eaa5d0dc35d522757fcbd2027d8940d4b908ca1b" +dependencies = [ + "anyhow", + "bindgen 0.71.1", + "cc", + "cmake", + "glob", + "tempfile", +] + +[[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 +881,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 +891,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 = "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 = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8e9348dd263d2680d657635d9dd084cdcd785d7486a41332ffa4c926d5882b54" +dependencies = [ + "bindgen 0.71.1", +] [[package]] name = "new_debug_unreachable" @@ -547,6 +944,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 +1012,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 +1054,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 +1090,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn", + "syn 1.0.98", ] [[package]] @@ -722,11 +1135,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 +1197,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 +1237,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 +1246,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 +1255,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 = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "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 = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.6", ] [[package]] @@ -821,6 +1289,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 +1313,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 +1346,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 +1375,7 @@ checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.98", ] [[package]] @@ -898,12 +1400,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 +1430,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 +1464,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 +1520,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 +1541,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 +1561,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 +1593,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 +1617,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 +1641,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 +1668,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 +1692,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 +1831,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 +1855,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 +1977,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..4496d0ff0 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,11 @@ 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_pass_pipeline: String, pub link_libraries : Vec } @@ -43,11 +51,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 +76,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 +94,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 +106,13 @@ 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_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 +129,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 +181,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 +217,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 +247,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 +274,9 @@ impl Input { pub fn prime(&self) -> String{ self.prime.clone() } + pub fn llzk_pass_pipeline(&self) -> String { + self.llzk_pass_pipeline.clone() + } } mod input_processing { use ansi_term::Colour; @@ -311,10 +355,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 +396,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 +437,13 @@ 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 view() -> ArgMatches<'static> { App::new("circom compiler") .version(VERSION) @@ -496,6 +569,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 +592,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 +625,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 +662,14 @@ 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"), + ) .get_matches() } diff --git a/circom/src/main.rs b/circom/src/main.rs index 40e2b2a01..8e6263a09 100644 --- a/circom/src/main.rs +++ b/circom/src/main.rs @@ -27,6 +27,23 @@ 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.llzk_file(), + &user_input.llzk_pass_pipeline(), + &user_input.prime(), + user_input.flag_verbose(), + user_input.flag_stabilize(), + ); + } + let config = ExecutionConfig { no_rounds: user_input.no_rounds(), flag_p: user_input.parallel_simplification_flag(), @@ -45,7 +62,35 @@ 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.llzk_file(), + &user_input.llzk_pass_pipeline(), + &user_input.prime(), + user_input.flag_verbose(), + user_input.flag_stabilize(), + ); + } + let compilation_config = CompilerConfig { vcp: circuit, debug_output: user_input.print_ir_flag(), @@ -69,3 +114,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..5439872a7 --- /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 -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..70eadc3bf --- /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 -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..daf7b1f36 --- /dev/null +++ b/circom/tests/arrays/array_copy2_loop.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array2<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array2<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Array2<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array2<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@Array2<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array2<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@Array2<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@b] : <@Array2<[@n]>>, !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_23]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_25]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_24]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..4c373d46f --- /dev/null +++ b/circom/tests/arrays/array_copy2_vec.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array2<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array2<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Array2<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array2<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_0]] : <@Array2<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array2<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Array2<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@Array2<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !array.type<@n x !felt.type>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return +// 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..1e9b038e2 --- /dev/null +++ b/circom/tests/arrays/array_copy3_loop.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array3<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array3<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n,@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) -> !struct.type<@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n,@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_11]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_11]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_14]], %[[VAL_15]]] : <@n,@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_17]], %[[VAL_18]]] = %[[VAL_16]] : <@n,@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_20]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3<[@n]>>, %[[VAL_24:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_23]][@out] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_29]], %[[VAL_25]]) +// CHECK-NEXT: scf.condition(%[[VAL_30]]) %[[VAL_29]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_34]], %[[VAL_25]]) +// CHECK-NEXT: scf.condition(%[[VAL_35]]) %[[VAL_34]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_37]], %[[VAL_38]]] : <@n,@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_31]] +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_36]] +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_40]], %[[VAL_41]]] : <@n,@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_42]], %[[VAL_39]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_44]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_46]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..b08ae93a0 --- /dev/null +++ b/circom/tests/arrays/array_copy3_partial.circom @@ -0,0 +1,65 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array3<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array3<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n,@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) -> !struct.type<@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n,@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n,@n x !felt.type> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.insert %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n,@n x !felt.type>, <@n x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@out] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_16]]) +// CHECK-NEXT: scf.condition(%[[VAL_21]]) %[[VAL_20]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_15]]{{\[}}%[[VAL_23]]] : <@n,@n x !felt.type> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.extract %[[VAL_17]]{{\[}}%[[VAL_25]]] : <@n,@n x !felt.type> +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_24]] : !array.type<@n x !felt.type>, !array.type<@n x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..aeef98b41 --- /dev/null +++ b/circom/tests/arrays/array_copy3_vec.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array3<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array3<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n,@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) -> !struct.type<@Array3<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array3<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_0]] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array3<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Array3<[@n]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@n,@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@Array3<[@n]>>, !array.type<@n,@n x !felt.type> +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !array.type<@n,@n x !felt.type>, !array.type<@n,@n x !felt.type> +// CHECK-NEXT: function.return +// 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..d655aea86 --- /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 -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..1934b0fc0 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_00.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// CHECK-NEXT: function.return +// 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..662566eb5 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_01.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// CHECK-NEXT: function.return +// 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..bfdcda3f7 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_02.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// CHECK-NEXT: function.return +// 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..cab43f429 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_03.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// CHECK-NEXT: function.return +// 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..e1b405ba1 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_04.circom @@ -0,0 +1,25 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[7]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[@N]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims<[@N]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: function.return +// 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..5de12b391 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_05.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// COM: The `@outp` field is not instantiated, so it's not clear what the array size is. +// COM: This can be refined when https://github.com/project-llzk/circom/issues/320 +// COM: is implemented. + +pragma circom 2.0.0; + +template ArrayDims(N) { + var M = N + 1; + signal output outp[M]; +} + +component main = ArrayDims(7); + +// CHECK: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<()[s0] -> (s0)> +// CHECK-LABEL: module attributes {llzk.main = !struct.type<@ArrayDims<[7]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[@N]> { +// CHECK-NEXT: struct.field @outp : !array.type<#[[$ATTR_0]] x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims<[@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 +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@outp] : <@ArrayDims<[@N]>>, !array.type<#[[$ATTR_0]] x !felt.type> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..c5d262fda --- /dev/null +++ b/circom/tests/arrays/array_dimensions_06.circom @@ -0,0 +1,15 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL: * +// COM: Template parameters expressions are currently unsupported as array dimensions for inputs. + +pragma circom 2.0.0; + +template ArrayDims(N) { + signal input arr[N + 1]; +} + +component main = ArrayDims(7); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/arrays/array_dimensions_07.circom b/circom/tests/arrays/array_dimensions_07.circom new file mode 100644 index 000000000..8dca6d71a --- /dev/null +++ b/circom/tests/arrays/array_dimensions_07.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[7]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[@N]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..a19ba05e1 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_08.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArrayDims<[7, 2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[@N, @M]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims<[@N, @M]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.new : <@N,@M x !felt.type> +// 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> +// 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>, <@M x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims<[@N, @M]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.new : <@N,@M x !felt.type> +// 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> +// 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>, <@M x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..39b85a402 --- /dev/null +++ b/circom/tests/arrays/array_dimensions_09.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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: #[[$ATTR_0:[0-9a-zA-Z_\.]+]] = affine_map<()[s0] -> (s0)> +// CHECK-LABEL: module attributes {llzk.main = !struct.type<@ArrayDims<[7, 2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArrayDims<[@N, @M]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@ArrayDims<[@N, @M]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ArrayDims<[@N, @M]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_1]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_5]]]} : <#[[$ATTR_0]] x !felt.type> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_6]], %[[VAL_7]] : <#[[$ATTR_0]] x !felt.type> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_11:[0-9a-zA-Z_\.]+]] = %[[VAL_9]] to %[[VAL_8]] step %[[VAL_10]] { +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_11]]] = %[[VAL_4]] : <#[[$ATTR_0]] x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ArrayDims<[@N, @M]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@ArrayDims<[@N, @M]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @M : !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = array.new{(){{\[}}%[[VAL_17]]]} : <#[[$ATTR_0]] x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_18]], %[[VAL_19]] : <#[[$ATTR_0]] x !felt.type> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_21]] to %[[VAL_20]] step %[[VAL_22]] { +// CHECK-NEXT: array.write %[[VAL_18]]{{\[}}%[[VAL_23]]] = %[[VAL_16]] : <#[[$ATTR_0]] x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..82d8aaf2b --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[A_0:[0-9a-zA-Z_\.]+]]: !array.type<17,13 x !felt.type>) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[V_S0:[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_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]], %[[V_S1:[0-9a-zA-Z_\.]+]] = %[[V_S0]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_7]]) +// CHECK-NEXT: scf.condition(%[[V_8]]) %[[V_I1]], %[[V_S1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_S2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[C0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = array.new %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]] : <13 x !felt.type> +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = array.extract %[[A_0]]{{\[}}%[[V_13]]] : <17,13 x !felt.type> +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.umod %[[V_I2]], %[[V_15]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_16]] +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = array.read %[[V_14]]{{\[}}%[[V_17]]] : <13 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_S3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_S2]], %[[V_18]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_I3]], %[[V_S3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_22:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[V_23:[0-9a-zA-Z_\.]+]]: !array.type<17,13 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[V_22]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_S0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_I0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]], %[[V_S1:[0-9a-zA-Z_\.]+]] = %[[V_S0]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_29]]) +// CHECK-NEXT: scf.condition(%[[V_30]]) %[[V_I1]], %[[V_S1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_S2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[C0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = array.new %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]], %[[C0]] : <13 x !felt.type> +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = array.extract %[[V_23]]{{\[}}%[[V_35]]] : <17,13 x !felt.type> +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.umod %[[V_I2]], %[[V_37]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_38]] +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = array.read %[[V_36]]{{\[}}%[[V_39]]] : <13 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_S3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_S2]], %[[V_40]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_42]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_I3]], %[[V_S3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..e646c16eb --- /dev/null +++ b/circom/tests/arrays/array_insert_in_function.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @lookup() -> !felt.type 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> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_9]]] = %[[VAL_7]] : <2,2 x !felt.type>, <2 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.insert %[[VAL_2]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_16]], %[[VAL_18]]] : <2,2 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_19]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Caller<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[]>> +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = function.call @lookup() : () -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_21]][@out] = %[[VAL_22]] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_21]] : !struct.type<@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[]>>, %[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_25:[0-9a-zA-Z_\.]+]] = function.call @lookup() : () -> !felt.type +// CHECK-DAG: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_23]][@out] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_26]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..5f73882a8 --- /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 -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.main = !struct.type<@Main<[]>>, veridise.lang = "llzk"} { 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..602583865 --- /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 -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.circom b/circom/tests/arrays/array_size_mismatch_store.circom new file mode 100644 index 000000000..b2659c0d0 --- /dev/null +++ b/circom/tests/arrays/array_size_mismatch_store.circom @@ -0,0 +1,75 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 assigned 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.main = !struct.type<@ImplicitExtension<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ImplicitExtension<[]> { +// CHECK-NEXT: struct.field @out : !array.type<10 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ImplicitExtension<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ImplicitExtension<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<10 x !felt.type> +// 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> +// 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> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_11]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_16]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_21]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_24]]] = %[[VAL_22]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_26]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_29]]] = %[[VAL_27]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_1]] : <@ImplicitExtension<[]>>, !array.type<10 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ImplicitExtension<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@ImplicitExtension<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_30]][@out] : <@ImplicitExtension<[]>>, !array.type<10 x !felt.type> +// 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]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]], %[[VAL_31]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 98 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 97 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 96 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 95 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]] : <5 x !felt.type> +// CHECK-NEXT: function.return +// 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..c4c1139ea --- /dev/null +++ b/circom/tests/arrays/array_write1_loop.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array1<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array1<[]> { +// CHECK-NEXT: struct.field @out : !array.type<5,1 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Array1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Array1<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<5,1 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_8]], %[[VAL_10]]] = %[[VAL_7]] : <5,1 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_1]] : <@Array1<[]>>, !array.type<5,1 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Array1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@Array1<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@out] : <@Array1<[]>>, !array.type<5,1 x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_16]], %[[VAL_17]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_20]]{{\[}}%[[VAL_21]], %[[VAL_23]]] : <5,1 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_19]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..3defabbc7 --- /dev/null +++ b/circom/tests/arrays/array_write2_loop.circom @@ -0,0 +1,97 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array1<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array1<[]> { +// CHECK-NEXT: struct.field @out : !array.type<5,2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Array1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Array1<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<5,2 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_7]] +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_8]], %[[VAL_10]]] = %[[VAL_7]] : <5,2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_17]]) %[[VAL_15]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_19]], %[[VAL_21]]] = %[[VAL_18]] : <5,2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_1]] : <@Array1<[]>>, !array.type<5,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Array1<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@Array1<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_24]][@out] : <@Array1<[]>>, !array.type<5,2 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_27]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_32]], %[[VAL_34]]] : <5,2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_35]], %[[VAL_30]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_36]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_37]] : !felt.type +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_40]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_46]] +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_45]], %[[VAL_47]]] : <5,2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_48]], %[[VAL_43]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_43]], %[[VAL_49]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_50]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..dd6b26eaa --- /dev/null +++ b/circom/tests/arrays/array_write3_loop.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { 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..12e299158 --- /dev/null +++ b/circom/tests/arrays/array_write_in_function.circom @@ -0,0 +1,69 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type> +// 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>, <1 x !felt.type> +// 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>, <1 x !felt.type> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_7]], %[[VAL_9]]] = %[[VAL_0]] : <2,1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_11]], %[[VAL_13]]] : <2,1 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]], %[[VAL_20]]] = %[[VAL_16]] : <2,1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_22]], %[[VAL_24]]] : <2,1 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Caller<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[]>> +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @compute(%[[VAL_26]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_27]][@out] = %[[VAL_28]] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_27]] : !struct.type<@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[]>>, %[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_31:[0-9a-zA-Z_\.]+]] = function.call @compute(%[[VAL_30]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_29]][@out] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_32]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..7b62f3f2e --- /dev/null +++ b/circom/tests/arrays/basic_array_00.circom @@ -0,0 +1,43 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Array00<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Array00<[]> { +// CHECK-NEXT: struct.field @b : !array.type<1 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<1 x !felt.type>) -> !struct.type<@Array00<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Array00<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1 x !felt.type> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_4]]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_2]] : <@Array00<[]>>, !array.type<1 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Array00<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@Array00<[]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !array.type<1 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@b] : <@Array00<[]>>, !array.type<1 x !felt.type> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_11]]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_13]]{{\[}}%[[VAL_15]]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_16]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..dc8380f7a --- /dev/null +++ b/circom/tests/arrays/basic_array_01.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/arrays/basic_array_02.circom b/circom/tests/arrays/basic_array_02.circom new file mode 100644 index 000000000..781212e06 --- /dev/null +++ b/circom/tests/arrays/basic_array_02.circom @@ -0,0 +1,44 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/arrays/basic_array_03.circom b/circom/tests/arrays/basic_array_03.circom new file mode 100644 index 000000000..07ee2f152 --- /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 -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..75641df41 --- /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 -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..7e449afad --- /dev/null +++ b/circom/tests/arrays/dimension_handling.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { diff --git a/circom/tests/arrays/multi_dim_arrays.circom b/circom/tests/arrays/multi_dim_arrays.circom new file mode 100644 index 000000000..286909bf8 --- /dev/null +++ b/circom/tests/arrays/multi_dim_arrays.circom @@ -0,0 +1,88 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Arrays<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Arrays<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Arrays<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type> +// 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>, <1 x !felt.type> +// 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>, <1 x !felt.type> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = array.new : <3,2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// 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> +// 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> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Arrays<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.new : <2,1 x !felt.type> +// 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>, <1 x !felt.type> +// 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>, <1 x !felt.type> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.new : <3,2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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>, <2,1 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// 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> +// 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> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// CHECK-NEXT: function.return +// 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..f87681020 --- /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 -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>, veridise.lang = "llzk"} { diff --git a/circom/tests/arrays/unknown_index_constrained.circom b/circom/tests/arrays/unknown_index_constrained.circom new file mode 100644 index 000000000..cf7ec9f8c --- /dev/null +++ b/circom/tests/arrays/unknown_index_constrained.circom @@ -0,0 +1,71 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { +// CHECK: struct.def @Num2Bits 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..f0c7b9eea --- /dev/null +++ b/circom/tests/arrays/unknown_index_load_store.circom @@ -0,0 +1,116 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownIndexLoadStore<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownIndexLoadStore<[]> { +// CHECK-NEXT: struct.field @out : !array.type<8 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownIndexLoadStore<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexLoadStore<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<8 x !felt.type> +// 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> +// 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> +// 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> +// 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> +// 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> +// 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> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_27]]{{\[}}%[[VAL_42]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_44]]] = %[[VAL_43]] : <8 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@UnknownIndexLoadStore<[]>>, !array.type<8 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexLoadStore<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexLoadStore<[]>>, %[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_45]][@out] : <@UnknownIndexLoadStore<[]>>, !array.type<8 x !felt.type> +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]], %[[VAL_47]] : <9 x !felt.type> +// 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_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_49]], %[[VAL_50]], %[[VAL_51]], %[[VAL_52]], %[[VAL_53]], %[[VAL_54]], %[[VAL_55]], %[[VAL_56]], %[[VAL_57]] : <9 x !felt.type> +// 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> +// 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> +// 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]], %[[VAL_72]] : <11 x !felt.type> +// 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_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_85:[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]], %[[VAL_84]] : <11 x !felt.type> +// CHECK-NEXT: function.return +// 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..5fa31700f --- /dev/null +++ b/circom/tests/arrays/unknown_index_overwrites_known_A.circom @@ -0,0 +1,97 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownIndexOverwriteKnown<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownIndexOverwriteKnown<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownIndexOverwriteKnown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_16]]] = %[[VAL_15]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_18]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_23]]] : <10 x !felt.type>, !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_26]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexOverwriteKnown<[]>>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_27]][@out] : <@UnknownIndexOverwriteKnown<[]>>, !felt.type +// 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> +// 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 2 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 9 +// 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> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_28]] +// CHECK-NEXT: array.write %[[VAL_41]]{{\[}}%[[VAL_43]]] = %[[VAL_42]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_45]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_46]], %[[VAL_47]]) +// CHECK-NEXT: bool.assert %[[VAL_48]], "assertion failed" +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_50]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_51]], %[[VAL_52]]) +// CHECK-NEXT: bool.assert %[[VAL_53]], "assertion failed" +// CHECK-NEXT: function.return +// 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..04eae6a18 --- /dev/null +++ b/circom/tests/arrays/unknown_index_overwrites_known_B.circom @@ -0,0 +1,210 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownIndexOverwriteKnown<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownIndexOverwriteKnown<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownIndexOverwriteKnown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// 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> +// 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> +// 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> +// 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> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: array.write %[[VAL_28]]{{\[}}%[[VAL_43]]] = %[[VAL_42]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_46]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_51]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_56]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_15]]{{\[}}%[[VAL_61]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_66]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_41]]{{\[}}%[[VAL_71]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_76]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexOverwriteKnown<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_77:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexOverwriteKnown<[]>>, %[[VAL_78:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_77]][@out] : <@UnknownIndexOverwriteKnown<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 45 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]], %[[VAL_80]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_85:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_82]], %[[VAL_83]], %[[VAL_84]], %[[VAL_85]], %[[VAL_86]], %[[VAL_87]], %[[VAL_88]], %[[VAL_89]], %[[VAL_90]], %[[VAL_91]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]], %[[VAL_93]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_96:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_100:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_101:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = felt.const 17 +// CHECK-NEXT: %[[VAL_103:[0-9a-zA-Z_\.]+]] = felt.const 18 +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_95]], %[[VAL_96]], %[[VAL_97]], %[[VAL_98]], %[[VAL_99]], %[[VAL_100]], %[[VAL_101]], %[[VAL_102]], %[[VAL_103]], %[[VAL_104]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]], %[[VAL_106]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = felt.const 21 +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 22 +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = felt.const 23 +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = felt.const 24 +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.const 25 +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = felt.const 26 +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 28 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = felt.const 29 +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_108]], %[[VAL_109]], %[[VAL_110]], %[[VAL_111]], %[[VAL_112]], %[[VAL_113]], %[[VAL_114]], %[[VAL_115]], %[[VAL_116]], %[[VAL_117]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_120:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_78]] +// CHECK-NEXT: array.write %[[VAL_105]]{{\[}}%[[VAL_120]]] = %[[VAL_119]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_121:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_122:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_123:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_122]] +// CHECK-NEXT: %[[VAL_124:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_123]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.const 19 +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_124]], %[[VAL_125]]) +// CHECK-NEXT: bool.assert %[[VAL_126]], "assertion failed" +// CHECK-NEXT: %[[VAL_127:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_128:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_127]] +// CHECK-NEXT: %[[VAL_129:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_128]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_129]], %[[VAL_130]]) +// CHECK-NEXT: bool.assert %[[VAL_131]], "assertion failed" +// CHECK-NEXT: %[[VAL_132:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_133:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_132]] +// CHECK-NEXT: %[[VAL_134:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_92]]{{\[}}%[[VAL_133]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_135:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_136:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_134]], %[[VAL_135]]) +// CHECK-NEXT: bool.assert %[[VAL_136]], "assertion failed" +// CHECK-NEXT: %[[VAL_137:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_138:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_137]] +// CHECK-NEXT: %[[VAL_139:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_92]]{{\[}}%[[VAL_138]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_140:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_141:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_139]], %[[VAL_140]]) +// CHECK-NEXT: bool.assert %[[VAL_141]], "assertion failed" +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_142]] +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_118]]{{\[}}%[[VAL_143]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_146:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_144]], %[[VAL_145]]) +// CHECK-NEXT: bool.assert %[[VAL_146]], "assertion failed" +// CHECK-NEXT: %[[VAL_147:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_147]] +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_118]]{{\[}}%[[VAL_148]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = felt.const 27 +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_149]], %[[VAL_150]]) +// CHECK-NEXT: bool.assert %[[VAL_151]], "assertion failed" +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.const 46 +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_121]], %[[VAL_152]]) +// CHECK-NEXT: bool.assert %[[VAL_153]], "assertion failed" +// CHECK-NEXT: function.return +// 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..f79514856 --- /dev/null +++ b/circom/tests/arrays/unknown_index_store.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownIndexStore<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownIndexStore<[]> { +// CHECK-NEXT: struct.field @out : !array.type<8 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownIndexStore<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownIndexStore<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<8 x !felt.type> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_4]]] = %[[VAL_3]] : <8 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@UnknownIndexStore<[]>>, !array.type<8 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndexStore<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndexStore<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_5]][@out] : <@UnknownIndexStore<[]>>, !array.type<8 x !felt.type> +// CHECK-NEXT: function.return +// 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..8ee0323aa --- /dev/null +++ b/circom/tests/arrays/unknown_index_unconstrained.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownIndex<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownIndex<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_14]]{{\[}}%[[VAL_15]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_16]] : <@UnknownIndex<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownIndex<[]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@out] : <@UnknownIndex<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]], %[[VAL_19]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// 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_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_24]], %[[VAL_25]], %[[VAL_26]], %[[VAL_27]], %[[VAL_28]], %[[VAL_29]], %[[VAL_30]] : <10 x !felt.type> +// CHECK-NEXT: function.return +// 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..a26fd89e5 --- /dev/null +++ b/circom/tests/calls/basic_call_01.circom @@ -0,0 +1,69 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 A should happen here + y <== a.x; +} + +component main = Call1(); + +// CHECK-LABEL: module attributes {llzk.main = !struct.type<@Call1<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @B<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[]>> +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_0]], %[[V_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_2]][@x] = %[[V_3]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_2]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_4:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[V_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_5]], %[[V_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[V_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_4]][@x] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_8]], %[[V_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Call1<[]> { +// CHECK-NEXT: struct.field @y : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @a : !struct.type<@B<[]>> +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_10:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Call1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = struct.new : <@Call1<[]>> +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = function.call @B::@compute(%[[V_9]], %[[V_10]]) : (!felt.type, !felt.type) -> !struct.type<@B<[]>> +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_13]][@x] : <@B<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_11]][@y] = %[[V_14]] : <@Call1<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_11]][@a] = %[[V_13]] : <@Call1<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.return %[[V_11]] : !struct.type<@Call1<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_15:[0-9a-zA-Z_\.]+]]: !struct.type<@Call1<[]>>, %[[V_16:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_17:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_15]][@y] : <@Call1<[]>>, !felt.type +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_15]][@a] : <@Call1<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.call @B::@constrain(%[[V_18]], %[[V_16]], %[[V_17]]) : (!struct.type<@B<[]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_18]][@x] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_21]], %[[V_20]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..0a7ef055f --- /dev/null +++ b/circom/tests/calls/basic_call_02.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @nbits(%[[V_A:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_N2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_N3]], %[[V_R3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_3]]#1 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Call2<[]> { +// CHECK-NEXT: struct.field @y : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_15:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Call2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = struct.new : <@Call2<[]>> +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[V_15]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_16]][@y] = %[[V_17]] : <@Call2<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_16]] : !struct.type<@Call2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_18:[0-9a-zA-Z_\.]+]]: !struct.type<@Call2<[]>>, %[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_18]][@y] : <@Call2<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..8d6c85358 --- /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 -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..3acb2a6a9 --- /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 -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..e62601e8a --- /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 -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..30ce27643 --- /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 -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..e10027368 --- /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 -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..b03752f0a --- /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 -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..caf00572e --- /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 -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..257b7938e --- /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 -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..d4c5d63d2 --- /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 -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..8c118ee47 --- /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 -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..23e5d21a8 --- /dev/null +++ b/circom/tests/calls/call_arg_scalar_multiple.circom @@ -0,0 +1,47 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_1]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]], %[[VAL_2]], %[[VAL_3]]) : (!felt.type, !felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// 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_\.]+]] = function.call @f(%[[VAL_6]], %[[VAL_7]], %[[VAL_8]]) : (!felt.type, !felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: function.return +// 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..e0903e975 --- /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 -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..d3739258f --- /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 -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..90e616099 --- /dev/null +++ b/circom/tests/calls/call_diff_type_per_call.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CallDiffTypeTest<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @CallDiffTypeTest<[]> { +// CHECK-NEXT: struct.field @out1 : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @out2 : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@CallDiffTypeTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@CallDiffTypeTest<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]], %[[VAL_1]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out1] = %[[VAL_3]] : <@CallDiffTypeTest<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]], %[[VAL_1]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out2] = %[[VAL_5]] : <@CallDiffTypeTest<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@CallDiffTypeTest<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@CallDiffTypeTest<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@out1] : <@CallDiffTypeTest<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@out2] : <@CallDiffTypeTest<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_7]], %[[VAL_8]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_11]], %[[VAL_8]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..fa5040df8 --- /dev/null +++ b/circom/tests/calls/call_in_function.circom @@ -0,0 +1,50 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CallInFnTest<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @passthrough( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// CHECK-LABEL: function.def @sum( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @passthrough(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @passthrough(%[[VAL_1]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_2]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @CallInFnTest<[]> { +// CHECK-NEXT: struct.field @z : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@CallInFnTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@CallInFnTest<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @sum(%[[VAL_0]], %[[VAL_1]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@z] = %[[VAL_3]] : <@CallInFnTest<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@CallInFnTest<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@CallInFnTest<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@z] : <@CallInFnTest<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..558856084 --- /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 -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..092442e16 --- /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 -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..99efb486d --- /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 -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..ebb630553 --- /dev/null +++ b/circom/tests/calls/call_ret_scalar.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CallRetTest<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @sum( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @CallRetTest<[]> { +// CHECK-NEXT: struct.field @y : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@CallRetTest<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CallRetTest<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @sum(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %self[@y] = %[[VAL_2]] : <@CallRetTest<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CallRetTest<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@CallRetTest<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@y] : <@CallRetTest<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..03766084f --- /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 -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..ad3bf4549 --- /dev/null +++ b/circom/tests/calls/circompairing_bigint.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..903f9b40b --- /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 -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..5fb181a29 --- /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 -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..0c4d55bd9 --- /dev/null +++ b/circom/tests/calls/recurse_known.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@FnAssign<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @Recurse( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_0]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_1]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_0]], %[[VAL_11]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @FnAssign<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@FnAssign<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@FnAssign<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_0]], %[[VAL_2]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_3]] : <@FnAssign<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@FnAssign<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@FnAssign<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@outp] : <@FnAssign<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 20 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @Recurse(%[[VAL_5]], %[[VAL_6]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1e2c08f4b --- /dev/null +++ b/circom/tests/calls/recurse_unknown.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @factorial(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// 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]]) +// 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) { +// 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 +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_7]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_7]]#1 : !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = function.call @factorial(%[[VAL_13]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_11]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Caller<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @factorial(%[[VAL_16]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_17]][@outp] = %[[VAL_18]] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_17]] : !struct.type<@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[]>>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_19]][@outp] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..46c63bfcf --- /dev/null +++ b/circom/tests/calls/return_intermediate.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Foo<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @Fn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Foo<[]> { +// CHECK-NEXT: struct.field @outp : !array.type<1 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_3:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>) -> !struct.type<@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1 x !felt.type> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_7]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_10]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = function.call @Fn(%[[VAL_8]], %[[VAL_11]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_5]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_4]][@outp] = %[[VAL_5]] : <@Foo<[]>>, !array.type<1 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_4]] : !struct.type<@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_15]][@outp] : <@Foo<[]>>, !array.type<1 x !felt.type> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_18]] +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_19]]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_22]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_25]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @Fn(%[[VAL_23]], %[[VAL_26]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1a397715f --- /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 -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.main = !struct.type<@Multiplier2<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Multiplier2<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..fbd8723c2 --- /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 -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.main = !struct.type<@Multiplier2<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Multiplier2<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..ebba24da2 --- /dev/null +++ b/circom/tests/circom_doc_examples/03.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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); + } + else{ + a = A(0); + } + a.in <== 1; + a.out ==> out; +} + +component main = B(1); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/04.circom b/circom/tests/circom_doc_examples/04.circom new file mode 100644 index 000000000..a90645f4c --- /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 -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>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Example<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Example<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Example<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Example<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Example<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @UsingExample<[]> { +// CHECK-NEXT: struct.field @example : !struct.type<@Example<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@UsingExample<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@UsingExample<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @Example::@compute() : () -> !struct.type<@Example<[]>> +// CHECK-NEXT: struct.writef %[[VAL_2]][@example] = %[[VAL_3]] : <@UsingExample<[]>>, !struct.type<@Example<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@UsingExample<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@UsingExample<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@example] : <@UsingExample<[]>>, !struct.type<@Example<[]>> +// CHECK-NEXT: function.call @Example::@constrain(%[[VAL_5]]) : (!struct.type<@Example<[]>>) -> () +// CHECK-NEXT: function.return +// 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..ec46ef392 --- /dev/null +++ b/circom/tests/circom_doc_examples/06.circom @@ -0,0 +1,81 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @example(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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) { +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[V_4]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[V_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_3]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.def @nbits(%[[V_6:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_N2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_N3]], %[[V_R3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_9]]#1 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Caller<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_21:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[]>> +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[V_21]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = function.call @example(%[[V_23]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_22]][@out] = %[[V_24]] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_22]] : !struct.type<@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_25:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[]>>, %[[V_26:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_25]][@out] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = function.call @nbits(%[[V_26]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = function.call @example(%[[V_27]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[V_29]], %[[V_28]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..5e8a713e2 --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_3]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..35b5cb1d3 --- /dev/null +++ b/circom/tests/circom_doc_examples/08.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@IsZero<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @IsZero<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @inv : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@inv] = %[[VAL_4]] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_4]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_11]] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@out] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@inv] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_13]] : !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_15]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_17]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_19]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_13]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..e3810ad19 --- /dev/null +++ b/circom/tests/circom_doc_examples/09.circom @@ -0,0 +1,88 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Num2Bits<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Num2Bits<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type {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 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_26]][@out] : <@Num2Bits<[@n]>>, !array.type<@n x !felt.type> +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..63f505a20 --- /dev/null +++ b/circom/tests/circom_doc_examples/10.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T14<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @T14<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@T14<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (!felt.type, !felt.type) { +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_10]] : <@T14<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T14<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@T14<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@out] : <@T14<[]>>, !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_16]] -> (!felt.type, !felt.type) { +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_19]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]]#0, %[[VAL_17]]#1 : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..22b716d21 --- /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 -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.main = !struct.type<@T15<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @T15<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@T15<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_i_3:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_y_3:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_11]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_5]]#1 : <@T15<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T15<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@T15<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_15]][@out] : <@T15<[]>>, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_i_3:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_y_3:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_28]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_29]], %[[VAL_20]]#1 : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..52b5de8f4 --- /dev/null +++ b/circom/tests/circom_doc_examples/12.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T16<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @T16<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@T16<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) +// CHECK-SAME: : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_3]]) +// CHECK-NEXT: scf.condition(%[[VAL_4]]) %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_5]]#1 : <@T16<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@T16<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@T16<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@out] : <@T16<[]>>, !felt.type +// 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_19:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_15]]) +// CHECK-SAME: : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_20]], %[[VAL_17]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_25]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_19]]#1 : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..4db036fc2 --- /dev/null +++ b/circom/tests/circom_doc_examples/13.circom @@ -0,0 +1,48 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@right<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @right<[@N]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@right<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@right<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@right<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@right<[@N]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..a5f626eab --- /dev/null +++ b/circom/tests/circom_doc_examples/14.circom @@ -0,0 +1,52 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@right<[10, 5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @right<[@N1, @N2]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@right<[@N1, @N2]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@right<[@N1, @N2]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N1 : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N2 : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_6]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@right<[@N1, @N2]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@right<[@N1, @N2]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @N1 : !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @N2 : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_15]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_16]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..aa6c327cb --- /dev/null +++ b/circom/tests/circom_doc_examples/15.circom @@ -0,0 +1,26 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/circom_doc_examples/16.circom b/circom/tests/circom_doc_examples/16.circom new file mode 100644 index 000000000..1bc4a821a --- /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 -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..3601aa592 --- /dev/null +++ b/circom/tests/circom_doc_examples/17.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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..75a2bac25 --- /dev/null +++ b/circom/tests/circom_doc_examples/18.circom @@ -0,0 +1,74 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_4]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type +// CHECK-NEXT: struct.field @temp_a : !struct.type<@A<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_15]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_18]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_16]], %[[VAL_19]]) : (!felt.type, !felt.type) -> !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_20]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@out] = %[[VAL_21]] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@temp_a] = %[[VAL_20]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[@n]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@out] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@temp_a] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_27]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_30]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_25]], %[[VAL_28]], %[[VAL_31]]) : (!struct.type<@A<[@n]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_32]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..e1fb72688 --- /dev/null +++ b/circom/tests/circom_doc_examples/19.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_4]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type +// CHECK-NEXT: struct.field @[[TEMP:[0-9a-zA-Z_\.]+]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_15]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_18]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_16]], %[[VAL_19]]) : (!felt.type, !felt.type) -> !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_20]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@out] = %[[VAL_21]] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@[[TEMP]]] = %[[VAL_20]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[@n]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@out] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@[[TEMP]]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_27]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_30]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_25]], %[[VAL_28]], %[[VAL_31]]) : (!struct.type<@A<[@n]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_32]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..653cea6e8 --- /dev/null +++ b/circom/tests/circom_doc_examples/20.circom @@ -0,0 +1,70 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_4]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type +// CHECK-NEXT: struct.field @[[TEMP:[0-9a-zA-Z_\.]+]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_15]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_17]] +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_18]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_16]], %[[VAL_19]]) : (!felt.type, !felt.type) -> !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_20]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@out] = %[[VAL_21]] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@[[TEMP]]] = %[[VAL_20]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[@n]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@out] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@[[TEMP]]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_27]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_23]]{{\[}}%[[VAL_30]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_25]], %[[VAL_28]], %[[VAL_31]]) : (!struct.type<@A<[@n]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_32]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..b850313b3 --- /dev/null +++ b/circom/tests/circom_doc_examples/21.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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]; + out <== in; +} + +component main = Ex(3,3); + +// CHECK-LABEL: module attributes { diff --git a/circom/tests/circom_doc_examples/22.circom b/circom/tests/circom_doc_examples/22.circom new file mode 100644 index 000000000..650dd0a70 --- /dev/null +++ b/circom/tests/circom_doc_examples/22.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Ex<[3, 3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Ex<[@n, @m]> { +// CHECK-NEXT: struct.field @out : !array.type<@m x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Ex<[@n, @m]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Ex<[@n, @m]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@m x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@m x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_4]] : <@Ex<[@n, @m]>>, !array.type<@m x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Ex<[@n, @m]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !struct.type<@Ex<[@n, @m]>>, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_15]][@out] : <@Ex<[@n, @m]>>, !array.type<@m x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_21]], %[[VAL_17]]) +// CHECK-NEXT: scf.condition(%[[VAL_22]]) %[[VAL_21]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_24]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_27]]] : <@m x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_28]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_29]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_30]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..b54eb24c4 --- /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 -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..136e76639 --- /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 -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..079818898 --- /dev/null +++ b/circom/tests/circom_doc_examples/25.circom @@ -0,0 +1,77 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @d : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@d] = %[[VAL_6]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_15]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[@n]> { +// CHECK-NEXT: struct.field @[[TEMP:[0-9a-zA-Z_\.]+]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[@n]>> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_20]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_22]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_23]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_16]]{{\[}}%[[VAL_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_21]], %[[VAL_24]], %[[VAL_27]]) : (!felt.type, !felt.type, !felt.type) -> !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_17]][@[[TEMP]]] = %[[VAL_28]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_17]] : !struct.type<@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[@n]>>, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_30]][@[[TEMP]]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_34]] +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_35]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_38]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_41]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_33]], %[[VAL_36]], %[[VAL_39]], %[[VAL_42]]) : (!struct.type<@A<[@n]>>, !felt.type, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_33]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return +// 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..8212c7d55 --- /dev/null +++ b/circom/tests/circom_doc_examples/26.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @b : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @d : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@A<[@n]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@c] = %[[VAL_5]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@d] = %[[VAL_8]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@b] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_10]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_13]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_16]], %[[VAL_15]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_10]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_17]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[@n]> { +// CHECK-NEXT: struct.field @out1 : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @[[TEMP:[0-9a-zA-Z_\.]+]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@B<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[@n]>> +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_21]]) : (!felt.type) -> !struct.type<@A<[@n]>> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_24]][@b] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_24]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_22]][@out1] = %[[VAL_26]] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_24]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_22]][@[[TEMP]]] = %[[VAL_24]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.return %[[VAL_22]] : !struct.type<@B<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[@n]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@out1] : <@B<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@[[TEMP]]] : <@B<[@n]>>, !struct.type<@A<[@n]>> +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_31]], %[[VAL_29]]) : (!struct.type<@A<[@n]>>, !felt.type) -> () +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_31]][@b] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_31]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_33]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_31]][@d] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return +// 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..12d95a8f3 --- /dev/null +++ b/circom/tests/circom_doc_examples/27.circom @@ -0,0 +1,101 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @b : !struct.type<@Bits2Num<[10]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @Bits2Num::@compute(%[[VAL_0]]) : (!array.type<10 x !felt.type>) -> !struct.type<@Bits2Num<[10]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@out] : <@Bits2Num<[10]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_2]] : <@A<[]>>, !struct.type<@Bits2Num<[10]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@b] : <@A<[]>>, !struct.type<@Bits2Num<[10]>> +// CHECK-NEXT: function.call @Bits2Num::@constrain(%[[VAL_6]], %[[VAL_5]]) : (!struct.type<@Bits2Num<[10]>>, !array.type<10 x !felt.type>) -> () +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@out] : <@Bits2Num<[10]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Bits2Num<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Bits2Num<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.new : <@Bits2Num<[@n]>> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_16:[0-9a-zA-Z_\.]+]] = %[[VAL_13]], %[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_14]], %[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_12]]) : (!felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_11]]) +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_16]], %[[VAL_17]], %[[VAL_18]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_9]]{{\[}}%[[VAL_23]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_24]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_20]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_28]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]], %[[VAL_29]], %[[VAL_26]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_10]][@out] = %[[VAL_15]]#2 : <@Bits2Num<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_10]] : !struct.type<@Bits2Num<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@Bits2Num<[@n]>>, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_30]][@out] : <@Bits2Num<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_34]], %[[VAL_38:[0-9a-zA-Z_\.]+]] = %[[VAL_35]], %[[VAL_39:[0-9a-zA-Z_\.]+]] = %[[VAL_33]]) : (!felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_38]], %[[VAL_32]]) +// CHECK-NEXT: scf.condition(%[[VAL_40]]) %[[VAL_37]], %[[VAL_38]], %[[VAL_39]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_42]] +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_44]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_45]], %[[VAL_34]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_43]], %[[VAL_46]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_41]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_42]], %[[VAL_49]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_48]], %[[VAL_50]], %[[VAL_47]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_51]], %[[VAL_36]]#2 : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..d3424f9df --- /dev/null +++ b/circom/tests/circom_doc_examples/28.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @intermediate : !felt.type +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: struct.writef %[[VAL_1]][@intermediate] = %[[VAL_0]] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_0]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@intermediate] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_4]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Caller<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @a : !struct.type<@A<[]>> +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Caller<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[]>> +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @A::@compute(%[[VAL_6]]) : (!felt.type) -> !struct.type<@A<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_7]][@out] = %[[VAL_9]] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_7]][@a] = %[[VAL_8]] : <@Caller<[]>>, !struct.type<@A<[]>> +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@Caller<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_10]][@out] : <@Caller<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_10]][@a] : <@Caller<[]>>, !struct.type<@A<[]>> +// CHECK-NEXT: function.call @A::@constrain(%[[VAL_12]], %[[VAL_11]]) : (!struct.type<@A<[]>>, !felt.type) -> () +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..fbfb39d05 --- /dev/null +++ b/circom/tests/circom_doc_examples/29.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@IsZero<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @IsZero<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @inv : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@IsZero<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@inv] = %[[VAL_4]] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_4]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_11]] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@IsZero<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@IsZero<[]>>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@out] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_12]][@inv] : <@IsZero<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_13]] : !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_15]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_17]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_19]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_13]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..409552435 --- /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 -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..25d776893 --- /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 -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..fd50be343 --- /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 -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..9df5b58ed --- /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 -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..b83ede5d1 --- /dev/null +++ b/circom/tests/circom_doc_examples/35A.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[0]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..496250fe3 --- /dev/null +++ b/circom/tests/circom_doc_examples/35B.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[1]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_4]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_9]], "assertion failed" +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_6]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..5af1d4035 --- /dev/null +++ b/circom/tests/circom_doc_examples/36.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Translate<[1]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Translate<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Translate<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Translate<[@n]>> +// CHECK-NEXT: %[[VAL_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 254 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_0]], %[[VAL_2]]) +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Translate<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Translate<[@n]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 254 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_5]], %[[VAL_6]]) +// CHECK-NEXT: bool.assert %[[VAL_7]], "assertion failed" +// CHECK-NEXT: function.return +// 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..eeac70ad6 --- /dev/null +++ b/circom/tests/circom_doc_examples/37.circom @@ -0,0 +1,80 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Multiplier3<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Multiplier2<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Multiplier2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_3]] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier2<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @Multiplier3<[]> { +// CHECK-DAG: struct.field @out : !felt.type {llzk.pub} +// CHECK-DAG: struct.field @mult2 : !struct.type<@Multiplier2<[]>> +// CHECK-DAG: struct.field @mult1 : !struct.type<@Multiplier2<[]>> +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Multiplier3<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@Multiplier3<[]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@compute(%[[VAL_9]], %[[VAL_10]]) : (!felt.type, !felt.type) -> !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @Multiplier2::@compute(%[[VAL_14]], %[[VAL_11]]) : (!felt.type, !felt.type) -> !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_15]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@out] = %[[VAL_16]] : <@Multiplier3<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[VAL_12]][@mult1] = %[[VAL_13]] : <@Multiplier3<[]>>, !struct.type<@Multiplier2<[]>> +// CHECK-DAG: struct.writef %[[VAL_12]][@mult2] = %[[VAL_15]] : <@Multiplier3<[]>>, !struct.type<@Multiplier2<[]>> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@Multiplier3<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@Multiplier3<[]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@mult2] : <@Multiplier3<[]>>, !struct.type<@Multiplier2<[]>> +// CHECK-DAG: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@mult1] : <@Multiplier3<[]>>, !struct.type<@Multiplier2<[]>> +// CHECK-DAG: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@out] : <@Multiplier3<[]>>, !felt.type +// CHECK-NEXT: function.call @Multiplier2::@constrain(%[[VAL_22]], %[[VAL_18]], %[[VAL_19]]) : (!struct.type<@Multiplier2<[]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: function.call @Multiplier2::@constrain(%[[VAL_21]], %[[VAL_23]], %[[VAL_20]]) : (!struct.type<@Multiplier2<[]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_21]][@out] : <@Multiplier2<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_25]], %[[VAL_24]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..2bbf8e2fc --- /dev/null +++ b/circom/tests/circom_doc_examples/38.circom @@ -0,0 +1,57 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/circom_doc_examples/39A.circom b/circom/tests/circom_doc_examples/39A.circom new file mode 100644 index 000000000..7eeedc756 --- /dev/null +++ b/circom/tests/circom_doc_examples/39A.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[3]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[@n]> { +// CHECK-DAG: struct.field @aux : !felt.type +// CHECK-DAG: struct.field @out : !felt.type +// CHECK-DAG: struct.field @[[B:[0-9a-zA-Z_\.]+]] : !struct.type<@B<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !struct.type<@B<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_4]]) +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_5]] -> (!struct.type<@B<[]>>, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @B::@compute(%[[VAL_7]]) : (!felt.type) -> !struct.type<@B<[]>> +// CHECK-NEXT: struct.writef %[[VAL_0]][@aux] = %[[VAL_7]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_9]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_8]], %[[VAL_7]], %[[VAL_9]] : !struct.type<@B<[]>>, !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_10]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_3]], %[[VAL_2]], %[[VAL_10]] : !struct.type<@B<[]>>, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@[[B]]] = %[[VAL_6]]#0 : <@A<[@n]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@aux] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@out] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@[[B]]] : <@A<[@n]>>, !struct.type<@B<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_12]], %[[VAL_15]]) +// CHECK-NEXT: scf.if %[[VAL_16]] { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: constrain.eq %[[VAL_19]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: function.call @B::@constrain(%[[VAL_14]], %[[VAL_19]]) : (!struct.type<@B<[]>>, !felt.type) -> () +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_21]], %[[VAL_20]] : !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: constrain.eq %[[VAL_21]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @B<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[]>> +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_24]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_25]][@out] = %[[VAL_27]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_25]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_29]], %[[VAL_30]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_32]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..b60d0e397 --- /dev/null +++ b/circom/tests/circom_doc_examples/39B.circom @@ -0,0 +1,95 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @aux : !felt.type +// CHECK-NEXT: struct.field @out : !felt.type +// CHECK-NEXT: struct.field @[[B:[0-9a-zA-Z_\.]+]] : !struct.type<@B<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !struct.type<@B<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_1]], %[[VAL_3]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_4]] -> (!struct.type<@B<[]>>, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @B::@compute(%[[VAL_6]]) : (!felt.type) -> !struct.type<@B<[]>> +// CHECK-NEXT: struct.writef %[[VAL_0]][@aux] = %[[VAL_6]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_8]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_7]], %[[VAL_6]], %[[VAL_8]] : !struct.type<@B<[]>>, !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@aux] = %[[VAL_9]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_10]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_2]], %[[VAL_9]], %[[VAL_10]] : !struct.type<@B<[]>>, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@[[B]]] = %[[VAL_5]]#0 : <@A<[@n]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@aux] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@out] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@[[B]]] : <@A<[@n]>>, !struct.type<@B<[]>> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_12]], %[[VAL_14]]) +// CHECK-NEXT: scf.if %[[VAL_15]] { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_17]] : !felt.type, !felt.type +// CHECK-NEXT: function.call @B::@constrain(%[[VAL_13]], %[[VAL_18]]) : (!struct.type<@B<[]>>, !felt.type) -> () +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[]>> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_26]][@out] = %[[VAL_28]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_26]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_29]][@out] : <@B<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_30]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_33]], %[[VAL_32]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..f94b0fad3 --- /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 -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..84fa0585b --- /dev/null +++ b/circom/tests/circom_doc_examples/40A.circom @@ -0,0 +1,112 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@check_bits<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Num2Bits<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Num2Bits<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Num2Bits<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_9]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_13]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_19]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_14]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_12]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]], %[[VAL_22]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@Num2Bits<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !struct.type<@Num2Bits<[@n]>>, %[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_26]][@out] : <@Num2Bits<[@n]>>, !array.type<@n x !felt.type> +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_28]]) +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_34]], %[[VAL_35]], %[[VAL_36]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_39:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_40:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_41]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_43]]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_42]], %[[VAL_46]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_47]], %[[VAL_48]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_49]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_50]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_40]], %[[VAL_51]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_38]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_53]], %[[VAL_55]], %[[VAL_52]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_33]]#2, %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @check_bits<[@n]> { +// CHECK-NEXT: struct.field @check : !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@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<[@n]>> +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = function.call @Num2Bits::@compute(%[[VAL_56]]) : (!felt.type) -> !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: struct.writef %[[VAL_57]][@check] = %[[VAL_59]] : <@check_bits<[@n]>>, !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: function.return %[[VAL_57]] : !struct.type<@check_bits<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_60:[0-9a-zA-Z_\.]+]]: !struct.type<@check_bits<[@n]>>, %[[VAL_61:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_60]][@check] : <@check_bits<[@n]>>, !struct.type<@Num2Bits<[@n]>> +// CHECK-NEXT: function.call @Num2Bits::@constrain(%[[VAL_63]], %[[VAL_61]]) : (!struct.type<@Num2Bits<[@n]>>, !felt.type) -> () +// CHECK-NEXT: function.return +// 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..597e9e58c --- /dev/null +++ b/circom/tests/circom_doc_examples/40B.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/circom_doc_examples/40C.circom b/circom/tests/circom_doc_examples/40C.circom new file mode 100644 index 000000000..f105bceb7 --- /dev/null +++ b/circom/tests/circom_doc_examples/40C.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/circom_doc_examples/41.circom b/circom/tests/circom_doc_examples/41.circom new file mode 100644 index 000000000..1bbdcac50 --- /dev/null +++ b/circom/tests/circom_doc_examples/41.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/constraints/arr_cpy_store.circom b/circom/tests/constraints/arr_cpy_store.circom new file mode 100644 index 000000000..71c469041 --- /dev/null +++ b/circom/tests/constraints/arr_cpy_store.circom @@ -0,0 +1,69 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Foo<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Foo<[@N]> { +// CHECK-NEXT: struct.field @c : !struct.type<@Sum<[@N]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@Foo<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[@N]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @Sum::@compute(%[[VAL_0]]) : (!array.type<@N x !felt.type>) -> !struct.type<@Sum<[@N]>> +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_6]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_4]] : <@Foo<[@N]>>, !struct.type<@Sum<[@N]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Foo<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[@N]>>, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@c] : <@Foo<[@N]>>, !struct.type<@Sum<[@N]>> +// CHECK-NEXT: function.call @Sum::@constrain(%[[VAL_15]], %[[VAL_12]]) : (!struct.type<@Sum<[@N]>>, !array.type<@N x !felt.type>) -> () +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_17:[0-9a-zA-Z_\.]+]] = %[[VAL_14]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_17]], %[[VAL_17]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_17]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_19]], %[[VAL_20]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Sum<[@N]> { +// CHECK-NEXT: function.def @compute(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@Sum<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum<[@N]>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: function.return %[[VAL_23]] : !struct.type<@Sum<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum<[@N]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: function.return +// 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..3337d1149 --- /dev/null +++ b/circom/tests/constraints/known0.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@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, !felt.type +// CHECK-NEXT: function.return +// 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..1e5555964 --- /dev/null +++ b/circom/tests/constraints/known1.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @val : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@val] = %[[VAL_1]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@val] : <@A<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..ad8361345 --- /dev/null +++ b/circom/tests/constraints/known2.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @val : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@val] = %[[VAL_1]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@val] : <@A<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_3]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..54702a7c8 --- /dev/null +++ b/circom/tests/constraints/known3.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @val : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@val] = %[[VAL_1]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@val] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_3]], %[[VAL_4]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..c4702fe06 --- /dev/null +++ b/circom/tests/constraints/known4.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @val : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@val] = %[[VAL_1]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@val] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..350cb9909 --- /dev/null +++ b/circom/tests/constraints/known5.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @valA : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @valB : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_0]][@valA] = %[[VAL_1]] : <@A<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@valB] = %[[VAL_3]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@valA] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@valB] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..c88c40e1c --- /dev/null +++ b/circom/tests/constraints/missed_index_simple.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/constraints/with_call_scalars.circom b/circom/tests/constraints/with_call_scalars.circom new file mode 100644 index 000000000..1d5e84cb9 --- /dev/null +++ b/circom/tests/constraints/with_call_scalars.circom @@ -0,0 +1,75 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ComputeFee<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @feeShiftTable(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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> +// 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> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_0]] +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_5]]{{\[}}%[[VAL_6]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @ComputeFee<[]> { +// CHECK-NEXT: struct.field @feeOut : !array.type<2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ComputeFee<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.new : <@ComputeFee<[]>> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = function.call @feeShiftTable(%[[VAL_15]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: array.write %[[VAL_9]]{{\[}}%[[VAL_17]]] = %[[VAL_16]] : <2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_8]][@feeOut] = %[[VAL_9]] : <@ComputeFee<[]>>, !array.type<2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_8]] : !struct.type<@ComputeFee<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@ComputeFee<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_20]][@feeOut] : <@ComputeFee<[]>>, !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_23]], %[[VAL_24]]) +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_23]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @feeShiftTable(%[[VAL_26]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_26]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_29]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_27]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ff616e562 --- /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 -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..d208d3c62 --- /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 -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..9f0d4e805 --- /dev/null +++ b/circom/tests/controlflow/bigmod_01.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..fa48262af --- /dev/null +++ b/circom/tests/controlflow/bigmod_02.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..f332e0e28 --- /dev/null +++ b/circom/tests/controlflow/bigmod_03.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..59eddb3c1 --- /dev/null +++ b/circom/tests/controlflow/bigmod_04.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..9dd346cd1 --- /dev/null +++ b/circom/tests/controlflow/bigmod_05.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..b1cfad9e6 --- /dev/null +++ b/circom/tests/controlflow/bigmod_06.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..05a5c1086 --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_00.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// 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 +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..fdd7b3a7b --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_01.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @negative( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..efc4b3af6 --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_02.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @negative( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_3]] -> (i1, !felt.type) { +// 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 +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..c856a9fbb --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_03.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @negative( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..13b66a4cb --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_04.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @negative( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..492710dfa --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_05.circom @@ -0,0 +1,63 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @complicated( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_5]] -> (!felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]], %[[VAL_2]], %[[VAL_3]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_6]]#0, %[[VAL_6]]#1 : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_10]], %[[VAL_6]]#2 : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[VAL_11]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @complicated(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..3e30e4eb6 --- /dev/null +++ b/circom/tests/controlflow/branch_in_func_06.circom @@ -0,0 +1,73 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @complicated( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]]:5 = scf.if %[[VAL_6]] -> (i1, !felt.type, !felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type, !felt.type +// 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, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_7]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_7]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]]#2, %[[VAL_7]]#3 : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_15]], %[[VAL_7]]#4 : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_13]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @complicated(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..09dac065f --- /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 -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.main = !struct.type<@Conditional<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Conditional<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Conditional<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Conditional<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_98]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Conditional<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Conditional<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_8]], %[[VAL_10]]) +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_99]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..7180ac500 --- /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 -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.main = !struct.type<@B<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @B<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_0]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_6]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_7]][@out] : <@B<[]>>, !felt.type +// 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 gt(%[[VAL_8]], %[[VAL_11]]) +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_12]] -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_9]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..254c135d4 --- /dev/null +++ b/circom/tests/controlflow/early_return_if_1.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @earlyReturnFn( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_4]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_0]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_0]], %[[VAL_2]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_3]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_5]], %[[VAL_6]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..91f50e76e --- /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 -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..d8dd31b3c --- /dev/null +++ b/circom/tests/controlflow/early_return_if_3.circom @@ -0,0 +1,68 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type) { +// 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]]) +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_13]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_14]][@outp] = %[[VAL_15]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_14]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_18:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_17]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[VAL_19:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_19]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..77794a81e --- /dev/null +++ b/circom/tests/controlflow/early_return_if_4.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_U:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[V_X0:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_0]], %[[V_3]]) +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[V_4]] -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[V_X1:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[V_R0]], %[[V_U]], %[[V_X1]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = felt.add %[[V_0]], %[[V_8]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_R1:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R1]], %[[V_9]], %[[V_X0]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = scf.if %[[V_5]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_5]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[V_5]]#2 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_11]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_12:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_12]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_13]][@outp] = %[[V_14]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_13]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_15:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_16:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_17:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_16]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_18:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_15]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_18]], %[[V_17]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1f63edcc7 --- /dev/null +++ b/circom/tests/controlflow/early_return_if_5.circom @@ -0,0 +1,86 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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(%[[V_IN]], %[[VAL_3]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_4]] -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_IN]], %[[VAL_6]]) +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_7]] -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[V_IN]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_10]], %[[VAL_2]] : i1, !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_8]]#0 -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: scf.yield %[[VAL_15]], %[[VAL_8]]#1, %[[VAL_16]] : i1, !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_14]]#0, %[[VAL_14]]#1, %[[VAL_14]]#2 : i1, !felt.type, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.add %[[V_IN]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_20]], %[[VAL_19]], %[[VAL_2]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_5]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_5]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]]#2 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_21]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_22]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_23]][@outp] = %[[VAL_24]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_23]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_27:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_26]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_28]], %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..a4537e4cd --- /dev/null +++ b/circom/tests/controlflow/early_return_if_6.circom @@ -0,0 +1,79 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_I:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_N:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_U:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_3]]) +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[V_4]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R0]], %[[V_I]] : i1, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_R1:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[V_R1]], %[[V_U]] : i1, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = scf.if %[[V_5]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_5]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_N]], %[[V_9]]) +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[V_10]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I]], %[[V_12]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_R2:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R2]], %[[V_13]] : i1, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = arith.constant false +// CHECK-NEXT: scf.yield %[[V_R3]], %[[V_5]]#1 : i1, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = scf.if %[[V_11]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_11]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[V_17]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_8]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_18:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_18]], %[[V_20]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_19]][@outp] = %[[V_21]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_19]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_22:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_23:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_22]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_23]], %[[V_24]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[V_26]], %[[V_25]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1d9fa4b97 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_1.circom @@ -0,0 +1,159 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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]]) +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: i1, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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]]) +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_16]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_0]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_17]]#0 -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: scf.yield %[[VAL_21]], %[[VAL_17]]#1, %[[VAL_22]] : i1, !felt.type, !felt.type +// 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]]) +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_17]]#0, %[[VAL_17]]#1, %[[VAL_27]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_20]]#0, %[[VAL_20]]#1, %[[VAL_20]]#2 : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[VAL_30]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_28]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.def @noEarlyReturnFn(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = undef.undef : 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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]]) +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: i1, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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]]) +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_47]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_31]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[VAL_48]]#0 -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: scf.yield %[[VAL_52]], %[[VAL_48]]#1, %[[VAL_53]] : i1, !felt.type, !felt.type +// 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]]) +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_48]]#0, %[[VAL_48]]#1, %[[VAL_57]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_51]]#0, %[[VAL_51]]#1, %[[VAL_51]]#2 : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_35]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_35]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[VAL_60]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_58]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !array.type<2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_61:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = function.call @noEarlyReturnFn(%[[VAL_61]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_66]]] = %[[VAL_64]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_61]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_68]] +// CHECK-NEXT: array.write %[[VAL_63]]{{\[}}%[[VAL_69]]] = %[[VAL_67]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_62]][@outp] = %[[VAL_63]] : <@EarlyReturn<[]>>, !array.type<2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_62]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_70:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[VAL_71:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_72:[0-9a-zA-Z_\.]+]] = function.call @noEarlyReturnFn(%[[VAL_71]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[VAL_73:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_70]][@outp] : <@EarlyReturn<[]>>, !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_74]] +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_73]]{{\[}}%[[VAL_75]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_76]], %[[VAL_72]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[VAL_71]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_79]] +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_73]]{{\[}}%[[VAL_80]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_81]], %[[VAL_77]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1bbd91359 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_2A.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_R2:[0-9a-zA-Z_\.]+]]: i1, %[[V_11:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R3]], %[[V_0]], %[[V_I3]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[V_18]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_19]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_20]][@outp] = %[[V_21]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_20]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_22:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_23:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_24:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_23]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_22]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_25]], %[[V_24]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..a56a0e9e4 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_2B.circom @@ -0,0 +1,68 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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) -> (i1, !felt.type) { +// 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 +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_E2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_E3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_E3]], %[[V_0]] : i1, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_15:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_15]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_16]][@outp] = %[[V_17]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_16]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_18:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_20:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_19]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_21:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_18]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_21]], %[[V_20]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..337b5ef51 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_3.circom @@ -0,0 +1,81 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_F2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_R4]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_R5]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[V_F3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_F3]], %[[V_R3]], %[[V_I3]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_23]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_26:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_26]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_27]][@outp] = %[[V_28]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_27]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_29:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_30:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_31:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_30]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_29]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_32]], %[[V_31]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..807ce0609 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_4.circom @@ -0,0 +1,80 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_R2:[0-9a-zA-Z_\.]+]]: i1, %[[V_11:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_17]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_19]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[V_R3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_R3]], %[[V_15]], %[[V_I3]] : i1, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_23]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_26:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_26]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_27]][@outp] = %[[V_28]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_27]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_29:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_30:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_31:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_30]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_32:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_29]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_32]], %[[V_31]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..1ab8f2bc2 --- /dev/null +++ b/circom/tests/controlflow/early_return_loop_5.circom @@ -0,0 +1,88 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EarlyReturn<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @earlyReturnFn(%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_R0:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: %[[V_F0:[0-9a-zA-Z_\.]+]] = undef.undef : 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, !felt.type) -> (i1, !felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_F2:[0-9a-zA-Z_\.]+]]: i1, %[[V_R2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) { +// CHECK-NEXT: %[[V_F3:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[V_F3]], %[[V_IN]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]]:3 = scf.if %[[V_15]]#0 -> (i1, !felt.type, !felt.type) { +// CHECK-NEXT: %[[V_F5:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: %[[V_I3:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: scf.yield %[[V_F5]], %[[V_15]]#1, %[[V_I3]] : i1, !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_15]]#0, %[[V_15]]#1, %[[V_I4]] : i1, !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = scf.if %[[V_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[V_4]]#1 : !felt.type +// 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 +// CHECK-NEXT: scf.yield %[[V_28]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_26]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @EarlyReturn<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_29:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@EarlyReturn<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = struct.new : <@EarlyReturn<[]>> +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_29]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[V_30]][@outp] = %[[V_31]] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_30]] : !struct.type<@EarlyReturn<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_32:[0-9a-zA-Z_\.]+]]: !struct.type<@EarlyReturn<[]>>, %[[V_33:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_34:[0-9a-zA-Z_\.]+]] = function.call @earlyReturnFn(%[[V_33]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[V_35:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_32]][@outp] : <@EarlyReturn<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_35]], %[[V_34]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..57a2150ba --- /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 -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..88e8348cc --- /dev/null +++ b/circom/tests/controlflow/early_return_simple.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Foo<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @Foo<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Foo<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..75b56b79a --- /dev/null +++ b/circom/tests/controlflow/indexing_within_unknown_if.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@TestSetAllUnknownWithinUnknownCondition<[1]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @TestSetAllUnknownWithinUnknownCondition<[@k]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@TestSetAllUnknownWithinUnknownCondition<[@k]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@TestSetAllUnknownWithinUnknownCondition<[@k]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type +// 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> +// 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]]) +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_6]] -> (!array.type<10 x !felt.type>) { +// 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, !array.type<10 x !felt.type>) -> (!felt.type, !array.type<10 x !felt.type>) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_10]], %[[VAL_11]] : !felt.type, !array.type<10 x !felt.type> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_13]] +// CHECK-NEXT: array.write %[[VAL_14]]{{\[}}%[[VAL_16]]] = %[[VAL_15]] : <10 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_14]] : !felt.type, !array.type<10 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_9]]#1 : !array.type<10 x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_4]] : !array.type<10 x !felt.type> +// 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]] +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_7]]{{\[}}%[[VAL_20]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@TestSetAllUnknownWithinUnknownCondition<[@k]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@TestSetAllUnknownWithinUnknownCondition<[@k]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type +// 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> +// 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]]) +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_28]] -> (!array.type<10 x !felt.type>) { +// 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, !array.type<10 x !felt.type>) -> (!felt.type, !array.type<10 x !felt.type>) { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_32]], %[[VAL_24]]) +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_32]], %[[VAL_33]] : !felt.type, !array.type<10 x !felt.type> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] +// CHECK-NEXT: array.write %[[VAL_36]]{{\[}}%[[VAL_38]]] = %[[VAL_37]] : <10 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_36]] : !felt.type, !array.type<10 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_31]]#1 : !array.type<10 x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_26]] : !array.type<10 x !felt.type> +// 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]] +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_29]]{{\[}}%[[VAL_42]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: function.return +// 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..89068821b --- /dev/null +++ b/circom/tests/controlflow/loop_multi_iv.circom @@ -0,0 +1,59 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@LoopMultiIV<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @complicatedLoopFn(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_10]]) %[[VAL_5]], %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_0]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_18]], %[[VAL_15]], %[[VAL_16]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]]#2 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @LoopMultiIV<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@LoopMultiIV<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.new : <@LoopMultiIV<[]>> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = function.call @complicatedLoopFn(%[[VAL_19]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_20]][@outp] = %[[VAL_21]] : <@LoopMultiIV<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_20]] : !struct.type<@LoopMultiIV<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !struct.type<@LoopMultiIV<[]>>, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_24:[0-9a-zA-Z_\.]+]] = function.call @complicatedLoopFn(%[[VAL_23]]) : (!felt.type) -> !felt.type +// CHECK-DAG: %[[VAL_25:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_22]][@outp] : <@LoopMultiIV<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_25]], %[[VAL_24]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..bb49b7f69 --- /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 -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..a09a8aacd --- /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 -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..03e60491b --- /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 -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..d96797a73 --- /dev/null +++ b/circom/tests/controlflow/return_in_branch.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Foo<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]]:2 = scf.if %[[VAL_3]] -> (i1, !felt.type) { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = arith.constant true +// CHECK-NEXT: scf.yield %[[VAL_6]], %[[VAL_5]] : i1, !felt.type +// 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 +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]]#0 -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_4]]#1 : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_8]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @Foo<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Foo<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..e02ce4a65 --- /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 -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..5da84fc33 --- /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 -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..20fca69c4 --- /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 -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..1ba16fac8 --- /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 -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..48e770cb9 --- /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 -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..2006dd85e --- /dev/null +++ b/circom/tests/declaration_in_function/array_dim_expr_var.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @f() -> !felt.type 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> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_3]] +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_2]]{{\[}}%[[VAL_4]]] : <6 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type +// CHECK-NEXT: function.return +// 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..85a3a32d1 --- /dev/null +++ b/circom/tests/declaration_in_function/scalar_number.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk --llzk_passes='any(remove-dead-values)' -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f() -> !felt.type 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 +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @f() : () -> !felt.type +// CHECK-NEXT: function.return +// 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..378a40fce --- /dev/null +++ b/circom/tests/declaration_in_function/scalar_var.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_1]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return +// 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..5a05b1fa3 --- /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 -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..0dfa19ebf --- /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 -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..3845f53f6 --- /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 -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..2773def01 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_number.circom @@ -0,0 +1,47 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type>) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_6]] +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_8]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !array.type<5 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@out] : <@A<[]>>, !felt.type +// 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]] : <5 x !felt.type> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_13]], %[[VAL_13]], %[[VAL_13]], %[[VAL_13]], %[[VAL_13]] : <5 x !felt.type> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_10]]{{\[}}%[[VAL_16]]] : <5 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_18]], %[[VAL_17]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..236bf38e6 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_param.circom @@ -0,0 +1,44 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[12]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@s]> { +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type>) -> !struct.type<@A<[@s]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@s]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@s]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@s]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..4ae479c1a --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_param_B.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[12]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@s]> { +// CHECK-NEXT: struct.field @out : !array.type<@s x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type>) -> !struct.type<@A<[@s]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@s]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_0]] : <@A<[@s]>>, !array.type<@s x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@s]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@s]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@s x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = poly.read_const @s : !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_10]][@out] : <@A<[@s]>>, !array.type<@s x !felt.type> +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_11]] : !array.type<@s x !felt.type>, !array.type<@s x !felt.type> +// CHECK-NEXT: function.return +// 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..ce17d455f --- /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 -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_var.circom b/circom/tests/declaration_in_template/array_dim_expr_var.circom new file mode 100644 index 000000000..e9b514fc0 --- /dev/null +++ b/circom/tests/declaration_in_template/array_dim_expr_var.circom @@ -0,0 +1,16 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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/scalar_number.circom b/circom/tests/declaration_in_template/scalar_number.circom new file mode 100644 index 000000000..42d0de32d --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@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: } 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..52cf26153 --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..1c3b26fb7 --- /dev/null +++ b/circom/tests/from_concrete/array_insert_in_template.circom @@ -0,0 +1,90 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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.main = !struct.type<@Main_0<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @default_init_0() -> !array.type<3,2 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<3,2 x !felt.type> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_4]]] = %[[VAL_2]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_7]]] = %[[VAL_5]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_9]]] = %[[VAL_1]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_11]]] = %[[VAL_1]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] +// CHECK-NEXT: array.insert %[[VAL_0]]{{\[}}%[[VAL_13]]] = %[[VAL_1]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !array.type<3,2 x !felt.type> +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @Main_0<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Main_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.new : <@Main_0<[]>> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<3,2 x !felt.type> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_24]]] = %[[VAL_16]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_26]]] = %[[VAL_16]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_28]]] = %[[VAL_16]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = function.call @default_init_0() : () -> !array.type<3,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_14]] : !struct.type<@Main_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !struct.type<@Main_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<3,2 x !felt.type> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_35]]] = %[[VAL_33]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_38]]] = %[[VAL_36]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_40]]] = %[[VAL_32]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_41]] +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_42]]] = %[[VAL_32]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] +// CHECK-NEXT: array.insert %[[VAL_31]]{{\[}}%[[VAL_44]]] = %[[VAL_32]] : <3,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = function.call @default_init_0() : () -> !array.type<3,2 x !felt.type> +// CHECK-NEXT: function.return +// 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..e5ea0fd38 --- /dev/null +++ b/circom/tests/from_concrete/assert.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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.main = !struct.type<@A_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A_0<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A_0<[]>> +// CHECK-NEXT: %[[V_nBits:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_1]], %[[VAL_2]]) +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_nBits:[0-9a-zA-Z_\.]+]] = felt.const 32 +// 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_\.]+]] = bool.cmp ne(%[[VAL_5]], %[[VAL_6]]) +// CHECK-NEXT: bool.assert %[[VAL_7]], "assertion failed" +// CHECK-NEXT: function.return +// 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..86217543e --- /dev/null +++ b/circom/tests/from_concrete/assign_array_from_param.circom @@ -0,0 +1,49 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete -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.main = !struct.type<@Template_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Template_0<[]> { +// CHECK-NEXT: struct.field @ret : !array.type<2,2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// CHECK-NEXT: struct.writef %[[VAL_0]][@ret] = %[[VAL_7]] : <@Template_0<[]>>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@Template_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_10]][@ret] : <@Template_0<[]>>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_11]], %[[VAL_12]] : <2 x !felt.type> +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_14]], %[[VAL_15]] : <2 x !felt.type> +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: array.insert %[[VAL_17]]{{\[}}%[[VAL_18]]] = %[[VAL_13]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: array.insert %[[VAL_17]]{{\[}}%[[VAL_19]]] = %[[VAL_16]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: constrain.eq %[[VAL_20]], %[[VAL_17]] : !array.type<2,2 x !felt.type>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return +// 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..06c2fd4c1 --- /dev/null +++ b/circom/tests/from_concrete/assign_array_from_var.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete -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.main = !struct.type<@Template_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Template_0<[]> { +// CHECK-NEXT: struct.field @ret : !array.type<2,2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Template_0<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_5]]] = %[[VAL_3]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_8]]] = %[[VAL_6]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_10]]] = %[[VAL_2]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_12]]] = %[[VAL_2]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_16]]] = %[[VAL_14]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_23]]] = %[[VAL_21]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_20]]{{\[}}%[[VAL_26]]] = %[[VAL_24]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_28]]] = %[[VAL_13]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] +// CHECK-NEXT: array.insert %[[VAL_1]]{{\[}}%[[VAL_30]]] = %[[VAL_20]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: struct.writef %[[VAL_0]][@ret] = %[[VAL_1]] : <@Template_0<[]>>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_31:[0-9a-zA-Z_\.]+]]: !struct.type<@Template_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_31]][@ret] : <@Template_0<[]>>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_35]] +// CHECK-NEXT: array.write %[[VAL_33]]{{\[}}%[[VAL_36]]] = %[[VAL_34]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] +// CHECK-NEXT: array.write %[[VAL_33]]{{\[}}%[[VAL_39]]] = %[[VAL_37]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_40]] +// CHECK-NEXT: array.insert %[[VAL_32]]{{\[}}%[[VAL_41]]] = %[[VAL_33]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_42]] +// CHECK-NEXT: array.insert %[[VAL_32]]{{\[}}%[[VAL_43]]] = %[[VAL_33]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_47]]] = %[[VAL_45]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_49]] +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_50]]] = %[[VAL_48]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] +// CHECK-NEXT: array.write %[[VAL_51]]{{\[}}%[[VAL_54]]] = %[[VAL_52]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_56]] +// CHECK-NEXT: array.write %[[VAL_51]]{{\[}}%[[VAL_57]]] = %[[VAL_55]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_58]] +// CHECK-NEXT: array.insert %[[VAL_32]]{{\[}}%[[VAL_59]]] = %[[VAL_44]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_60]] +// CHECK-NEXT: array.insert %[[VAL_32]]{{\[}}%[[VAL_61]]] = %[[VAL_51]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: constrain.eq %[[VAL_62]], %[[VAL_32]] : !array.type<2,2 x !felt.type>, !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return +// 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..e2268215f --- /dev/null +++ b/circom/tests/from_concrete/bool_4.circom @@ -0,0 +1,106 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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.main = !struct.type<@A_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @binop_bool_array_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) -> !array.type<10 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<10 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_5]]] = %[[VAL_3]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_8]]] = %[[VAL_6]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]]] = %[[VAL_9]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_14]]] = %[[VAL_12]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_20]]] = %[[VAL_18]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_23]]] = %[[VAL_21]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_26]]] = %[[VAL_24]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_29]]] = %[[VAL_27]] : <10 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_32]]] = %[[VAL_30]] : <10 x !felt.type>, !felt.type +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_35]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_39]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_41]]] : <10 x !felt.type>, !felt.type +// 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]]) +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_47]] : i1 +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_48]]] = %[[VAL_49]] : <10 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_51]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_2]] : !array.type<10 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A_0<[]> { +// CHECK-NEXT: struct.field @out : !array.type<10 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_52:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>, %[[VAL_53:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) -> !struct.type<@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = struct.new : <@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>, !array.type<10 x !felt.type>) -> !array.type<10 x !felt.type> +// CHECK-NEXT: struct.writef %[[VAL_54]][@out] = %[[VAL_55]] : <@A_0<[]>>, !array.type<10 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_54]] : !struct.type<@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0<[]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>, %[[VAL_58:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_56]][@out] : <@A_0<[]>>, !array.type<10 x !felt.type> +// CHECK-NEXT: function.return +// 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..6c68a6145 --- /dev/null +++ b/circom/tests/from_concrete/call_and_return.circom @@ -0,0 +1,52 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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.main = !struct.type<@C_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @negative_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @C_0<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.new : <@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) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_7]][@out] = %[[VAL_9]] : <@C_0<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_7]] : !struct.type<@C_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !struct.type<@C_0<[]>>, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-DAG: %[[VAL_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_10]][@out] : <@C_0<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..f5e89feb2 --- /dev/null +++ b/circom/tests/from_concrete/dimension_handling.circom @@ -0,0 +1,132 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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.main = !struct.type<@A_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @myAdd_1(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_3:[0-9a-zA-Z_\.]+]]: !felt.type) -> !array.type<2 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_6]] +// CHECK-NEXT: array.write %[[V_4]]{{\[}}%[[V_7]]] = %[[V_5]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_9]] +// CHECK-NEXT: array.write %[[V_4]]{{\[}}%[[V_10]]] = %[[V_8]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = felt.add %[[V_11]], %[[V_1]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_13]] +// CHECK-NEXT: array.write %[[V_4]]{{\[}}%[[V_14]]] = %[[V_12]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.add %[[V_2]], %[[V_3]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_16]] +// CHECK-NEXT: array.write %[[V_4]]{{\[}}%[[V_17]]] = %[[V_15]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[V_4]] : !array.type<2 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @myFun_0() -> !array.type<1,1 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1,1 x !felt.type> +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1 x !felt.type> +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_21]] +// CHECK-NEXT: array.write %[[V_19]]{{\[}}%[[V_22]]] = %[[V_20]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_23]] +// CHECK-NEXT: array.insert %[[V_18]]{{\[}}%[[V_24]]] = %[[V_19]] : <1,1 x !felt.type>, <1 x !felt.type> +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_27]] +// CHECK-NEXT: array.write %[[V_25]]{{\[}}%[[V_28]]] = %[[V_26]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_30]] +// CHECK-NEXT: array.write %[[V_25]]{{\[}}%[[V_31]]] = %[[V_29]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551557 +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_33]] +// CHECK-NEXT: array.write %[[V_25]]{{\[}}%[[V_34]]] = %[[V_32]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = felt.const 18446744073709551557 +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_36]] +// CHECK-NEXT: array.write %[[V_25]]{{\[}}%[[V_37]]] = %[[V_35]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_40:[0-9a-zA-Z_\.]+]] = %[[V_25]], %[[V_41:[0-9a-zA-Z_\.]+]] = %[[V_38]]) : (!array.type<2 x !felt.type>, !felt.type) -> (!array.type<2 x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_41]], %[[V_42]]) +// CHECK-NEXT: scf.condition(%[[V_43]]) %[[V_40]], %[[V_41]] : !array.type<2 x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_44:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>, %[[V_45:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_46]] +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = array.read %[[V_44]]{{\[}}%[[V_47]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_49]] +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = array.read %[[V_44]]{{\[}}%[[V_50]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_52]] +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = array.read %[[V_44]]{{\[}}%[[V_53]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_55]] +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]] = array.read %[[V_44]]{{\[}}%[[V_56]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = function.call @myAdd_1(%[[V_48]], %[[V_51]], %[[V_54]], %[[V_57]]) : (!felt.type, !felt.type, !felt.type, !felt.type) -> !array.type<2 x !felt.type> +// CHECK-NEXT: %[[V_59:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = felt.add %[[V_45]], %[[V_59]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_58]], %[[V_60]] : !array.type<2 x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_18]] : !array.type<1,1 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A_0<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = struct.new : <@A_0<[]>> +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1,1 x !felt.type> +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1 x !felt.type> +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_65]] +// CHECK-NEXT: array.write %[[V_63]]{{\[}}%[[V_66]]] = %[[V_64]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_67]] +// CHECK-NEXT: array.insert %[[V_62]]{{\[}}%[[V_68]]] = %[[V_63]] : <1,1 x !felt.type>, <1 x !felt.type> +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = function.call @myFun_0() : () -> !array.type<1,1 x !felt.type> +// CHECK-NEXT: function.return %[[V_61]] : !struct.type<@A_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[V_70:[0-9a-zA-Z_\.]+]]: !struct.type<@A_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_71:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1,1 x !felt.type> +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<1 x !felt.type> +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_74]] +// CHECK-NEXT: array.write %[[V_72]]{{\[}}%[[V_75]]] = %[[V_73]] : <1 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_76:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_77:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_76]] +// CHECK-NEXT: array.insert %[[V_71]]{{\[}}%[[V_77]]] = %[[V_72]] : <1,1 x !felt.type>, <1 x !felt.type> +// CHECK-NEXT: %[[V_78:[0-9a-zA-Z_\.]+]] = function.call @myFun_0() : () -> !array.type<1,1 x !felt.type> +// CHECK-NEXT: function.return +// 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..21c3b6120 --- /dev/null +++ b/circom/tests/from_concrete/fib_input.circom @@ -0,0 +1,102 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete -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.main = !struct.type<@Fibonacci_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Fibonacci_0<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Fibonacci_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_13]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_16]], %[[VAL_18]], %[[VAL_16]] : !felt.type, !felt.type, !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_20]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_25]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_27]] : <@Fibonacci_0<[]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_21]] : <@Fibonacci_0<[]>>, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Fibonacci_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@Fibonacci_0<[]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@out] : <@Fibonacci_0<[]>>, !felt.type +// 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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_40]]) %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : !felt.type, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_45]], %[[VAL_47]], %[[VAL_45]] : !felt.type, !felt.type, !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_49]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_51]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_34]]#0, %[[VAL_34]]#1 : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_52]] : !felt.type +// 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]]) +// CHECK-NEXT: scf.if %[[VAL_54]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..5f9a8203c --- /dev/null +++ b/circom/tests/from_concrete/inner_conditional_03.circom @@ -0,0 +1,82 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete -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.main = !struct.type<@InnerConditional3_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional3_0<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@InnerConditional3_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional3_0<[]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = felt.const 3 +// 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_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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_8:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_7]]) +// CHECK-NEXT: scf.condition(%[[V_8]]) %[[V_A1]], %[[V_I1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_0]], %[[V_11]]) +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = scf.if %[[V_12]] -> (!felt.type) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_15]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_13]], %[[V_17]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_4]]#0 : <@InnerConditional3_0<[]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerConditional3_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional3_0<[]>>, %[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@out] : <@InnerConditional3_0<[]>>, !felt.type +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = felt.const 3 +// 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_22:[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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_25]]) +// CHECK-NEXT: scf.condition(%[[V_26]]) %[[V_A1]], %[[V_I1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[V_19]], %[[V_29]]) +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = scf.if %[[V_30]] -> (!felt.type) { +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_32]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_33]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_31]], %[[V_35]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..864f40ee5 --- /dev/null +++ b/circom/tests/from_concrete/pass_and_constrain_arrays.circom @@ -0,0 +1,483 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk=concrete -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>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @add_1(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) -> !array.type<2 x !felt.type> 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_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_9]]] = %[[VAL_7]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_12]]] = %[[VAL_10]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_13]], %[[VAL_14]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_18]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_19]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_20]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_15]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_25]]] = %[[VAL_23]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_1]], %[[VAL_3]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_28]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_26]], %[[VAL_29]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_33]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_34]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_35]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_31]], %[[VAL_36]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_30]], %[[VAL_37]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_39]] +// CHECK-NEXT: array.write %[[VAL_6]]{{\[}}%[[VAL_40]]] = %[[VAL_38]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: function.return %[[VAL_6]] : !array.type<2 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @mul_0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type) -> !array.type<16,2 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_47]]] = %[[VAL_45]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_50]]] = %[[VAL_48]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_52]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_54]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_55]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_56]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_57]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_58]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_60]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_62]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_64]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_65]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_66]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_67]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_68]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_69]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_70]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_71]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_72]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_73]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_74]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_75]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_76]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_77]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_78]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_79]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_80]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_81]] +// CHECK-NEXT: array.insert %[[VAL_43]]{{\[}}%[[VAL_82]]] = %[[VAL_44]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_84]]{{\[}}%[[VAL_87]]] = %[[VAL_85]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_84]]{{\[}}%[[VAL_90]]] = %[[VAL_88]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_91]]{{\[}}%[[VAL_94]]] = %[[VAL_92]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_91]]{{\[}}%[[VAL_97]]] = %[[VAL_95]] : <2 x !felt.type>, !felt.type +// 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>, !felt.type) -> (!array.type<2 x !felt.type>, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: %[[VAL_104:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_101]], %[[VAL_103]]) +// CHECK-NEXT: scf.condition(%[[VAL_104]]) %[[VAL_100]], %[[VAL_101]] : !array.type<2 x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_105:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>, %[[VAL_106:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_107]] +// CHECK-NEXT: %[[VAL_109:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_108]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_110:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_111:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_110]] +// CHECK-NEXT: %[[VAL_112:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_111]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_113:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_114:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_113]] +// CHECK-NEXT: %[[VAL_115:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_114]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_116:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_117:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_116]] +// CHECK-NEXT: %[[VAL_118:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_105]]{{\[}}%[[VAL_117]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_119:[0-9a-zA-Z_\.]+]] = function.call @add_1(%[[VAL_109]], %[[VAL_112]], %[[VAL_115]], %[[VAL_118]]) : (!felt.type, !felt.type, !felt.type, !felt.type) -> !array.type<2 x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_119]], %[[VAL_121]] : !array.type<2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_125:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_126:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_125]] +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_124]], %[[VAL_126]]] = %[[VAL_122]] : <16,2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_130:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_131:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_130]] +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_129]], %[[VAL_131]]] = %[[VAL_127]] : <16,2 x !felt.type>, !felt.type +// 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, !array.type<2 x !felt.type>) -> (!felt.type, !array.type<2 x !felt.type>) { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_137]]) %[[VAL_134]], %[[VAL_135]] : !felt.type, !array.type<2 x !felt.type> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_138:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_139:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type>): +// 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, !felt.type +// CHECK-NEXT: %[[VAL_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_141]] +// CHECK-NEXT: %[[VAL_143:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_144:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_143]] +// CHECK-NEXT: %[[VAL_145:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_43]]{{\[}}%[[VAL_142]], %[[VAL_144]]] : <16,2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_148:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_147]] +// CHECK-NEXT: %[[VAL_149:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_150:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_149]] +// CHECK-NEXT: %[[VAL_151:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_43]]{{\[}}%[[VAL_148]], %[[VAL_150]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_152:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_153:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_152]] +// CHECK-NEXT: %[[VAL_154:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_99]]#0{{\[}}%[[VAL_153]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_155:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_156:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_155]] +// CHECK-NEXT: %[[VAL_157:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_99]]#0{{\[}}%[[VAL_156]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_158:[0-9a-zA-Z_\.]+]] = function.call @add_1(%[[VAL_145]], %[[VAL_151]], %[[VAL_154]], %[[VAL_157]]) : (!felt.type, !felt.type, !felt.type, !felt.type) -> !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_159:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_160:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_159]] +// CHECK-NEXT: %[[VAL_161:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_158]]{{\[}}%[[VAL_160]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_162:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] +// CHECK-NEXT: %[[VAL_163:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_164:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_163]] +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_162]], %[[VAL_164]]] = %[[VAL_161]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_165:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_166:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_165]] +// CHECK-NEXT: %[[VAL_167:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_158]]{{\[}}%[[VAL_166]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_168:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_138]] +// CHECK-NEXT: %[[VAL_169:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_170:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_169]] +// CHECK-NEXT: array.write %[[VAL_43]]{{\[}}%[[VAL_168]], %[[VAL_170]]] = %[[VAL_167]] : <16,2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_172]], %[[VAL_158]] : !felt.type, !array.type<2 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_43]] : !array.type<16,2 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Main_0<[]> { +// CHECK-NEXT: struct.field @out : !array.type<16,2 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Main_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_173:[0-9a-zA-Z_\.]+]] = struct.new : <@Main_0<[]>> +// CHECK-NEXT: %[[VAL_174:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_175:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_178]]] = %[[VAL_176]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_181]]] = %[[VAL_179]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_184]]] = %[[VAL_182]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_175]]{{\[}}%[[VAL_187]]] = %[[VAL_185]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_188:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_189:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_189]]{{\[}}%[[VAL_192]]] = %[[VAL_190]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_189]]{{\[}}%[[VAL_195]]] = %[[VAL_193]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_196:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_197:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_196]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_197]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_198:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_199:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_198]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_199]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_200:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_201:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_200]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_201]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_202:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_203:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_202]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_203]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_204:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_205:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_204]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_205]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_206:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_207:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_206]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_207]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_208:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_209:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_208]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_209]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_210:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_211:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_210]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_211]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_212:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_213:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_212]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_213]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_214:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_215:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_214]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_215]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_216:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_217:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_216]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_217]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_218:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_219:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_218]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_219]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_220:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_221:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_220]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_221]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_222:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_223:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_222]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_223]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_224:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_225:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_224]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_225]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_226:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_227:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_226]] +// CHECK-NEXT: array.insert %[[VAL_188]]{{\[}}%[[VAL_227]]] = %[[VAL_189]] : <16,2 x !felt.type>, <2 x !felt.type> +// 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>, !felt.type) -> !array.type<16,2 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_234]]) %[[VAL_232]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_235:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_236:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] +// CHECK-NEXT: %[[VAL_237:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_238:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_237]] +// CHECK-NEXT: %[[VAL_239:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_229]]{{\[}}%[[VAL_236]], %[[VAL_238]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_240:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] +// CHECK-NEXT: %[[VAL_241:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_242:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_241]] +// CHECK-NEXT: array.write %[[VAL_174]]{{\[}}%[[VAL_240]], %[[VAL_242]]] = %[[VAL_239]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_243:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] +// CHECK-NEXT: %[[VAL_244:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_245:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_244]] +// CHECK-NEXT: %[[VAL_246:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_229]]{{\[}}%[[VAL_243]], %[[VAL_245]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_247:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_235]] +// CHECK-NEXT: %[[VAL_248:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_249:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_248]] +// CHECK-NEXT: array.write %[[VAL_174]]{{\[}}%[[VAL_247]], %[[VAL_249]]] = %[[VAL_246]] : <16,2 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_251]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_173]][@out] = %[[VAL_174]] : <@Main_0<[]>>, !array.type<16,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_173]] : !struct.type<@Main_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_252:[0-9a-zA-Z_\.]+]]: !struct.type<@Main_0<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_318:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_252]][@out] : <@Main_0<[]>>, !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_253:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_254:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_255:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_256:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_255]] +// CHECK-NEXT: array.write %[[VAL_253]]{{\[}}%[[VAL_256]]] = %[[VAL_254]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_257:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_258:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_259:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_258]] +// CHECK-NEXT: array.write %[[VAL_253]]{{\[}}%[[VAL_259]]] = %[[VAL_257]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_260:[0-9a-zA-Z_\.]+]] = felt.const 5299619240641551281634865583518297030282874472190772894086521144482721001553 +// CHECK-NEXT: %[[VAL_261:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_262:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_261]] +// CHECK-NEXT: array.write %[[VAL_253]]{{\[}}%[[VAL_262]]] = %[[VAL_260]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_263:[0-9a-zA-Z_\.]+]] = felt.const 16950150798460657717958625567821834550301663161624707787222815936182638968203 +// CHECK-NEXT: %[[VAL_264:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_265:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_264]] +// CHECK-NEXT: array.write %[[VAL_253]]{{\[}}%[[VAL_265]]] = %[[VAL_263]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_266:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_267:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// CHECK-NEXT: %[[VAL_268:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_269:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_270:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_269]] +// CHECK-NEXT: array.write %[[VAL_267]]{{\[}}%[[VAL_270]]] = %[[VAL_268]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_271:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_272:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_273:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_272]] +// CHECK-NEXT: array.write %[[VAL_267]]{{\[}}%[[VAL_273]]] = %[[VAL_271]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_274:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_275:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_274]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_275]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_276:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_277:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_276]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_277]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_278:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_279:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_278]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_279]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_280:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_281:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_280]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_281]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_282:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_283:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_282]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_283]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_284:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_285:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_284]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_285]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_286:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_287:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_286]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_287]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_288:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_289:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_288]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_289]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_290:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_291:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_290]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_291]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_292:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_293:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_292]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_293]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_294:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_295:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_294]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_295]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_296:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_297:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_296]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_297]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_298:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_299:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_298]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_299]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_300:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_301:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_300]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_301]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_302:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_303:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_302]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_303]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_304:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_305:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_304]] +// CHECK-NEXT: array.insert %[[VAL_266]]{{\[}}%[[VAL_305]]] = %[[VAL_267]] : <16,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_306:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_307:[0-9a-zA-Z_\.]+]] = function.call @mul_0(%[[VAL_253]], %[[VAL_306]]) : (!array.type<2 x !felt.type>, !felt.type) -> !array.type<16,2 x !felt.type> +// CHECK-NEXT: %[[VAL_308:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_309:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_310:[0-9a-zA-Z_\.]+]] = %[[VAL_308]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_311:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_312:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_310]], %[[VAL_311]]) +// CHECK-NEXT: scf.condition(%[[VAL_312]]) %[[VAL_310]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_313:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_314:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_313]] +// CHECK-NEXT: %[[VAL_315:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_316:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_315]] +// CHECK-NEXT: %[[VAL_317:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_307]]{{\[}}%[[VAL_314]], %[[VAL_316]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_319:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_313]] +// CHECK-NEXT: %[[VAL_320:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_321:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_320]] +// CHECK-NEXT: %[[VAL_322:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_318]]{{\[}}%[[VAL_319]], %[[VAL_321]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_322]], %[[VAL_317]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_323:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_313]] +// CHECK-NEXT: %[[VAL_324:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_325:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_324]] +// CHECK-NEXT: %[[VAL_326:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_307]]{{\[}}%[[VAL_323]], %[[VAL_325]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_328:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_313]] +// CHECK-NEXT: %[[VAL_329:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_330:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_329]] +// CHECK-NEXT: %[[VAL_331:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_318]]{{\[}}%[[VAL_328]], %[[VAL_330]]] : <16,2 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_331]], %[[VAL_326]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_332:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_333:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_313]], %[[VAL_332]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_333]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..b306f0fab --- /dev/null +++ b/circom/tests/from_concrete/template_param_uses.circom @@ -0,0 +1,146 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk concrete -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.main = !struct.type<@Template_0<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @f_0(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2,2 x !felt.type>) -> !array.type<2,2 x !felt.type> attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !array.type<2,2 x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Template_0<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template_0<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_19]]] = %[[VAL_17]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_22]]] = %[[VAL_20]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_24]]] = %[[VAL_16]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: array.insert %[[VAL_15]]{{\[}}%[[VAL_26]]] = %[[VAL_16]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_27]]{{\[}}%[[VAL_30]]] = %[[VAL_28]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_27]]{{\[}}%[[VAL_33]]] = %[[VAL_31]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_35]]{{\[}}%[[VAL_38]]] = %[[VAL_36]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_35]]{{\[}}%[[VAL_41]]] = %[[VAL_39]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_42]] +// CHECK-NEXT: array.insert %[[VAL_34]]{{\[}}%[[VAL_43]]] = %[[VAL_35]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] +// CHECK-NEXT: array.insert %[[VAL_34]]{{\[}}%[[VAL_45]]] = %[[VAL_35]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = function.call @f_0(%[[VAL_9]]) : (!array.type<2,2 x !felt.type>) -> !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Template_0<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !struct.type<@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> +// 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> +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.new : <2,2 x !felt.type> +// 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>, <2 x !felt.type> +// 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>, <2 x !felt.type> +// 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> +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_62]]{{\[}}%[[VAL_65]]] = %[[VAL_63]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_62]]{{\[}}%[[VAL_68]]] = %[[VAL_66]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_69]] +// CHECK-NEXT: array.insert %[[VAL_61]]{{\[}}%[[VAL_70]]] = %[[VAL_62]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_71]] +// CHECK-NEXT: array.insert %[[VAL_61]]{{\[}}%[[VAL_72]]] = %[[VAL_62]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_73]]{{\[}}%[[VAL_76]]] = %[[VAL_74]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_73]]{{\[}}%[[VAL_79]]] = %[[VAL_77]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2,2 x !felt.type> +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<2 x !felt.type> +// 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]] +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_84]]] = %[[VAL_82]] : <2 x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_81]]{{\[}}%[[VAL_87]]] = %[[VAL_85]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_88]] +// CHECK-NEXT: array.insert %[[VAL_80]]{{\[}}%[[VAL_89]]] = %[[VAL_81]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_90]] +// CHECK-NEXT: array.insert %[[VAL_80]]{{\[}}%[[VAL_91]]] = %[[VAL_81]] : <2,2 x !felt.type>, <2 x !felt.type> +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = function.call @f_0(%[[VAL_55]]) : (!array.type<2,2 x !felt.type>) -> !array.type<2,2 x !felt.type> +// CHECK-NEXT: function.return +// 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..2e89b93b6 --- /dev/null +++ b/circom/tests/loops/assign_in_loop_01.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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() { + 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 { 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..b5b1b6b14 --- /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 -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..7eaab4aa8 --- /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 -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..dc786c545 --- /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 -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..4dc53dd62 --- /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 -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..962728e92 --- /dev/null +++ b/circom/tests/loops/confusing_merge.circom @@ -0,0 +1,105 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 "undef : !array.type<@N x !felt.type>" 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.main = !struct.type<@Foo<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Foo<[@N]> { +// CHECK-NEXT: struct.field @outp : !array.type<@N x !felt.type> {llzk.pub} +// CHECK-NEXT: struct.field @internal : !array.type<@N x !felt.type> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@Foo<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = undef.undef : !array.type<@N x !felt.type> +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = undef.undef : !array.type<@N x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_4:[0-9a-zA-Z_\.]+]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_14]] : !felt.type +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_17]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_4]]{{\[}}%[[VAL_20]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: array.write %[[VAL_3:[0-9a-zA-Z_\.]+]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_24]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@internal] = %[[VAL_4]] : <@Foo<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_3]] : <@Foo<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Foo<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[@N]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_OUTP:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@outp] : <@Foo<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@internal] : <@Foo<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_31:[0-9a-zA-Z_\.]+]] = %[[VAL_29]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_31]], %[[VAL_27]]) +// CHECK-NEXT: scf.condition(%[[VAL_32]]) %[[VAL_31]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_33:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_26]]{{\[}}%[[VAL_34]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_33]] +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_37]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_38]], %[[VAL_35]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_33]], %[[VAL_39]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_40]] : !felt.type +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_43]], %[[VAL_27]]) +// CHECK-NEXT: scf.condition(%[[VAL_44]]) %[[VAL_43]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_46]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_45]] +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_OUTP]]{{\[}}%[[VAL_49]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_50]], %[[VAL_47]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_51]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_52]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..801068c5b --- /dev/null +++ b/circom/tests/loops/fib_input.circom @@ -0,0 +1,87 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Fibonacci<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Fibonacci<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Fibonacci<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_6]], %[[VAL_7]], %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_12]], %[[VAL_13]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]], %[[VAL_16]], %[[VAL_18]], %[[VAL_16]] : !felt.type, !felt.type, !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_20]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_24]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]]#0, %[[VAL_5]]#1 : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[VAL_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_21]] : <@Fibonacci<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Fibonacci<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@Fibonacci<[]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@out] : <@Fibonacci<[]>>, !felt.type +// 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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_40]]) %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : !felt.type, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_41:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_42:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_41]], %[[VAL_42]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_42]], %[[VAL_45]], %[[VAL_47]], %[[VAL_45]] : !felt.type, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..14bec804a --- /dev/null +++ b/circom/tests/loops/fib_template.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@FibonacciTmpl<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @FibonacciTmpl<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@FibonacciTmpl<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@FibonacciTmpl<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_C2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_X2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_X3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_B2]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_B2]], %[[V_X3]], %[[V_C3]], %[[V_X3]] : !felt.type, !felt.type, !felt.type, !felt.type +// 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) { +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_22]] : <@FibonacciTmpl<[@N]>>, !felt.type +// CHECK-NEXT: scf.yield %[[V_22]] : !felt.type +// 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) { +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_26]] : <@FibonacciTmpl<[@N]>>, !felt.type +// CHECK-NEXT: scf.yield %[[V_26]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.add %[[V_5]]#0, %[[V_5]]#1 : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_27]] : <@FibonacciTmpl<[@N]>>, !felt.type +// CHECK-NEXT: scf.yield %[[V_27]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@FibonacciTmpl<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[SELF]][@out] : <@FibonacciTmpl<[@N]>>, !felt.type +// 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, !felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_C2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_X2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_X3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_B2]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_B2]], %[[V_X3]], %[[V_C3]], %[[V_X3]] : !felt.type, !felt.type, !felt.type, !felt.type +// 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: } 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..80f46657e --- /dev/null +++ b/circom/tests/loops/fixed_idx_nested_lhs.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@FixIdxNested<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @FixIdxNested<[]> { +// CHECK-NEXT: struct.field @out : !array.type<9 x !felt.type> +// CHECK-NEXT: function.def @compute() -> !struct.type<@FixIdxNested<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@FixIdxNested<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<9 x !felt.type> +// 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> +// 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> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_13]]{{\[}}%[[VAL_20]]] : <9 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_13]]{{\[}}%[[VAL_22]]] : <9 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_23]] +// CHECK-NEXT: array.write %[[VAL_1]]{{\[}}%[[VAL_24]]] = %[[VAL_21]] : <9 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_1]] : <@FixIdxNested<[]>>, !array.type<9 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@FixIdxNested<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@FixIdxNested<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_27]][@out] : <@FixIdxNested<[]>>, !array.type<9 x !felt.type> +// 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]] : <9 x !felt.type> +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_30]], %[[VAL_31]], %[[VAL_32]], %[[VAL_33]], %[[VAL_34]], %[[VAL_35]], %[[VAL_36]], %[[VAL_37]], %[[VAL_38]] : <9 x !felt.type> +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_42:[0-9a-zA-Z_\.]+]] = %[[VAL_40]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_42]], %[[VAL_43]]) +// CHECK-NEXT: scf.condition(%[[VAL_44]]) %[[VAL_42]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_45]], %[[VAL_46]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_47]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ea044c5e3 --- /dev/null +++ b/circom/tests/loops/fixed_idx_nested_rhs.circom @@ -0,0 +1,106 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@FixIdxNested<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @FixIdxNested<[]> { +// CHECK-NEXT: struct.field @out : !array.type<16 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<16 x !felt.type>) -> !struct.type<@FixIdxNested<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@FixIdxNested<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<16 x !felt.type> +// 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> +// 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> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_24]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_28]]] : <16 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_29]] +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_30]]] : <16 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_32]]] = %[[VAL_31]] : <16 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_34]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@FixIdxNested<[]>>, !array.type<16 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@FixIdxNested<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !struct.type<@FixIdxNested<[]>>, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !array.type<16 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_35]][@out] : <@FixIdxNested<[]>>, !array.type<16 x !felt.type> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]], %[[VAL_37]] : <16 x !felt.type> +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.const 15 +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.const 14 +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 13 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_51:[0-9a-zA-Z_\.]+]] = felt.const 12 +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 11 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_39]], %[[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]] : <16 x !felt.type> +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_58:[0-9a-zA-Z_\.]+]] = %[[VAL_56]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 16 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_58]], %[[VAL_59]]) +// CHECK-NEXT: scf.condition(%[[VAL_60]]) %[[VAL_58]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_61:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_55]]{{\[}}%[[VAL_62]]] : <16 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_63]] +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_36]]{{\[}}%[[VAL_64]]] : <16 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_61]] +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_66]]{{\[}}%[[VAL_67]]] : <16 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_68]], %[[VAL_65]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_61]], %[[VAL_69]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_70]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..d6e54fb2a --- /dev/null +++ b/circom/tests/loops/for_known.circom @@ -0,0 +1,59 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ForKnown<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ForKnown<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@ForKnown<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@ForKnown<[@N]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_1]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_0]][@out] = %[[VAL_4]]#0 : <@ForKnown<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@ForKnown<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@out] : <@ForKnown<[@N]>>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_15]], %[[VAL_19:[0-9a-zA-Z_\.]+]] = %[[VAL_16]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_19]], %[[VAL_14]]) +// CHECK-NEXT: scf.condition(%[[VAL_20]]) %[[VAL_18]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_22]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ce80db190 --- /dev/null +++ b/circom/tests/loops/for_unknown.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ForUnknown<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @ForUnknown<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ForUnknown<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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_5:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_6:[0-9a-zA-Z_\.]+]] = %[[VAL_2]], %[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_3]]) +// CHECK-SAME: : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_7]], %[[VAL_0]]) +// CHECK-NEXT: scf.condition(%[[VAL_4]]) %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_10]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_5]]#0 : <@ForUnknown<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ForUnknown<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknown<[]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@out] : <@ForUnknown<[]>>, !felt.type +// 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_19:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_20:[0-9a-zA-Z_\.]+]] = %[[VAL_16]], %[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_17]]) +// CHECK-SAME: : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_21]], %[[VAL_15]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_22:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_22]], %[[VAL_23]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ca35f2ec0 --- /dev/null +++ b/circom/tests/loops/for_unknown_index.circom @@ -0,0 +1,63 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ForUnknownIndex<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ForUnknownIndex<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) -> !struct.type<@ForUnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_7]], %[[VAL_0]]) +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_6]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_9]], %[[VAL_10]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_11]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_5]]#0 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_14]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_15]] : <@ForUnknownIndex<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@ForUnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknownIndex<[]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<10 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@out] : <@ForUnknownIndex<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_19]], %[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_23]], %[[VAL_17]]) +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_22]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_26]], %[[VAL_28]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]], %[[VAL_29]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ca0801f39 --- /dev/null +++ b/circom/tests/loops/init_nonzero.circom @@ -0,0 +1,117 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@NonZeroInit<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @NonZeroInit<[]> { +// CHECK-NEXT: struct.field @b : !array.type<9 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<9 x !felt.type>) -> !struct.type<@NonZeroInit<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@NonZeroInit<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<9 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <9 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <9 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_16]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_20]]] : <9 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_19]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_22]]] = %[[VAL_21]] : <9 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_24]] : !felt.type +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_27]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_31]]] : <9 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_30]] +// CHECK-NEXT: array.write %[[VAL_2]]{{\[}}%[[VAL_33]]] = %[[VAL_32]] : <9 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_35]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_2]] : <@NonZeroInit<[]>>, !array.type<9 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@NonZeroInit<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !struct.type<@NonZeroInit<[]>>, %[[VAL_37:[0-9a-zA-Z_\.]+]]: !array.type<9 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_36]][@b] : <@NonZeroInit<[]>>, !array.type<9 x !felt.type> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_40:[0-9a-zA-Z_\.]+]] = %[[VAL_38]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_40]], %[[VAL_41]]) +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_40]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_43]], %[[VAL_44]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_45]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_48:[0-9a-zA-Z_\.]+]] = %[[VAL_46]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_48]], %[[VAL_49]]) +// CHECK-NEXT: scf.condition(%[[VAL_50]]) %[[VAL_48]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_51:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_51]], %[[VAL_52]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_53]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_56:[0-9a-zA-Z_\.]+]] = %[[VAL_54]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_56]], %[[VAL_57]]) +// CHECK-NEXT: scf.condition(%[[VAL_58]]) %[[VAL_56]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_59:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_59]], %[[VAL_60]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_61]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..30fb4344c --- /dev/null +++ b/circom/tests/loops/inner_conditional_01.circom @@ -0,0 +1,93 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional1<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional1<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerConditional1<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional1<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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_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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_7]]) %[[V_A1]], %[[V_I1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I2]], %[[V_10]]) +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = scf.if %[[V_11]] -> (!felt.type) { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_13]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_15]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_12]], %[[V_16]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_4]]#0 : <@InnerConditional1<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerConditional1<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional1<[@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 +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[SELF]][@out] : <@InnerConditional1<[@N]>>, !felt.type +// 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_21:[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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_24]]) %[[V_A1]], %[[V_I1]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I2]], %[[V_27]]) +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = scf.if %[[V_28]] -> (!felt.type) { +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_30]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_31]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_32]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_29]], %[[V_33]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..ca9332e2f --- /dev/null +++ b/circom/tests/loops/inner_conditional_02.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/loops/inner_conditional_03.circom b/circom/tests/loops/inner_conditional_03.circom new file mode 100644 index 000000000..04254ac15 --- /dev/null +++ b/circom/tests/loops/inner_conditional_03.circom @@ -0,0 +1,82 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional3<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional3<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@InnerConditional3<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional3<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_14]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_15]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_13]], %[[V_17]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_5]]#0 : <@InnerConditional3<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerConditional3<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional3<[@N]>>, %[[V_IN:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@out] : <@InnerConditional3<[@N]>>, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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) { +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_32]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_A2]], %[[V_I2]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_33]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_31]], %[[V_35]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..bf7428050 --- /dev/null +++ b/circom/tests/loops/inner_conditional_04.circom @@ -0,0 +1,73 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional4<[6]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional4<[@N]> { +// CHECK-NEXT: struct.field @out : !array.type<@N x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@InnerConditional4<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional4<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@N x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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]]) +// CHECK-NEXT: scf.if %[[VAL_10]] { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_12]]] = %[[VAL_11]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_0]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@InnerConditional4<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerConditional4<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional4<[@N]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_16]][@out] : <@InnerConditional4<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_21]], %[[VAL_18]]) +// CHECK-NEXT: scf.condition(%[[VAL_22]]) %[[VAL_21]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_23]], %[[VAL_24]]) +// CHECK-NEXT: scf.if %[[VAL_25]] { +// CHECK-NEXT: } else { +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..2f5e20441 --- /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 -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..919006df9 --- /dev/null +++ b/circom/tests/loops/inner_conditional_06.circom @@ -0,0 +1,86 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional6<[4]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional6<[@N]> { +// CHECK-NEXT: struct.field @out : !array.type<@N x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@InnerConditional6<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional6<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@N x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_10]]] : <@N x !felt.type>, !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_13]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_17]]] = %[[VAL_14]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@InnerConditional6<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerConditional6<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional6<[@N]>>, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_20]][@out] : <@InnerConditional6<[@N]>>, !array.type<@N x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_25]], %[[VAL_22]]) +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_25]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_21]]{{\[}}%[[VAL_29]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_30]], %[[VAL_31]]) +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_32]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: scf.yield %[[VAL_34]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: scf.yield %[[VAL_35]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_27]], %[[VAL_36]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_37]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..1479e0b35 --- /dev/null +++ b/circom/tests/loops/inner_conditional_07.circom @@ -0,0 +1,151 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional7<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional7<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerConditional7<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional7<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>) { +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_17]]#0, %[[V_I3]] : !array.type<@N x !felt.type>, !felt.type +// 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>, !felt.type +// 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>, !felt.type +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_42]], %[[V_45]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_0]][@out] = %[[V_46]] : <@InnerConditional7<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional7<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_47:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_47]][@out] : <@InnerConditional7<[@N]>>, !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>) { +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_64]]#0, %[[V_I3]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..50ec33ea5 --- /dev/null +++ b/circom/tests/loops/inner_conditional_08.circom @@ -0,0 +1,152 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional8<[4]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional8<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerConditional8<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional8<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>) { +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_A4]] : !array.type<@N x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !array.type<@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_17]]#0, %[[V_I3]] : !array.type<@N x !felt.type>, !felt.type +// 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>, !felt.type +// 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>, !felt.type +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_42]], %[[V_45]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_0]][@out] = %[[V_46]] : <@InnerConditional8<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional8<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_47:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_47]][@out] : <@InnerConditional8<[@N]>>, !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_61:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_62:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_68:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_69:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>) { +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_68]] : !array.type<@N x !felt.type> +// 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>, !felt.type +// 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, !felt.type +// 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>, !felt.type +// CHECK-NEXT: scf.yield %[[V_68]] : !array.type<@N x !felt.type> +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_72]], %[[V_84]] : !array.type<@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_64]]#0, %[[V_86]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..11e29e69d --- /dev/null +++ b/circom/tests/loops/inner_conditional_09.circom @@ -0,0 +1,175 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional9<[4]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional9<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerConditional9<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional9<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type> +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = array.len %[[V_3]], %[[V_4]] : <@N x !felt.type> +// 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_3]]{{\[}}%[[V_8]]] = %[[V_2]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_11:[0-9a-zA-Z_\.]+]] = %[[V_3]], %[[V_12:[0-9a-zA-Z_\.]+]] = %[[V_9]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_12]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_13]]) %[[V_11]], %[[V_12]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_14:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_15:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_15]], %[[V_16]]) +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = scf.if %[[V_17]] -> (!array.type<@N x !felt.type>) { +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_21:[0-9a-zA-Z_\.]+]] = %[[V_14]], %[[V_22:[0-9a-zA-Z_\.]+]] = %[[V_19]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_22]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_23]]) %[[V_21]], %[[V_22]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_24:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_15]] +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = array.read %[[V_24]]{{\[}}%[[V_26]]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_15]] +// CHECK-NEXT: array.write %[[V_24]]{{\[}}%[[V_30]]] = %[[V_29]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_31]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_24]], %[[V_32]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_20]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_35:[0-9a-zA-Z_\.]+]] = %[[V_14]], %[[V_36:[0-9a-zA-Z_\.]+]] = %[[V_33]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_36]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_37]]) %[[V_35]], %[[V_36]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_38:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_39:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_15]] +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = array.read %[[V_38]]{{\[}}%[[V_40]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = felt.add %[[V_41]], %[[V_42]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_15]] +// CHECK-NEXT: array.write %[[V_38]]{{\[}}%[[V_44]]] = %[[V_43]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_39]], %[[V_45]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_38]], %[[V_46]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_34]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = felt.add %[[V_15]], %[[V_47]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_18]], %[[V_48]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_49]] +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_50]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_52]] +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_53]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = felt.add %[[V_51]], %[[V_54]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_0]][@out] = %[[V_55]] : <@InnerConditional9<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional9<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_56:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional9<[@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 +// CHECK-NEXT: %[[V_105:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_56]][@out] : <@InnerConditional9<[@N]>>, !felt.type +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type> +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_60]] : <@N x !felt.type> +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_64:[0-9a-zA-Z_\.]+]] = %[[V_62]] to %[[V_61]] step %[[V_63]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_64]]] = %[[V_58]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_67:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_68:[0-9a-zA-Z_\.]+]] = %[[V_65]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_68]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_69]]) %[[V_67]], %[[V_68]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_70:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_71:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_71]], %[[V_72]]) +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = scf.if %[[V_73]] -> (!array.type<@N x !felt.type>) { +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_76:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_77:[0-9a-zA-Z_\.]+]] = %[[V_70]], %[[V_78:[0-9a-zA-Z_\.]+]] = %[[V_75]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_78]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_79]]) %[[V_77]], %[[V_78]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_80:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_81:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = array.read %[[V_80]]{{\[}}%[[V_82]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_84:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = felt.add %[[V_83]], %[[V_84]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: array.write %[[V_80]]{{\[}}%[[V_86]]] = %[[V_85]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_88:[0-9a-zA-Z_\.]+]] = felt.add %[[V_81]], %[[V_87]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_80]], %[[V_88]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_76]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_89:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_90:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_91:[0-9a-zA-Z_\.]+]] = %[[V_70]], %[[V_92:[0-9a-zA-Z_\.]+]] = %[[V_89]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_93:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_92]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_93]]) %[[V_91]], %[[V_92]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_94:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_95:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_96:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: %[[V_97:[0-9a-zA-Z_\.]+]] = array.read %[[V_94]]{{\[}}%[[V_96]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_98:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[V_99:[0-9a-zA-Z_\.]+]] = felt.add %[[V_97]], %[[V_98]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_100:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: array.write %[[V_94]]{{\[}}%[[V_100]]] = %[[V_99]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_101:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_102:[0-9a-zA-Z_\.]+]] = felt.add %[[V_95]], %[[V_101]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_94]], %[[V_102]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_90]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_103:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_104:[0-9a-zA-Z_\.]+]] = felt.add %[[V_71]], %[[V_103]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_74]], %[[V_104]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..e50262633 --- /dev/null +++ b/circom/tests/loops/inner_conditional_10.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/loops/inner_conditional_11.circom b/circom/tests/loops/inner_conditional_11.circom new file mode 100644 index 000000000..09a0fc8b5 --- /dev/null +++ b/circom/tests/loops/inner_conditional_11.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/loops/inner_conditional_12.circom b/circom/tests/loops/inner_conditional_12.circom new file mode 100644 index 000000000..af5cc3a09 --- /dev/null +++ b/circom/tests/loops/inner_conditional_12.circom @@ -0,0 +1,171 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerConditional12<[4]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerConditional12<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerConditional12<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_0:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerConditional12<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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_11:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_I1:[0-9a-zA-Z_\.]+]] = %[[V_I0]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I1]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_13]]) %[[V_11]], %[[V_I1]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_14:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_I2]], %[[V_16]]) +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = scf.if %[[V_17]] -> (!array.type<@N x !felt.type>) { +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_21:[0-9a-zA-Z_\.]+]] = %[[V_14]], %[[V_22:[0-9a-zA-Z_\.]+]] = %[[V_19]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_22]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_23]]) %[[V_21]], %[[V_22]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_24:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = array.read %[[V_24]]{{\[}}%[[V_26]]] : <@N x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_24]]{{\[}}%[[V_30]]] = %[[V_29]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_31]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_24]], %[[V_32]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_20]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_35:[0-9a-zA-Z_\.]+]] = %[[V_14]], %[[V_36:[0-9a-zA-Z_\.]+]] = %[[V_33]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_36]], %[[V_I2]]) +// CHECK-NEXT: scf.condition(%[[V_37]]) %[[V_35]], %[[V_36]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_38:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_39:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = array.read %[[V_38]]{{\[}}%[[V_40]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]] = felt.add %[[V_41]], %[[V_42]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_I2]] +// CHECK-NEXT: array.write %[[V_38]]{{\[}}%[[V_44]]] = %[[V_43]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.add %[[V_39]], %[[V_45]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_38]], %[[V_46]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_34]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = felt.add %[[V_I2]], %[[V_47]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_18]], %[[V_48]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_49]] +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_50]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_52]] +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = array.read %[[V_10]]#0{{\[}}%[[V_53]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = felt.add %[[V_51]], %[[V_54]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_0]][@out] = %[[V_55]] : <@InnerConditional12<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[V_0]] : !struct.type<@InnerConditional12<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_56:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerConditional12<[@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 +// CHECK-NEXT: %[[V_105:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_56]][@out] : <@InnerConditional12<[@N]>>, !felt.type +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new : <@N x !felt.type> +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = array.len %[[V_A]], %[[V_60]] : <@N x !felt.type> +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_64:[0-9a-zA-Z_\.]+]] = %[[V_62]] to %[[V_61]] step %[[V_63]] { +// CHECK-NEXT: array.write %[[V_A]]{{\[}}%[[V_64]]] = %[[V_58]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_67:[0-9a-zA-Z_\.]+]] = %[[V_A]], %[[V_68:[0-9a-zA-Z_\.]+]] = %[[V_65]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_68]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_69]]) %[[V_67]], %[[V_68]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_70:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_71:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_71]], %[[V_72]]) +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = scf.if %[[V_73]] -> (!array.type<@N x !felt.type>) { +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_76:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_77:[0-9a-zA-Z_\.]+]] = %[[V_70]], %[[V_78:[0-9a-zA-Z_\.]+]] = %[[V_75]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_78]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_79]]) %[[V_77]], %[[V_78]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_80:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_81:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = array.read %[[V_80]]{{\[}}%[[V_82]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_84:[0-9a-zA-Z_\.]+]] = felt.const 999 +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = felt.add %[[V_83]], %[[V_84]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_86:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: array.write %[[V_80]]{{\[}}%[[V_86]]] = %[[V_85]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_88:[0-9a-zA-Z_\.]+]] = felt.add %[[V_81]], %[[V_87]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_80]], %[[V_88]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_76]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[V_89:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_90:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_91:[0-9a-zA-Z_\.]+]] = %[[V_70]], %[[V_92:[0-9a-zA-Z_\.]+]] = %[[V_89]]) : (!array.type<@N x !felt.type>, !felt.type) -> (!array.type<@N x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_93:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_92]], %[[V_71]]) +// CHECK-NEXT: scf.condition(%[[V_93]]) %[[V_91]], %[[V_92]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_94:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>, %[[V_95:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_96:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: %[[V_97:[0-9a-zA-Z_\.]+]] = array.read %[[V_94]]{{\[}}%[[V_96]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_98:[0-9a-zA-Z_\.]+]] = felt.const 888 +// CHECK-NEXT: %[[V_99:[0-9a-zA-Z_\.]+]] = felt.add %[[V_97]], %[[V_98]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_100:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_71]] +// CHECK-NEXT: array.write %[[V_94]]{{\[}}%[[V_100]]] = %[[V_99]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_101:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_102:[0-9a-zA-Z_\.]+]] = felt.add %[[V_95]], %[[V_101]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_94]], %[[V_102]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: scf.yield %[[V_90]]#0 : !array.type<@N x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_103:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_104:[0-9a-zA-Z_\.]+]] = felt.add %[[V_71]], %[[V_103]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_74]], %[[V_104]] : !array.type<@N x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..7bc41320c --- /dev/null +++ b/circom/tests/loops/inner_loop_00.circom @@ -0,0 +1,110 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2, 3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n, @m]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_IN:[0-9a-zA-Z_\.]+]]: !array.type<@m x !felt.type>) -> !struct.type<@InnerLoops<[@n, @m]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n, @m]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_M:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_I3:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_23:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_23]], %[[V_29]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_19]]#0, %[[V_31]] : !array.type<@n x !felt.type>, !felt.type +// 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>, !felt.type +// CHECK-NEXT: struct.writef %[[V_1]][@out] = %[[V_34]] : <@InnerLoops<[@n, @m]>>, !felt.type +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n, @m]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_IN:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n, @m]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@m x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_M:[0-9a-zA-Z_\.]+]] = poly.read_const @m : !felt.type +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_IN]][@out] : <@InnerLoops<[@n, @m]>>, !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_51:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_52:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_58:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_59:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_58]], %[[V_64]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_54]]#0, %[[V_66]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..5136e7091 --- /dev/null +++ b/circom/tests/loops/inner_loop_01.circom @@ -0,0 +1,108 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_J3:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_I2]], %[[V_J3]] : !felt.type, !felt.type +// 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>, !felt.type +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_28]] : !felt.type, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_B4]], %[[V_32]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_18]]#0, %[[V_34]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_50:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_51:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_57:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_58:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_51]], %[[V_58]] : !felt.type, !felt.type +// 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>, !felt.type +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = felt.add %[[V_60]], %[[V_63]] : !felt.type, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_57]], %[[V_67]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_53]]#0, %[[V_69]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..20132729c --- /dev/null +++ b/circom/tests/loops/inner_loop_02.circom @@ -0,0 +1,243 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_11:[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_14:[0-9a-zA-Z_\.]+]] = %[[V_11]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_14]], %[[V_10]]) +// CHECK-NEXT: scf.condition(%[[V_15]]) %[[V_B1]], %[[V_14]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_17:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_10]], %[[V_17]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_18]] +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_19]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_10]] +// CHECK-NEXT: array.write %[[V_B2]]{{\[}}%[[V_21]]] = %[[V_20]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = felt.add %[[V_17]], %[[V_22]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_B2]], %[[V_23]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.add %[[V_10]], %[[V_24]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B3:[0-9a-zA-Z_\.]+]] = %[[V_12]]#0, %[[V_29:[0-9a-zA-Z_\.]+]] = %[[V_26]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_29]], %[[V_25]]) +// CHECK-NEXT: scf.condition(%[[V_30]]) %[[V_B3]], %[[V_29]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_32:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_25]], %[[V_32]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_33]] +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_34]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_25]] +// CHECK-NEXT: array.write %[[V_B4]]{{\[}}%[[V_36]]] = %[[V_35]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.add %[[V_32]], %[[V_37]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_B4]], %[[V_38]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_39]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B5:[0-9a-zA-Z_\.]+]] = %[[V_27]]#0, %[[V_44:[0-9a-zA-Z_\.]+]] = %[[V_41]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_44]], %[[V_40]]) +// CHECK-NEXT: scf.condition(%[[V_45]]) %[[V_B5]], %[[V_44]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B6:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_47:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_40]], %[[V_47]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_49:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_48]] +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_49]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_40]] +// CHECK-NEXT: array.write %[[V_B6]]{{\[}}%[[V_51]]] = %[[V_50]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = felt.add %[[V_47]], %[[V_52]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_B6]], %[[V_53]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = felt.add %[[V_40]], %[[V_54]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B7:[0-9a-zA-Z_\.]+]] = %[[V_42]]#0, %[[V_59:[0-9a-zA-Z_\.]+]] = %[[V_56]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_59]], %[[V_55]]) +// CHECK-NEXT: scf.condition(%[[V_60]]) %[[V_B7]], %[[V_59]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B8:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_62:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_55]], %[[V_62]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_63]] +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_64]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_55]] +// CHECK-NEXT: array.write %[[V_B8]]{{\[}}%[[V_66]]] = %[[V_65]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_68:[0-9a-zA-Z_\.]+]] = felt.add %[[V_62]], %[[V_67]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_B8]], %[[V_68]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_70:[0-9a-zA-Z_\.]+]] = felt.add %[[V_55]], %[[V_69]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_71:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_B9:[0-9a-zA-Z_\.]+]] = %[[V_57]]#0, %[[V_74:[0-9a-zA-Z_\.]+]] = %[[V_71]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_74]], %[[V_70]]) +// CHECK-NEXT: scf.condition(%[[V_75]]) %[[V_B9]], %[[V_74]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_76:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_77:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_78:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_70]], %[[V_77]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_78]] +// CHECK-NEXT: %[[V_80:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_79]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_81:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_70]] +// CHECK-NEXT: array.write %[[V_76]]{{\[}}%[[V_81]]] = %[[V_80]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = felt.add %[[V_77]], %[[V_82]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_76]], %[[V_83]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_84:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = felt.add %[[V_70]], %[[V_84]] : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n]>>, %[[V_87:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_89:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type> +// CHECK-NEXT: %[[V_91:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_92:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_91]] : <@n x !felt.type> +// CHECK-NEXT: %[[V_93:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_94:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_95:[0-9a-zA-Z_\.]+]] = %[[V_93]] to %[[V_92]] step %[[V_94]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_95]]] = %[[V_89]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_96:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_97:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_98:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_99:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_100:[0-9a-zA-Z_\.]+]] = %[[V_97]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_101:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_100]], %[[V_96]]) +// CHECK-NEXT: scf.condition(%[[V_101]]) %[[V_99]], %[[V_100]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_102:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_103:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_104:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_96]], %[[V_103]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_105:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_104]] +// CHECK-NEXT: %[[V_106:[0-9a-zA-Z_\.]+]] = array.read %[[V_87]]{{\[}}%[[V_105]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_107:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_96]] +// CHECK-NEXT: array.write %[[V_102]]{{\[}}%[[V_107]]] = %[[V_106]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_108:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_109:[0-9a-zA-Z_\.]+]] = felt.add %[[V_103]], %[[V_108]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_102]], %[[V_109]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_110:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_111:[0-9a-zA-Z_\.]+]] = felt.add %[[V_96]], %[[V_110]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_112:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_113:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_114:[0-9a-zA-Z_\.]+]] = %[[V_98]]#0, %[[V_115:[0-9a-zA-Z_\.]+]] = %[[V_112]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_116:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_115]], %[[V_111]]) +// CHECK-NEXT: scf.condition(%[[V_116]]) %[[V_114]], %[[V_115]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_117:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_118:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_119:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_111]], %[[V_118]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_120:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_119]] +// CHECK-NEXT: %[[V_121:[0-9a-zA-Z_\.]+]] = array.read %[[V_87]]{{\[}}%[[V_120]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_122:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_111]] +// CHECK-NEXT: array.write %[[V_117]]{{\[}}%[[V_122]]] = %[[V_121]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_123:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_124:[0-9a-zA-Z_\.]+]] = felt.add %[[V_118]], %[[V_123]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_117]], %[[V_124]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_125:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_126:[0-9a-zA-Z_\.]+]] = felt.add %[[V_111]], %[[V_125]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_127:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_128:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_129:[0-9a-zA-Z_\.]+]] = %[[V_113]]#0, %[[V_130:[0-9a-zA-Z_\.]+]] = %[[V_127]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_131:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_130]], %[[V_126]]) +// CHECK-NEXT: scf.condition(%[[V_131]]) %[[V_129]], %[[V_130]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_132:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_133:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_134:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_126]], %[[V_133]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_135:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_134]] +// CHECK-NEXT: %[[V_136:[0-9a-zA-Z_\.]+]] = array.read %[[V_87]]{{\[}}%[[V_135]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_137:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_126]] +// CHECK-NEXT: array.write %[[V_132]]{{\[}}%[[V_137]]] = %[[V_136]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_138:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_139:[0-9a-zA-Z_\.]+]] = felt.add %[[V_133]], %[[V_138]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_132]], %[[V_139]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_140:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_141:[0-9a-zA-Z_\.]+]] = felt.add %[[V_126]], %[[V_140]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_142:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_143:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_144:[0-9a-zA-Z_\.]+]] = %[[V_128]]#0, %[[V_145:[0-9a-zA-Z_\.]+]] = %[[V_142]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_146:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_145]], %[[V_141]]) +// CHECK-NEXT: scf.condition(%[[V_146]]) %[[V_144]], %[[V_145]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_147:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_148:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_149:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_141]], %[[V_148]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_150:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_149]] +// CHECK-NEXT: %[[V_151:[0-9a-zA-Z_\.]+]] = array.read %[[V_87]]{{\[}}%[[V_150]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_152:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_141]] +// CHECK-NEXT: array.write %[[V_147]]{{\[}}%[[V_152]]] = %[[V_151]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_153:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_154:[0-9a-zA-Z_\.]+]] = felt.add %[[V_148]], %[[V_153]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_147]], %[[V_154]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_155:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_156:[0-9a-zA-Z_\.]+]] = felt.add %[[V_141]], %[[V_155]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_157:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_158:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_159:[0-9a-zA-Z_\.]+]] = %[[V_143]]#0, %[[V_160:[0-9a-zA-Z_\.]+]] = %[[V_157]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_161:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_160]], %[[V_156]]) +// CHECK-NEXT: scf.condition(%[[V_161]]) %[[V_159]], %[[V_160]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_162:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_163:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_164:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_156]], %[[V_163]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_165:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_164]] +// CHECK-NEXT: %[[V_166:[0-9a-zA-Z_\.]+]] = array.read %[[V_87]]{{\[}}%[[V_165]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_167:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_156]] +// CHECK-NEXT: array.write %[[V_162]]{{\[}}%[[V_167]]] = %[[V_166]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_168:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_169:[0-9a-zA-Z_\.]+]] = felt.add %[[V_163]], %[[V_168]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_162]], %[[V_169]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_170:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_171:[0-9a-zA-Z_\.]+]] = felt.add %[[V_156]], %[[V_170]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..bcf45b579 --- /dev/null +++ b/circom/tests/loops/inner_loop_03.circom @@ -0,0 +1,242 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_12:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_13:[0-9a-zA-Z_\.]+]] = %[[V_10]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_13]], %[[V_14]]) +// CHECK-NEXT: scf.condition(%[[V_15]]) %[[V_12]], %[[V_13]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_17:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_18]], %[[V_17]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_19]] +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_20]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_22]] +// CHECK-NEXT: array.write %[[V_16]]{{\[}}%[[V_23]]] = %[[V_21]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.add %[[V_17]], %[[V_24]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_16]], %[[V_25]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_28:[0-9a-zA-Z_\.]+]] = %[[V_11]]#0, %[[V_29:[0-9a-zA-Z_\.]+]] = %[[V_26]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_29]], %[[V_30]]) +// CHECK-NEXT: scf.condition(%[[V_31]]) %[[V_28]], %[[V_29]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_32:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_33:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_34:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_35:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_34]], %[[V_33]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_36:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_35]] +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_36]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_38]] +// CHECK-NEXT: array.write %[[V_32]]{{\[}}%[[V_39]]] = %[[V_37]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = felt.add %[[V_33]], %[[V_40]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_32]], %[[V_41]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_43:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_44:[0-9a-zA-Z_\.]+]] = %[[V_27]]#0, %[[V_45:[0-9a-zA-Z_\.]+]] = %[[V_42]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_47:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_45]], %[[V_46]]) +// CHECK-NEXT: scf.condition(%[[V_47]]) %[[V_44]], %[[V_45]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_48:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_49:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_50:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_50]], %[[V_49]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_51]] +// CHECK-NEXT: %[[V_53:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_52]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_54:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_54]] +// CHECK-NEXT: array.write %[[V_48]]{{\[}}%[[V_55]]] = %[[V_53]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_56:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_57:[0-9a-zA-Z_\.]+]] = felt.add %[[V_49]], %[[V_56]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_48]], %[[V_57]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_59:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_60:[0-9a-zA-Z_\.]+]] = %[[V_43]]#0, %[[V_61:[0-9a-zA-Z_\.]+]] = %[[V_58]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_61]], %[[V_62]]) +// CHECK-NEXT: scf.condition(%[[V_63]]) %[[V_60]], %[[V_61]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_64:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_65:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_66]], %[[V_65]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_68:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_67]] +// CHECK-NEXT: %[[V_69:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_68]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_70:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_71:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_70]] +// CHECK-NEXT: array.write %[[V_64]]{{\[}}%[[V_71]]] = %[[V_69]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_72:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_73:[0-9a-zA-Z_\.]+]] = felt.add %[[V_65]], %[[V_72]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_64]], %[[V_73]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_74:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_75:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_76:[0-9a-zA-Z_\.]+]] = %[[V_59]]#0, %[[V_77:[0-9a-zA-Z_\.]+]] = %[[V_74]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_78:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_79:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_77]], %[[V_78]]) +// CHECK-NEXT: scf.condition(%[[V_79]]) %[[V_76]], %[[V_77]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_80:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_81:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_82:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_83:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_82]], %[[V_81]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_84:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_83]] +// CHECK-NEXT: %[[V_85:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_84]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_86:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_87:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_86]] +// CHECK-NEXT: array.write %[[V_80]]{{\[}}%[[V_87]]] = %[[V_85]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_88:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_89:[0-9a-zA-Z_\.]+]] = felt.add %[[V_81]], %[[V_88]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_80]], %[[V_89]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n]>>, %[[V_91:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_93:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_B:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type> +// CHECK-NEXT: %[[V_95:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_96:[0-9a-zA-Z_\.]+]] = array.len %[[V_B]], %[[V_95]] : <@n x !felt.type> +// CHECK-NEXT: %[[V_97:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_98:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_99:[0-9a-zA-Z_\.]+]] = %[[V_97]] to %[[V_96]] step %[[V_98]] { +// CHECK-NEXT: array.write %[[V_B]]{{\[}}%[[V_99]]] = %[[V_93]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_100:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_101:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_102:[0-9a-zA-Z_\.]+]] = %[[V_B]], %[[V_103:[0-9a-zA-Z_\.]+]] = %[[V_100]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_104:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_105:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_103]], %[[V_104]]) +// CHECK-NEXT: scf.condition(%[[V_105]]) %[[V_102]], %[[V_103]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_106:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_107:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_108:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_109:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_108]], %[[V_107]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_110:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_109]] +// CHECK-NEXT: %[[V_111:[0-9a-zA-Z_\.]+]] = array.read %[[V_91]]{{\[}}%[[V_110]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_112:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_113:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_112]] +// CHECK-NEXT: array.write %[[V_106]]{{\[}}%[[V_113]]] = %[[V_111]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_114:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_115:[0-9a-zA-Z_\.]+]] = felt.add %[[V_107]], %[[V_114]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_106]], %[[V_115]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_116:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_117:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_118:[0-9a-zA-Z_\.]+]] = %[[V_101]]#0, %[[V_119:[0-9a-zA-Z_\.]+]] = %[[V_116]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_120:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_121:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_119]], %[[V_120]]) +// CHECK-NEXT: scf.condition(%[[V_121]]) %[[V_118]], %[[V_119]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_122:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_123:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_124:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_125:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_124]], %[[V_123]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_126:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_125]] +// CHECK-NEXT: %[[V_127:[0-9a-zA-Z_\.]+]] = array.read %[[V_91]]{{\[}}%[[V_126]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_128:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_129:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_128]] +// CHECK-NEXT: array.write %[[V_122]]{{\[}}%[[V_129]]] = %[[V_127]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_130:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_131:[0-9a-zA-Z_\.]+]] = felt.add %[[V_123]], %[[V_130]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_122]], %[[V_131]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_132:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_133:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_134:[0-9a-zA-Z_\.]+]] = %[[V_117]]#0, %[[V_135:[0-9a-zA-Z_\.]+]] = %[[V_132]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_136:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_137:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_135]], %[[V_136]]) +// CHECK-NEXT: scf.condition(%[[V_137]]) %[[V_134]], %[[V_135]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_138:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_139:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_140:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_141:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_140]], %[[V_139]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_142:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_141]] +// CHECK-NEXT: %[[V_143:[0-9a-zA-Z_\.]+]] = array.read %[[V_91]]{{\[}}%[[V_142]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_144:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_145:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_144]] +// CHECK-NEXT: array.write %[[V_138]]{{\[}}%[[V_145]]] = %[[V_143]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_146:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_147:[0-9a-zA-Z_\.]+]] = felt.add %[[V_139]], %[[V_146]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_138]], %[[V_147]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_148:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_149:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_150:[0-9a-zA-Z_\.]+]] = %[[V_133]]#0, %[[V_151:[0-9a-zA-Z_\.]+]] = %[[V_148]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_152:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_153:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_151]], %[[V_152]]) +// CHECK-NEXT: scf.condition(%[[V_153]]) %[[V_150]], %[[V_151]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_154:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_155:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_156:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_157:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_156]], %[[V_155]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_158:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_157]] +// CHECK-NEXT: %[[V_159:[0-9a-zA-Z_\.]+]] = array.read %[[V_91]]{{\[}}%[[V_158]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_160:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[V_161:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_160]] +// CHECK-NEXT: array.write %[[V_154]]{{\[}}%[[V_161]]] = %[[V_159]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_162:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_163:[0-9a-zA-Z_\.]+]] = felt.add %[[V_155]], %[[V_162]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_154]], %[[V_163]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_164:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_165:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_166:[0-9a-zA-Z_\.]+]] = %[[V_149]]#0, %[[V_167:[0-9a-zA-Z_\.]+]] = %[[V_164]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_168:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_169:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_167]], %[[V_168]]) +// CHECK-NEXT: scf.condition(%[[V_169]]) %[[V_166]], %[[V_167]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_170:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_171:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_172:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_173:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_172]], %[[V_171]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_174:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_173]] +// CHECK-NEXT: %[[V_175:[0-9a-zA-Z_\.]+]] = array.read %[[V_91]]{{\[}}%[[V_174]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_176:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[V_177:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_176]] +// CHECK-NEXT: array.write %[[V_170]]{{\[}}%[[V_177]]] = %[[V_175]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_178:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_179:[0-9a-zA-Z_\.]+]] = felt.add %[[V_171]], %[[V_178]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_170]], %[[V_179]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..54c18a90c --- /dev/null +++ b/circom/tests/loops/inner_loop_04.circom @@ -0,0 +1,104 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type, !felt.type) { +// 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>, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B2:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_B4:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_J4:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_I2]], %[[V_J4]] : !felt.type, !felt.type +// 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>, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_B4]], %[[V_J5]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_21]]#0, %[[V_I3]], %[[V_21]]#1 : !array.type<@n x !felt.type>, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_A:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n]>>, %[[V_36:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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>, !felt.type, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type, !felt.type) { +// 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>, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_52:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_53:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_54:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// 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>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_60:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_61:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = felt.sub %[[V_53]], %[[V_61]] : !felt.type, !felt.type +// 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>, !felt.type +// 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>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_60]], %[[V_67]] : !array.type<@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_56]]#0, %[[V_69]], %[[V_56]]#1 : !array.type<@n x !felt.type>, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..261c9a74c --- /dev/null +++ b/circom/tests/loops/inner_loop_05.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/loops/inner_loop_06.circom b/circom/tests/loops/inner_loop_06.circom new file mode 100644 index 000000000..1ba107be6 --- /dev/null +++ b/circom/tests/loops/inner_loop_06.circom @@ -0,0 +1,106 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@n]> { +// CHECK-NEXT: function.def @compute +// CHECK-SAME: (%[[V_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@InnerLoops<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = array.len %[[V_4]], %[[V_5]] : <@n x !felt.type> +// 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_4]]{{\[}}%[[V_9]]] = %[[V_3]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_12:[0-9a-zA-Z_\.]+]] = %[[V_4]], %[[V_13:[0-9a-zA-Z_\.]+]] = %[[V_10]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_13]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_14]]) %[[V_12]], %[[V_13]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_16:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_19:[0-9a-zA-Z_\.]+]] = %[[V_15]], %[[V_20:[0-9a-zA-Z_\.]+]] = %[[V_17]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_20]], %[[V_16]]) +// CHECK-NEXT: scf.condition(%[[V_21]]) %[[V_19]], %[[V_20]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_22:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_16]] +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = array.read %[[V_22]]{{\[}}%[[V_24]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_16]] +// CHECK-NEXT: %[[V_27:[0-9a-zA-Z_\.]+]] = array.read %[[V_0]]{{\[}}%[[V_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_28:[0-9a-zA-Z_\.]+]] = felt.add %[[V_25]], %[[V_27]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_16]] +// CHECK-NEXT: array.write %[[V_22]]{{\[}}%[[V_29]]] = %[[V_28]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_30:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_31:[0-9a-zA-Z_\.]+]] = felt.add %[[V_23]], %[[V_30]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_22]], %[[V_31]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_33:[0-9a-zA-Z_\.]+]] = felt.add %[[V_16]], %[[V_32]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_18]]#0, %[[V_33]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[V_1]] : !struct.type<@InnerLoops<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain +// CHECK-SAME: (%[[V_34:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@n]>>, %[[V_35:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_38:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type> +// CHECK-NEXT: %[[V_39:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_40:[0-9a-zA-Z_\.]+]] = array.len %[[V_38]], %[[V_39]] : <@n x !felt.type> +// CHECK-NEXT: %[[V_41:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[V_42:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[V_43:[0-9a-zA-Z_\.]+]] = %[[V_41]] to %[[V_40]] step %[[V_42]] { +// CHECK-NEXT: array.write %[[V_38]]{{\[}}%[[V_43]]] = %[[V_37]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_44:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_45:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_46:[0-9a-zA-Z_\.]+]] = %[[V_38]], %[[V_47:[0-9a-zA-Z_\.]+]] = %[[V_44]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_48:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[V_47]], %[[V_N]]) +// CHECK-NEXT: scf.condition(%[[V_48]]) %[[V_46]], %[[V_47]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_49:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_50:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_51:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_52:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[V_53:[0-9a-zA-Z_\.]+]] = %[[V_49]], %[[V_54:[0-9a-zA-Z_\.]+]] = %[[V_51]]) : (!array.type<@n x !felt.type>, !felt.type) -> (!array.type<@n x !felt.type>, !felt.type) { +// CHECK-NEXT: %[[V_55:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[V_54]], %[[V_50]]) +// CHECK-NEXT: scf.condition(%[[V_55]]) %[[V_53]], %[[V_54]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_56:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[V_57:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_50]] +// CHECK-NEXT: %[[V_59:[0-9a-zA-Z_\.]+]] = array.read %[[V_56]]{{\[}}%[[V_58]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_50]] +// CHECK-NEXT: %[[V_61:[0-9a-zA-Z_\.]+]] = array.read %[[V_35]]{{\[}}%[[V_60]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_62:[0-9a-zA-Z_\.]+]] = felt.add %[[V_59]], %[[V_61]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_63:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_50]] +// CHECK-NEXT: array.write %[[V_56]]{{\[}}%[[V_63]]] = %[[V_62]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_64:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_65:[0-9a-zA-Z_\.]+]] = felt.add %[[V_57]], %[[V_64]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_56]], %[[V_65]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[V_66:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_67:[0-9a-zA-Z_\.]+]] = felt.add %[[V_50]], %[[V_66]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[V_52]]#0, %[[V_67]] : !array.type<@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..2503df78c --- /dev/null +++ b/circom/tests/loops/inner_loop_07.circom @@ -0,0 +1,83 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_11]]#0, %[[V_I3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_4]]#0 : <@InnerLoops<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[V_46:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@out] : <@InnerLoops<[@N]>>, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_34]]#0, %[[V_I3]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..4f4968064 --- /dev/null +++ b/circom/tests/loops/inner_loop_08.circom @@ -0,0 +1,94 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@N]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_B3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_B2]], %[[V_A2]] : !felt.type, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type, !felt.type +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_15]]#0, %[[V_B4]], %[[V_I3]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_5]]#0 : <@InnerLoops<[@N]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[V_58:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@out] : <@InnerLoops<[@N]>>, !felt.type +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// 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, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_B2:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_I2:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[V_B3:[0-9a-zA-Z_\.]+]] = felt.add %[[V_B2]], %[[V_A2]] : !felt.type, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[V_A4:[0-9a-zA-Z_\.]+]]: !felt.type, %[[V_J2:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_A5]], %[[V_J3]] : !felt.type, !felt.type +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[V_44]]#0, %[[V_B4]], %[[V_I3]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..5c3dc609c --- /dev/null +++ b/circom/tests/loops/inner_loop_09.circom @@ -0,0 +1,101 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@InnerLoops<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @InnerLoops<[@N]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @out2 : !array.type<@N x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) -> !struct.type<@InnerLoops<[@N]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@InnerLoops<[@N]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@N x !felt.type> +// 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, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_10]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_11]]) %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_13:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_15]]] : <@N x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_16]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@N x !felt.type>, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_22]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_23]]) %[[VAL_21]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_24:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]], %[[VAL_29]] : !felt.type, !felt.type +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_20]]#0, %[[VAL_31]], %[[VAL_33]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_7]]#0 : <@InnerLoops<[@N]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out2] = %[[VAL_3]] : <@InnerLoops<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@InnerLoops<[@N]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_34:[0-9a-zA-Z_\.]+]]: !struct.type<@InnerLoops<[@N]>>, %[[VAL_35:[0-9a-zA-Z_\.]+]]: !array.type<@N x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = poly.read_const @N : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_34]][@out] : <@InnerLoops<[@N]>>, !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_34]][@out2] : <@InnerLoops<[@N]>>, !array.type<@N x !felt.type> +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]]:3 = scf.while (%[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_37]], %[[VAL_42:[0-9a-zA-Z_\.]+]] = %[[VAL_38]], %[[VAL_43:[0-9a-zA-Z_\.]+]] = %[[VAL_39]]) : (!felt.type, !felt.type, !felt.type) -> (!felt.type, !felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_43]], %[[VAL_36]]) +// CHECK-NEXT: scf.condition(%[[VAL_44]]) %[[VAL_41]], %[[VAL_42]], %[[VAL_43]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_45:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_50:[0-9a-zA-Z_\.]+]] = %[[VAL_45]], %[[VAL_51:[0-9a-zA-Z_\.]+]] = %[[VAL_48]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_51]], %[[VAL_36]]) +// CHECK-NEXT: scf.condition(%[[VAL_52]]) %[[VAL_50]], %[[VAL_51]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_53:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_54:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_53]], %[[VAL_55]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_54]], %[[VAL_57]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_56]], %[[VAL_58]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_46]], %[[VAL_59]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_61]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_49]]#0, %[[VAL_60]], %[[VAL_62]] : !felt.type, !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..edd1158f3 --- /dev/null +++ b/circom/tests/loops/known_function.circom @@ -0,0 +1,100 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@KnownFunctionArgs<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @funWithLoop(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_5]], %[[VAL_0]]) +// CHECK-NEXT: scf.condition(%[[VAL_6]]) %[[VAL_4]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_9]], %[[VAL_11]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]]#0 : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @KnownFunctionArgs<[]> { +// CHECK-NEXT: struct.field @out : !array.type<3 x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@KnownFunctionArgs<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@KnownFunctionArgs<[]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<3 x !felt.type> +// 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) -> !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_16]] +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_17]]] = %[[VAL_15]] : <3 x !felt.type>, !felt.type +// 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) -> !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_21]]] = %[[VAL_19]] : <3 x !felt.type>, !felt.type +// 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, !felt.type) -> (!felt.type, !felt.type) { +// 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) -> !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_26]], %[[VAL_28]]) +// CHECK-NEXT: scf.condition(%[[VAL_29]]) %[[VAL_25]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_30:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_30]], %[[VAL_31]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_34]] : !felt.type, !felt.type +// 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]] +// CHECK-NEXT: array.write %[[VAL_13]]{{\[}}%[[VAL_36]]] = %[[VAL_24]]#0 : <3 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_12]][@out] = %[[VAL_13]] : <@KnownFunctionArgs<[]>>, !array.type<3 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@KnownFunctionArgs<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_37:[0-9a-zA-Z_\.]+]]: !struct.type<@KnownFunctionArgs<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_37]][@out] : <@KnownFunctionArgs<[]>>, !array.type<3 x !felt.type> +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_38]], %[[VAL_42:[0-9a-zA-Z_\.]+]] = %[[VAL_39]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = function.call @funWithLoop(%[[VAL_43]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_42]], %[[VAL_44]]) +// CHECK-NEXT: scf.condition(%[[VAL_45]]) %[[VAL_41]], %[[VAL_42]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_46:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_46]], %[[VAL_47]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_50:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_49]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_48]], %[[VAL_50]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..051b65a55 --- /dev/null +++ b/circom/tests/loops/known_signal_value.circom @@ -0,0 +1,80 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@KnownLoopViaSignal<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @KnownLoopViaSignal<[]> { +// CHECK-NEXT: struct.field @y : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @a : !struct.type<@accumulate<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@KnownLoopViaSignal<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@KnownLoopViaSignal<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @accumulate::@compute(%[[VAL_1]]) : (!felt.type) -> !struct.type<@accumulate<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@o] : <@accumulate<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@y] = %[[VAL_3]] : <@KnownLoopViaSignal<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@a] = %[[VAL_2]] : <@KnownLoopViaSignal<[]>>, !struct.type<@accumulate<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@KnownLoopViaSignal<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@KnownLoopViaSignal<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@y] : <@KnownLoopViaSignal<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@a] : <@KnownLoopViaSignal<[]>>, !struct.type<@accumulate<[]>> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: function.call @accumulate::@constrain(%[[VAL_5]], %[[VAL_6]]) : (!struct.type<@accumulate<[]>>, !felt.type) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @accumulate<[]> { +// CHECK-NEXT: struct.field @o : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@accumulate<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@accumulate<[]>> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_8]]) +// CHECK-NEXT: scf.condition(%[[VAL_13]]) %[[VAL_12]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_9]][@o] = %[[VAL_11]] : <@accumulate<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@accumulate<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@accumulate<[]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@o] : <@accumulate<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_21]], %[[VAL_18]]) +// CHECK-NEXT: scf.condition(%[[VAL_22]]) %[[VAL_21]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_23:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_24]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_25]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..90d234884 --- /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 -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..39e8f06cd --- /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 -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_variant_idx.circom b/circom/tests/loops/simple_variant_idx.circom new file mode 100644 index 000000000..1cbcefc4a --- /dev/null +++ b/circom/tests/loops/simple_variant_idx.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SimpleVariantIdx<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @SimpleVariantIdx<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@SimpleVariantIdx<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@SimpleVariantIdx<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_12]]] = %[[VAL_0]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_13]]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_16]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@SimpleVariantIdx<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@SimpleVariantIdx<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@SimpleVariantIdx<[@n]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_17]][@out] : <@SimpleVariantIdx<[@n]>>, !array.type<@n x !felt.type> +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_19]]) +// CHECK-NEXT: scf.condition(%[[VAL_26]]) %[[VAL_24]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_20]]{{\[}}%[[VAL_29]]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_32]], %[[VAL_30]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..9d25d3845 --- /dev/null +++ b/circom/tests/loops/unknown_index_from_array.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Example<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Example<[@n]> { +// CHECK-NEXT: struct.field @c : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Example<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Example<[@n]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_7]], %[[VAL_3]]) +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_7]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_11]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_12]] +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_13]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_4]]{{\[}}%[[VAL_15]]] = %[[VAL_14]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_4]] : <@Example<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Example<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@Example<[@n]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_18]][@c] : <@Example<[@n]>>, !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_24]], %[[VAL_21]]) +// CHECK-NEXT: scf.condition(%[[VAL_25]]) %[[VAL_24]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_28]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..7a3375973 --- /dev/null +++ b/circom/tests/loops/unknown_index_from_function.circom @@ -0,0 +1,67 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Example<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @identity(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Example<[@n]> { +// CHECK-NEXT: struct.field @c : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Example<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Example<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_8]], %[[VAL_4]]) +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_8]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = function.call @identity(%[[VAL_2]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_11]] +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_12]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: array.write %[[VAL_5]]{{\[}}%[[VAL_14]]] = %[[VAL_13]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_3]][@c] = %[[VAL_5]] : <@Example<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Example<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_17:[0-9a-zA-Z_\.]+]]: !struct.type<@Example<[@n]>>, %[[VAL_18:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_17]][@c] : <@Example<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_23:[0-9a-zA-Z_\.]+]] = %[[VAL_21]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_23]], %[[VAL_20]]) +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_23]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_25]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..c2fa0feb8 --- /dev/null +++ b/circom/tests/loops/unknown_local_array_index.circom @@ -0,0 +1,146 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ForUnknownIndex<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ForUnknownIndex<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ForUnknownIndex<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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> +// 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> +// 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> +// 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> +// 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> +// 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> +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_45]], %[[VAL_0]]) +// CHECK-NEXT: scf.condition(%[[VAL_46]]) %[[VAL_44]], %[[VAL_45]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_47:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_48:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_47]], %[[VAL_48]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_49]], %[[VAL_51]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_54:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_53]] +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_27]]{{\[}}%[[VAL_54]]] : <10 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_55]] : <@ForUnknownIndex<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ForUnknownIndex<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_56:[0-9a-zA-Z_\.]+]]: !struct.type<@ForUnknownIndex<[]>>, %[[VAL_57:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_108:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_56]][@out] : <@ForUnknownIndex<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]], %[[VAL_58]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_63:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_64:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_65:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_66:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_67:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_68:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_69:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_70:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_60]], %[[VAL_61]], %[[VAL_62]], %[[VAL_63]], %[[VAL_64]], %[[VAL_65]], %[[VAL_66]], %[[VAL_67]], %[[VAL_68]], %[[VAL_69]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_71:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_72:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]], %[[VAL_71]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_73:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_74:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_75:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_76:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_77:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_78:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_79:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_80:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_81:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_82:[0-9a-zA-Z_\.]+]] = felt.const 9 +// CHECK-NEXT: %[[VAL_83:[0-9a-zA-Z_\.]+]] = array.new %[[VAL_73]], %[[VAL_74]], %[[VAL_75]], %[[VAL_76]], %[[VAL_77]], %[[VAL_78]], %[[VAL_79]], %[[VAL_80]], %[[VAL_81]], %[[VAL_82]] : <10 x !felt.type> +// CHECK-NEXT: %[[VAL_84:[0-9a-zA-Z_\.]+]] = felt.const 0 +// 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> +// CHECK-NEXT: %[[VAL_86:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_87:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_88:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_89:[0-9a-zA-Z_\.]+]] = felt.const 3 +// CHECK-NEXT: %[[VAL_90:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_91:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[VAL_92:[0-9a-zA-Z_\.]+]] = felt.const 6 +// CHECK-NEXT: %[[VAL_93:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: %[[VAL_94:[0-9a-zA-Z_\.]+]] = felt.const 8 +// CHECK-NEXT: %[[VAL_95:[0-9a-zA-Z_\.]+]] = felt.const 9 +// 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> +// CHECK-NEXT: %[[VAL_97:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_98:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_100:[0-9a-zA-Z_\.]+]] = %[[VAL_97]], %[[VAL_101:[0-9a-zA-Z_\.]+]] = %[[VAL_98]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_102:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_101]], %[[VAL_57]]) +// CHECK-NEXT: scf.condition(%[[VAL_102]]) %[[VAL_100]], %[[VAL_101]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_103:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_104:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_105:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_103]], %[[VAL_104]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_106:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_107:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_104]], %[[VAL_106]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_105]], %[[VAL_107]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..54170abaf --- /dev/null +++ b/circom/tests/loops/unknown_loop_component.circom @@ -0,0 +1,92 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@UnknownLoopComponent<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @UnknownLoopComponent<[]> { +// CHECK-NEXT: struct.field @bits : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @nb : !struct.type<@nbits<[]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@UnknownLoopComponent<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@UnknownLoopComponent<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @nbits::@compute(%[[VAL_0]]) : (!felt.type) -> !struct.type<@nbits<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@out] : <@nbits<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@bits] = %[[VAL_3]] : <@UnknownLoopComponent<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@nb] = %[[VAL_2]] : <@UnknownLoopComponent<[]>>, !struct.type<@nbits<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@UnknownLoopComponent<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@UnknownLoopComponent<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@bits] : <@UnknownLoopComponent<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@nb] : <@UnknownLoopComponent<[]>>, !struct.type<@nbits<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-NEXT: function.call @nbits::@constrain(%[[VAL_6]], %[[VAL_7]]) : (!struct.type<@nbits<[]>>, !felt.type) -> () +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @nbits<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@nbits<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.new : <@nbits<[]>> +// 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_\.]+]]:2 = scf.while (%[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_11]], %[[VAL_15:[0-9a-zA-Z_\.]+]] = %[[VAL_12]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_14]], %[[VAL_16]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_17]], %[[VAL_9]]) +// CHECK-NEXT: scf.condition(%[[VAL_18]]) %[[VAL_14]], %[[VAL_15]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_19]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_10]][@out] = %[[VAL_13]]#1 : <@nbits<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_10]] : !struct.type<@nbits<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !struct.type<@nbits<[]>>, %[[VAL_26:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_25]][@out] : <@nbits<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = felt.const 0 +// 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, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_30]], %[[VAL_32]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_33]], %[[VAL_26]]) +// CHECK-NEXT: scf.condition(%[[VAL_34]]) %[[VAL_30]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_35:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_37]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_35]], %[[VAL_39]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_38]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..33089c9bd --- /dev/null +++ b/circom/tests/loops/unknown_loop_index.circom @@ -0,0 +1,72 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/loops/unknown_loop_oob.circom b/circom/tests/loops/unknown_loop_oob.circom new file mode 100644 index 000000000..b311f5573 --- /dev/null +++ b/circom/tests/loops/unknown_loop_oob.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { 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..8afcc22a4 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_01.circom @@ -0,0 +1,57 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@VariantIndex<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @VariantIndex<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@VariantIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@VariantIndex<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_9]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@VariantIndex<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@VariantIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@VariantIndex<[@n]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_13]][@out] : <@VariantIndex<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_16]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_18]], %[[VAL_15]]) +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_18]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_21]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..5a5148505 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_02.circom @@ -0,0 +1,86 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@VariantIndex<[2]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @VariantIndex<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@VariantIndex<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@VariantIndex<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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> +// 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> +// 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>, !felt.type +// 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, !array.type<@n x !felt.type>) -> (!felt.type, !array.type<@n x !felt.type>) { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_12]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_14]]) %[[VAL_12]], %[[VAL_13]] : !felt.type, !array.type<@n x !felt.type> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_16:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>): +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_0]], %[[VAL_15]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: array.write %[[VAL_16]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_20]], %[[VAL_16]] : !felt.type, !array.type<@n x !felt.type> +// 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]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]#1{{\[}}%[[VAL_22]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_24]] +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]#1{{\[}}%[[VAL_25]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_23]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_27]] : <@VariantIndex<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@VariantIndex<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_28:[0-9a-zA-Z_\.]+]]: !struct.type<@VariantIndex<[@n]>>, %[[VAL_29:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_28]][@out] : <@VariantIndex<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = array.new : <@n x !felt.type> +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.len %[[VAL_32]], %[[VAL_33]] : <@n x !felt.type> +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = arith.constant 0 : index +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = arith.constant 1 : index +// CHECK-NEXT: scf.for %[[VAL_37:[0-9a-zA-Z_\.]+]] = %[[VAL_35]] to %[[VAL_34]] step %[[VAL_36]] { +// CHECK-NEXT: array.write %[[VAL_32]]{{\[}}%[[VAL_37]]] = %[[VAL_31]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// 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_38]], %[[VAL_41:[0-9a-zA-Z_\.]+]] = %[[VAL_32]]) : (!felt.type, !array.type<@n x !felt.type>) -> (!felt.type, !array.type<@n x !felt.type>) { +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_40]], %[[VAL_30]]) +// CHECK-NEXT: scf.condition(%[[VAL_42]]) %[[VAL_40]], %[[VAL_41]] : !felt.type, !array.type<@n x !felt.type> +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_43:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_44:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>): +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = felt.shr %[[VAL_29]], %[[VAL_43]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_43]] +// CHECK-NEXT: array.write %[[VAL_44]]{{\[}}%[[VAL_46]]] = %[[VAL_45]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_43]], %[[VAL_47]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_48]], %[[VAL_44]] : !felt.type, !array.type<@n x !felt.type> +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..6644b9667 --- /dev/null +++ b/circom/tests/loops/variant_idx_in_loop_03.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/misc/array_computation.circom b/circom/tests/misc/array_computation.circom new file mode 100644 index 000000000..66dc0e1e0 --- /dev/null +++ b/circom/tests/misc/array_computation.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..38211cfec --- /dev/null +++ b/circom/tests/misc/circomlib_smtprocessor.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +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; 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>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @x : !felt.type +// CHECK-NEXT: struct.field @cb : !struct.type<@B<[]>> +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_1:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = function.call @B::@compute(%[[V_0]], %[[V_1]]) : (!felt.type, !felt.type) -> !struct.type<@B<[]>> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_4]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_2]][@x] = %[[V_5]] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_5]], %[[V_6]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_2]][@c] = %[[V_7]] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_2]][@cb] = %[[V_4]] : <@A<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.return %[[V_2]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[V_9:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_10:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@c] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@x] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@cb] : <@A<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.call @B::@constrain(%[[V_11]], %[[V_9]], %[[V_10]]) : (!struct.type<@B<[]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[V_13:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_11]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_14]], %[[V_13]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = felt.const 5 +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_14]], %[[V_15]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[V_17]], %[[V_16]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_18:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[]>> +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_18]], %[[V_19]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_20]][@c] = %[[V_21]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_20]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_22:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[V_23:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_24:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_23]], %[[V_24]] : !felt.type, !felt.type +// CHECK-DAG: %[[V_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_22]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_26]], %[[V_25]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..7e8d3b329 --- /dev/null +++ b/circom/tests/misc/signal_index2.circom @@ -0,0 +1,120 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @c : !array.type<4 x !felt.type> {llzk.pub} +// CHECK-NEXT: struct.field @x : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type> {llzk.pub}, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type> {llzk.pub}) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<4 x !felt.type> +// 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) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_8]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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]]) +// 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, !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_15]] +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_16]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_18]]] = %[[VAL_17]] : <4 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_20]] +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_1]]{{\[}}%[[VAL_21]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_23]]] = %[[VAL_22]] : <4 x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_25]] : !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_3]]{{\[}}%[[VAL_27]]] : <4 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@x] = %[[VAL_28]] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@c] = %[[VAL_3]] : <@A<[]>>, !array.type<4 x !felt.type> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_29:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_30:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type> {llzk.pub}, %[[VAL_31:[0-9a-zA-Z_\.]+]]: !array.type<2 x !felt.type> {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_47:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_29]][@c] : <@A<[]>>, !array.type<4 x !felt.type> +// CHECK-NEXT: %[[VAL_62:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_29]][@x] : <@A<[]>>, !felt.type +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_37:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_35]], %[[VAL_36]]) +// CHECK-NEXT: scf.condition(%[[VAL_37]]) %[[VAL_35]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_38:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.umod %[[VAL_38]], %[[VAL_39]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_40]], %[[VAL_41]]) +// CHECK-NEXT: scf.if %[[VAL_42]] { +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[VAL_44:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_38]], %[[VAL_43]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_45:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_44]] +// CHECK-NEXT: %[[VAL_46:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_30]]{{\[}}%[[VAL_45]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_48:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] +// CHECK-NEXT: %[[VAL_49:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_48]]] : <4 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_49]], %[[VAL_46]] : !felt.type, !felt.type +// 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_38]], %[[VAL_50]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_52:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_51]] +// CHECK-NEXT: %[[VAL_53:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_31]]{{\[}}%[[VAL_52]]] : <2 x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_55:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_38]] +// CHECK-NEXT: %[[VAL_56:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_55]]] : <4 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_56]], %[[VAL_53]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_57:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_58:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_38]], %[[VAL_57]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_58]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: %[[VAL_59:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_60:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_59]] +// CHECK-NEXT: %[[VAL_61:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_47]]{{\[}}%[[VAL_60]]] : <4 x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_62]], %[[VAL_61]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..c7b41ab5d --- /dev/null +++ b/circom/tests/misc/unreachable_code_crash.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +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 { diff --git a/circom/tests/operators/arith1.circom b/circom/tests/operators/arith1.circom new file mode 100644 index 000000000..68afb9463 --- /dev/null +++ b/circom/tests/operators/arith1.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Arith1<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Arith1<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Arith1<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Arith1<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@x] = %[[VAL_6]] : <@Arith1<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Arith1<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Arith1<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@x] : <@Arith1<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_11]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..77528b23d --- /dev/null +++ b/circom/tests/operators/arith2.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Arith2<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Arith2<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Arith2<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Arith2<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@x] = %[[VAL_6]] : <@Arith2<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Arith2<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Arith2<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@x] : <@Arith2<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_11]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..96d725cd6 --- /dev/null +++ b/circom/tests/operators/arith_add.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithAdd<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @ArithAdd<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithAdd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithAdd<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@x] = %[[VAL_3]] : <@ArithAdd<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@ArithAdd<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithAdd<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@x] : <@ArithAdd<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..bf04b3b53 --- /dev/null +++ b/circom/tests/operators/arith_div.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Div<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Div<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Div<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@Div<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Div<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Div<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@Div<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..a17abb9af --- /dev/null +++ b/circom/tests/operators/arith_idiv.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithQuotient<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArithQuotient<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithQuotient<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithQuotient<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_2]], %[[VAL_0]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_5]] : <@ArithQuotient<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithQuotient<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithQuotient<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@out] : <@ArithQuotient<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..3a976547c --- /dev/null +++ b/circom/tests/operators/arith_mod.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithRemainder<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArithRemainder<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @inv : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithRemainder<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_3]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_6]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@inv] = %[[VAL_4]] : <@ArithRemainder<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_4]] : <@ArithRemainder<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithRemainder<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithRemainder<[]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@inv] : <@ArithRemainder<[]>>, !felt.type +// CHECK-DAG: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@out] : <@ArithRemainder<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_9]], %[[VAL_11]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..780b4c1ad --- /dev/null +++ b/circom/tests/operators/arith_neg.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithNeg<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @ArithNeg<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithNeg<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ArithNeg<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_0]] : !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@x] = %[[VAL_2]] : <@ArithNeg<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithNeg<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithNeg<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.neg %[[VAL_4]] : !felt.type +// CHECK-DAG: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@x] : <@ArithNeg<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..154b2f3ca --- /dev/null +++ b/circom/tests/operators/arith_pow.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithPower<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ArithPower<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithPower<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@ArithPower<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ArithPower<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithPower<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@out] : <@ArithPower<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..8c2edb89d --- /dev/null +++ b/circom/tests/operators/arith_sub.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ArithSubtract<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @ArithSubtract<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ArithSubtract<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_0]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@x] = %[[VAL_6]] : <@ArithSubtract<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@ArithSubtract<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@ArithSubtract<[]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@x] : <@ArithSubtract<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 10 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_9]], %[[VAL_11]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_8]], %[[VAL_12]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_13]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..a00091c98 --- /dev/null +++ b/circom/tests/operators/assert.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[32]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@nBits]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[@nBits]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@nBits]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @nBits : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[@nBits]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_7]], "assertion failed" +// CHECK-NEXT: function.return +// 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..10ae1ccc7 --- /dev/null +++ b/circom/tests/operators/bitwise_and.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseAnd<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @BitwiseAnd<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @check_v : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseAnd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseAnd<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseAnd<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseAnd<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseAnd<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@type] : <@BitwiseAnd<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@check_v] : <@BitwiseAnd<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..db6aeccc6 --- /dev/null +++ b/circom/tests/operators/bitwise_comp.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseComplement<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BitwiseComplement<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @check_v : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseComplement<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@BitwiseComplement<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.bit_not %[[VAL_0]] : !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_2]] : <@BitwiseComplement<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@check_v] = %[[VAL_4]] : <@BitwiseComplement<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseComplement<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseComplement<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@type] : <@BitwiseComplement<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@check_v] : <@BitwiseComplement<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..0aa9ea1c1 --- /dev/null +++ b/circom/tests/operators/bitwise_or.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseOr<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @BitwiseOr<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @check_v : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseOr<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseOr<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseOr<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseOr<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseOr<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@type] : <@BitwiseOr<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@check_v] : <@BitwiseOr<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..2dd2d1325 --- /dev/null +++ b/circom/tests/operators/bitwise_shl.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseShiftLeft<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BitwiseShiftLeft<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseShiftLeft<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseShiftLeft<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseShiftLeft<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseShiftLeft<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@type] : <@BitwiseShiftLeft<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..b2d1ea1ea --- /dev/null +++ b/circom/tests/operators/bitwise_shr.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseShiftRight<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BitwiseShiftRight<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseShiftRight<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseShiftRight<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseShiftRight<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseShiftRight<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@type] : <@BitwiseShiftRight<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..d8be81e67 --- /dev/null +++ b/circom/tests/operators/bitwise_xor.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BitwiseXOR<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @BitwiseXOR<[]> { +// CHECK-NEXT: struct.field @type : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @check_v : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BitwiseXOR<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@type] = %[[VAL_3]] : <@BitwiseXOR<[]>>, !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@check_v] = %[[VAL_5]] : <@BitwiseXOR<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@BitwiseXOR<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BitwiseXOR<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@type] : <@BitwiseXOR<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@check_v] : <@BitwiseXOR<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 32 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_8]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..586d06f5e --- /dev/null +++ b/circom/tests/operators/bool_and.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BoolAnd<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BoolAnd<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BoolAnd<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// 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]]) +// 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 +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_8]] : <@BoolAnd<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolAnd<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolAnd<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@out] : <@BoolAnd<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..00767db4b --- /dev/null +++ b/circom/tests/operators/bool_not.circom @@ -0,0 +1,32 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BoolNot<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BoolNot<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BoolNot<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@BoolNot<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_0]], %[[VAL_1]]) +// 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 +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_5]] : <@BoolNot<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolNot<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolNot<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@out] : <@BoolNot<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..09307ba4a --- /dev/null +++ b/circom/tests/operators/bool_or.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@BoolOr<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @BoolOr<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@BoolOr<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// 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]]) +// 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 +// CHECK-NEXT: struct.writef %[[VAL_2]][@out] = %[[VAL_8]] : <@BoolOr<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@BoolOr<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@BoolOr<[]>>, %[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_11:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_9]][@out] : <@BoolOr<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..3054e5a99 --- /dev/null +++ b/circom/tests/operators/cmp_eq.circom @@ -0,0 +1,72 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CmpEQ<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @CmpEQ<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@CmpEQ<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpEQ<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <@n x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_12]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpEQ<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpEQ<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpEQ<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@b] : <@CmpEQ<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_18]], %[[VAL_19]]) +// CHECK-NEXT: scf.if %[[VAL_20]] { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_22]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_23]] : !felt.type, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_29]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_33]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_30]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..3bb7df75e --- /dev/null +++ b/circom/tests/operators/cmp_ge.circom @@ -0,0 +1,67 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CmpGE<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @CmpGE<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@CmpGE<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpGE<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_7:[0-9a-zA-Z_\.]+]] = %[[VAL_5]]) : (!felt.type) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_7]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_12]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpGE<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpGE<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpGE<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@b] : <@CmpGE<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_18]], %[[VAL_19]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp ge(%[[VAL_22]], %[[VAL_23]]) +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_22]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_29]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_27]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_25]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..cb62da987 --- /dev/null +++ b/circom/tests/operators/cmp_gt.circom @@ -0,0 +1,71 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CmpGT<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @CmpGT<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@CmpGT<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpGT<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_2]]) : (!felt.type) -> !felt.type { +// 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]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_5]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_12]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpGT<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpGT<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpGT<[@n]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_18]][@b] : <@CmpGT<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_22:[0-9a-zA-Z_\.]+]] = %[[VAL_20]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_22]], %[[VAL_23]]) +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_22]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_25]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_27]] +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_19]]{{\[}}%[[VAL_28]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_25]], %[[VAL_31]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_30]]{{\[}}%[[VAL_33]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_29]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_36:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_25]], %[[VAL_35]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_36]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..d6acce4b8 --- /dev/null +++ b/circom/tests/operators/cmp_le.circom @@ -0,0 +1,67 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CmpLE<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @CmpLE<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@CmpLE<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpLE<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// 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, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_6]], %[[VAL_8]]) +// CHECK-NEXT: scf.condition(%[[VAL_9]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_10:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_11]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_10]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_13]]] = %[[VAL_12]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpLE<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpLE<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpLE<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_28:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@b] : <@CmpLE<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_21:[0-9a-zA-Z_\.]+]] = %[[VAL_19]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.sub %[[VAL_18]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp le(%[[VAL_21]], %[[VAL_23]]) +// CHECK-NEXT: scf.condition(%[[VAL_24]]) %[[VAL_21]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_25:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_29]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_30]], %[[VAL_27]] : !felt.type, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_32]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..018e5a8a6 --- /dev/null +++ b/circom/tests/operators/cmp_lt.circom @@ -0,0 +1,72 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CmpLT<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @CmpLT<[@n]> { +// CHECK-NEXT: struct.field @b : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@CmpLT<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@CmpLT<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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]]) +// 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]] +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_7]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_9]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_10]]] = %[[VAL_8]] : <@n x !felt.type>, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_12]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_14]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_15]]] = %[[VAL_13]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_3]] : <@CmpLT<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@CmpLT<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_16:[0-9a-zA-Z_\.]+]]: !struct.type<@CmpLT<[@n]>>, %[[VAL_17:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_16]][@b] : <@CmpLT<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_18]], %[[VAL_19]]) +// CHECK-NEXT: scf.if %[[VAL_20]] { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_22]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_25]] +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_26]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_27]], %[[VAL_23]] : !felt.type, !felt.type +// 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]] +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_17]]{{\[}}%[[VAL_29]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_33:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_32]] +// CHECK-NEXT: %[[VAL_34:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_24]]{{\[}}%[[VAL_33]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_34]], %[[VAL_30]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..5acb66973 --- /dev/null +++ b/circom/tests/operators/inline_switch_function.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@CallInlineSwitch<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @InlineSwitch +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_99:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_0]], %[[VAL_3]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_99]] -> (!felt.type) { +// CHECK-NEXT: scf.yield %[[VAL_1]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_2]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_4]] : !felt.type +// CHECK-NEXT: } +// CHECK-LABEL: struct.def @CallInlineSwitch<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@CallInlineSwitch<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.new : <@CallInlineSwitch<[]>> +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_5]], %[[VAL_7]]) +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_8]] : i1 +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_10]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = function.call @InlineSwitch(%[[VAL_9]], %[[VAL_11]], %[[VAL_12]]) : (!felt.type, !felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_6]][@out] = %[[VAL_13]] : <@CallInlineSwitch<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_6]] : !struct.type<@CallInlineSwitch<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@CallInlineSwitch<[]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_14]][@out] : <@CallInlineSwitch<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..4b68eb8cf --- /dev/null +++ b/circom/tests/operators/inline_switch_template.circom @@ -0,0 +1,75 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@MultByInv<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @MultByInv<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @inv : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@MultByInv<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_4]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_10]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_12]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@inv] = %[[VAL_11]] : <@MultByInv<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_11]] : <@MultByInv<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@MultByInv<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_13:[0-9a-zA-Z_\.]+]]: !struct.type<@MultByInv<[]>>, %[[VAL_14:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@out] : <@MultByInv<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_13]][@inv] : <@MultByInv<[]>>, !felt.type +// 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_14]], %[[VAL_16]]) +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_17]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = felt.div %[[VAL_19]], %[[VAL_14]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_20]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_21]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_23]], %[[VAL_22]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_14]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: constrain.eq %[[VAL_24]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..49808c0db --- /dev/null +++ b/circom/tests/simple/assert.circom @@ -0,0 +1,34 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[5]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[@n]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_2]]) +// CHECK-NEXT: bool.assert %[[VAL_3]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_7]], "assertion failed" +// CHECK-NEXT: function.return +// 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..ed1adda4c --- /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 -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.main = !struct.type<@UCO<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @UCO<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@UCO<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@UCO<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_5:[0-9a-zA-Z_\.]+]] = %[[VAL_1]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_5]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_3]]) %[[VAL_5]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_8]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@UCO<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@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_13:[0-9a-zA-Z_\.]+]] = scf.while (%[[VAL_14:[0-9a-zA-Z_\.]+]] = %[[VAL_10]]) : (!felt.type) -> !felt.type { +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.const 100 +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_14]], %[[VAL_11]]) +// CHECK-NEXT: scf.condition(%[[VAL_12]]) %[[VAL_14]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_15:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_19]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..793c775a6 --- /dev/null +++ b/circom/tests/simple/blocks_01.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @B<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_8]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_17]], "assertion failed" +// CHECK-NEXT: function.return +// 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..19782fa77 --- /dev/null +++ b/circom/tests/simple/blocks_02.circom @@ -0,0 +1,56 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @B<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_8]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_9:[0-9a-zA-Z_\.]+]]: !struct.type<@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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_17]], "assertion failed" +// CHECK-NEXT: function.return +// 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..3bfc3ba0f --- /dev/null +++ b/circom/tests/simple/blocks_03.circom @@ -0,0 +1,66 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@B<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @B<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// 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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_11]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_12:[0-9a-zA-Z_\.]+]]: !struct.type<@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]]) +// 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]]) +// 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]]) +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_23]], "assertion failed" +// CHECK-NEXT: function.return +// 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..c79d97b33 --- /dev/null +++ b/circom/tests/simple/call_and_return.circom @@ -0,0 +1,53 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@C<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @negative( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_4]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @C<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@C<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@C<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @negative(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_2]] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@C<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@C<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@out] : <@C<[]>>, !felt.type +// CHECK-NEXT: function.return +// 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..b3b0dd92d --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_01.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_2]] : <@T<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@outp] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..b7be70ad1 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_02.circom @@ -0,0 +1,50 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_0]], %[[VAL_0]] : !felt.type, !felt.type +// 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, !felt.type +// 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, !felt.type +// CHECK-NEXT: function.return %[[VAL_5]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_2]] : <@T<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@outp] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..5ee1a6b9c --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_03.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_2]] -> (!felt.type) { +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_5]] : !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_2]] : <@T<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@outp] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_4]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..2cb590adc --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_04.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_5]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..b5eb15af1 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_05.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, +// CHECK-SAME: %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_6]], %[[VAL_7]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..46a49357a --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_06.circom @@ -0,0 +1,46 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @temp : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@temp] = %[[VAL_4]] : <@T<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@temp] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..e74422ed7 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_07.circom @@ -0,0 +1,42 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } + +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_6]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..89734646b --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_08.circom @@ -0,0 +1,48 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @o1 : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writef %[[VAL_2]][@o1] = %[[VAL_3]] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@o1] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_6]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..5fd168556 --- /dev/null +++ b/circom/tests/simple/call_in_constraint_gen_09.circom @@ -0,0 +1,51 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@T<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: function.def @f( +// CHECK-SAME: %[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return %[[VAL_0]] : !felt.type +// CHECK-NEXT: } +// +// CHECK-LABEL: struct.def @T<[]> { +// CHECK-NEXT: struct.field @o1 : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @temp : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@T<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@T<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: struct.writef %[[VAL_2]][@o1] = %[[VAL_3]] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = function.call @f(%[[VAL_0]]) : (!felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_2]][@temp] = %[[VAL_4]] : <@T<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@T<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@T<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@o1] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@temp] : <@T<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..387018b2a --- /dev/null +++ b/circom/tests/simple/constrain_before_assign.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Template<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Template<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Template<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Template<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: struct.writef %[[VAL_0]][@outp] = %[[VAL_1]] : <@Template<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Template<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Template<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@outp] : <@Template<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 7 +// CHECK-NEXT: constrain.eq %[[VAL_3]], %[[VAL_4]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..9a8ccbe86 --- /dev/null +++ b/circom/tests/simple/constraint.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_0]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..2e9620704 --- /dev/null +++ b/circom/tests/simple/constraint_constant.circom @@ -0,0 +1,31 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Simple2<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Simple2<[@a]> { +// CHECK-NEXT: struct.field @b : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@Simple2<[@a]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple2<[@a]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@b] = %[[VAL_1]] : <@Simple2<[@a]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Simple2<[@a]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@b] : <@Simple2<[@a]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..9e167e5ec --- /dev/null +++ b/circom/tests/simple/create_component.circom @@ -0,0 +1,20 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { 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..6c5d26c03 --- /dev/null +++ b/circom/tests/simple/idiv_1.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@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 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_1]], %[[VAL_3]] : !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_6]], "assertion failed" +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_8]], %[[VAL_10]] : !felt.type, !felt.type +// 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]]) +// CHECK-NEXT: bool.assert %[[VAL_13]], "assertion failed" +// CHECK-NEXT: function.return +// 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..6109501fd --- /dev/null +++ b/circom/tests/simple/idiv_2.circom @@ -0,0 +1,33 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[12]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute() -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_0]][@c] = %[[VAL_3]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@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 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@c] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = felt.const 4 +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.uintdiv %[[VAL_5]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_7]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..627e5c279 --- /dev/null +++ b/circom/tests/simple/load.circom @@ -0,0 +1,55 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[1]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_0:[0-9a-zA-Z_\.]+]]: !array.type<3 x !felt.type>) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_4:[0-9a-zA-Z_\.]+]] = array.new %[[V_3]], %[[V_3]], %[[V_3]] : <3 x !felt.type> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new %[[V_5]], %[[V_6]], %[[V_7]] : <3 x !felt.type> +// CHECK-NEXT: %[[V_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_N]] +// CHECK-NEXT: %[[V_10:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_9]]] : <3 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_10]] +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = array.read %[[V_0]]{{\[}}%[[V_11]]] : <3 x !felt.type>, !felt.type +// CHECK-NEXT: struct.writef %[[SELF]][@out] = %[[V_12]] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[V_14:[0-9a-zA-Z_\.]+]]: !array.type<3 x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_N:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[V_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@out] : <@A<[@n]>>, !felt.type +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = array.new %[[V_16]], %[[V_16]], %[[V_16]] : <3 x !felt.type> +// CHECK-NEXT: %[[V_18:[0-9a-zA-Z_\.]+]] = felt.const 2 +// CHECK-NEXT: %[[V_19:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[V_A:[0-9a-zA-Z_\.]+]] = array.new %[[V_18]], %[[V_19]], %[[V_20]] : <3 x !felt.type> +// CHECK-NEXT: %[[V_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_N]] +// CHECK-NEXT: %[[V_23:[0-9a-zA-Z_\.]+]] = array.read %[[V_A]]{{\[}}%[[V_22]]] : <3 x !felt.type>, !felt.type +// CHECK-NEXT: %[[V_24:[0-9a-zA-Z_\.]+]] = cast.toindex %[[V_23]] +// CHECK-NEXT: %[[V_25:[0-9a-zA-Z_\.]+]] = array.read %[[V_14]]{{\[}}%[[V_24]]] : <3 x !felt.type>, !felt.type +// CHECK-NEXT: function.return +// 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..016c65ec2 --- /dev/null +++ b/circom/tests/simple/log.circom @@ -0,0 +1,35 @@ +// REQUIRES: circom +// COM: Setup and run the test +// RUN: rm -rf %t && mkdir %t +// RUN: %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// IR-NEXT: struct.def @A<[]> { +// IR-NEXT: function.def @compute() -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// IR-NEXT: %self = struct.new : <@A<[]>> +// IR-NEXT: function.return %self : !struct.type<@A<[]>> +// IR-NEXT: } +// IR-NEXT: function.def @constrain(%arg0: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// IR-NEXT: function.return +// 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..2e98964a4 --- /dev/null +++ b/circom/tests/simple/loop_and_block.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[3]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@n]> { +// CHECK-NEXT: struct.field @out : !array.type<@n x !felt.type> {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@A<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = undef.undef : !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_6]], %[[VAL_2]]) +// CHECK-NEXT: scf.condition(%[[VAL_7]]) %[[VAL_6]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_0]]{{\[}}%[[VAL_9]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_8]] +// CHECK-NEXT: array.write %[[VAL_3]]{{\[}}%[[VAL_11]]] = %[[VAL_10]] : <@n x !felt.type>, !felt.type +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_13]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_3]] : <@A<[@n]>>, !array.type<@n x !felt.type> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_14:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@n]>>, %[[VAL_15:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %{{[0-9a-zA-Z_\.]+}} = struct.readf %[[VAL_14]][@out] : <@A<[@n]>>, !array.type<@n x !felt.type> +// 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) -> !felt.type { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_19]], %[[VAL_16]]) +// CHECK-NEXT: scf.condition(%[[VAL_20]]) %[[VAL_19]] : !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type): +// 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, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_23]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..586c61777 --- /dev/null +++ b/circom/tests/simple/main_exprs.circom @@ -0,0 +1,46 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[24, 12, 20, 5, 5, 1, 1, 9, 2, 1, 1]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@a, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@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, @b, @c, @d, @e, @f, @g, @h, @i, @j, @k]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @b : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @c : !felt.type +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = poly.read_const @d : !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = poly.read_const @e : !felt.type +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @f : !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @g : !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = poly.read_const @h : !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = poly.read_const @i : !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @j : !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@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, @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 +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = poly.read_const @b : !felt.type +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = poly.read_const @c : !felt.type +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = poly.read_const @d : !felt.type +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = poly.read_const @e : !felt.type +// CHECK-NEXT: %[[VAL_18:[0-9a-zA-Z_\.]+]] = poly.read_const @f : !felt.type +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = poly.read_const @g : !felt.type +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @h : !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = poly.read_const @i : !felt.type +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = poly.read_const @j : !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = poly.read_const @k : !felt.type +// CHECK-NEXT: function.return +// 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..c06536a98 --- /dev/null +++ b/circom/tests/simple/parallel_subcmp.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Parallel<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Foo<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Parallel<[]> { +// CHECK-NEXT: struct.field @foo : !struct.type<@Foo<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Parallel<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Parallel<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @Foo::@compute() : () -> !struct.type<@Foo<[]>> +// CHECK-NEXT: struct.writef %[[VAL_2]][@foo] = %[[VAL_3]] : <@Parallel<[]>>, !struct.type<@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Parallel<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Parallel<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@foo] : <@Parallel<[]>>, !struct.type<@Foo<[]>> +// CHECK-NEXT: function.call @Foo::@constrain(%[[VAL_5]]) : (!struct.type<@Foo<[]>>) -> () +// CHECK-NEXT: function.return +// 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..e6c332c11 --- /dev/null +++ b/circom/tests/simple/parallel_template.circom @@ -0,0 +1,39 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Parallel<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Foo<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@Foo<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Foo<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_1:[0-9a-zA-Z_\.]+]]: !struct.type<@Foo<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Parallel<[]> { +// CHECK-NEXT: struct.field @foo : !struct.type<@Foo<[]>> +// CHECK-NEXT: function.def @compute() -> !struct.type<@Parallel<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Parallel<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @Foo::@compute() : () -> !struct.type<@Foo<[]>> +// CHECK-NEXT: struct.writef %[[VAL_2]][@foo] = %[[VAL_3]] : <@Parallel<[]>>, !struct.type<@Foo<[]>> +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Parallel<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Parallel<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@foo] : <@Parallel<[]>>, !struct.type<@Foo<[]>> +// CHECK-NEXT: function.call @Foo::@constrain(%[[VAL_5]]) : (!struct.type<@Foo<[]>>) -> () +// CHECK-NEXT: function.return +// 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..cddc6cef7 --- /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 -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.main = !struct.type<@ReadFromOutput<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ReadFromOutput<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @intermediate : !felt.type +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ReadFromOutput<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@ReadFromOutput<[]>> +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_0]] : <@ReadFromOutput<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@intermediate] = %[[VAL_0]] : <@ReadFromOutput<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ReadFromOutput<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_3:[0-9a-zA-Z_\.]+]]: !struct.type<@ReadFromOutput<[]>>, %[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@outp] : <@ReadFromOutput<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@intermediate] : <@ReadFromOutput<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_6]], %[[VAL_4]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_7]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..a761680d2 --- /dev/null +++ b/circom/tests/simple/read_from_output_with_felt_cast.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@ReadFromOutputWithFeltCast<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @ReadFromOutputWithFeltCast<[]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @intermediate : !felt.type +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@ReadFromOutputWithFeltCast<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@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]]) +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_3]] : i1 +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_4]] : <@ReadFromOutputWithFeltCast<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@intermediate] = %[[VAL_4]] : <@ReadFromOutputWithFeltCast<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@ReadFromOutputWithFeltCast<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@ReadFromOutputWithFeltCast<[]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@outp] : <@ReadFromOutputWithFeltCast<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@intermediate] : <@ReadFromOutputWithFeltCast<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = bool.cmp eq(%[[VAL_6]], %[[VAL_7]]) +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_8]] : i1 +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_11]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..26eee0908 --- /dev/null +++ b/circom/tests/simple/simple_01.circom @@ -0,0 +1,38 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Simple3<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Simple3<[]> { +// CHECK-NEXT: struct.field @b : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Simple3<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple3<[]>> +// CHECK-NEXT: struct.writef %[[VAL_1]][@b] = %[[VAL_0]] : <@Simple3<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@c] = %[[VAL_0]] : <@Simple3<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Simple3<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple3<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@b] : <@Simple3<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_2]][@c] : <@Simple3<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_4]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_5]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..bdee03082 --- /dev/null +++ b/circom/tests/simple/simple_02.circom @@ -0,0 +1,44 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Simple4<[10]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Simple4<[@a]> { +// CHECK-NEXT: struct.field @b : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Simple4<[@a]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple4<[@a]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type +// 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.writef %[[VAL_2]][@b] = %[[VAL_3]] : <@Simple4<[@a]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_2]] : !struct.type<@Simple4<[@a]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_7:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple4<[@a]>>, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @a : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_7]][@b] : <@Simple4<[@a]>>, !felt.type +// 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_\.]+]] = felt.const 11 +// CHECK-NEXT: constrain.eq %[[VAL_14]], %[[VAL_10]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..778852677 --- /dev/null +++ b/circom/tests/simple/simple_03.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Simple5<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Simple5<[]> { +// CHECK-NEXT: struct.field @x : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @y : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @z : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@Simple5<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@Simple5<[]>> +// CHECK-NEXT: struct.writef %[[VAL_3]][@x] = %[[VAL_0]] : <@Simple5<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@y] = %[[VAL_1]] : <@Simple5<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@Simple5<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_4:[0-9a-zA-Z_\.]+]]: !struct.type<@Simple5<[]>>, %[[VAL_5:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@x] : <@Simple5<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@y] : <@Simple5<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_Z:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_4]][@z] : <@Simple5<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_8]], %[[VAL_5]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_9]], %[[VAL_6]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..c7289b0d9 --- /dev/null +++ b/circom/tests/simple/simple_04.circom @@ -0,0 +1,78 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-NEXT: struct.field @x : !felt.type +// CHECK-NEXT: struct.field @cb : !struct.type<@B<[]>> +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_0:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_1:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[V_5:[0-9a-zA-Z_\.]+]] = function.call @B::@compute(%[[V_0]], %[[V_1]]) : (!felt.type, !felt.type) -> !struct.type<@B<[]>> +// CHECK-NEXT: %[[V_6:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_5]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_3]][@x] = %[[V_6]] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_7:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_6]], %[[V_2]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_3]][@c] = %[[V_7]] : <@A<[]>>, !felt.type +// CHECK-NEXT: struct.writef %[[V_3]][@cb] = %[[V_5]] : <@A<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.return %[[V_3]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[V_9:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_10:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_11:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[V_17:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@c] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_15:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@x] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[V_12:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_8]][@cb] : <@A<[]>>, !struct.type<@B<[]>> +// CHECK-NEXT: function.call @B::@constrain(%[[V_12]], %[[V_9]], %[[V_10]]) : (!struct.type<@B<[]>>, !felt.type, !felt.type) -> () +// CHECK-NEXT: %[[V_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_12]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_15]], %[[V_14]] : !felt.type, !felt.type +// CHECK-NEXT: %[[V_16:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_15]], %[[V_11]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[V_17]], %[[V_16]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @B<[]> { +// CHECK-NEXT: struct.field @c : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[V_18:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_19:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) -> !struct.type<@B<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[V_20:[0-9a-zA-Z_\.]+]] = struct.new : <@B<[]>> +// CHECK-NEXT: %[[V_21:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_18]], %[[V_19]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[V_20]][@c] = %[[V_21]] : <@B<[]>>, !felt.type +// CHECK-NEXT: function.return %[[V_20]] : !struct.type<@B<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[V_22:[0-9a-zA-Z_\.]+]]: !struct.type<@B<[]>>, %[[V_23:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}, %[[V_24:[0-9a-zA-Z_\.]+]]: !felt.type {llzk.pub}) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[V_25:[0-9a-zA-Z_\.]+]] = felt.mul %[[V_23]], %[[V_24]] : !felt.type, !felt.type +// CHECK-DAG: %[[V_26:[0-9a-zA-Z_\.]+]] = struct.readf %[[V_22]][@c] : <@B<[]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[V_26]], %[[V_25]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..46dff6d1c --- /dev/null +++ b/circom/tests/simple/simple_05.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_2:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_0]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_4]], %[[VAL_2]] : !felt.type, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_3]][@out] = %[[VAL_5]] : <@A<[]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_3]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_6:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_7:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_6]][@out] : <@A<[]>>, !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_7]], %[[VAL_8]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = felt.mul %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_12]], %[[VAL_11]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..28e1ba933 --- /dev/null +++ b/circom/tests/simple/trivially_empty.circom @@ -0,0 +1,21 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@EmptyTemplate<[]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @EmptyTemplate<[]> { +// CHECK-NEXT: function.def @compute() -> !struct.type<@EmptyTemplate<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@EmptyTemplate<[]>> +// CHECK-NEXT: function.return %[[SELF]] : !struct.type<@EmptyTemplate<[]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%arg0: !struct.type<@EmptyTemplate<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..3adc91a26 --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = felt.const 99 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..4009412c0 --- /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 -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.main = !struct.type<@A<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @A<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: (%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_2:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[]>>, %[[VAL_3:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: function.return +// 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..4b1071be8 --- /dev/null +++ b/circom/tests/simple/var_consts.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Van276<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Van276<[]> { +// CHECK-LABEL: function.def @compute +// CHECK-SAME: () -> !struct.type<@Van276<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.new : <@Van276<[]>> +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = felt.const 999999 +// CHECK-NEXT: function.return %[[VAL_0]] : !struct.type<@Van276<[]>> +// CHECK-NEXT: } +// CHECK-LABEL: function.def @constrain +// CHECK-SAME: (%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@Van276<[]>>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = felt.const 168700 +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = felt.const 999999 +// CHECK-NEXT: function.return +// 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..e7b718255 --- /dev/null +++ b/circom/tests/subcmps/array_copy_constraints.circom @@ -0,0 +1,97 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@Caller<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @Caller<[@n]> { +// CHECK-NEXT: struct.field @outp : !felt.type +// CHECK-NEXT: struct.field @op : !struct.type<@Sum<[@n]>> +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Caller<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@Caller<[@n]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = function.call @Sum::@compute(%[[VAL_0]]) : (!array.type<@n x !felt.type>) -> !struct.type<@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_4:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_3]][@outp] : <@Sum<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@outp] = %[[VAL_4]] : <@Caller<[@n]>>, !felt.type +// CHECK-NEXT: struct.writef %[[VAL_1]][@op] = %[[VAL_3]] : <@Caller<[@n]>>, !struct.type<@Sum<[@n]>> +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@Caller<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_5:[0-9a-zA-Z_\.]+]]: !struct.type<@Caller<[@n]>>, %[[VAL_6:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@outp] : <@Caller<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_8:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_5]][@op] : <@Caller<[@n]>>, !struct.type<@Sum<[@n]>> +// CHECK-NEXT: function.call @Sum::@constrain(%[[VAL_8]], %[[VAL_6]]) : (!struct.type<@Sum<[@n]>>, !array.type<@n x !felt.type>) -> () +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@outp] : <@Sum<[@n]>>, !felt.type +// CHECK-NEXT: constrain.eq %[[VAL_10]], %[[VAL_9]] : !felt.type, !felt.type +// CHECK-NEXT: function.return +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @Sum<[@n]> { +// CHECK-NEXT: struct.field @outp : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) -> !struct.type<@Sum<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_12:[0-9a-zA-Z_\.]+]] = struct.new : <@Sum<[@n]>> +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// 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_14]], %[[VAL_18:[0-9a-zA-Z_\.]+]] = %[[VAL_15]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_19:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_18]], %[[VAL_13]]) +// CHECK-NEXT: scf.condition(%[[VAL_19]]) %[[VAL_17]], %[[VAL_18]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_20:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_21:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_21]] +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_11]]{{\[}}%[[VAL_22]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_20]], %[[VAL_23]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_21]], %[[VAL_25]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_24]], %[[VAL_26]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_12]][@outp] = %[[VAL_16]]#0 : <@Sum<[@n]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_12]] : !struct.type<@Sum<[@n]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_27:[0-9a-zA-Z_\.]+]]: !struct.type<@Sum<[@n]>>, %[[VAL_28:[0-9a-zA-Z_\.]+]]: !array.type<@n x !felt.type>) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_29:[0-9a-zA-Z_\.]+]] = poly.read_const @n : !felt.type +// CHECK-NEXT: %[[VAL_43:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_27]][@outp] : <@Sum<[@n]>>, !felt.type +// CHECK-NEXT: %[[VAL_30:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_31:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_32:[0-9a-zA-Z_\.]+]]:2 = scf.while (%[[VAL_33:[0-9a-zA-Z_\.]+]] = %[[VAL_30]], %[[VAL_34:[0-9a-zA-Z_\.]+]] = %[[VAL_31]]) : (!felt.type, !felt.type) -> (!felt.type, !felt.type) { +// CHECK-NEXT: %[[VAL_35:[0-9a-zA-Z_\.]+]] = bool.cmp lt(%[[VAL_34]], %[[VAL_29]]) +// CHECK-NEXT: scf.condition(%[[VAL_35]]) %[[VAL_33]], %[[VAL_34]] : !felt.type, !felt.type +// CHECK-NEXT: } do { +// CHECK-NEXT: ^bb0(%[[VAL_36:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_37:[0-9a-zA-Z_\.]+]]: !felt.type): +// CHECK-NEXT: %[[VAL_38:[0-9a-zA-Z_\.]+]] = cast.toindex %[[VAL_37]] +// CHECK-NEXT: %[[VAL_39:[0-9a-zA-Z_\.]+]] = array.read %[[VAL_28]]{{\[}}%[[VAL_38]]] : <@n x !felt.type>, !felt.type +// CHECK-NEXT: %[[VAL_40:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_36]], %[[VAL_39]] : !felt.type, !felt.type +// CHECK-NEXT: %[[VAL_41:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: %[[VAL_42:[0-9a-zA-Z_\.]+]] = felt.add %[[VAL_37]], %[[VAL_41]] : !felt.type, !felt.type +// CHECK-NEXT: scf.yield %[[VAL_40]], %[[VAL_42]] : !felt.type, !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: constrain.eq %[[VAL_43]], %[[VAL_32]]#0 : !felt.type, !felt.type +// CHECK-NEXT: function.return +// 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..157dad39b --- /dev/null +++ b/circom/tests/subcmps/conv_map2idx_A.circom @@ -0,0 +1,24 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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: module attributes { diff --git a/circom/tests/subcmps/conv_map2idx_B.circom b/circom/tests/subcmps/conv_map2idx_B.circom new file mode 100644 index 000000000..ec8b4ef31 --- /dev/null +++ b/circom/tests/subcmps/conv_map2idx_B.circom @@ -0,0 +1,27 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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: module attributes { diff --git a/circom/tests/subcmps/conv_map2idx_C.circom b/circom/tests/subcmps/conv_map2idx_C.circom new file mode 100644 index 000000000..b73406606 --- /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 -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..19a6ac550 --- /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 -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..89327bc4f --- /dev/null +++ b/circom/tests/subcmps/mapped_01.circom @@ -0,0 +1,45 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..98560514c --- /dev/null +++ b/circom/tests/subcmps/mapped_02.circom @@ -0,0 +1,62 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..4c49dc115 --- /dev/null +++ b/circom/tests/subcmps/mapped_03.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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..8a6d992d4 --- /dev/null +++ b/circom/tests/subcmps/mapped_04.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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/subcmps0A.circom b/circom/tests/subcmps/subcmps0A.circom new file mode 100644 index 000000000..0496302f4 --- /dev/null +++ b/circom/tests/subcmps/subcmps0A.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +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 { diff --git a/circom/tests/subcmps/subcmps0B.circom b/circom/tests/subcmps/subcmps0B.circom new file mode 100644 index 000000000..0e8d12e81 --- /dev/null +++ b/circom/tests/subcmps/subcmps0B.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +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 { diff --git a/circom/tests/subcmps/subcmps0C.circom b/circom/tests/subcmps/subcmps0C.circom new file mode 100644 index 000000000..ebc789859 --- /dev/null +++ b/circom/tests/subcmps/subcmps0C.circom @@ -0,0 +1,29 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/subcmps/subcmps0D.circom b/circom/tests/subcmps/subcmps0D.circom new file mode 100644 index 000000000..4665163f8 --- /dev/null +++ b/circom/tests/subcmps/subcmps0D.circom @@ -0,0 +1,30 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/subcmps/subcmps1.circom b/circom/tests/subcmps/subcmps1.circom new file mode 100644 index 000000000..fe10d0352 --- /dev/null +++ b/circom/tests/subcmps/subcmps1.circom @@ -0,0 +1,36 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/subcmps/subcmps2.circom b/circom/tests/subcmps/subcmps2.circom new file mode 100644 index 000000000..a3da579ae --- /dev/null +++ b/circom/tests/subcmps/subcmps2.circom @@ -0,0 +1,40 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -o %t %s | sed -n 's/.*Written successfully:.* \(.*\)/\1/p' | xargs cat | FileCheck %s --enable-var-scope +// END. +// XFAIL:.* + +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 { diff --git a/circom/tests/subcmps/subcmps3.circom b/circom/tests/subcmps/subcmps3.circom new file mode 100644 index 000000000..09f439bd5 --- /dev/null +++ b/circom/tests/subcmps/subcmps3.circom @@ -0,0 +1,37 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/subcmps/subcmps4.circom b/circom/tests/subcmps/subcmps4.circom new file mode 100644 index 000000000..f3e37af3f --- /dev/null +++ b/circom/tests/subcmps/subcmps4.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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 { diff --git a/circom/tests/subcmps/subcmps5a.circom b/circom/tests/subcmps/subcmps5a.circom new file mode 100644 index 000000000..4836e333b --- /dev/null +++ b/circom/tests/subcmps/subcmps5a.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SubCmp<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Nop<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop<[]>> +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[ARG_0]] : <@Nop<[]>>, !felt.type +// CHECK: function.return %[[SELF]] : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_0]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK: function.return +// CHECK-LABEL: struct.def @SubCmp<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK: struct.field @n : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp<[]>> +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = function.call @Nop::@compute(%[[ARG_0]]) : (!felt.type) -> !struct.type<@Nop<[]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[VAL_1]] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@n] = %[[VAL_0]] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK: function.return %[[SELF]] : !struct.type<@SubCmp<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@n] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK-DAG: function.call @Nop::@constrain(%[[VAL_0]], %[[ARG_0]]) : (!struct.type<@Nop<[]>>, !felt.type) -> () +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_2]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK: function.return diff --git a/circom/tests/subcmps/subcmps5b.circom b/circom/tests/subcmps/subcmps5b.circom new file mode 100644 index 000000000..7cca732d7 --- /dev/null +++ b/circom/tests/subcmps/subcmps5b.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SubCmp<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Nop<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop<[]>> +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[ARG_0]] : <@Nop<[]>>, !felt.type +// CHECK: function.return %[[SELF]] : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_0]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK: function.return +// CHECK-LABEL: struct.def @SubCmp<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK: struct.field @n : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp<[]>> +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = function.call @Nop::@compute(%[[ARG_0]]) : (!felt.type) -> !struct.type<@Nop<[]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[VAL_1]] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@n] = %[[VAL_0]] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK: function.return %[[SELF]] : !struct.type<@SubCmp<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@n] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-DAG: function.call @Nop::@constrain(%[[VAL_0]], %[[VAL_1]]) : (!struct.type<@Nop<[]>>, !felt.type) -> () +// CHECK: function.return diff --git a/circom/tests/subcmps/subcmps5c.circom b/circom/tests/subcmps/subcmps5c.circom new file mode 100644 index 000000000..281ac97aa --- /dev/null +++ b/circom/tests/subcmps/subcmps5c.circom @@ -0,0 +1,64 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SubCmp<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Nop<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@Nop<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop<[]>> +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[ARG_0]] : <@Nop<[]>>, !felt.type +// CHECK: function.return %[[SELF]] : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_0]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK: function.return +// CHECK-LABEL: struct.def @SubCmp<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK: struct.field @n : !struct.type<@Nop<[]>> +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp<[]>> +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = function.call @Nop::@compute(%[[ARG_0]]) : (!felt.type) -> !struct.type<@Nop<[]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[VAL_1]] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@n] = %[[VAL_0]] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK: function.return %[[SELF]] : !struct.type<@SubCmp<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@n] : <@SubCmp<[]>>, !struct.type<@Nop<[]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-DAG: function.call @Nop::@constrain(%[[VAL_0]], %[[VAL_1]]) : (!struct.type<@Nop<[]>>, !felt.type) -> () +// CHECK-DAG: constrain.eq %[[VAL_1]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK-DAG: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: %[[VAL_3:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_2]], %[[VAL_3]] : !felt.type, !felt.type +// CHECK: function.return diff --git a/circom/tests/subcmps/subcmps6a.circom b/circom/tests/subcmps/subcmps6a.circom new file mode 100644 index 000000000..5e6789b88 --- /dev/null +++ b/circom/tests/subcmps/subcmps6a.circom @@ -0,0 +1,60 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SubCmp<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Nop<[@n]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@Nop<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop<[@n]>> +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[ARG_0]] : <@Nop<[@n]>>, !felt.type +// CHECK: function.return %[[SELF]] : !struct.type<@Nop<[@n]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop<[@n]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@Nop<[@n]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_0]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK: function.return +// CHECK-LABEL: struct.def @SubCmp<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK: struct.field @n : !struct.type<@Nop<[1]>> +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp<[]>> +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = function.call @Nop::@compute(%[[ARG_0]]) : (!felt.type) -> !struct.type<@Nop<[1]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[1]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[VAL_1]] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@n] = %[[VAL_0]] : <@SubCmp<[]>>, !struct.type<@Nop<[1]>> +// CHECK: function.return %[[SELF]] : !struct.type<@SubCmp<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@n] : <@SubCmp<[]>>, !struct.type<@Nop<[1]>> +// CHECK-DAG: function.call @Nop::@constrain(%[[VAL_0]], %[[ARG_0]]) : (!struct.type<@Nop<[1]>>, !felt.type) -> () +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[1]>>, !felt.type +// CHECK-DAG: %[[VAL_2:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_2]], %[[VAL_1]] : !felt.type, !felt.type +// CHECK: function.return diff --git a/circom/tests/subcmps/subcmps6b.circom b/circom/tests/subcmps/subcmps6b.circom new file mode 100644 index 000000000..743f1a7d9 --- /dev/null +++ b/circom/tests/subcmps/subcmps6b.circom @@ -0,0 +1,58 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@SubCmp<[]>>, veridise.lang = "llzk"} { +// CHECK-LABEL: struct.def @Nop<[@n]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@Nop<[@n]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@Nop<[@n]>> +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[ARG_0]] : <@Nop<[@n]>>, !felt.type +// CHECK: function.return %[[SELF]] : !struct.type<@Nop<[@n]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@Nop<[@n]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@o] : <@Nop<[@n]>>, !felt.type +// CHECK-DAG: constrain.eq %[[VAL_0]], %[[ARG_0]] : !felt.type, !felt.type +// CHECK: function.return +// CHECK-LABEL: struct.def @SubCmp<[]> { +// CHECK: struct.field @o : !felt.type {llzk.pub} +// CHECK: struct.field @n : !struct.type<@Nop<[1]>> +// CHECK-LABEL: function.def @compute( +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) -> !struct.type<@SubCmp<[]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-DAG: %[[SELF:[0-9a-zA-Z_\.]+]] = struct.new : <@SubCmp<[]>> +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = function.call @Nop::@compute(%[[ARG_0]]) : (!felt.type) -> !struct.type<@Nop<[1]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_0]][@o] : <@Nop<[1]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@o] = %[[VAL_1]] : <@SubCmp<[]>>, !felt.type +// CHECK-DAG: struct.writef %[[SELF]][@n] = %[[VAL_0]] : <@SubCmp<[]>>, !struct.type<@Nop<[1]>> +// CHECK: function.return %[[SELF]] : !struct.type<@SubCmp<[]>> +// CHECK-LABEL: function.def @constrain( +// CHECK-SAME: %[[SELF:[0-9a-zA-Z_\.]+]]: !struct.type<@SubCmp<[]>>, +// CHECK-SAME: %[[ARG_0:[0-9a-zA-Z_\.]+]]: !felt.type +// CHECK-SAME: ) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-DAG: %[[VAL_0:[0-9a-zA-Z_\.]+]] = struct.readf %[[SELF]][@n] : <@SubCmp<[]>>, !struct.type<@Nop<[1]>> +// CHECK-DAG: %[[VAL_1:[0-9a-zA-Z_\.]+]] = undef.undef : !felt.type +// CHECK-DAG: function.call @Nop::@constrain(%[[VAL_0]], %[[VAL_1]]) : (!struct.type<@Nop<[1]>>, !felt.type) -> () +// CHECK: function.return diff --git a/circom/tests/subcmps/subcmps7.circom b/circom/tests/subcmps/subcmps7.circom new file mode 100644 index 000000000..02f581f3b --- /dev/null +++ b/circom/tests/subcmps/subcmps7.circom @@ -0,0 +1,28 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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 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(); + +// CHECK-LABEL: module attributes { +// 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: struct.field @n : !array.type<2, !struct.type<@Nop<[1]>>> diff --git a/circom/tests/type_conversions/bool_1.circom b/circom/tests/type_conversions/bool_1.circom new file mode 100644 index 000000000..891f17d68 --- /dev/null +++ b/circom/tests/type_conversions/bool_1.circom @@ -0,0 +1,41 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[5]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @binop_comp(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type attributes {function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = bool.cmp gt(%[[VAL_0]], %[[VAL_1]]) +// CHECK-NEXT: %[[VAL_3:[0-9a-zA-Z_\.]+]] = cast.tofelt %[[VAL_2]] : i1 +// CHECK-NEXT: function.return %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A<[@x]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_4:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_5:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@x]>> +// CHECK-NEXT: %[[VAL_6:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// CHECK-NEXT: %[[VAL_7:[0-9a-zA-Z_\.]+]] = function.call @binop_comp(%[[VAL_4]], %[[VAL_6]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: struct.writef %[[VAL_5]][@out] = %[[VAL_7]] : <@A<[@x]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_5]] : !struct.type<@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@x]>>, %[[VAL_9:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// CHECK-NEXT: %[[VAL_11:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_8]][@out] : <@A<[@x]>>, !felt.type +// CHECK-NEXT: function.return +// 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..f9b883e01 --- /dev/null +++ b/circom/tests/type_conversions/bool_2.circom @@ -0,0 +1,77 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[555]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: function.def @binop_bool(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type, %[[VAL_1:[0-9a-zA-Z_\.]+]]: !felt.type) -> !felt.type 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]]) +// 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]]) +// 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 +// CHECK-NEXT: function.return %[[VAL_7]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.def @A<[@x]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_8:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_9:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@x]>> +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// 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, !felt.type) -> !felt.type +// 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]]) +// CHECK-NEXT: %[[VAL_15:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_14]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_16:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_16]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_17:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_17]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_9]][@out] = %[[VAL_15]] : <@A<[@x]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_9]] : !struct.type<@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_18:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@x]>>, %[[VAL_19:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_20:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// CHECK-NEXT: %[[VAL_UNUSED:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_18]][@out] : <@A<[@x]>>, !felt.type +// CHECK-NEXT: %[[VAL_21:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = function.call @binop_bool(%[[VAL_19]], %[[VAL_20]]) : (!felt.type, !felt.type) -> !felt.type +// CHECK-NEXT: %[[VAL_23:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: %[[VAL_24:[0-9a-zA-Z_\.]+]] = bool.cmp ne(%[[VAL_22]], %[[VAL_23]]) +// CHECK-NEXT: %[[VAL_25:[0-9a-zA-Z_\.]+]] = scf.if %[[VAL_24]] -> (!felt.type) { +// CHECK-NEXT: %[[VAL_26:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_26]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: %[[VAL_27:[0-9a-zA-Z_\.]+]] = felt.const 0 +// CHECK-NEXT: scf.yield %[[VAL_27]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..05b85df44 --- /dev/null +++ b/circom/tests/type_conversions/bool_3.circom @@ -0,0 +1,61 @@ +// REQUIRES: circom +// RUN: rm -rf %t && mkdir %t && %circom --stabilize --llzk -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.main = !struct.type<@A<[99]>>, veridise.lang = "llzk"} { +// CHECK-NEXT: struct.def @A<[@x]> { +// CHECK-NEXT: struct.field @out : !felt.type {llzk.pub} +// CHECK-NEXT: function.def @compute(%[[VAL_0:[0-9a-zA-Z_\.]+]]: !felt.type) -> !struct.type<@A<[@x]>> attributes {function.allow_non_native_field_ops, function.allow_witness} { +// CHECK-NEXT: %[[VAL_1:[0-9a-zA-Z_\.]+]] = struct.new : <@A<[@x]>> +// CHECK-NEXT: %[[VAL_2:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// 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]]) +// 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]]) +// 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) { +// CHECK-NEXT: %[[VAL_10:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_10]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_3]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: struct.writef %[[VAL_1]][@out] = %[[VAL_9]] : <@A<[@x]>>, !felt.type +// CHECK-NEXT: function.return %[[VAL_1]] : !struct.type<@A<[@x]>> +// CHECK-NEXT: } +// CHECK-NEXT: function.def @constrain(%[[VAL_11:[0-9a-zA-Z_\.]+]]: !struct.type<@A<[@x]>>, %[[VAL_12:[0-9a-zA-Z_\.]+]]: !felt.type) attributes {function.allow_constraint, function.allow_non_native_field_ops} { +// CHECK-NEXT: %[[VAL_13:[0-9a-zA-Z_\.]+]] = poly.read_const @x : !felt.type +// CHECK-NEXT: %[[VAL_14:[0-9a-zA-Z_\.]+]] = struct.readf %[[VAL_11]][@out] : <@A<[@x]>>, !felt.type +// 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]]) +// 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]]) +// 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) { +// CHECK-NEXT: %[[VAL_22:[0-9a-zA-Z_\.]+]] = felt.const 1 +// CHECK-NEXT: scf.yield %[[VAL_22]] : !felt.type +// CHECK-NEXT: } else { +// CHECK-NEXT: scf.yield %[[VAL_15]] : !felt.type +// CHECK-NEXT: } +// CHECK-NEXT: function.return +// 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..e1d21cfa4 --- /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 -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..54b91f857 --- /dev/null +++ b/flake.lock @@ -0,0 +1,206 @@ +{ + "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-pkgs", + "flake-utils" + ], + "llzk-pkgs": [ + "llzk-pkgs" + ], + "nixpkgs": [ + "llzk-pkgs", + "nixpkgs" + ], + "pcl-mlir-pkg": "pcl-mlir-pkg", + "release-helpers": "release-helpers" + }, + "locked": { + "lastModified": 1769727630, + "narHash": "sha256-owf7/FpxfsiQr4kNDvv6xpH8f0f3FGBhk10DGUWrTk0=", + "owner": "project-llzk", + "repo": "llzk-lib", + "rev": "3a42147c4089d0d4504d4a75712e0330438ce64b", + "type": "github" + }, + "original": { + "owner": "project-llzk", + "repo": "llzk-lib", + "type": "github" + } + }, + "llzk-pkgs": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1768329036, + "narHash": "sha256-aScxpJJ7VMDETL4hi+6Pojxb4VwKucW0VwOPCZDt53Q=", + "owner": "project-llzk", + "repo": "llzk-nix-pkgs", + "rev": "830b084608fe510c2bd2612bf66def7cdabf7cf7", + "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" + ], + "release-helpers": [ + "llzk-rs-pkgs", + "llzk-lib", + "release-helpers" + ] + }, + "locked": { + "lastModified": 1769729896, + "narHash": "sha256-VFl5uZpjmOVtalhJWWejlwYAcqRR790THaMBr8EZwPY=", + "ref": "refs/heads/main", + "rev": "eaa5d0dc35d522757fcbd2027d8940d4b908ca1b", + "revCount": 327, + "submodules": true, + "type": "git", + "url": "https://github.com/project-llzk/llzk-rs" + }, + "original": { + "submodules": true, + "type": "git", + "url": "https://github.com/project-llzk/llzk-rs" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1768302833, + "narHash": "sha256-h5bRFy9bco+8QcK7rGoOiqMxMbmn21moTACofNLRMP4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "61db79b0c6b838d9894923920b612048e1201926", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pcl-mlir-pkg": { + "inputs": { + "flake-utils": [ + "llzk-lib", + "llzk-pkgs", + "flake-utils" + ], + "nixpkgs": [ + "llzk-lib", + "llzk-pkgs", + "nixpkgs" + ], + "release-helpers": [ + "llzk-lib", + "release-helpers" + ], + "shared-pkgs": [ + "llzk-lib", + "llzk-pkgs" + ] + }, + "locked": { + "lastModified": 1762369885, + "narHash": "sha256-j7aARWOa10cranctQ39cfEELMlEAeZsRrJIGKPJ5o88=", + "owner": "Veridise", + "repo": "pcl-mlir", + "rev": "a82620c4dabf6e51ceda66bba2abb19f7f74ac7d", + "type": "github" + }, + "original": { + "owner": "Veridise", + "repo": "pcl-mlir", + "type": "github" + } + }, + "release-helpers": { + "inputs": { + "flake-utils": [ + "llzk-lib", + "llzk-pkgs", + "flake-utils" + ], + "nixpkgs": [ + "llzk-lib", + "llzk-pkgs", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747068965, + "narHash": "sha256-px7s7IVHnR3SiShc7FKCUiL6vfqew5AM6jnkH7qO03I=", + "owner": "project-llzk", + "repo": "open-source-release-helpers", + "rev": "c3d1ab6082cc62b167927d074f2836f290b9e3b7", + "type": "github" + }, + "original": { + "owner": "project-llzk", + "repo": "open-source-release-helpers", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": [ + "llzk-pkgs", + "flake-utils" + ], + "llzk-lib": "llzk-lib", + "llzk-pkgs": "llzk-pkgs", + "llzk-rs-pkgs": "llzk-rs-pkgs", + "nixpkgs": [ + "llzk-pkgs", + "nixpkgs" + ], + "release-helpers": [ + "llzk-lib", + "release-helpers" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..2682c945e --- /dev/null +++ b/flake.nix @@ -0,0 +1,110 @@ +{ + inputs = { + llzk-pkgs.url = "github:project-llzk/llzk-nix-pkgs"; + nixpkgs.follows = "llzk-pkgs/nixpkgs"; + flake-utils.follows = "llzk-pkgs/flake-utils"; + llzk-lib = { + url = "github:project-llzk/llzk-lib"; + inputs = { + nixpkgs.follows = "llzk-pkgs/nixpkgs"; + flake-utils.follows = "llzk-pkgs/flake-utils"; + llzk-pkgs.follows = "llzk-pkgs"; + }; + }; + release-helpers.follows = "llzk-lib/release-helpers"; + + llzk-rs-pkgs = { + url = "git+https://github.com/project-llzk/llzk-rs?submodules=1"; + inputs = { + nixpkgs.follows = "llzk-pkgs/nixpkgs"; + flake-utils.follows = "llzk-pkgs/flake-utils"; + llzk-pkgs.follows = "llzk-pkgs"; + llzk-lib.follows = "llzk-lib"; + }; + }; + }; + + # 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, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ + 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 + 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; + + 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..c5d900fbb --- /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.readf` 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 `undef.undef` 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.readf` operation that +reads the signal from the subcomponent. + +## 3. Subcomponent writing + +Once lowering is complete, the backend inserts `struct.writef` 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/codegen.rs b/llzk_backend/src/codegen.rs new file mode 100644 index 000000000..f40fe4b11 --- /dev/null +++ b/llzk_backend/src/codegen.rs @@ -0,0 +1,132 @@ +//! Entry point for LLZK code generation. + +use crate::module::GenerateLLZKInModule; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::LlzkCodegen; +use ansi_term::Color; +use anyhow::anyhow; +use anyhow::Result; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::IntegerAttribute; +use llzk::prelude::LlzkContext; +use llzk::prelude::Module; +use llzk::prelude::OperationMutLike; +use llzk::prelude::StructType; +use llzk::prelude::TypeAttribute; +use llzk::prelude::MAIN_ATTR_NAME; +use melior::ir::Type; +use num_bigint_dig::BigUint; +use program_structure::constants::UsefulConstants; + +/// 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(FlatSymbolRefAttribute::new(context, &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, + filename: &str, + pass_pipeline: &str, + prime: &str, + verbose: bool, + stabilize: bool, +) -> Result<(), ()> { + let prime = UsefulConstants::new(&prime.to_string()).get_p().to_biguint(); + if prime.is_none() { + eprintln!( + "{} prime should be convertible to unsigned", + Color::Red.paint("LLZK config error:"), + ); + std::process::exit(10); // force exit to avoid hang if MLIR state is inconsistent + } + let prime = prime.unwrap(); + + let ctx = LlzkContext::new(); + let module = new_llzk_module(&ctx, program, &prime).map_err(|err| { + if 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, prime, verbose, stabilize); + + program.gen_llzk(&codegen).map_err(|err| { + if 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_passes(pass_pipeline).map_err(|err| { + if 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 + codegen.write_to_file(filename).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..961e29023 --- /dev/null +++ b/llzk_backend/src/function.rs @@ -0,0 +1,2088 @@ +//! 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::abstract_syntax_tree::ast::Expression) and +//! [Statement](program_structure::abstract_syntax_tree::ast::Statement) nodes. + +use crate::function::felt::is_felt_type; +use crate::function_ext::FunctionLike; +use crate::gen_context::BlockContextStack; +use crate::gen_context::GenWithCircomScopeHandling; +use crate::gen_context::NestedBlockInfo; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::insert_after_if_op_result; +use crate::shared::is_bool; +use crate::shared::is_index; +use crate::shared::new_array_type; +use crate::shared::next_in_block_mut; +use crate::shared::no_results; +use crate::shared::parent_operation_mut; +use crate::shared::remove_from_parent; +use crate::shared::replace_uses_with_new_block_argument; +use crate::shared::set_operand_if_undef; +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::SubcmpCallsMap; +use crate::template_ext::TemplateLike as _; +use crate::try_for_loop_heuristic; +use anyhow::anyhow; +use anyhow::Context as _; +use anyhow::Result; +use llzk::builder::OpBuilder; +use llzk::dialect::array::ArrayCtor; +use llzk::dialect::cast; +use llzk::dialect::undef; +use llzk::operation::erase_op; +use llzk::operation::WalkOperationMutLike; +use llzk::prelude::array; +use llzk::prelude::bool; +use llzk::prelude::felt; +use llzk::prelude::function; +use llzk::prelude::function::is_func_def; +use llzk::prelude::function::is_func_return; +use llzk::prelude::melior_dialects::arith; +use llzk::prelude::melior_dialects::index; +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::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::FuncDefOpLike as _; +use llzk::prelude::FuncDefOpRefMut; +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; +use llzk::prelude::OperationRef; +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 melior::dialect::ods::math; +use num_bigint_dig::BigInt; +use num_traits::Zero; +use program_structure::ast::Access; +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 std::collections::HashMap; +use std::convert::TryFrom; +use std::convert::TryInto; +use std::ops::Deref; +use std::ops::DerefMut; + +/// Special variable name used to reference the return Value throughout the +/// conversion of circom return locations to LLZK return locations. +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`. +const VAR_NAME_HAD_RETURN: &str = "**had_return**"; +/// LLZK attribute used to mark yield/return ops generated from circom return statements. +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. +const OPERAND_VAL_NAMES: &str = "operand_val_names"; + +/// 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<'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// The function reference. + pub(crate) func: FuncDefOpRefMut<'ctx, 'func>, + /// Nested block context within the function. + pub(crate) block_ctx: BlockContextStack<'ctx, 'blk, 'val>, + /// Calls to subcomponents + pub(crate) subcmp_calls: SubcmpCallsMap<'ctx>, +} + +/// 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<'ctx, 'func, 'blk, 'val> FunctionContext<'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. + pub fn new<'ast, const FREE_FUNC: bool>( + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + func: FuncDefOpRefMut<'ctx, 'func>, + param_name_to_value: HashMap>, + ) -> Result { + let mut block_ctx = BlockContextStack::new(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, block_ctx, subcmp_calls: Default::default() }) + } + + /// 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") + } + + /// 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) + } + + /// 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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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) + } + + /// 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) + } + + /// Create 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, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + if !is_felt_type(val.r#type()) { + self.append_op_unnamed_result(cast::tofelt(location, val)) + } else { + Ok(val) + } + } + + /// Create 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) + } + } + + /// Create a cast to bool type (i1) if the given value is not already a bool. + #[inline] + pub fn cast_to_bool_if_needed<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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) + } + } + + /// Create an op to cast `val` to match the `expected` type. + #[inline] + pub fn cast_to_expected_type_if_needed<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + expected: Type<'ctx>, + ) -> Result> { + if expected == val.r#type() { + Ok(val) + } else if is_felt_type(expected) { + self.cast_to_felt_if_needed(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() + ) + } + } + + /// Create an op to cast `val` to match the return type of the function. + #[inline] + pub fn cast_to_return_type_if_needed<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + location: Location<'ctx>, + val: Value<'ctx, 'val>, + ) -> Result> { + self.cast_to_expected_type_if_needed(codegen, location, val, self.return_type()) + } + + /// Generates a list of undef ops inside the given function context. + #[inline] + pub fn gen_arg_undefs( + &mut self, + args: &[Type<'ctx>], + loc: Location<'ctx>, + ) -> Result>> { + args.iter().copied().map(|t| self.append_op_unnamed_result(undef::undef(loc, t))).collect() + } + + /// Searches for the argument index of a subcomponent's signal. + pub fn lookup_arg_idx( + &self, + subcmp_signal: &str, + subcmp_value: &Value<'ctx, 'val>, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + ) -> Result { + let name = self.subcmp_calls.get(subcmp_value).ok_or_else(|| { + anyhow::anyhow!( + "template constructed by {subcmp_value}@{:?} not found (map: {:?})", + subcmp_value.to_raw().ptr, + self.subcmp_calls + ) + })?; + let template_data = codegen.find_template_data(name).ok_or_else(|| { + anyhow::anyhow!("template with name {name:?} constructed by {subcmp_value} not found") + })?; + template_data.get_declaration_input_idx(subcmp_signal) + } + + /// Assigns the `rhe` value to the given subcomponent signal. + /// + /// The subcomponent is determined by + /// `var`, which must correspond to a named value in the current scope. + /// Since the subcomponent's call is different depending on the current function the + /// `get_call` callback needs to return the operation representing the call from the value + /// representing the subcomponent. + pub fn assign_subcmp<'op>( + &mut self, + rhe: Value<'ctx, 'val>, + var: &str, + subcmp_signal: &str, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + arg_offset: usize, + get_call: impl FnOnce(Value<'ctx, 'val>) -> Result>, + ) -> Result<()> + where + 'ctx: 'op, + { + let subcmp_value = self.block_ctx.get_named_value(var)?; + + let arg_idx = self.lookup_arg_idx(subcmp_signal, subcmp_value, codegen)?; + + let call_op = get_call(*subcmp_value)?; + set_operand_if_undef(call_op, arg_idx + arg_offset, rhe)?; + insert_after_if_op_result(rhe, call_op) + } + + /// Generate LLZK code in the current function for a circom prefix operation. + pub fn gen_prefix_op<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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.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. + fn generate_simple_scf_if_arm<'ast, F>( + &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<'ast, F1, F2>( + &mut self, + codegen: &LlzkCodegen<'ast, '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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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) + } + + /// 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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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::Values(&[]) + }; + let new_arr = self.append_op_unnamed_result(array::new( + &OpBuilder::new(codegen.context), + 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()); + let builder = &OpBuilder::new(codegen.context); + if let Ok(const_dim) = const_dim { + let values = vec![value; usize::try_from(const_dim.value())?]; + let ctor = ArrayCtor::Values(&values); + self.append_op_unnamed_result(array::new(builder, location, arr_ty, ctor)) + } 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::Values(&[]) + }; + let array_ref = + self.append_op_unnamed_result(array::new(builder, 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<'ast>( + &mut self, + codegen: &LlzkCodegen<'ast, '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.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()) + } + + /// Finalizes the context. + pub fn finalize(&mut self, codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>) -> Result<()> { + self.func.walk_mut(WalkOrder::PreOrder, |mut op| { + // Remove any `undef.undef` 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 undef::is_undef_op(&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 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!(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 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 undef values of the appropriate 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| 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(()) + } + + /// 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 [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_dimensions(codegen, dimensions)? + }; + if codegen.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<'ast, 'ctx, 'func, 'blk, 'val> DimExprConverter<'ctx, 'ast, 'val> + for FunctionContext<'ctx, 'func, 'blk, 'val> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + fn convert_dim_expr( + &self, + codegen: &LlzkCodegen<'ast, '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 } => match access.as_slice() { + [] => { + if let Ok(v) = self.block_ctx.get_named_value(name) { + let id_map = codegen.affine_map_attr("affine_map<()[i] -> (i)>")?; + ArrayDimensionResult::new(id_map, &[*v]) + } else { + todo!("Handle Variable expression in dimension for non-integer, non-template parameter attributes in FunctionContext") + } + } + a => { + todo!("Handle Variable expression with accesses in FunctionContext") + } + }, + Expression::InfixOp { meta, lhe, infix_op, rhe } => { + todo!("Handle Infix expression in dimension for non-integer attributes") + } + Expression::PrefixOp { meta, prefix_op, rhe } => { + todo!("Handle Prefix expression in dimension for non-integer attributes") + } + Expression::InlineSwitchOp { meta, cond, if_true, if_false } => { + todo!( + "Handle InlineSwitchOp expression in dimension for non-integer attributes" + ) + } + Expression::Call { meta, id, args } => { + todo!("Handle Call expression in dimension") + } + // 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 be single arithmetic expressions") + } + } + } + } +} + +/// 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 FunctionContext<'ctx, 'func, 'blk, 'val>, + &mut NestedBlockInfo<'ctx, 'blk, 'val>, + HashMap>, + ) -> Result<()>, + { + let popped = self.block_ctx.pop(); + overwrite_handler(self, 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. [Statement] nodes do not produce a value so this + /// should be the unit type whereas [Expression] nodes produce a Value. + type Output; + + /// Generates LLZK IR from [Statement] and [Expression] nodes in a circom function. + /// + /// 'ast: lifetime of the circom AST element + fn gen_llzk_in_function<'ast>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + ) -> 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<'ast, 'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'ast, '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<'ast, 'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'ast, '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<'ast, 'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + meta: &Meta, + cond: &Expression, + if_case: &Statement, + else_case: &Option>, +) -> Result +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + // 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), + &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), + &mut else_info, + )?; + } + + let location = codegen.location_from_meta(meta); + let condition = cond.gen_llzk_in_function(codegen, function)?; + + // 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<'ast, 'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + meta: &Meta, + cond: &Expression, + body_stmt: &Statement, + loop_bounds: Option>, +) -> Result +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + // 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_function(codegen, fc), + &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), + &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<'ast, 'ctx, 'func, 'blk, 'val>( + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + initializations: &[Statement], +) -> Result<()> +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + initializations.gen_llzk_in_function(codegen, function) +} + +impl<'ctx, 'func, 'blk, 'val> GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> for Statement +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output = SkipRestOfBlock; + + fn gen_llzk_in_function<'ast>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + ) -> Result { + 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, 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); + // Fallback to standard block handling. + stmts.gen_llzk_in_function(codegen, function)?; + 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"); + } + let rvalue = rhe.gen_llzk_in_function(codegen, function)?; + match access.as_slice() { + [] => { + // Since there's no simple assignment in LLZK, just update the mapped Value + // which essentially propagates the assignment. + function.block_ctx.set_named_value(var.clone(), rvalue)?; + } + a => { + let location = codegen.location_from_meta(meta); + let indices = &a + .iter() + .map(|access| { + let idx = match access { + Access::ArrayAccess(index_expr) => { + index_expr.gen_llzk_in_function(codegen, function) + } + #[allow(unused_variables)] // TODO: TEMP + Access::ComponentAccess(name) => { + todo!("Handle Substitution component access in function") + } + }?; + function.cast_to_index_if_needed(location, idx) + }) + .collect::>>>()?; + let arr_ref = function.block_ctx.get_named_value(var)?; + function.append_array_write( + codegen, + *arr_ref, + indices, + location, + rvalue, + Some(var), + )?; + } + } + 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_function(codegen, function).map(drop)?; + Ok(false) + } + Statement::IfThenElse { meta, cond, if_case, else_case } => { + gen_if_then_else(codegen, function, meta, cond, if_case, else_case) + } + Statement::While { meta, cond, stmt } => { + gen_while(codegen, function, meta, cond, stmt, None) + } + Statement::Return { meta, value } => { + let value = value.gen_llzk_in_function(codegen, function)?; + 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_function(codegen, function)?; + let location = codegen.location_from_meta(meta); + let cond = function.cast_to_bool_if_needed(codegen, location, cond)?; + let msg = Some("assertion failed"); + function.append_op_no_result(llzk::dialect::bool::assert(location, cond, msg)?)?; + 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"); + } + } + } +} + +/// This handles translation of circom [Expression] nodes within functions and templates (i.e. +/// [crate::template::GenerateLLZKInTemplate] implementation for [Expression] directly calls this). +/// Therefore, it must handle things that are not legal in functions such as [Expression::BusCall] +/// and [Access::ComponentAccess]. 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, 'func, 'blk, 'val> GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> for Expression +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output = Value<'ctx, 'val>; + + fn gen_llzk_in_function<'ast>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + ) -> 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. + function.append_op_unnamed_result( + codegen.new_felt_const_op(big_int, codegen.location_from_meta(meta))?, + ) + } + Expression::Variable { meta, name, access } => match access.as_slice() { + [] => { + let v = function.block_ctx.get_named_value(name)?; + Ok(*v) + } + a => { + let location = codegen.location_from_meta(meta); + let indices = a + .iter() + .map(|access| { + let idx = match access { + Access::ArrayAccess(index_expr) => { + index_expr.gen_llzk_in_function(codegen, function) + } + #[allow(unused_variables)] // TODO: TEMP + Access::ComponentAccess(name) => { + todo!("Handle component access") + } + }?; + function.cast_to_index_if_needed(location, idx) + }) + .collect::>>>()?; + let arr_ref = function.block_ctx.get_named_value(name)?; + function.append_array_read(*arr_ref, &indices, location, Some(name)) + } + }, + Expression::InfixOp { meta, lhe, infix_op, rhe } => { + let lhs = lhe.gen_llzk_in_function(codegen, function)?; + let rhs = rhe.gen_llzk_in_function(codegen, function)?; + function.gen_infix_op(codegen, meta, infix_op, lhs, rhs) + } + Expression::PrefixOp { meta, prefix_op, rhe } => { + let rhs = rhe.gen_llzk_in_function(codegen, function)?; + function.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_function(codegen, function)?; + let condition = function.cast_to_bool_if_needed(codegen, location, cond_val)?; + let scf_if_op = function.generate_simple_scf_if( + codegen, + meta, + condition, + |fc| if_true.gen_llzk_in_function(codegen, fc), + |fc| if_false.gen_llzk_in_function(codegen, fc), + )?; + + function.append_op_unnamed_result(scf_if_op) + } + Expression::ArrayInLine { meta, values } => { + let location = codegen.location_from_meta(meta); + let builder = &OpBuilder::new(codegen.context); + // Multi-dimensional arrays are made up of array values as their elements + let values = values + .iter() + .map(|val_expr| val_expr.gen_llzk_in_function(codegen, function)) + .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 = function.append_op_unnamed_result(array::new( + builder, + location, + arr_ty, + llzk::dialect::array::ArrayCtor::Values(&[]), + ))?; + for (idx, val) in values.iter().enumerate() { + let idx_val = function.append_op_unnamed_result( + codegen.new_index_const_op(i64::try_from(idx)?, location), + )?; + function.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()]); + function.append_op_unnamed_result(array::new( + builder, + location, + arr_ty, + llzk::dialect::array::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_function(codegen, function)?; + let dim_result = function.convert_dim_expr(codegen, dimension)?; + let dim = Option::from(dim_result) + .ok_or_else(|| anyhow!("unable to convert dimension in function context"))?; + function.generate_uniform_array(codegen, location, value, &dim) + } + Expression::Call { meta, id, args } => { + let builder = OpBuilder::new(codegen.context.deref()); + 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_function(codegen, function)?; + function.cast_to_expected_type_if_needed( + codegen, + location, + operand_val, + expected_type, + ) + }) + .collect::>>()?; + // Create the CallOp in each function using the collected args. + function.append_op_unnamed_result( + function::call( + &builder, + location, + FlatSymbolRefAttribute::new(codegen.context, 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") + } + } + } +} + +impl<'ctx, 'func, 'blk, 'val> GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val> for [Statement] +where + 'ctx: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output = (); + + fn gen_llzk_in_function<'ast>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + function: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + ) -> Result { + for s in self { + let skip_rest_of_block = s.gen_llzk_in_function(codegen, function)?; + 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..92a53f732 --- /dev/null +++ b/llzk_backend/src/gen_context.rs @@ -0,0 +1,497 @@ +//! Handles circom var scoping and LLZK blocks stack management. + +use crate::function::FunctionContext; +use crate::shared::single_result_as_value; +use anyhow::anyhow; +use anyhow::ensure; +use anyhow::Result; +use llzk::prelude::Block; +use llzk::prelude::BlockLike as _; +use llzk::prelude::BlockRef; +use llzk::prelude::FuncDefOp; +use llzk::prelude::Operation; +use llzk::prelude::OperationLike as _; +use llzk::prelude::OperationRef; +use llzk::prelude::Region; +use llzk::prelude::RegionLike as _; +use llzk::prelude::Value; +use std::collections::HashMap; + +/// 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 { + BlockContext { + block, + scope_local_name_to_value: Default::default(), + overwriting_name_to_value: Default::default(), + op_queue: Default::default(), + } + } + + /// Create a new [BlockContext] for the given block, initializing the scope local + /// declarations with the mapping of parameter names to values. + fn new_with_params( + block: BlockRef<'ctx, 'blk>, + param_name_to_value: HashMap>, + ) -> Self { + BlockContext { + block, + scope_local_name_to_value: param_name_to_value, + overwriting_name_to_value: Default::default(), + op_queue: Default::default(), + } + } + + /// 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] for the given function with an initial name-to-value + /// mapping containing function parameters. + pub fn new( + 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(BlockContextStack { + root: BlockContext::new_with_params(root_block, 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, 'func, 'blk, 'val> NestedBlockInfo<'ctx, 'blk, 'val> +where + 'ctx: 'func, + 'func: '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 [FunctionContext] for missing keys. + pub fn add_missing_values( + &mut self, + other: &NestedBlockInfo<'ctx, 'blk, 'val>, + fc: &FunctionContext<'ctx, 'func, '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 FunctionContext<'ctx, 'func, '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 FunctionContext<'ctx, 'func, '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") +} diff --git a/llzk_backend/src/lib.rs b/llzk_backend/src/lib.rs new file mode 100644 index 000000000..53ad07877 --- /dev/null +++ b/llzk_backend/src/lib.rs @@ -0,0 +1,27 @@ +//! 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)] +#![warn(redundant_imports)] +#![deny(unused_must_use)] +#![allow(clippy::useless_conversion)] + +mod codegen; +mod function; +mod function_ext; +mod gen_context; +mod module; +mod program_ext; +#[macro_use] +mod shared; +mod subcmp; +mod template; +mod template_ext; +mod traversal; +mod write_chain; + +pub use codegen::generate_llzk; +pub use program_ext::CachedParseInfo; +pub use program_ext::VCPPlus; diff --git a/llzk_backend/src/module.rs b/llzk_backend/src/module.rs new file mode 100644 index 000000000..6f33eb633 --- /dev/null +++ b/llzk_backend/src/module.rs @@ -0,0 +1,603 @@ +//! 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::function::FunctionContext; +use crate::function::GenerateLLZKInFunction as _; +use crate::function_ext::FunctionLike; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::map_name_to_arg_value; +use crate::shared::ArrayDimensionResult; +use crate::shared::DimExprConverter; +use crate::shared::LlzkCodegen; +use crate::subcmp::unique_instance_types; +use crate::subcmp::SubcmpDeclInfo; +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::builder::OpBuilder; +use llzk::dialect::array::ArrayCtor::MapDimSlice; +use llzk::error::Error; +use llzk::prelude::r#struct::helpers::compute_fn; +use llzk::prelude::r#struct::helpers::constrain_fn; +use llzk::prelude::*; +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::collections::HashMap; +use std::collections::HashSet; +use std::convert::TryFrom; + +/// 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 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, Error>>, + /// Map var/signal name to its LLZK declaration Operation (usually `undef.undef`). + decl_inits: HashMap>, + /// Map `component` name to its declaration information. + subcmp_decls: HashMap>, + /// The template params that may be used to instantiate array dimensions. + template_params: HashSet, +} + +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 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)) + } + + /// 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(&mut self) -> Vec<(String, Type<'ctx>)> { + let mut ops = vec![]; + for (name, info) in &self.subcmp_decls { + let instances = info.instances(); + let field_type: Type<'_> = match instances { + [] => todo!("Handle uninitialized component decl"), + instances => { + let types = unique_instance_types(instances); + if types.len() > 1 { + todo!("Handle subcomponents with different instantiations") + } + match info.dimensions() { + [] => types[0].into(), + dims => ArrayType::new(types[0].into(), dims).into(), + } + } + }; + self.struct_fields.push( + r#struct::field(info.location(), name, field_type, false, false).map(Into::into), + ); + ops.push((name.clone(), field_type)); + } + 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<()> { + 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 an `undef` of the appropriate type. When the actual assignment is + // processed later, this is replaced with the appropriate value. + let dimensions = self.get_dimensions(codegen, dimensions)?; + self.decl_inits.insert( + name.clone(), + dimensions.new_nondet_felt_of_dimensions(codegen, meta)?, + ); + 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), + } + } + + /// 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<'ast>( + &self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + expression: &'ast Expression, + ) -> Result> { + match expression { + Expression::Call { meta, id, args, .. } if meta.get_type_knowledge().is_component() => { + let dims = self.get_dimensions(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_dimensions(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_dimensions(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 { + let new = r#struct::field( + location, + name, + decl_type, + false, + SignalType::Output == *signal_type, + ); + self.struct_fields.push(new.map(Into::into)); + } + // Create an `undef` 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. + fn search_component_instances( + &mut self, + codegen: &LlzkCodegen<'_, 'ctx, impl ProgramLike>, + stmt: &Statement, + ) -> Result<()> { + match stmt { + Statement::Substitution { var, op, rhe, .. } + if matches!(op, AssignOp::AssignVar) && self.subcmp_decls.contains_key(var) => + { + let struct_type = self.find_subcmp_ctor_call(codegen, rhe)?; + self.subcmp_decls.entry(var.clone()).and_modify(|info| { + info.instances_mut().push(struct_type); + }); + + Ok(()) + } + Statement::IfThenElse { if_case, else_case, .. } => { + self.search_component_instances(codegen, if_case.as_ref())?; + if let Some(else_case) = else_case.as_deref() { + self.search_component_instances(codegen, else_case)?; + } + Ok(()) + } + Statement::While { stmt, .. } => { + self.search_component_instances(codegen, stmt.as_ref()) + } + Statement::InitializationBlock { initializations: stmts, .. } + | Statement::Block { stmts, .. } => { + for stmt in stmts { + self.search_component_instances(codegen, stmt)?; + } + Ok(()) + } + _ => Ok(()), + } + } +} + +impl<'ast, 'ctx, 'val> DimExprConverter<'ctx, 'ast, 'val> for DeclarationInfo<'ctx> +where + 'ctx: 'val, +{ + fn convert_dim_expr( + &self, + codegen: &LlzkCodegen<'ast, '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 } => match access.as_slice() { + [] => { + if self.template_params.contains(name) { + let template_param_attr = + FlatSymbolRefAttribute::new(codegen.context, name); + ArrayDimensionResult::new(template_param_attr.into(), &[]) + } else if let Some(op) = self.decl_inits.get(name) { + let id_map = codegen.affine_map_attr("affine_map<()[i] -> (i)>")?; + let value_range = op + .results() + .map(Into::>::into) + .collect::>(); + ArrayDimensionResult::new(id_map, &value_range) + } else { + todo!("Handle Variable expression in dimension for non-integer, non-template parameter attributes in DeclarationInfo") + } + } + a => { + todo!("Handle Variable expression with accesses in DeclarationInfo") + } + }, + Expression::InfixOp { meta, lhe, infix_op, rhe } => { + todo!("Handle Infix expression in dimension for non-integer attributes in DeclarationInfo") + } + Expression::PrefixOp { meta, prefix_op, rhe } => { + todo!("Handle Prefix expression in dimension for non-integer attributes in DeclarationInfo") + } + Expression::InlineSwitchOp { meta, cond, if_true, if_false } => { + todo!( + "Handle InlineSwitchOp expression in dimension for non-integer attributes in DeclarationInfo" + ) + } + Expression::Call { meta, id, args } => { + todo!("Handle Call expression in dimension") + } + // 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 be single arithmetic expressions") + } + } + } + } +} + +/// 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<'ast, 'ctx, impl ProgramLike>, +) -> Result<()> { + if codegen.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 mut func_context = FunctionContext::new::(codegen, func, name_to_value)?; + func_like.get_body().gen_llzk_in_function(codegen, &mut func_context)?; + 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<'ast, 'ctx, impl ProgramLike>, +) -> Result<()> { + if codegen.verbose { + println!("Generating LLZK for template {}", template_like.get_name()); + } + // Collect declarations first to determine struct fields and function parameters. + let mut declarations = codegen.take_template_decl(template_like.get_name())?; + let subcmps = declarations.complete(); + + // Generate the struct definition, prepopulated with fields. + let struct_loc = template_like.get_location(codegen); + let struct_params: Vec<_> = + template_like.get_name_of_params().iter().map(String::as_str).collect(); + let struct_def = r#struct::def( + struct_loc, + template_like.get_name(), + &struct_params, + declarations.struct_fields, + )?; + let new_struct = codegen.add_struct(struct_def)?; + + // 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(struct_loc, new_struct_type, &inputs, Some(&arg_attrs))?.into(), + ))? + .into(); + let constrain_func = FuncDefOpRef::try_from(struct_body.append_operation( + constrain_fn(struct_loc, 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())?, + )?; + 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)?, + )?; + + // Insert Operations to read templated struct parameters into an SSA Value in each function. + // This ensures the struct parameter is available as a Value in the block context. + for name in struct_params.into_iter() { + compute_ctx.block_ctx.declare_name_if_not_present(name, || { + Ok(poly::read_const(struct_loc, name, codegen.felt_type().into())) + })?; + constrain_ctx.block_ctx.declare_name_if_not_present(name, || { + Ok(poly::read_const(struct_loc, name, codegen.felt_type().into())) + })?; + } + + // Insert read operations for struct fields into constrain functions. + let location = codegen.location_unknown(); + let builder = OpBuilder::new(codegen.context); + for field in new_struct.get_field_defs() { + let field_name = field.field_name(); + constrain_ctx.block_ctx.declare_name_if_not_present(field_name, || { + r#struct::readf( + &builder, + location, + field.field_type(), + constrain_func.self_value_of_constrain()?, + field_name, + ) + .map_err(Into::into) + })?; + } + + // 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 declarations.decl_inits { + // 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 op_builder = OpBuilder::new(codegen.context); + let subcmp_decls = declarations.subcmp_decls; + // Insert the Operations created from subcomponent Declaration statements and map the + // circom variable name to a LLZK op result Value. + for (name, subcmp_type) in subcmps { + compute_ctx.block_ctx.declare_name_if_not_present(&name, || { + ArrayType::try_from(subcmp_type) + .map(|subcmp_type| { + array::new( + &op_builder, + codegen.location_unknown(), + subcmp_type, + MapDimSlice(&[], &[]), + ) + }) + .or_else(|_| Ok(undef::undef(codegen.location_unknown(), subcmp_type))) + })?; + + let self_ref = constrain_ctx.func.self_value_of_constrain()?; + constrain_ctx.block_ctx.declare_name_if_not_present(&name, || { + Ok(r#struct::readf( + &op_builder, + subcmp_decls[&name].location(), + subcmp_type, + self_ref, + &name, + )?) + })?; + } + + let subcmp_names = subcmp_decls.into_keys().collect(); + + // Visit the body of the template and generate LLZK IR for it within the struct functions. + let template_context = + TemplateContext::new(new_struct, compute_ctx, constrain_ctx, &subcmp_names); + template_like.gen_preamble(codegen, &template_context)?; + template_like.get_body().gen_llzk_in_template(codegen, &template_context)?; + template_context.finalize(codegen) +} + +/// 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.stabilize) { + gen_function_llzk(f, codegen)?; + } + // Collect declaration information for all templates first to avoid duplicating work. + for t in self.get_templates(false) { + codegen.put_template_decl(t.get_name(), t.get_declarations(codegen)?); + } + for t in self.get_templates(codegen.stabilize) { + gen_template_llzk(t, codegen)?; + } + Ok(()) + } +} diff --git a/llzk_backend/src/program_ext.rs b/llzk_backend/src/program_ext.rs new file mode 100644 index 000000000..7b91752df --- /dev/null +++ b/llzk_backend/src/program_ext.rs @@ -0,0 +1,191 @@ +//! Extensions for the [`ProgramArchive`] and [`VCP`] types. + +use crate::function_ext::FunctionLike; +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; + +/// 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(&self, name: &str) -> &impl TemplateLike { + 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(&self, name: &str) -> &impl TemplateLike { + 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 [&T]) { + 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..18f601c24 --- /dev/null +++ b/llzk_backend/src/shared.rs @@ -0,0 +1,1138 @@ +//! Shared code generation utilities. + +use crate::module::DeclarationInfo; +use crate::program_ext::ProgramLike; +use crate::template_ext::TemplateLike; +use crate::template_ext::WireLike; +use crate::traversal::walk_from_block; +use crate::traversal::WalkCallbacks; +use ansi_term::Color; +use anyhow::anyhow; +use anyhow::ensure; +use anyhow::Result; +use llzk::dialect::undef; +use llzk::operation::move_op_after; +use llzk::prelude::felt; +use llzk::prelude::melior_dialects::arith; +use llzk::prelude::verify_operation_with_diags; +use llzk::prelude::ArrayType; +use llzk::prelude::Attribute; +use llzk::prelude::AttributeLike; +use llzk::prelude::BlockLike; +use llzk::prelude::BlockRef; +use llzk::prelude::BoolAttribute; +use llzk::prelude::CallOpLike as _; +use llzk::prelude::CallOpRef; +use llzk::prelude::FeltConstAttribute; +use llzk::prelude::FeltType; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::FuncDefOp; +use llzk::prelude::FuncDefOpLike; +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::OperationRef; +use llzk::prelude::OperationResult; +use llzk::prelude::PassManager; +use llzk::prelude::StringAttribute; +use llzk::prelude::StructDefOp; +use llzk::prelude::StructDefOpRef; +use llzk::prelude::StructDefOpRefMut; +use llzk::prelude::StructType; +use llzk::prelude::Type; +use llzk::prelude::TypeLike as _; +use llzk::prelude::Value; +use llzk::prelude::ValueLike; +use llzk::value_ext::get_single_user; +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::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::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; + +/// 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>), + /// Just the map of signal name to type left behind after generating LLZK for a template. + Remnant(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) +} + +/// 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>, + /// The prime field modulus. + pub prime: BigUint, + /// State of the `--verbose` flag. + pub verbose: bool, + /// State of the `--stabilize` flag. + pub stabilize: bool, + /// Declaration info pre-computed for all templates. + template_decls: RefCell>>, +} + +impl<'ast, 'ctx, P: ProgramLike> LlzkCodegen<'ast, 'ctx, P> { + /// Construct. + pub fn new( + program: &'ast P, + context: &'ctx LlzkContext, + module: Module<'ctx>, + prime: BigUint, + verbose: bool, + stabilize: bool, + ) -> Self { + LlzkCodegen { + program, + context, + module, + prime, + verbose, + stabilize, + template_decls: RefCell::new(Default::default()), + } + } + + /// 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) { + borrow.insert(name, DeclInfo::Remnant(decl_info.build_input_name_to_type_map())); + 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_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(map)) => map.get(signal_name).copied(), + } + .ok_or_else(|| anyhow!("No input 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.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 struct into the module and return a reference to it. + pub fn add_struct(&self, s: StructDefOp<'ctx>) -> Result> { + let s: StructDefOpRef = self.module.body().append_operation(s.into()).try_into()?; + Ok(s.into()) + } + + /// Insert the 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(undef::undef(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::new(self.context) + } + + /// Get the struct type for the given struct name. + #[inline] + pub fn struct_type(&self, name: &str) -> StructType<'ctx> { + StructType::from_str(self.context, name) + } + + /// 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 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) + } + + /// 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(), + ); + felt::constant(location, attr).map_err(Into::into) + } + + /// Run cleanup passes on the generated `Module`. + pub fn run_passes(&mut self, pass_pipeline: &str) -> Result<()> { + if 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(), pass_pipeline) + .map_err(anyhow::Error::from)?; + manager.run(&mut self.module).map_err(Into::into) + } + + /// Verify the generated `Module`. + 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_to_file(self, filename: &str) -> Result<()> { + 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, filename: &str) -> 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 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: &'ast 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: &'ast 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 => FeltType::new(self.context).into(), + WireType::Bus(name) => StructType::from_str(self.context, &name).into(), + } + }) + .collect()) + } + + /// 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(',')) + } +} + +/// 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 +} + +/// Sets the n-th operand of the operation to the given value if the current value is an +/// `undef.undef` op. +pub fn set_operand_if_undef<'ctx, 'op>( + op: OperationRef<'ctx, 'op>, + idx: usize, + value: impl ValueLike<'ctx>, +) -> Result<()> { + if let Ok(arg) = OperationResult::try_from(op.operand(idx)?) { + if !undef::is_undef_op(&arg.owner()) { + anyhow::bail!("Argument {idx} was assigned twice: {arg}"); + } + } + unsafe { mlir_sys::mlirOperationSetOperand(op.to_raw(), idx as isize, value.to_raw()) } + Ok(()) +} + +/// Moves the operation after the value if the value comes from another operation. +/// +/// If the operation the value comes from is in an inner block, moves the op right after the parent +/// op that is in the same block as the op about to be moved. +/// +/// If the value is already before the op in a different but dominating block, +/// this function does nothing. +/// +/// # Panics +/// +/// If the value's op is not owned by a block. +pub fn insert_after_if_op_result<'ctx, 'val, 'op>( + val: Value<'ctx, 'val>, + op: OperationRef<'ctx, 'op>, +) -> Result<()> { + if let Ok(owner) = op_result_owner(val) { + anyhow::ensure!(owner.block().is_some(), "reference op must belong to a block"); + if let Some(op_block) = op.block() { + if let Some(owner) = find_parent_in_block(op_block, owner) { + move_op_after(&owner, &op); + } + } + } + Ok(()) +} + +/// Returns the op (or a parent op) that belongs to the block. +fn find_parent_in_block<'ctx, 'blk, 'op>( + block: BlockRef<'ctx, 'blk>, + op: OperationRef<'ctx, 'op>, +) -> Option> { + let parent_block = op.block()?; + if block == parent_block { + Some(op) + } else { + find_parent_in_block(block, parent_block.parent_operation()?) + } +} + +/// 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) +} + +/// Tries to obtain the owner operation of a [`Value`](melior::ir::Value). +/// +/// This function works around a lifetime issue in [`OperationResult::owner`] that +/// is resolved in [mlir-sys/melior#784](https://github.com/mlir-rs/melior/pull/784) but that +/// we cannot benefit from yet. +#[inline] +pub fn op_result_owner<'ctx, 'val, 'op: 'val>( + value: Value<'ctx, 'val>, +) -> Result> { + if !value.is_operation_result() { + anyhow::bail!("Value {value} is not an operation result"); + } + unsafe { OperationRef::from_option_raw(mlir_sys::mlirOpResultGetOwner(value.to_raw())) } + .ok_or_else(|| anyhow::anyhow!("owner of {value} is not a valid operation")) +} + +/// Looks for a call op to a constrain function where the given value is the first argument. +/// +/// Fails if: +/// - The value has more than one use. +/// - The use is not a constrain call. +/// - The used value is not the first operand. +#[inline] +pub fn get_constrain_call<'ctx, 'op, 'val>( + value: Value<'ctx, 'val>, +) -> Result> { + let owner: CallOpRef<'ctx, 'op> = get_single_user(value)?.try_into()?; + if !owner.callee_is_constrain() { + anyhow::bail!("operation {owner} is not a call to a constrain function"); + } + + let fst_operand = owner.operand(0)?; + if fst_operand != value { + anyhow::bail!("first operand {fst_operand} does not match target: {value}"); + } + + Ok(owner.into()) +} + +/// 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) => { + 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, initializations)?; + return gen_while($codegen, $gen_context, $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()), + ) + } +} + +/// Removes itself from a parent block and returns the owned [Operation]. +pub fn remove_from_parent<'c: 'a, 'a>( + op: &mut impl melior::ir::operation::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> From> for Option> { + fn from(value: ArrayDimensionResult<'ctx, 'val>) -> Self { + match value { + ArrayDimensionResult::Computed(array_dimension) => Some(array_dimension), + ArrayDimensionResult::InsufficientData => None, + } + } +} + +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<'ast, '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<'ast, '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<'ast, '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<'ast, 'ctx, impl ProgramLike>, + name: &str, + ) -> StructType<'ctx> { + if self.is_empty() { + StructType::from_str(codegen.context, name) + } else { + StructType::new(FlatSymbolRefAttribute::new(codegen.context, name), &self.attrs()) + } + } +} + +/// Constructs a new [ArrayDimensions] if all input [ArrayDimensionResult] are +/// [ArrayDimensionResult::Computed], returns [Err] otherwise. +impl<'ctx, 'val> TryFrom<&[ArrayDimensionResult<'ctx, 'val>]> for ArrayDimensions<'ctx, 'val> { + type Error = (); + + fn try_from(dim_results: &[ArrayDimensionResult<'ctx, 'val>]) -> Result { + let dims = dim_results + .iter() + .cloned() + .map(Option::from) + .filter_map(|mut x| Option::take(&mut x)) + .collect::>(); + if dims.len() == dim_results.len() { + Ok(ArrayDimensions(dims)) + } else { + Err(()) + } + } +} + +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, 'ast, '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. + /// + /// Note: The LLZK ArrayType can only use the following Attribute types for dimensions: + /// IntegerAttr (`index` or `i1`) or AffineMapAttr (with single result). + /// To simplify the implementation, template parameters are read using `poly.read_const` + /// and passed to an affine map rather than trying to use a symbol attribute as the + /// dimension (which would only work for bare template parameters without computation anyways). + fn convert_dim_expr( + &self, + codegen: &LlzkCodegen<'ast, '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 of the underling [Expression]s generated a + /// [ArrayDimensionResult::InsufficientData] + /// - [Some] otherwise + fn get_dimensions_if_able( + &self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + dimension_exprs: &[Expression], + ) -> Result>> { + let dim_result_vec = dimension_exprs + .iter() + .map(|e| self.convert_dim_expr(codegen, e)) + .collect::>>()?; + Ok(ArrayDimensions::try_from(dim_result_vec.as_slice()).ok()) + } + + /// Same as [DimExprConverter::get_dimensions_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_dimensions( + &self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + dimension_exprs: &[Expression], + ) -> Result> { + self.get_dimensions_if_able(codegen, dimension_exprs)?.ok_or_else(|| { + anyhow!("unexpected lack of data needed to convert dimension expressions") + }) + } +} diff --git a/llzk_backend/src/subcmp.rs b/llzk_backend/src/subcmp.rs new file mode 100644 index 000000000..c8df1e73e --- /dev/null +++ b/llzk_backend/src/subcmp.rs @@ -0,0 +1,131 @@ +//! Helper types for handling subcomponents. + +use llzk::prelude::*; +use std::collections::HashMap; +use std::collections::HashSet; +use std::marker::PhantomData; + +/// Information collected about a subcomponent. +#[derive(Debug)] +pub struct SubcmpDeclInfo<'ctx> { + /// 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 { dimensions, location, instances: Default::default() } + } + + /// 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 + } +} + +/// 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); + } +} + +/// Maps the values of a subcomponent call to it's name. +/// +/// The actual value used depends on the context; if it's a call to `@compute` then is the value +/// returned by the call op. If it's a call to `@constrain` then is the value of the first operand +/// of the call op. +/// +/// # Safety +/// +/// Uses the raw pointer as key since [`Value`] does not implement [`Hash`](std::hash::Hash). To +/// minimize risk, it has a lifetime parameter tied to a [`Context`]. +#[derive(Debug)] +pub struct SubcmpCallsMap<'ctx> { + /// Mapping between a value representing a constructor and the name of the type it constructs. + map: HashMap<*const std::ffi::c_void, String>, + /// Marker to link the lifetime of a MLIR context to this instance. + _marker: PhantomData<&'ctx Context>, +} + +impl<'ctx> SubcmpCallsMap<'ctx> { + /// Creates an empty map. + pub fn new() -> Self { + Self { map: HashMap::new(), _marker: PhantomData } + } + + /// Inserts a new mapping. + /// + /// Panics if the key already exists. + pub fn insert(&mut self, value: &impl ValueLike<'ctx>, name: String) { + assert!(self.map.insert(value.to_raw().ptr, name).is_none()); + } + + /// Returns the name of the type or `None` if not found. + pub fn get(&self, value: &impl ValueLike<'ctx>) -> Option<&str> { + self.map.get(&value.to_raw().ptr).map(String::as_str) + } + + /// Updates the mapping with the new key. + /// + /// The new key must not be already mapped to another name. + pub fn update_keys(&mut self, key: impl ValueLike<'ctx>, new: impl ValueLike<'ctx>) { + if let Some(name) = self.map.remove(&key.to_raw().ptr) { + self.insert(&new, name) + } + } + + /// If the left value exists in the map, adds the right value with the same name. + /// + /// Returns the right value. + pub fn propagate(&mut self, lhs: &impl ValueLike<'ctx>, rhs: V) -> V + where + V: ValueLike<'ctx>, + { + if let Some(name) = self.get(lhs) { + self.insert(&rhs, name.to_string()) + } + rhs + } +} + +impl Default for SubcmpCallsMap<'_> { + fn default() -> Self { + Self::new() + } +} diff --git a/llzk_backend/src/template.rs b/llzk_backend/src/template.rs new file mode 100644 index 000000000..40feb8db4 --- /dev/null +++ b/llzk_backend/src/template.rs @@ -0,0 +1,1384 @@ +//! 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::abstract_syntax_tree::ast::Expression) and +//! [Statement](program_structure::abstract_syntax_tree::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::GenWithCircomScopeHandling; +use crate::gen_context::NestedBlockInfo; +use crate::program_ext::ProgramLike; +use crate::shared; +use crate::shared::get_constrain_call; +use crate::shared::op_result_owner; +use crate::shared::ArrayDimensionResult; +use crate::shared::DimExprConverter; +use crate::shared::LlzkCodegen; +use crate::template_ext::TemplateLike as _; +use crate::write_chain::RootWriteOp; +use crate::write_chain::WriteChain; +use crate::write_chain::WriteTarget; +use anyhow::anyhow; +use anyhow::Result; +use llzk::builder::OpBuilder; +use llzk::dialect::cast; +use llzk::prelude::constrain; +use llzk::prelude::function; +use llzk::prelude::is_felt_type; +use llzk::prelude::r#struct; +use llzk::prelude::r#struct::is_struct_readf; +use llzk::prelude::BlockRef; +use llzk::prelude::CallOpLike as _; +use llzk::prelude::CallOpRef; +use llzk::prelude::FeltType; +use llzk::prelude::FieldDefOpLike; +use llzk::prelude::FlatSymbolRefAttribute; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::Location; +use llzk::prelude::LoopBoundsAttribute; +use llzk::prelude::OperationLike; +use llzk::prelude::StructDefOpLike; +use llzk::prelude::StructDefOpRefMut; +use llzk::prelude::SymbolRefAttribute; +use llzk::prelude::Type; +use llzk::prelude::Value; +use llzk::prelude::ValueLike; +use llzk::value_ext::replace_all_uses; +use program_structure::ast::Access; +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::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<'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + /// Current LLZK `StructDefOp` + 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>>>, + /// Set of subcomponent names. + subcmps: &'str HashSet, + /// Tracks for what component signals we have created their `struct.writef` op already. + written_signals: Rc>>, +} + +impl<'ctx, 'str, 'func, 'blk, 'val> TemplateContext<'ctx, 'str, 'func, 'blk, 'val> { + /// Creates a new [TemplateContext]. + #[inline] + pub fn new( + struct_def: StructDefOpRefMut<'ctx, 'str>, + compute: FunctionContext<'ctx, 'func, 'blk, 'val>, + constrain: FunctionContext<'ctx, 'func, 'blk, 'val>, + subcmps: &'str HashSet, + ) -> TemplateContext<'ctx, 'str, 'func, 'blk, 'val> { + Self { + struct_def, + compute: Some(Rc::new(RefCell::new(compute))), + constrain: Some(Rc::new(RefCell::new(constrain))), + subcmps, + written_signals: Default::default(), + } + } + + /// Creates a new [TemplateContext] that will only generate within the "@compute" function. + #[inline] + pub fn compute_only(&self) -> TemplateContext<'ctx, 'str, 'func, 'blk, 'val> { + Self { + struct_def: self.struct_def, + compute: self.compute.as_ref().map(Rc::clone), + constrain: None, + subcmps: self.subcmps, + written_signals: self.written_signals.clone(), + } + } + + /// Creates a new [TemplateContext] that will only generate within the "@constrain" function. + #[inline] + pub fn constrain_only(&self) -> TemplateContext<'ctx, 'str, 'func, 'blk, 'val> { + Self { + struct_def: self.struct_def, + compute: None, + constrain: self.constrain.as_ref().map(Rc::clone), + subcmps: self.subcmps, + written_signals: self.written_signals.clone(), + } + } + + /// Returns true if we already generated a `struct.writef` 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_field_def(name) + .ok_or_else(|| anyhow!("no field '{}' in struct", name))? + .field_type()) + } + + /// 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<()> { + let subcmps = self.subcmps; + self.and_then::<_, _, GenResultUnit>( + |fc, _| { + // Write the subcomponent declarations to self. + let self_value = fc.func.self_value_of_compute()?; + for name in subcmps { + let val = fc.block_ctx.get_named_value(name)?; + fc.append_op_no_result(r#struct::writef( + Location::unknown(codegen.context), + self_value, + name, + *val, + )?)?; + } + 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(()) + }, + |fc, _| { + fc.block_ctx.append_queue(); + if !fc.block_ctx.is_only_root() { + anyhow::bail!( + "Template generation reached final step with more than one scope" + ); + } + Ok(()) + }, + )? + .and_then_same(|fc, _| fc.finalize(codegen)) + } +} + +/// 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 FunctionContext<'ctx, 'func, '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<'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<'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<'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'ctx, 'str, 'func, 'blk, 'val, 'r, Value<'ctx, 'val>>; + +/// Alias for [GenResult] containing a list of SSA Value results. +type GenResultMultiVal<'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'ctx, 'str, 'func, 'blk, 'val, 'r, Vec>>; + +/// Alias for [GenResult] containing the unit type (i.e. nothing). +type GenResultUnit<'ctx, 'str, 'func, 'blk, 'val, 'r> = + GenResult<'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<'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<'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<'ctx, 'str, 'func, 'blk, 'val, 'r, ResultType> ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r> + for GenResult<'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<'ctx, 'str, 'func, 'blk, 'val>, + compute_res: ShouldGenerate, + constrain_res: ShouldGenerate, + ) -> Self { + GenResult::<'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<'ctx, 'str, 'func, 'blk, 'val, 'r> ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r> for () +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerOutput = (); + + fn produce( + _: &'r TemplateContext<'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<'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<'ast, F1, F2, CR: ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r>>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'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<'ast, F, CR: ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r>>( + self, + handle: F, + ) -> Result + where + Self: Sized, + F: Fn( + &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + { + self.and_then::<&F, &F, CR>(&handle, &handle) + } +} + +impl<'ctx, 'str, 'func, 'blk, 'val, 'r> GenResultMultiVal<'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<'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<'ctx, 'str, 'func, 'blk, 'val>, + codegen: &LlzkCodegen<'ast, '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<'ctx, 'str, 'func, 'blk, 'val, 'r, T> Chainable<'ctx, 'str, 'func, 'blk, 'val, 'r> + for GenResult<'ctx, 'str, 'func, 'blk, 'val, 'r, T> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerInput = T; + + fn and_then<'ast, F1, F2, CR: ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r>>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'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<'ctx, 'str, 'func, 'blk, 'val, 'r> Chainable<'ctx, 'str, 'func, 'blk, 'val, 'r> + for &'r TemplateContext<'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, + 'val: 'r, +{ + type HandlerInput = (); + + fn and_then<'ast, F1, F2, CR: ChainResult<'ctx, 'str, 'func, 'blk, 'val, 'r>>( + self, + gen_compute: F1, + gen_constrain: F2, + ) -> Result + where + F1: FnOnce( + &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + Self::HandlerInput, + ) -> Result, + F2: FnOnce( + &mut FunctionContext<'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<'ast, 'ctx, 'func, 'blk, 'val, 'str> DimExprConverter<'ctx, 'ast, 'val> + for TemplateContext<'ctx, 'str, 'func, 'blk, 'val> +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + fn convert_dim_expr( + &self, + codegen: &LlzkCodegen<'ast, '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 } => match access.as_slice() { + [] => { + // Grab the parameter name if it exists, else, defer to function generation. + if self.struct_def.has_param_name(name) { + ArrayDimensionResult::new( + FlatSymbolRefAttribute::new(codegen.context, name).into(), + &[], + ) + } else { + // Other variables are unsupported, defer to function context + ArrayDimensionResult::insufficient_data_result() + } + } + a => { + todo!("Handle Variable expression in dimension for non-integer attributes") + } + }, + Expression::InfixOp { meta, lhe, infix_op, rhe } => { + todo!("Handle Infix expression in dimension for non-integer attributes") + } + Expression::PrefixOp { meta, prefix_op, rhe } => { + todo!("Handle Prefix expression in dimension for non-integer attributes") + } + Expression::InlineSwitchOp { meta, cond, if_true, if_false } => { + todo!( + "Handle InlineSwitchOp expression in dimension for non-integer attributes" + ) + } + Expression::Call { meta, id, args } => { + todo!("Handle Call expression in dimension") + } + // 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 be single arithmetic expressions") + } + } + } + } +} + +/// 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<'ctx, 'str, 'func, 'blk, 'val> +where + '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 + 'val: 'r; + + /// Generates LLZK IR from [Statement] and [Expression] nodes in a circom template. + /// + /// 'ast: lifetime of the circom AST element + fn gen_llzk_in_template<'ast, 'r>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r; +} + +impl<'ctx, 'str, 'func, 'blk, 'val> GenerateLLZKInTemplate<'ctx, 'str, 'func, 'blk, 'val> + for [Statement] +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output<'r> + = () + where + 'val: 'r; + + fn gen_llzk_in_template<'ast, 'r>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'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<'ast, 'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'ast, '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<'ast, 'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'ast, '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<'ast, 'ctx, 'str, 'func, 'blk, 'val, 'r>( + codegen: &LlzkCodegen<'ast, '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) +} + +/// Insert cast operations as needed to make `lhs` and `rhs` have compatible types for equality +/// constraints. +fn unify_constrain_eq_types<'ctx, 'func, 'blk, 'val>( + fc: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + location: Location<'ctx>, + lhs: Value<'ctx, 'val>, + rhs: Value<'ctx, 'val>, +) -> Result<(Value<'ctx, 'val>, Value<'ctx, 'val>)> { + // May need to cast between scalar types + let mut to_felt = + |val: Value<'ctx, 'val>| fc.append_op_unnamed_result(cast::tofelt(location, val).into()); + + match (lhs.r#type(), rhs.r#type()) { + (t0, t1) if is_felt_type(t0) && !is_felt_type(t1) => Ok((lhs, to_felt(rhs)?)), + (t0, t1) if !is_felt_type(t0) && is_felt_type(t1) => Ok((to_felt(lhs)?, rhs)), + _ => Ok((lhs, rhs)), + } +} + +impl<'ctx, 'str, 'func, 'blk, 'val> GenerateLLZKInTemplate<'ctx, 'str, 'func, 'blk, 'val> + for Statement +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output<'r> + = () + where + 'val: 'r; + + fn gen_llzk_in_template<'ast, 'r>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'ctx, 'str, 'func, 'blk, 'val>, + ) -> Result> + where + 'val: 'r, + { + match self { + Statement::InitializationBlock { initializations, .. } => { + gen_init_block(codegen, template, initializations) + } + Statement::Declaration { meta, name, dimensions, .. } => { + template.and_then_same(|fc, _| fc.gen_declaration(codegen, meta, name, dimensions)) + } + 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() { + if template.subcmps.contains(var) { + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, rhe| { + if let Ok(current) = fc.block_ctx.get_named_value(var) { + replace_all_uses(*current, rhe); + } + fc.block_ctx.set_named_value(var.clone(), rhe) + }, + |fc, rhe| { + // Replace value + let field_read = fc.block_ctx.get_named_value(var)?; + // ASSERT: value comes from a `struct.readf` + assert!(is_struct_readf( + &op_result_owner(*field_read).unwrap() + )); + replace_all_uses(rhe, *field_read); + fc.subcmp_calls.update_keys(rhe, *field_read); + Ok(()) + }, + ) + } else { + rhe.gen_llzk_in_template(codegen, template)?.and_then_same( + |fc, val| fc.block_ctx.set_named_value(var.clone(), val), + ) + } + } else { + let write_op = if template.subcmps.contains(var) { + RootWriteOp::Subcmp + } else { + RootWriteOp::Var + }; + let location = codegen.location_from_meta(meta); + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, val| { + WriteChain::new(var, write_op, access).write( + val, + WriteTarget::Compute, + codegen, + fc, + location, + template, + ) + }, + |fc, val| { + WriteChain::new(var, write_op, access).write( + val, + WriteTarget::Constrain, + codegen, + fc, + location, + 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. + fc.append_op_no_result( + r#struct::writef( + location, + fc.func.self_value_of_compute()?, + 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| { + let write_op = if template.subcmps.contains(var) { + RootWriteOp::Subcmp + } else { + RootWriteOp::Signal + }; + WriteChain::new(var, write_op, access).write( + rhe, + WriteTarget::Compute, + codegen, + fc, + codegen.location_from_meta(meta), + 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. + fc.append_op_no_result( + r#struct::writef( + location, + fc.func.self_value_of_compute()?, + 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 location = codegen.location_from_meta(meta); + let signal_val = fc.block_ctx.get_named_value(var)?; + let (lhs, rhs) = unify_constrain_eq_types( + fc, + location, + *signal_val, + val, + )?; + fc.append_op_no_result( + constrain::eq(location, lhs, rhs).into(), + ) + }, + ) + } + [Access::ComponentAccess(subcmp_signal)] => { + // Assigning to a subcomponent signal is translated into replacing + // the corresponding argument of the `@compute` and `@constrain` + // calls. + // + // The value representing the call is mapped to the name of + // the subcomponent and mapped to the name of + // the subcomponent's template. For `@compute` that value is the + // call op to `@compute` and in `@constrain` is the first operand + // to the `@constrain` call. + // + // We use that name to look for the signal's declaration index and + // use it to locate the corresponding operand and + // replace it with the given `rhe`. + rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, rhe| { + fc.assign_subcmp( + rhe, + var, + subcmp_signal, + codegen, + 0, + op_result_owner, + ) + }, + |fc, rhe| { + fc.assign_subcmp( + rhe, + var, + subcmp_signal, + codegen, + 1, + get_constrain_call, + ) + }, + ) + } + access => rhe.gen_llzk_in_template(codegen, template)?.and_then( + |fc, rhv| { + WriteChain::new(var, RootWriteOp::Signal, access).write( + rhv, + WriteTarget::Compute, + codegen, + fc, + codegen.location_from_meta(meta), + template, + ) + }, + |fc, rhv| { + let location = codegen.location_from_meta(meta); + let lhv = WriteChain::new(var, RootWriteOp::Signal, access) + .get_value(codegen, fc, location, WriteTarget::Constrain)?; + 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| { + let (lhs, rhs) = unify_constrain_eq_types( + fc, + codegen.location_from_meta(meta), + vals[0], + vals[1], + )?; + fc.append_op_no_result( + constrain::eq(codegen.location_from_meta(meta), lhs, rhs).into(), + ) + }, + ) + } + 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| { + let location = codegen.location_from_meta(meta); + let cond = fc.cast_to_bool_if_needed(codegen, location, val)?; + let msg = Some("assertion failed"); + fc.append_op_no_result(llzk::dialect::bool::assert(location, cond, msg)?.into()) + }) + } + 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") + } + } + } +} + +impl<'ctx, 'str, 'func, 'blk, 'val> GenerateLLZKInTemplate<'ctx, 'str, 'func, 'blk, 'val> + for Expression +where + 'ctx: 'str, + 'str: 'func, + 'func: 'blk, + 'blk: 'val, +{ + type Output<'r> + = GenResultSingleVal<'ctx, 'str, 'func, 'blk, 'val, 'r> + where + 'val: 'r; + + fn gen_llzk_in_template<'ast, 'r>( + &'ast self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + template: &'r TemplateContext<'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::Variable { meta, name, access } + if { matches!(access[..], [Access::ComponentAccess(_)]) } => + { + match access.as_slice() { + [Access::ComponentAccess(signal_name)] => template.and_then( + |fc, _| { + let subcmp_value = fc.block_ctx.get_named_value(name)?; + let template_data = fc + .subcmp_calls + .get(subcmp_value) + .ok_or_else(|| { + anyhow::anyhow!( + "subcomponent call for {subcmp_value} not found" + ) + }) + .and_then(|name| { + codegen.find_template_data(name).ok_or_else(|| { + anyhow::anyhow!("template {name:?} not found") + }) + })?; + if template_data.get_outputs().contains_key(signal_name) { + fc.append_op_unnamed_result(r#struct::readf( + &OpBuilder::new(codegen.context), + codegen.location_from_meta(meta), + FeltType::new(codegen.context).into(), + *subcmp_value, + signal_name, + )?) + } else if template_data.get_inputs().contains_key(signal_name) { + let idx = template_data + .get_declaration_input_idx(signal_name) + .expect("signal from mapping"); + let call = CallOpRef::try_from(op_result_owner(*subcmp_value)?)?; + assert!(call.callee_is_struct_compute()); + Ok(call.operand(idx)?) + } else { + anyhow::bail!( + "signal {signal_name} of subcomponent {name} is internal" + ); + } + }, + |fc, _| { + let subcmp_value = fc.block_ctx.get_named_value(name)?; + let template_data = fc + .subcmp_calls + .get(subcmp_value) + .ok_or_else(|| { + anyhow::anyhow!( + "subcomponent call for {subcmp_value} not found" + ) + }) + .and_then(|name| { + codegen.find_template_data(name).ok_or_else(|| { + anyhow::anyhow!("template {name:?} not found") + }) + })?; + if template_data.get_outputs().contains_key(signal_name) { + fc.append_op_unnamed_result(r#struct::readf( + &OpBuilder::new(codegen.context), + codegen.location_from_meta(meta), + FeltType::new(codegen.context).into(), + *subcmp_value, + signal_name, + )?) + } else if template_data.get_inputs().contains_key(signal_name) { + let idx = template_data + .get_declaration_input_idx(signal_name) + .expect("signal from mapping"); + let call = get_constrain_call(*subcmp_value)?; + Ok(call.operand(idx + 1)?) + } else { + anyhow::bail!( + "signal {signal_name} of subcomponent {name} is internal" + ); + } + }, + ), + _ => { + unreachable!() + } + } + } + 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 location = codegen.location_from_meta(meta); + let dimensions = template.get_dimensions(codegen, args)?; + let subcmp_type = dimensions.struct_type_with_concrete_dimensions(codegen, id); + let arg_types = std::iter::once(Type::from(subcmp_type)) + .chain(codegen.get_template_input_types(id)?) + .collect::>(); + // Undefs have unknown locations at creation and we set it when we encounter + // the corresponding write. + let unk = Location::unknown(codegen.context); + let builder = OpBuilder::new(codegen.context); + + // Generate here the call to @compute and @constrain. + // Passing undefs to the methods. These undefs get associated with the + // signals of the subcomponent s.t. when we encounter an assignment + // to one of the signals we replace the undef with the actual value, + // similar to how we do for variables assignment. + template.and_then( + |fc, _| { + let undefs = fc.gen_arg_undefs(&arg_types[1..], unk)?; + let val = fc.append_op_unnamed_result( + function::call( + &builder, + location, + SymbolRefAttribute::new(codegen.context, id, &["compute"]), + &undefs, + &[subcmp_type], + )? + .into(), + )?; + fc.subcmp_calls.insert(&val, id.clone()); + Ok(val) + }, + |fc, _| { + let undefs = fc.gen_arg_undefs(&arg_types, unk)?; + let empty_result: [Type<'ctx>; 0] = []; + fc.append_op_no_result( + function::call( + &builder, + location, + SymbolRefAttribute::new(codegen.context, id, &["constrain"]), + &undefs, + &empty_result, + )? + .into(), + )?; + fc.subcmp_calls.insert(&undefs[0], id.clone()); + // Return the reference to the subcomponent to match the result of + // compute. + Ok(undefs[0]) + }, + ) + } + Expression::UniformArray { meta, value, dimension } => { + let location = codegen.location_from_meta(meta); + let template_dim_res = template.convert_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, or defer to function context if unsuccessful. + let final_dim = match &template_dim_res { + ArrayDimensionResult::Computed(array_dimension) => array_dimension, + ArrayDimensionResult::InsufficientData => { + &Option::from(fc.convert_dim_expr(codegen, dimension)?) + .ok_or_else(|| + anyhow!("missing data required to compute uniform array dimensions in template"))? + }, + }; + 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, _| { + // The import is here rather than top level because it is very important that + // `gen_llzk_in_function()` is not used while translating statements. + use crate::function::GenerateLLZKInFunction; + expr.gen_llzk_in_function(codegen, fc) + }) + } + } + } +} diff --git a/llzk_backend/src/template_ext.rs b/llzk_backend/src/template_ext.rs new file mode 100644 index 000000000..0318681a1 --- /dev/null +++ b/llzk_backend/src/template_ext.rs @@ -0,0 +1,372 @@ +//! 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::FlatSymbolRefAttribute; +use llzk::prelude::Location; +use llzk::prelude::StructType; +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; + +/// 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, .. } => { + // See ExecutedTemplate::export_to_circuit for header construction + let last_underscore = header + .rfind("_") + .ok_or_else(|| anyhow!("unexpected header string format"))?; + let (template_name, _) = header.split_at(last_underscore); + let struct_type = StructType::new( + FlatSymbolRefAttribute::new(codegen.context, template_name), + &[], + ); + subcmp_decl.instances_mut().push(struct_type); + } + } + } + } + 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..d8c76cb45 --- /dev/null +++ b/llzk_backend/src/write_chain.rs @@ -0,0 +1,612 @@ +//! Helper type for constructing operations that write using [`Access`]. + +use crate::function::FunctionContext; +use crate::function::GenerateLLZKInFunction; +use crate::program_ext::ProgramLike; +use crate::shared::get_constrain_call; +use crate::shared::insert_after_if_op_result; +use crate::shared::op_result_owner; +use crate::shared::set_operand_if_undef; +use crate::shared::LlzkCodegen; +use crate::template::TemplateContext; +use crate::template_ext::TemplateLike as _; +use anyhow::Result; +use llzk::builder::OpBuilder; +use llzk::dialect::cast; +use llzk::dialect::r#struct; +use llzk::prelude::r#struct::is_struct_readf; +use llzk::prelude::CallOpLike as _; +use llzk::prelude::CallOpRef; +use llzk::prelude::FuncDefOpLike as _; +use llzk::prelude::Location; +use llzk::prelude::OperationLike as _; +use llzk::prelude::Value; +use llzk::value_ext::replace_all_uses; +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::cmp; +use std::convert::TryFrom as _; +use std::fmt; + +/// Type of write operation performed at the root of the chain +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum RootWriteOp { + /// Write into a signal. + Signal, + /// Write into a felt var. + Var, + /// Write into a subcomponent var. + Subcmp, +} + +/// 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`. + #[allow(unused)] + #[inline] + fn is_constrain(&self) -> bool { + matches!(self, WriteTarget::Constrain) + } +} + +/// Helper type that defines a chain of write operations. +#[derive(Debug)] +pub enum WriteChain<'ast> { + /// Root of the chain. + Root { + /// Name of the variable. + var: &'ast str, + /// Type of the variable written into. + op: RootWriteOp, + }, + /// Represents a write into an array. + Array { + /// List of indexing expressions. + indices: Vec<&'ast Expression>, + /// Location accessed with as an array. + prev: Box>, + }, + /// Represents a write into a subcomponent signal. + Subcmp { + /// Name of the field read with dot-notation. + name: &'ast str, + /// Location the field is read from. + prev: Box>, + }, +} + +impl<'ast> WriteChain<'ast> { + /// Creates a new write chain. + pub fn new(var: &'ast str, op: RootWriteOp, access: &'ast [Access]) -> Self { + access.iter().fold(Self::Root { var, op }, |wc, access| match (wc, access) { + (WriteChain::Array { mut indices, prev }, Access::ArrayAccess(expression)) => { + indices.push(expression); + WriteChain::Array { indices, prev } + } + (wc, Access::ComponentAccess(name)) => WriteChain::Subcmp { name, prev: Box::new(wc) }, + (wc, Access::ArrayAccess(expression)) => { + WriteChain::Array { indices: vec![expression], prev: Box::new(wc) } + } + }) + } + + /// Handle [WriteChain::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<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + template: &TemplateContext<'ctx, '_, '_, '_, 'val>, + ) -> Result<()> { + let arr_ref = prev.get_value(codegen, fc, location, target)?; + let indices = gen_index_ops(indices, codegen, fc, location)?; + fc.append_array_write(codegen, arr_ref, &indices, location, val, None)?; + prev.write(arr_ref, target, codegen, fc, location, template) + } + + /// Handle [WriteChain::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<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + template: &TemplateContext<'ctx, '_, '_, '_, 'val>, + ) -> Result<()> { + let subcmp_value = prev.get_value(codegen, fc, location, target)?; + let arg_idx = fc.lookup_arg_idx(name, &subcmp_value, codegen)?; + let (arg_offset, call_op) = match target { + WriteTarget::Compute => (0, op_result_owner(subcmp_value)?), + WriteTarget::Constrain => (1, get_constrain_call(subcmp_value)?), + WriteTarget::Free => unreachable!(), + }; + set_operand_if_undef(call_op, arg_idx + arg_offset, val)?; + insert_after_if_op_result(val, call_op)?; + prev.write(subcmp_value, target, codegen, fc, location, template) + } + + /// Emits the write operations. + pub fn write<'ctx, 'val>( + self, + val: Value<'ctx, 'val>, + target: WriteTarget, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + template: &TemplateContext<'ctx, '_, '_, '_, 'val>, + ) -> Result<()> { + /// Handle [WriteChain::Root] with [RootWriteOp::Signal] case. + fn write_root_signal<'ctx, 'val>( + var: &str, + val: Value<'ctx, 'val>, + target: WriteTarget, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + template: &TemplateContext<'ctx, '_, '_, '_, 'val>, + ) -> Result<()> { + if target.is_compute() && !template.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::writef(location, self_value, var, val)?.into(), + block, + )?; + fc.block_ctx.set_named_value_at_declaration(var.to_string(), val)?; + template.mark_signal_as_written(var.to_string()) + } + Ok(()) + } + + /// Handle [WriteChain::Root] case other than [RootWriteOp::Signal]. + fn write_root_var<'ctx, 'val>( + var: &str, + val: Value<'ctx, 'val>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + ) -> Result<()> { + fc.block_ctx.set_named_value(var.to_string(), val) + } + + /// Handle [WriteChain::Root] with [RootWriteOp::Subcmp] and [WriteTarget::Compute]. + fn write_root_subcmp_in_compute<'ctx, 'val>( + var: &str, + val: Value<'ctx, 'val>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + ) -> Result<()> { + let current = fc.block_ctx.get_named_value(var)?; + if *current != val { + replace_all_uses(*current, val); + } + fc.subcmp_calls.update_keys(*current, val); + fc.block_ctx.set_named_value(var.to_string(), val)?; + Ok(()) + } + + /// Handle [WriteChain::Root] with [RootWriteOp::Subcmp] and [WriteTarget::Constrain]. + fn write_root_subcmp_in_constrain<'ctx, 'val>( + var: &str, + val: Value<'ctx, 'val>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + ) -> Result<()> { + // Replace value + let field_read = fc.block_ctx.get_named_value(var)?; + // ASSERT: value comes from a `struct.readf` + assert!(is_struct_readf(&op_result_owner(*field_read).unwrap())); + if val != *field_read { + replace_all_uses(val, *field_read); + } + fc.subcmp_calls.update_keys(val, *field_read); + Ok(()) + } + + match self { + WriteChain::Root { var, op: RootWriteOp::Signal } => { + write_root_signal(var, val, target, fc, location, template) + } + WriteChain::Root { var, op: RootWriteOp::Var } => write_root_var(var, val, fc), + WriteChain::Root { var, op: RootWriteOp::Subcmp } => match target { + WriteTarget::Compute => write_root_subcmp_in_compute(var, val, fc), + WriteTarget::Constrain => write_root_subcmp_in_constrain(var, val, fc), + WriteTarget::Free => unreachable!(), + }, + WriteChain::Array { indices, prev } => { + Self::write_array(indices, *prev, val, target, codegen, fc, location, template) + } + WriteChain::Subcmp { name, prev } => { + Self::write_subcmp(name, *prev, val, target, codegen, fc, location, template) + } + } + } + + /// Handle [WriteChain::Root] with [RootWriteOp::Signal] case of [`WriteChain::get_value`]. + fn get_root_signal<'ctx, 'val>( + &self, + var: &str, + fc: &mut FunctionContext<'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 `readf` in `gen_template_llzk`. + fc.block_ctx.get_named_value(var).copied() + } + + /// Handle [WriteChain::Root] case of [`WriteChain::get_value`] other than + /// [RootWriteOp::Signal]. + fn get_root_value<'ctx, 'val>( + &self, + var: &str, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + ) -> Result> { + fc.block_ctx.get_named_value(var).copied() + } + + /// Handle [WriteChain::Array] case of [`WriteChain::get_value`]. + fn get_array_value<'ctx, 'val>( + &self, + indices: &[&Expression], + prev: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + ) -> Result> { + let indices = gen_index_ops(indices.iter().copied(), codegen, fc, location)?; + fc.append_array_read(prev, &indices, location, None) + .map(|v| fc.subcmp_calls.propagate(&prev, v)) + } + + /// Handle [WriteChain::Subcmp] with [WriteTarget::Compute] in [`WriteChain::get_value`]. + fn get_subcmp_in_compute<'ctx, 'val>( + &self, + signal_name: &str, + subcmp_value: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + ) -> Result> { + let template_data = fc + .subcmp_calls + .get(&subcmp_value) + .ok_or_else(|| anyhow::anyhow!("subcomponent call for {subcmp_value} not found")) + .and_then(|name| { + codegen + .find_template_data(name) + .ok_or_else(|| anyhow::anyhow!("template {name:?} not found")) + })?; + if template_data.get_outputs().contains_key(signal_name) { + fc.append_op_unnamed_result(r#struct::readf( + &OpBuilder::new(codegen.context), + location, + codegen.felt_type().into(), + subcmp_value, + signal_name, + )?) + } else if template_data.get_inputs().contains_key(signal_name) { + let idx = + template_data.get_declaration_input_idx(signal_name).expect("signal from mapping"); + let call = CallOpRef::try_from(op_result_owner(subcmp_value)?)?; + assert!(call.callee_is_struct_compute()); + Ok(call.operand(idx)?) + } else { + anyhow::bail!("signal {signal_name} is internal"); + } + } + + /// Handle [WriteChain::Subcmp] with [WriteTarget::Constrain] in [`WriteChain::get_value`]. + fn get_subcmp_in_constrain<'ctx, 'val>( + &self, + signal_name: &str, + subcmp_value: Value<'ctx, 'val>, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + ) -> Result> { + let template_data = fc + .subcmp_calls + .get(&subcmp_value) + .ok_or_else(|| anyhow::anyhow!("subcomponent call for {subcmp_value} not found")) + .and_then(|name| { + codegen + .find_template_data(name) + .ok_or_else(|| anyhow::anyhow!("template {name:?} not found")) + })?; + if template_data.get_outputs().contains_key(signal_name) { + fc.append_op_unnamed_result(r#struct::readf( + &OpBuilder::new(codegen.context), + location, + codegen.felt_type().into(), + subcmp_value, + signal_name, + )?) + } else if template_data.get_inputs().contains_key(signal_name) { + let idx = + template_data.get_declaration_input_idx(signal_name).expect("signal from mapping"); + let call = get_constrain_call(subcmp_value)?; + Ok(call.operand(idx + 1)?) + } else { + anyhow::bail!("signal {signal_name} is internal"); + } + } + + /// Returns a SSA representing the op. + /// + /// It could be a placeholder operation at this point (usually represented with `undef.undef`). + pub fn get_value<'ctx, 'val>( + &self, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, '_, '_, 'val>, + location: Location<'ctx>, + target: WriteTarget, + ) -> Result> { + match self { + WriteChain::Root { var, op: RootWriteOp::Signal } => self.get_root_signal(var, fc), + WriteChain::Root { var, .. } => self.get_root_value(var, fc), + WriteChain::Array { indices, prev } => self.get_array_value( + indices, + prev.get_value(codegen, fc, location, target)?, + codegen, + fc, + location, + ), + WriteChain::Subcmp { name: signal_name, prev } => match target { + WriteTarget::Compute => self.get_subcmp_in_compute( + signal_name, + prev.get_value(codegen, fc, location, target)?, + codegen, + fc, + location, + ), + WriteTarget::Constrain => self.get_subcmp_in_constrain( + signal_name, + prev.get_value(codegen, fc, location, target)?, + codegen, + fc, + location, + ), + WriteTarget::Free => unreachable!(), + }, + } + } +} + +/// Creates LLZK ops for array indexing from the collection of elements. +fn gen_index_ops<'ctx, 'func, 'blk, 'val, 'ast, E>( + indices: impl IntoIterator, + codegen: &LlzkCodegen<'ast, 'ctx, impl ProgramLike>, + fc: &mut FunctionContext<'ctx, 'func, 'blk, 'val>, + location: Location<'ctx>, +) -> Result>> +where + E: GenerateLLZKInFunction<'ctx, 'func, 'blk, 'val, Output = Value<'ctx, 'val>> + 'ast, +{ + indices + .into_iter() + .map(|e| { + let val = e.gen_llzk_in_function(codegen, fc)?; + fc.append_op_unnamed_result(cast::toindex(location, val)) + }) + .collect() +} + +impl fmt::Display for WriteChain<'_> { + 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 { + WriteChain::Root { var, op } => { + write!( + f, + "{}:{var}", + match op { + RootWriteOp::Signal => "Signal", + RootWriteOp::Var => "Var", + RootWriteOp::Subcmp => "Subcmp", + } + ) + } + WriteChain::Array { indices, prev } => { + fmt::Display::fmt(prev.as_ref(), f)?; + for index in indices { + write!(f, "[")?; + display_expr(index, f)?; + write!(f, "]")?; + } + Ok(()) + } + WriteChain::Subcmp { name, prev } => { + fmt::Display::fmt(prev.as_ref(), f)?; + write!(f, ".{name}") + } + } + } +} 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..fecc2012b --- /dev/null +++ b/scripts/update_llzk_lib.sh @@ -0,0 +1,17 @@ +#!/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-lib +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..6782a7898 --- /dev/null +++ b/scripts/update_test_cases.sh @@ -0,0 +1,56 @@ +#!/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 + +for f in $positional; do + o=$(mktemp -d) + name=$(basename "$f" .circom) + cargo run -p circom -- --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