Bringing in remote trunk

This commit is contained in:
Chad Stearns 2020-10-28 21:24:15 -04:00
commit a9c0185225
50 changed files with 1047 additions and 1080 deletions

2
compiler/builtins/bitcode/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
zig-cache
src/zig-cache

View file

@ -1,13 +0,0 @@
[package]
name = "roc_builtins_bitcode"
version = "0.1.0"
authors = ["Richard Feldman <oss@rtfeldman.com>"]
repository = "https://github.com/rtfeldman/roc"
readme = "README.md"
edition = "2018"
description = "Generate LLVM bitcode for Roc builtins"
license = "Apache-2.0"
[dev-dependencies]
pretty_assertions = "0.5.1"

View file

@ -5,52 +5,19 @@ 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 Zig, then 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/builtins/build.rs`.
Then `builtins/src/bitcode/rs` staticlly imports the compiled bitcode for use in the compiler.
## Building the bitcode
You can find the compiled bitcode in `target/debug/build/roc_builtins-[some random characters]/out/builtins.bc`.
There will be two directories like `roc_builtins-[some random characters]`, look for the one that has an
`out` directory as a child.
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:
>
> ```bash
> $ cargo rustc --release --lib -- --emit=llvm-ir
> ```
>
> 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 bitcode is a bunch of bytes that aren't particularly human-readable.
> If you want to take a look at the human-readable LLVM IR, look at
> `target/debug/build/roc_builtins-[some random characters]/out/builtins.ll`
## Calling bitcode functions

View file

@ -1,19 +0,0 @@
#!/bin/bash
set -euxo pipefail
# Clear out any existing output files. Sometimes if these are there, rustc
# doesn't generate the .bc file - or we can end up with more than one .bc
rm -rf ../../../target/release/deps/
# Regenerate the .bc file
cargo rustc --release --lib -- --emit=llvm-bc
bc_files=$(ls ../../../target/release/deps/*.bc | wc -l)
if [[ $bc_files != 1 ]]; then
echo "More than one .bc file was emitted somehow."
exit 1;
fi
cp ../../../target/release/deps/*.bc ../../gen/src/llvm/builtins.bc

View file

@ -1,134 +0,0 @@
// NOTE: Editing this file on its own does nothing! The procedure for
// incorporating changes here is in this crate' README.
#![crate_type = "lib"]
#![no_std]
mod libm;
/// TODO this is no longer used. Feel free to delete it the next time
/// we need to rebuild builtins.bc!
#[no_mangle]
pub fn i64_to_f64_(num: i64) -> f64 {
num as f64
}
/// Adapted from Rust's core::num module, by the Rust core team,
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
///
/// Thank you, Rust core team!
#[no_mangle]
pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 {
let mut acc = 1;
while exp > 1 {
if (exp & 1) == 1 {
acc *= base;
}
exp /= 2;
base *= base;
}
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
if exp == 1 {
acc *= base;
}
acc
}
#[no_mangle]
pub fn str_split_<'a>(
list: &'a mut [&'a [u8]],
str_: &'a [u8],
delimiter: &[u8],
) -> &'a [&'a [u8]] {
let mut ret_list_index = 0;
let str_len = str_.len();
let delimiter_len = delimiter.len();
let mut slice_start_index = 0;
let mut str_index = 0;
if str_len > delimiter_len {
while str_index <= (str_len - delimiter_len) {
let mut delimiter_index = 0;
let mut matches_delimiter = true;
while matches_delimiter && delimiter_index < delimiter_len {
let delimiter_char = delimiter[delimiter_index];
let str_char = str_[str_index + delimiter_index];
if delimiter_char != str_char {
matches_delimiter = false;
}
delimiter_index += 1;
}
if matches_delimiter {
list[ret_list_index] = &str_[slice_start_index..str_index];
slice_start_index = str_index + delimiter_len;
ret_list_index += 1;
str_index += delimiter_len;
} else {
str_index += 1;
}
}
}
list[ret_list_index] = &str_[slice_start_index..str_len];
list
}
#[no_mangle]
pub fn count_segments_(str: &[u8], delimiter: &[u8]) -> i64 {
let mut count: i64 = 1;
let str_len = str.len();
let delimiter_len = delimiter.len();
if str_len > delimiter_len {
for str_index in 0..(str_len - delimiter_len) {
let mut delimiter_index = 0;
let mut matches_delimiter = true;
while matches_delimiter && delimiter_index < delimiter_len {
let delimiter_char = delimiter[delimiter_index];
let str_char = str[str_index + delimiter_index];
if delimiter_char != str_char {
matches_delimiter = false;
}
delimiter_index += 1;
}
if matches_delimiter {
count += 1;
}
}
}
count
}
/// Adapted from Rust's core::num module, by the Rust core team,
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
///
/// Thank you, Rust core team!
#[no_mangle]
pub fn is_finite_(num: f64) -> bool {
f64::is_finite(num)
}
#[no_mangle]
pub fn atan_(x: f64) -> f64 {
libm::atan(x)
}

View file

@ -1,199 +0,0 @@
/// Adapted from Rust's libm module, by the Rust core team,
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
/// https://github.com/rust-lang/libm/blob/master/LICENSE-APACHE
///
/// Thank you, Rust core team!
// From https://github.com/rust-lang/libm/blob/master/src/math/mod.rs
#[cfg(not(debug_assertions))]
macro_rules! i {
($array:expr, $index:expr) => {
unsafe { *$array.get_unchecked($index) }
};
($array:expr, $index:expr, = , $rhs:expr) => {
unsafe {
*$array.get_unchecked_mut($index) = $rhs;
}
};
($array:expr, $index:expr, += , $rhs:expr) => {
unsafe {
*$array.get_unchecked_mut($index) += $rhs;
}
};
($array:expr, $index:expr, -= , $rhs:expr) => {
unsafe {
*$array.get_unchecked_mut($index) -= $rhs;
}
};
($array:expr, $index:expr, &= , $rhs:expr) => {
unsafe {
*$array.get_unchecked_mut($index) &= $rhs;
}
};
($array:expr, $index:expr, == , $rhs:expr) => {
unsafe { *$array.get_unchecked_mut($index) == $rhs }
};
}
#[cfg(debug_assertions)]
macro_rules! i {
($array:expr, $index:expr) => {
*$array.get($index).unwrap()
};
($array:expr, $index:expr, = , $rhs:expr) => {
*$array.get_mut($index).unwrap() = $rhs;
};
($array:expr, $index:expr, -= , $rhs:expr) => {
*$array.get_mut($index).unwrap() -= $rhs;
};
($array:expr, $index:expr, += , $rhs:expr) => {
*$array.get_mut($index).unwrap() += $rhs;
};
($array:expr, $index:expr, &= , $rhs:expr) => {
*$array.get_mut($index).unwrap() &= $rhs;
};
($array:expr, $index:expr, == , $rhs:expr) => {
*$array.get_mut($index).unwrap() == $rhs
};
}
macro_rules! llvm_intrinsically_optimized {
(#[cfg($($clause:tt)*)] $e:expr) => {
#[cfg(all(feature = "unstable", $($clause)*))]
{
if true { // thwart the dead code lint
$e
}
}
};
}
macro_rules! force_eval {
($e:expr) => {
unsafe {
::core::ptr::read_volatile(&$e);
}
};
}
// From https://github.com/rust-lang/libm/blob/master/src/math/atan.rs
// Clippy fails CI if we don't include overrides below. Since this is copied
// straight from the libm crate, I figure they had a reason to include
// the extra percision, so we just silence this warning.
#[allow(clippy::excessive_precision)]
const ATANHI: [f64; 4] = [
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
];
#[allow(clippy::excessive_precision)]
const ATANLO: [f64; 4] = [
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
];
#[allow(clippy::excessive_precision)]
const AT: [f64; 11] = [
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
];
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn fabs(x: f64) -> f64 {
// On wasm32 we know that LLVM's intrinsic will compile to an optimized
// `f64.abs` native instruction, so we can leverage this for both code size
// and speed.
llvm_intrinsically_optimized! {
#[cfg(target_arch = "wasm32")] {
return unsafe { ::core::intrinsics::fabsf64(x) }
}
}
f64::from_bits(x.to_bits() & (u64::MAX / 2))
}
#[inline(always)]
#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
pub fn atan(x: f64) -> f64 {
let mut x = x;
let mut ix = (x.to_bits() >> 32) as u32;
let sign = ix >> 31;
ix &= 0x7fff_ffff;
if ix >= 0x4410_0000 {
if x.is_nan() {
return x;
}
let z = ATANHI[3] + f64::from_bits(0x0380_0000); // 0x1p-120f
return if sign != 0 { -z } else { z };
}
let id = if ix < 0x3fdc_0000 {
/* |x| < 0.4375 */
if ix < 0x3e40_0000 {
/* |x| < 2^-27 */
if ix < 0x0010_0000 {
/* raise underflow for subnormal x */
force_eval!(x as f32);
}
return x;
}
-1
} else {
x = fabs(x);
if ix < 0x3ff30000 {
/* |x| < 1.1875 */
if ix < 0x3fe60000 {
/* 7/16 <= |x| < 11/16 */
x = (2. * x - 1.) / (2. + x);
0
} else {
/* 11/16 <= |x| < 19/16 */
x = (x - 1.) / (x + 1.);
1
}
} else if ix < 0x40038000 {
/* |x| < 2.4375 */
x = (x - 1.5) / (1. + 1.5 * x);
2
} else {
/* 2.4375 <= |x| < 2^66 */
x = -1. / x;
3
}
};
let z = x * x;
let w = z * z;
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
let s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
let s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
if id < 0 {
return x - x * (s1 + s2);
}
let z = i!(ATANHI, id as usize) - (x * (s1 + s2) - i!(ATANLO, id as usize) - x);
if sign != 0 {
-z
} else {
z
}
}

View file

@ -0,0 +1,14 @@
const std = @import("std");
const math = std.math;
export fn atan_(num: f64) f64 {
return math.atan(num);
}
export fn is_finite_(num: f64) bool {
return math.isFinite(num);
}
export fn pow_int_(base: i64, exp: i64) i64 {
return math.pow(i64, base, exp);
}