Compile bitcode as part of build script

This commit is contained in:
Jared Ramirez 2020-10-25 14:09:53 -07:00
parent 586b899e73
commit 48f8aad180
7 changed files with 47 additions and 59 deletions

View File

@ -1 +1 @@
out
lib.ll

View File

@ -1,7 +0,0 @@
.PHONY: build clean
build:
./generate.sh ../../gen/src/llvm/builtins.bc
clean:
rm -rf ./out

View File

@ -5,52 +5,24 @@ When their implementations are simple enough (e.g. addition), they
can be implemented directly in Inkwell.
When their implementations are complex enough, it's nicer to
implement them in a higher-level language like Rust, compile the
result to LLVM bitcode, and import that bitcode into the compiler.
implement them in a higher-level language like C (or eventually Zig),
compile the result to LLVM bitcode, and import that bitcode into the compiler.
Here is the process for doing that.
Compiling the bitcode happens automatically in a Rust build script at `compiler/gen/build.rs`.
You can find the compiled bitcode in `target/debug/build/roc_gen-[some random characters]/out/builtins.bc`.
## Building the bitcode
The source we'll use to generate the bitcode is in `src/lib.rs` in this directory.
To generate the bitcode, `cd` into `compiler/builtins/bitcode/` and run:
```bash
$ ./regenerate.sh
```
> If you want to take a look at the human-readable LLVM IR rather than the
> bitcode, run this instead and look for a `.ll` file instead of a `.bc` file:
> If you want to take a look at the human-readable LLVM IR, cd into `compiler/builtins/bitcode` and
> run the following command. It should create `compiler/builtins/bitcode/lib.ll`
>
> ```bash
> $ cargo rustc --release --lib -- --emit=llvm-ir
> clang -S -emit-llvm src/lib.c
> ```
>
> Then look in the root `roc` source directory under `target/release/deps/` for a file
> with a name like `roc_builtins_bitcode-8da0901c58a73ebf.bc` - except
> probably with a different hash before the `.bc`. If there's more than one
> `*.bc` file in that directory, delete the whole `deps/` directory and re-run
> the `cargo rustc` command above to regenerate it.
**Note**: In order to be able to address the bitcode functions by name, they need to be defined with the `#[no_mangle]` attribute.
The bitcode is a bunch of bytes that aren't particularly human-readable.
Since Roc is designed to be distributed as a single binary, these bytes
need to be included in the raw source somewhere.
The `llvm/src/build.rs` file statically imports these raw bytes
using the [`include_bytes!` macro](https://doc.rust-lang.org/std/macro.include_bytes.html).
The current `.bc` file is located at:
```
compiler/gen/src/llvm/builtins.bc
```
The script will automatically replace this `.bc` file with the new one.
Once that's done, `git status` should show that the `builtins.bc` file
has been changed. Commit that change and you're done!
The `llvm/src/build.rs` file statically imports these raw bytes.
## Calling bitcode functions

View File

@ -1,13 +0,0 @@
#!/bin/bash
set -euxo pipefail
# Clear out any existing output files
rm -rf ./out
mkdir ./out
# Regenerate the .bc file
clang -emit-llvm -o out/lib.bc -c src/lib.c
# Copy bc file for it to be used
cp ./out/lib.bc "$1"

23
compiler/gen/build.rs Normal file
View File

@ -0,0 +1,23 @@
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
fn main() {
let src_path = fs::canonicalize("./../builtins/bitcode/src/lib.c")
.expect("Failed to resolve bitcode source");
let src = src_path.to_str().expect("Invalid src path");
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("builtins.bc");
let dest = dest_path.to_str().expect("Invalid dest path");
Command::new("clang")
.args(&["-emit-llvm", "-o", dest, "-c", src])
.status()
.unwrap();
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed={}", src);
println!("cargo:rustc-env=BUILTINS_BC={}", dest);
}

View File

@ -34,6 +34,8 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{JoinPointId, Wrapped};
use roc_mono::layout::{Builtin, Layout, MemoryMode};
use std::fs::File;
use std::io::prelude::Read;
use target_lexicon::CallingConvention;
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
@ -181,8 +183,19 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
}
pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> {
let memory_buffer =
MemoryBuffer::create_from_memory_range(include_bytes!("builtins.bc"), module_name);
// In the build script for the gen module, we compile the builtins bitcode and set
// BUILTINS_BC to the path to the compiled output.
let path: &'static str = env!(
"BUILTINS_BC",
"Env var BUILTINS_BC not found. Is there a problem with the build script?"
);
let mut builtins_bitcode = File::open(path).expect("Unable to find builtins bitcode source");
let mut buffer = std::vec::Vec::new();
builtins_bitcode
.read_to_end(&mut buffer)
.expect("Unable to read builtins bitcode");
let memory_buffer = MemoryBuffer::create_from_memory_range(&buffer, module_name);
let module = Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err));

Binary file not shown.