about summary refs log tree commit diff
path: root/library/compiler-builtins/CONTRIBUTING.md
blob: 9ae4f893c60d1f81a4bd2df4d027df117cbf893b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# How to contribute

## compiler-builtins

1. From the [pending list](compiler-builtins/README.md#progress), pick one or
   more intrinsics.
2. Port the version from [`compiler-rt`] and, if applicable, their
   [tests][rt-tests]. Note that this crate has generic implementations for a lot
   of routines, which may be usable without porting the entire implementation.
3. Add a test to `builtins-test`, comparing the behavior of the ported
   intrinsic(s) with their implementation on the testing host.
4. Add the intrinsic to `builtins-test-intrinsics/src/main.rs` to verify it can
   be linked on all targets.
5. Send a Pull Request (PR) :tada:.

[`compiler-rt`]: https://github.com/llvm/llvm-project/tree/b6820c35c59a4da3e59c11f657093ffbd79ae1db/compiler-rt/lib/builtins
[rt-tests]: https://github.com/llvm/llvm-project/tree/b6820c35c59a4da3e59c11f657093ffbd79ae1db/compiler-rt/test/builtins

## Porting Reminders

1. [Rust][prec-rust] and [C][prec-c] have slightly different operator
   precedence. C evaluates comparisons (`== !=`) before bitwise operations
   (`& | ^`), while Rust evaluates the other way.
2. C assumes wrapping operations everywhere. Rust panics on overflow when in
   debug mode. Consider using the [Wrapping][wrap-ty] type or the explicit
   [wrapping_*][wrap-fn] functions where applicable.
3. Note [C implicit casts][casts], especially integer promotion. Rust is much
   more explicit about casting, so be sure that any cast which affects the
   output is ported to the Rust implementation.
4. Rust has [many functions][i32] for integer or floating point manipulation in
   the standard library. Consider using one of these functions rather than
   porting a new one.

[prec-rust]: https://doc.rust-lang.org/reference/expressions.html#expression-precedence
[prec-c]: http://en.cppreference.com/w/c/language/operator_precedence
[wrap-ty]: https://doc.rust-lang.org/core/num/struct.Wrapping.html
[wrap-fn]: https://doc.rust-lang.org/std/primitive.i32.html#method.wrapping_add
[casts]: http://en.cppreference.com/w/cpp/language/implicit_conversion
[i32]: https://doc.rust-lang.org/std/primitive.i32.html

## Tips and tricks

- _IMPORTANT_ The code in this crate will end up being used in the `core` crate
  so it can **not** have any external dependencies (other than a subset of
  `core` itself).
- Only use relative imports within the `math` directory / module, e.g.
  `use self::fabs::fabs` or `use super::k_cos`. Absolute imports from core are
  OK, e.g. `use core::u64`.
- To reinterpret a float as an integer use the `to_bits` method. The MUSL code
  uses the `GET_FLOAT_WORD` macro, or a union, to do this operation.
- To reinterpret an integer as a float use the `f32::from_bits` constructor. The
  MUSL code uses the `SET_FLOAT_WORD` macro, or a union, to do this operation.
- You may use other methods from core like `f64::is_nan`, etc. as appropriate.
- Rust does not have hex float literals. This crate provides two `hf16!`,
  `hf32!`, `hf64!`, and `hf128!` which convert string literals to floats at
  compile time.

  ```rust
  assert_eq!(hf32!("0x1.ffep+8").to_bits(), 0x43fff000);
  assert_eq!(hf64!("0x1.ffep+8").to_bits(), 0x407ffe0000000000);
  ```

- Rust code panics on arithmetic overflows when not optimized. You may need to
  use the [`Wrapping`] newtype to avoid this problem, or individual methods like
  [`wrapping_add`].

[`Wrapping`]: https://doc.rust-lang.org/std/num/struct.Wrapping.html
[`wrapping_add`]: https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_add

## Testing

Testing for these crates can be somewhat complex, so feel free to rely on CI.

The easiest way replicate CI testing is using Docker. This can be done by
running `./ci/run-docker.sh [target]`. If no target is specified, all targets
will be run.

Tests can also be run without Docker:

```sh
# Run basic tests
#
# --no-default-features always needs to be passed, an unfortunate limitation
# since the `#![compiler_builtins]` feature is enabled by default.
cargo test --workspace --no-default-features

# Test with all interesting features
cargo test --workspace --no-default-features \
    --features arch,unstable-float,unstable-intrinsics,mem

# Run with more detailed tests for libm
cargo test --workspace --no-default-features \
    --features arch,unstable-float,unstable-intrinsics,mem \
    --features build-mpfr,build-musl \
    --profile release-checked
```

The multiprecision tests use the [`rug`] crate for bindings to MPFR. MPFR can be
difficult to build on non-Unix systems, refer to [`gmp_mpfr_sys`] for help.

`build-musl` does not build with MSVC, Wasm, or Thumb.

[`rug`]: https://docs.rs/rug/latest/rug/
[`gmp_mpfr_sys`]: https://docs.rs/gmp-mpfr-sys/1.6.4/gmp_mpfr_sys/

In order to run all tests, some dependencies may be required:

```sh
# Allow testing compiler-builtins
./ci/download-compiler-rt.sh

# Optional, initialize musl for `--features build-musl`
git submodule init
git submodule update

# `--release` ables more test cases
cargo test --release
```

### Extensive tests

Libm also has tests that are exhaustive (for single-argument `f32` and 1- or 2-
argument `f16`) or extensive (for all other float and argument combinations).
These take quite a long time to run, but are launched in CI when relevant files
are changed.

Exhaustive tests can be selected by passing an environment variable:

```sh
LIBM_EXTENSIVE_TESTS=sqrt,sqrtf cargo test --features build-mpfr \
    --test z_extensive \
    --profile release-checked

# Run all tests for one type
LIBM_EXTENSIVE_TESTS=all_f16 cargo test ...

# Ensure `f64` tests can run exhaustively. Estimated completion test for a
# single test is 57306 years on my machine so this may be worth skipping.
LIBM_EXTENSIVE_TESTS=all LIBM_EXTENSIVE_ITERATIONS=18446744073709551615 cargo test ...
```

## Benchmarking

Regular walltime benchmarks can be run with `cargo bench`:

```sh
cargo bench --no-default-features \
    --features arch,unstable-float,unstable-intrinsics,mem \
    --features benchmarking-reports
```

There are also benchmarks that check instruction count behind the `icount`
feature. These require [`iai-callgrind-runner`] (via Cargo) and [Valgrind]
to be installed, which means these only run on limited platforms.

Instruction count benchmarks are run as part of CI to flag performance
regresions.

```sh
cargo bench --no-default-features \
    --features arch,unstable-float,unstable-intrinsics,mem \
    --features icount \
    --bench icount --bench mem_icount
```

[`iai-callgrind-runner`]: https://crates.io/crates/iai-callgrind-runner
[Valgrind]: https://valgrind.org/

## Subtree synchronization

`compiler-builtins` is included as a [Josh subtree] in the main compiler
repository (`rust-lang/rust`). You can find a guide on how to create synchronization
(pull and push) PRs at the [`rustc-dev-guide` page].

[Josh subtree]: https://rustc-dev-guide.rust-lang.org/external-repos.html#josh-subtrees
[`rustc-dev-guide` page]: https://rustc-dev-guide.rust-lang.org/external-repos.html#synchronizing-a-josh-subtree