diff --git a/WORKSPACE b/WORKSPACE index cb899f63..1b0553c7 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,59 +1,16 @@ workspace(name = "zirgen") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -http_archive( - name = "bazel_skylib", - sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.3.0.tar.gz", - ], -) +load("//bazel/rules/zirgen:deps.bzl", "zirgen_dependencies") +zirgen_dependencies() load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") - bazel_skylib_workspace() -LLVM_COMMIT = "0d72fe9777e7c131dfb50c172b944d64437e2ece" - -LLVM_SHA256 = "41f69c116fd1e25d290031c408f6ea0246cafb993b1fdab6f781c87fc0b903cf" - -http_archive( - name = "llvm-raw", - build_file_content = "# empty", - sha256 = LLVM_SHA256, - strip_prefix = "llvm-project-" + LLVM_COMMIT, - urls = ["https://github.com/llvm/llvm-project/archive/{commit}.tar.gz".format(commit = LLVM_COMMIT)], -) - load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure") - llvm_configure(name = "llvm-project") -maybe( - http_archive, - name = "llvm_zlib", - build_file = "@llvm-raw//utils/bazel/third_party_build:zlib-ng.BUILD", - sha256 = "e36bb346c00472a1f9ff2a0a4643e590a254be6379da7cddd9daeb9a7f296731", - strip_prefix = "zlib-ng-2.0.7", - urls = [ - "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.0.7.zip", - ], -) - -maybe( - http_archive, - name = "llvm_zstd", - build_file = "@llvm-raw//utils/bazel/third_party_build:zstd.BUILD", - sha256 = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0", - strip_prefix = "zstd-1.5.2", - urls = [ - "https://github.com/facebook/zstd/releases/download/v1.5.2/zstd-1.5.2.tar.gz", - ], -) - load("//bazel/toolchain/risc0:repo.bzl", "risc0_toolchain") risc0_toolchain(name = "risc0_toolchain") diff --git a/bazel/rules/zirgen/deps.bzl b/bazel/rules/zirgen/deps.bzl new file mode 100644 index 00000000..4ab54de3 --- /dev/null +++ b/bazel/rules/zirgen/deps.bzl @@ -0,0 +1,63 @@ +""" +Utility to resolve Zirgen compiler dependencies +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +def zirgen_dependencies(): + """ + Resolve Zirgen compiler dependencies. + + usage: + ``` + load("//bazel/rules/zirgen:deps.bzl", "zirgen_dependencies") + zirgen_dependencies() + + load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + bazel_skylib_workspace() + + load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure") + llvm_configure(name = "llvm-project") + ``` + """ + http_archive( + name = "bazel_skylib", + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.3.0.tar.gz", + ], + ) + + LLVM_COMMIT = "0d72fe9777e7c131dfb50c172b944d64437e2ece" + LLVM_SHA256 = "41f69c116fd1e25d290031c408f6ea0246cafb993b1fdab6f781c87fc0b903cf" + http_archive( + name = "llvm-raw", + build_file_content = "# empty", + sha256 = LLVM_SHA256, + strip_prefix = "llvm-project-" + LLVM_COMMIT, + urls = ["https://github.com/llvm/llvm-project/archive/{commit}.tar.gz".format(commit = LLVM_COMMIT)], + ) + + maybe( + http_archive, + name = "llvm_zlib", + build_file = "@llvm-raw//utils/bazel/third_party_build:zlib-ng.BUILD", + sha256 = "e36bb346c00472a1f9ff2a0a4643e590a254be6379da7cddd9daeb9a7f296731", + strip_prefix = "zlib-ng-2.0.7", + urls = [ + "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/2.0.7.zip", + ], + ) + + maybe( + http_archive, + name = "llvm_zstd", + build_file = "@llvm-raw//utils/bazel/third_party_build:zstd.BUILD", + sha256 = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0", + strip_prefix = "zstd-1.5.2", + urls = [ + "https://github.com/facebook/zstd/releases/download/v1.5.2/zstd-1.5.2.tar.gz", + ], + ) diff --git a/zirgen/docs/01_Getting_Started.md b/zirgen/docs/01_Getting_Started.md index cc49e838..c4dafe84 100644 --- a/zirgen/docs/01_Getting_Started.md +++ b/zirgen/docs/01_Getting_Started.md @@ -1,52 +1,128 @@ # Getting Started We don't currently release Zirgen in any packaged form, so it's only available -through this repository. Assuming you've cloned and built things from this repo -before, building Zirgen with Bazel is simple with the following command. Note, -though, that this isn't strictly necessary, and that Bazel will automatically -(re)build Zirgen if you use it to invoke the tests as well. +through this repository. This guide assumes you're using Bazel as your build +system, and want to use Zirgen "out of tree" from a separate project. + +# Project structure + +## Pulling in the Zirgen compiler + +From a fresh project directory, we're going to need to create three build +configuration files. First, create a `.bazelrc` file, and put the following two +lines in it. These are necessary to ensure MLIR, one of Zirgen's dependencies, +compiles correctly. ``` -bazel build //zirgen/dsl:zirgen +build --cxxopt=-std=c++17 +build --host_cxxopt=-std=c++17 ``` -## Hello world! +Second, we need to pin our Bazel version. Zirgen is currently built with Bazel +6.0, so we recommend creating a `.bazelversion` file with the following content. +If you want to use a different version of Bazel for any reason, your mileage may +vary. +``` +6.0.0 +``` -Following in the footsteps of our forebears, let's take a look at a classic -"Hello world" program. This program is already available as a test and example, -so we can run it with the following command from the root of this repository: +Third, create a `WORKSPACE` file. This is a Bazel configuration file that deals +with "global" configurations like project dependencies, and this is where we're +going to define how to pull in the Zirgen compiler: +``` +workspace(name = "zirgen-oot") + +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +git_repository( + name = "zirgen", + branch = "main", # feel free to pin a particular commit instead for stability! + remote = "https://github.com/risc0/zirgen.git", +) + +load("@zirgen//bazel/rules/zirgen:deps.bzl", "zirgen_dependencies") +zirgen_dependencies() + +# configure transitive dependencies +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() + +load("@llvm-raw//utils/bazel:configure.bzl", "llvm_configure") +llvm_configure(name = "llvm-project") +``` +At this point, it should be possible to build the Zirgen compiler, which might +take a few minutes but only needs to be done once: ``` -$ bazel run //zirgen/dsl:zirgen -- $(pwd)/zirgen/dsl/test/hello_world.zir --test -... -Running 0 -[0] Log: Hello world! -Lookups resolved +bazel build @zirgen//zirgen/dsl:zirgen ``` -This command passes two arguments to the Zirgen executable. The first, -`$(pwd)/zirgen/dsl/test/hello_world.zir`, specifies the path of the Zirgen file -we want to run on. The second, `--test`, specifies that we want to run all the -tests defined in the file we're running. Typically, the output of Zirgen is a -generated Rust or C++ library that then needs to be integrated with the RISC -Zero proof system. For the sake of simplicity here and as a useful practice -during the development, it is easiest to experiment with Zirgen by writing tests -alongside your circuit code, which can be run in the builtin interpreter without -doing this integration work using the `--test` option. Now, the important part -of the file is the following: +## Setting up your new circuit +Now that the compiler is set up, we just need to start the new circuit. Create a +new directory called `circuit`; the name is not important, but consistency is +key! In this directory, create a new file called `circuit.zir`, and include the +following code: ``` -test { - Log("Hello world!"); +test Hello { + Log("Hello world!"); } ``` -The keyword `test` declares that the thing that follows (enclosed in curly -braces) is a test. Tests can be given an optional name, but if they aren't named -then they are labeled with sequential numbers. This causes the text "Running 0" -to be written to stdout, marking the beginning of the execution of that test. -The statement `Log("Hello world!");` is what causes the text "[0] Log: Hello -world!" to be written to stdout. +Next, add a `BUILD.bazel` file in the same directory, and add the following to +it to configure a new build rule that generates Rust code for the circuit. +``` +load("@zirgen//bazel/rules/zirgen:dsl-defs.bzl", "zirgen_genfiles") + +filegroup( + name = "imports", + srcs = glob(["*.zir"]), +) + +zirgen_genfiles( + name = "CircuitIncs", + zir_file = ":circuit.zir", + data = [":imports"], + zirgen_outs = [ + ( + ["--emit=rust"], + "circuit.rs.inc", + ), + ], +) +``` + +## Hello world! + +Now with everything set up, we're ready to run the new circuit. The output of +the Zirgen compiler is generated code that can then be called by 0STARK to +generate real proofs. Our circuit is so trivial that there's not really much to +generate, but it can be done with the following Bazel command, which will place +the code in the Bazel build directory: +``` +bazel build //circuit:GenerateCircuitIncs +``` + +Of more immediate interest, it's also possible to test and debug circuits using +a built-in interpreter. This doesn't generate real proofs, but it does make it +easy to try things out as you go along. To run our circuit in the interpreter: +``` +bazel run @zirgen//zirgen/dsl:zirgen -- $(pwd)/circuit/circuit.zir --test +``` +> ``` +> Running Hello +> [0] Log: Hello world! +> Lookups resolved +> Verifying constraints for Hello +> Verifying zll constraints for Hello +> ``` + +This command invokes the Zirgen compiler through Bazel. Everything after `--` is +passed directly to the `zirgen` executable as a command line option: the first +argument indicates the "main" file of the circuit. The `--test` indicates that +all the tests in the source code should be run in the interpreter. In this case, +our code has one test named `Hello`, and it logs the string "Hello world!" on +cycle zero. And presto! We've set up, written, and run a brand-new circuit! [Prev](README.md) [Next](02_Conceptual_Overview.md) diff --git a/zirgen/docs/03_Building_a_Fibonacci_Circuit.md b/zirgen/docs/03_Building_a_Fibonacci_Circuit.md index b7b16fff..bf0b5f49 100644 --- a/zirgen/docs/03_Building_a_Fibonacci_Circuit.md +++ b/zirgen/docs/03_Building_a_Fibonacci_Circuit.md @@ -19,7 +19,7 @@ component Top() {} ``` Next, we need to tell Bazel about our new circuit, and tell it how to build our -project. Create a `Build.bazel` file in the same directory as our new circuit +project. Create a `BUILD.bazel` file in the same directory as our new circuit source file, with these contents: ```