mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-02 10:04:23 +00:00
Merge commit '39683d8eb7a32a74bea96ecbf1e87675d3338506' into sync_cg_gcc-2022-03-26
This commit is contained in:
commit
bbff48e094
@ -10,10 +10,17 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
libgccjit_version: ["libgccjit.so", "libgccjit_without_int128.so"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: llvm/llvm-project
|
||||||
|
path: llvm
|
||||||
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
run: sudo apt-get install ninja-build ripgrep
|
run: sudo apt-get install ninja-build ripgrep
|
||||||
|
|
||||||
@ -21,19 +28,25 @@ jobs:
|
|||||||
uses: dawidd6/action-download-artifact@v2
|
uses: dawidd6/action-download-artifact@v2
|
||||||
with:
|
with:
|
||||||
workflow: main.yml
|
workflow: main.yml
|
||||||
name: libgccjit.so
|
name: ${{ matrix.libgccjit_version }}
|
||||||
path: gcc-build
|
path: gcc-build
|
||||||
repo: antoyo/gcc
|
repo: antoyo/gcc
|
||||||
|
search_artifacts: true # Because, instead, the action only check the last job ran and that won't work since we want multiple artifacts.
|
||||||
|
|
||||||
- name: Setup path to libgccjit
|
- name: Setup path to libgccjit
|
||||||
run: |
|
run: |
|
||||||
echo $(readlink -f gcc-build) > gcc_path
|
echo $(readlink -f gcc-build) > gcc_path
|
||||||
|
# NOTE: the filename is still libgccjit.so even when the artifact name is different.
|
||||||
ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
|
ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
|
||||||
|
|
||||||
- name: Set LIBRARY_PATH
|
- name: Set env
|
||||||
run: |
|
run: |
|
||||||
echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
||||||
echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
||||||
|
echo "workspace="$GITHUB_WORKSPACE >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set RUST_COMPILER_RT_ROOT
|
||||||
|
run: echo "RUST_COMPILER_RT_ROOT="${{ env.workspace }}/llvm/compiler-rt >> $GITHUB_ENV
|
||||||
|
|
||||||
# https://github.com/actions/cache/issues/133
|
# https://github.com/actions/cache/issues/133
|
||||||
- name: Fixup owner of ~/.cargo/
|
- name: Fixup owner of ~/.cargo/
|
1
compiler/rustc_codegen_gcc/.gitignore
vendored
1
compiler/rustc_codegen_gcc/.gitignore
vendored
@ -18,3 +18,4 @@ gimple*
|
|||||||
res
|
res
|
||||||
test-backend
|
test-backend
|
||||||
gcc_path
|
gcc_path
|
||||||
|
benchmarks
|
||||||
|
@ -41,7 +41,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gccjit"
|
name = "gccjit"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/antoyo/gccjit.rs#0672b78d162d65b6f36ea4062947253affe9fdef"
|
source = "git+https://github.com/antoyo/gccjit.rs#bdecdecfb8a02ec861a39a350f990faa33bd31c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gccjit_sys",
|
"gccjit_sys",
|
||||||
]
|
]
|
||||||
@ -49,7 +49,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gccjit_sys"
|
name = "gccjit_sys"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/antoyo/gccjit.rs#0672b78d162d65b6f36ea4062947253affe9fdef"
|
source = "git+https://github.com/antoyo/gccjit.rs#bdecdecfb8a02ec861a39a350f990faa33bd31c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.12",
|
"libc 0.1.12",
|
||||||
]
|
]
|
||||||
|
@ -21,6 +21,8 @@ You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already i
|
|||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git
|
$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git
|
||||||
$ cd rustc_codegen_gcc
|
$ cd rustc_codegen_gcc
|
||||||
|
$ git clone https://github.com/llvm/llvm-project llvm --depth 1 --single-branch
|
||||||
|
$ export RUST_COMPILER_RT_ROOT="$PWD/llvm/compiler-rt"
|
||||||
$ ./prepare_build.sh # download and patch sysroot src
|
$ ./prepare_build.sh # download and patch sysroot src
|
||||||
$ ./build.sh --release
|
$ ./build.sh --release
|
||||||
```
|
```
|
||||||
@ -109,6 +111,13 @@ Or add a breakpoint to `add_error` in gdb and print the line number using:
|
|||||||
|
|
||||||
```
|
```
|
||||||
p loc->m_line
|
p loc->m_line
|
||||||
|
p loc->m_filename->m_buffer
|
||||||
|
```
|
||||||
|
|
||||||
|
To print a debug representation of a tree:
|
||||||
|
|
||||||
|
```c
|
||||||
|
debug_tree(expr);
|
||||||
```
|
```
|
||||||
|
|
||||||
To get the `rustc` command to run in `gdb`, add the `--verbose` flag to `cargo build`.
|
To get the `rustc` command to run in `gdb`, add the `--verbose` flag to `cargo build`.
|
||||||
@ -134,4 +143,5 @@ To get the `rustc` command to run in `gdb`, add the `--verbose` flag to `cargo b
|
|||||||
* Set `linker='-Clinker=m68k-linux-gcc'`.
|
* Set `linker='-Clinker=m68k-linux-gcc'`.
|
||||||
* Set the path to the cross-compiling libgccjit in `gcc_path`.
|
* Set the path to the cross-compiling libgccjit in `gcc_path`.
|
||||||
* Disable the 128-bit integer types if the target doesn't support them by using `let i128_type = context.new_type::<i64>();` in `context.rs` (same for u128_type).
|
* Disable the 128-bit integer types if the target doesn't support them by using `let i128_type = context.new_type::<i64>();` in `context.rs` (same for u128_type).
|
||||||
|
* Comment the line: `context.add_command_line_option("-masm=intel");` in src/base.rs.
|
||||||
* (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?).
|
* (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?).
|
||||||
|
@ -3,7 +3,27 @@
|
|||||||
#set -x
|
#set -x
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -f ./gcc_path ]; then
|
codegen_channel=debug
|
||||||
|
sysroot_channel=debug
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--release)
|
||||||
|
codegen_channel=release
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--release-sysroot)
|
||||||
|
sysroot_channel=release
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -f ./gcc_path ]; then
|
||||||
export GCC_PATH=$(cat gcc_path)
|
export GCC_PATH=$(cat gcc_path)
|
||||||
else
|
else
|
||||||
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
||||||
@ -13,13 +33,21 @@ fi
|
|||||||
export LD_LIBRARY_PATH="$GCC_PATH"
|
export LD_LIBRARY_PATH="$GCC_PATH"
|
||||||
export LIBRARY_PATH="$GCC_PATH"
|
export LIBRARY_PATH="$GCC_PATH"
|
||||||
|
|
||||||
if [[ "$1" == "--release" ]]; then
|
features=
|
||||||
|
|
||||||
|
if [[ "$1" == "--features" ]]; then
|
||||||
|
shift
|
||||||
|
features="--features $1"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$codegen_channel" == "release" ]]; then
|
||||||
export CHANNEL='release'
|
export CHANNEL='release'
|
||||||
CARGO_INCREMENTAL=1 cargo rustc --release
|
CARGO_INCREMENTAL=1 cargo rustc --release $features
|
||||||
else
|
else
|
||||||
echo $LD_LIBRARY_PATH
|
echo $LD_LIBRARY_PATH
|
||||||
export CHANNEL='debug'
|
export CHANNEL='debug'
|
||||||
cargo rustc
|
cargo rustc $features
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source config.sh
|
source config.sh
|
||||||
@ -28,4 +56,9 @@ rm -r target/out || true
|
|||||||
mkdir -p target/out/gccjit
|
mkdir -p target/out/gccjit
|
||||||
|
|
||||||
echo "[BUILD] sysroot"
|
echo "[BUILD] sysroot"
|
||||||
time ./build_sysroot/build_sysroot.sh $CHANNEL
|
if [[ "$sysroot_channel" == "release" ]]; then
|
||||||
|
time ./build_sysroot/build_sysroot.sh --release
|
||||||
|
else
|
||||||
|
time ./build_sysroot/build_sysroot.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ if [[ "$1" == "--release" ]]; then
|
|||||||
RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release
|
RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release
|
||||||
else
|
else
|
||||||
sysroot_channel='debug'
|
sysroot_channel='debug'
|
||||||
cargo build --target $TARGET_TRIPLE
|
cargo build --target $TARGET_TRIPLE --features compiler_builtins/c
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy files to sysroot
|
# Copy files to sysroot
|
||||||
|
@ -8,7 +8,7 @@ pushd $(dirname "$0") >/dev/null
|
|||||||
source config.sh
|
source config.sh
|
||||||
|
|
||||||
# read nightly compiler from rust-toolchain file
|
# read nightly compiler from rust-toolchain file
|
||||||
TOOLCHAIN=$(cat rust-toolchain)
|
TOOLCHAIN=$(cat rust-toolchain | grep channel | sed 's/channel = "\(.*\)"/\1/')
|
||||||
|
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ unsafe extern "C" fn _Unwind_Resume() {
|
|||||||
#[lang = "sized"]
|
#[lang = "sized"]
|
||||||
pub trait Sized {}
|
pub trait Sized {}
|
||||||
|
|
||||||
|
#[lang = "destruct"]
|
||||||
|
pub trait Destruct {}
|
||||||
|
|
||||||
#[lang = "unsize"]
|
#[lang = "unsize"]
|
||||||
pub trait Unsize<T: ?Sized> {}
|
pub trait Unsize<T: ?Sized> {}
|
||||||
|
|
||||||
@ -59,6 +62,7 @@ unsafe impl Copy for i16 {}
|
|||||||
unsafe impl Copy for i32 {}
|
unsafe impl Copy for i32 {}
|
||||||
unsafe impl Copy for isize {}
|
unsafe impl Copy for isize {}
|
||||||
unsafe impl Copy for f32 {}
|
unsafe impl Copy for f32 {}
|
||||||
|
unsafe impl Copy for f64 {}
|
||||||
unsafe impl Copy for char {}
|
unsafe impl Copy for char {}
|
||||||
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
|
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
|
||||||
unsafe impl<T: ?Sized> Copy for *const T {}
|
unsafe impl<T: ?Sized> Copy for *const T {}
|
||||||
@ -443,12 +447,22 @@ pub trait Deref {
|
|||||||
fn deref(&self) -> &Self::Target;
|
fn deref(&self) -> &Self::Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Allocator {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Global;
|
||||||
|
|
||||||
|
impl Allocator for Global {}
|
||||||
|
|
||||||
#[lang = "owned_box"]
|
#[lang = "owned_box"]
|
||||||
pub struct Box<T: ?Sized>(*mut T);
|
pub struct Box<
|
||||||
|
T: ?Sized,
|
||||||
|
A: Allocator = Global,
|
||||||
|
>(*mut T, A);
|
||||||
|
|
||||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized> Drop for Box<T> {
|
impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// drop is currently performed by compiler.
|
// drop is currently performed by compiler.
|
||||||
}
|
}
|
||||||
@ -468,7 +482,7 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[lang = "box_free"]
|
#[lang = "box_free"]
|
||||||
unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
|
unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: *mut T, alloc: A) {
|
||||||
libc::free(ptr as *mut u8);
|
libc::free(ptr as *mut u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ index 0000000..46fd999
|
|||||||
+[package]
|
+[package]
|
||||||
+name = "core"
|
+name = "core"
|
||||||
+version = "0.0.0"
|
+version = "0.0.0"
|
||||||
+edition = "2018"
|
+edition = "2021"
|
||||||
+
|
+
|
||||||
+[lib]
|
+[lib]
|
||||||
+name = "coretests"
|
+name = "coretests"
|
||||||
|
@ -46,24 +46,4 @@ index 4bc44e9..8e3c7a4 100644
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cell_allows_array_cycle() {
|
fn cell_allows_array_cycle() {
|
||||||
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
|
|
||||||
index 3e00e0a..8e5663b 100644
|
|
||||||
--- a/library/core/tests/slice.rs
|
|
||||||
+++ b/library/core/tests/slice.rs
|
|
||||||
@@ -2108,6 +2108,7 @@ fn test_copy_within_panics_src_out_of_bounds() {
|
|
||||||
bytes.copy_within(usize::MAX..=usize::MAX, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
+/*
|
|
||||||
#[test]
|
|
||||||
fn test_is_sorted() {
|
|
||||||
let empty: [i32; 0] = [];
|
|
||||||
@@ -2122,6 +2123,7 @@ fn test_is_sorted() {
|
|
||||||
assert!(!["c", "bb", "aaa"].is_sorted());
|
|
||||||
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
|
|
||||||
}
|
|
||||||
+*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slice_run_destructors() {
|
|
||||||
-- 2.21.0 (Apple Git-122)
|
-- 2.21.0 (Apple Git-122)
|
||||||
|
@ -7,18 +7,222 @@ Subject: [PATCH] [core] Disable portable-simd test
|
|||||||
library/core/tests/lib.rs | 1 -
|
library/core/tests/lib.rs | 1 -
|
||||||
1 file changed, 1 deletion(-)
|
1 file changed, 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
|
||||||
|
index aa1ad93..95fbf55 100644
|
||||||
|
--- a/library/core/src/lib.rs
|
||||||
|
+++ b/library/core/src/lib.rs
|
||||||
|
@@ -398,23 +398,4 @@ pub mod arch {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-// Pull in the `core_simd` crate directly into libcore. The contents of
|
||||||
|
-// `core_simd` are in a different repository: rust-lang/portable-simd.
|
||||||
|
-//
|
||||||
|
-// `core_simd` depends on libcore, but the contents of this module are
|
||||||
|
-// set up in such a way that directly pulling it here works such that the
|
||||||
|
-// crate uses this crate as its libcore.
|
||||||
|
-#[path = "../../portable-simd/crates/core_simd/src/mod.rs"]
|
||||||
|
-#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)]
|
||||||
|
-#[allow(rustdoc::bare_urls)]
|
||||||
|
-#[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
-mod core_simd;
|
||||||
|
-
|
||||||
|
-#[doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")]
|
||||||
|
-#[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
-pub mod simd {
|
||||||
|
- #[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
- pub use crate::core_simd::simd::*;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
include!("primitive_docs.rs");
|
||||||
|
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
|
||||||
|
index cd38c3a..ad632dc 100644
|
||||||
|
--- a/library/core/src/slice/mod.rs
|
||||||
|
+++ b/library/core/src/slice/mod.rs
|
||||||
|
@@ -17,6 +17,5 @@ use crate::ptr;
|
||||||
|
use crate::result::Result;
|
||||||
|
use crate::result::Result::{Err, Ok};
|
||||||
|
-use crate::simd::{self, Simd};
|
||||||
|
use crate::slice;
|
||||||
|
|
||||||
|
#[unstable(
|
||||||
|
@@ -3475,121 +3474,6 @@ impl<T> [T] {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.
|
||||||
|
- ///
|
||||||
|
- /// This is a safe wrapper around [`slice::align_to`], so has the same weak
|
||||||
|
- /// postconditions as that method. You're only assured that
|
||||||
|
- /// `self.len() == prefix.len() + middle.len() * LANES + suffix.len()`.
|
||||||
|
- ///
|
||||||
|
- /// Notably, all of the following are possible:
|
||||||
|
- /// - `prefix.len() >= LANES`.
|
||||||
|
- /// - `middle.is_empty()` despite `self.len() >= 3 * LANES`.
|
||||||
|
- /// - `suffix.len() >= LANES`.
|
||||||
|
- ///
|
||||||
|
- /// That said, this is a safe method, so if you're only writing safe code,
|
||||||
|
- /// then this can at most cause incorrect logic, not unsoundness.
|
||||||
|
- ///
|
||||||
|
- /// # Panics
|
||||||
|
- ///
|
||||||
|
- /// This will panic if the size of the SIMD type is different from
|
||||||
|
- /// `LANES` times that of the scalar.
|
||||||
|
- ///
|
||||||
|
- /// At the time of writing, the trait restrictions on `Simd<T, LANES>` keeps
|
||||||
|
- /// that from ever happening, as only power-of-two numbers of lanes are
|
||||||
|
- /// supported. It's possible that, in the future, those restrictions might
|
||||||
|
- /// be lifted in a way that would make it possible to see panics from this
|
||||||
|
- /// method for something like `LANES == 3`.
|
||||||
|
- ///
|
||||||
|
- /// # Examples
|
||||||
|
- ///
|
||||||
|
- /// ```
|
||||||
|
- /// #![feature(portable_simd)]
|
||||||
|
- ///
|
||||||
|
- /// let short = &[1, 2, 3];
|
||||||
|
- /// let (prefix, middle, suffix) = short.as_simd::<4>();
|
||||||
|
- /// assert_eq!(middle, []); // Not enough elements for anything in the middle
|
||||||
|
- ///
|
||||||
|
- /// // They might be split in any possible way between prefix and suffix
|
||||||
|
- /// let it = prefix.iter().chain(suffix).copied();
|
||||||
|
- /// assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
|
||||||
|
- ///
|
||||||
|
- /// fn basic_simd_sum(x: &[f32]) -> f32 {
|
||||||
|
- /// use std::ops::Add;
|
||||||
|
- /// use std::simd::f32x4;
|
||||||
|
- /// let (prefix, middle, suffix) = x.as_simd();
|
||||||
|
- /// let sums = f32x4::from_array([
|
||||||
|
- /// prefix.iter().copied().sum(),
|
||||||
|
- /// 0.0,
|
||||||
|
- /// 0.0,
|
||||||
|
- /// suffix.iter().copied().sum(),
|
||||||
|
- /// ]);
|
||||||
|
- /// let sums = middle.iter().copied().fold(sums, f32x4::add);
|
||||||
|
- /// sums.reduce_sum()
|
||||||
|
- /// }
|
||||||
|
- ///
|
||||||
|
- /// let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
|
||||||
|
- /// assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
|
||||||
|
- /// ```
|
||||||
|
- #[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
- pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
|
||||||
|
- where
|
||||||
|
- Simd<T, LANES>: AsRef<[T; LANES]>,
|
||||||
|
- T: simd::SimdElement,
|
||||||
|
- simd::LaneCount<LANES>: simd::SupportedLaneCount,
|
||||||
|
- {
|
||||||
|
- // These are expected to always match, as vector types are laid out like
|
||||||
|
- // arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
||||||
|
- // might as well double-check since it'll optimize away anyhow.
|
||||||
|
- assert_eq!(mem::size_of::<Simd<T, LANES>>(), mem::size_of::<[T; LANES]>());
|
||||||
|
-
|
||||||
|
- // SAFETY: The simd types have the same layout as arrays, just with
|
||||||
|
- // potentially-higher alignment, so the de-facto transmutes are sound.
|
||||||
|
- unsafe { self.align_to() }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix.
|
||||||
|
- ///
|
||||||
|
- /// This is a safe wrapper around [`slice::align_to_mut`], so has the same weak
|
||||||
|
- /// postconditions as that method. You're only assured that
|
||||||
|
- /// `self.len() == prefix.len() + middle.len() * LANES + suffix.len()`.
|
||||||
|
- ///
|
||||||
|
- /// Notably, all of the following are possible:
|
||||||
|
- /// - `prefix.len() >= LANES`.
|
||||||
|
- /// - `middle.is_empty()` despite `self.len() >= 3 * LANES`.
|
||||||
|
- /// - `suffix.len() >= LANES`.
|
||||||
|
- ///
|
||||||
|
- /// That said, this is a safe method, so if you're only writing safe code,
|
||||||
|
- /// then this can at most cause incorrect logic, not unsoundness.
|
||||||
|
- ///
|
||||||
|
- /// This is the mutable version of [`slice::as_simd`]; see that for examples.
|
||||||
|
- ///
|
||||||
|
- /// # Panics
|
||||||
|
- ///
|
||||||
|
- /// This will panic if the size of the SIMD type is different from
|
||||||
|
- /// `LANES` times that of the scalar.
|
||||||
|
- ///
|
||||||
|
- /// At the time of writing, the trait restrictions on `Simd<T, LANES>` keeps
|
||||||
|
- /// that from ever happening, as only power-of-two numbers of lanes are
|
||||||
|
- /// supported. It's possible that, in the future, those restrictions might
|
||||||
|
- /// be lifted in a way that would make it possible to see panics from this
|
||||||
|
- /// method for something like `LANES == 3`.
|
||||||
|
- #[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
- pub fn as_simd_mut<const LANES: usize>(&mut self) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
|
||||||
|
- where
|
||||||
|
- Simd<T, LANES>: AsMut<[T; LANES]>,
|
||||||
|
- T: simd::SimdElement,
|
||||||
|
- simd::LaneCount<LANES>: simd::SupportedLaneCount,
|
||||||
|
- {
|
||||||
|
- // These are expected to always match, as vector types are laid out like
|
||||||
|
- // arrays per <https://llvm.org/docs/LangRef.html#vector-type>, but we
|
||||||
|
- // might as well double-check since it'll optimize away anyhow.
|
||||||
|
- assert_eq!(mem::size_of::<Simd<T, LANES>>(), mem::size_of::<[T; LANES]>());
|
||||||
|
-
|
||||||
|
- // SAFETY: The simd types have the same layout as arrays, just with
|
||||||
|
- // potentially-higher alignment, so the de-facto transmutes are sound.
|
||||||
|
- unsafe { self.align_to_mut() }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/// Checks if the elements of this slice are sorted.
|
||||||
|
///
|
||||||
|
/// That is, for each element `a` and its following element `b`, `a <= b` must hold. If the
|
||||||
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
|
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
|
||||||
index ec70034..7cd9e21 100644
|
index 06c7be0..359e2e7 100644
|
||||||
--- a/library/core/tests/lib.rs
|
--- a/library/core/tests/lib.rs
|
||||||
+++ b/library/core/tests/lib.rs
|
+++ b/library/core/tests/lib.rs
|
||||||
@@ -121,7 +121,6 @@ mod pattern;
|
@@ -75,7 +75,6 @@
|
||||||
mod pin;
|
#![feature(never_type)]
|
||||||
|
#![feature(unwrap_infallible)]
|
||||||
|
#![feature(result_into_ok_or_err)]
|
||||||
|
-#![feature(portable_simd)]
|
||||||
|
#![feature(ptr_metadata)]
|
||||||
|
#![feature(once_cell)]
|
||||||
|
#![feature(option_result_contains)]
|
||||||
|
@@ -127,7 +126,6 @@ mod pin;
|
||||||
|
mod pin_macro;
|
||||||
mod ptr;
|
mod ptr;
|
||||||
mod result;
|
mod result;
|
||||||
-mod simd;
|
-mod simd;
|
||||||
mod slice;
|
mod slice;
|
||||||
mod str;
|
mod str;
|
||||||
mod str_lossy;
|
mod str_lossy;
|
||||||
--
|
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
|
||||||
|
index 5dc586d..b6fc48f 100644
|
||||||
|
--- a/library/std/src/lib.rs
|
||||||
|
+++ b/library/std/src/lib.rs
|
||||||
|
@@ -312,6 +312,5 @@
|
||||||
|
#![feature(panic_can_unwind)]
|
||||||
|
#![feature(panic_unwind)]
|
||||||
|
#![feature(platform_intrinsics)]
|
||||||
|
-#![feature(portable_simd)]
|
||||||
|
#![feature(prelude_import)]
|
||||||
|
#![feature(ptr_as_uninit)]
|
||||||
|
@@ -508,23 +508,6 @@ pub mod time;
|
||||||
|
#[unstable(feature = "once_cell", issue = "74465")]
|
||||||
|
pub mod lazy;
|
||||||
|
|
||||||
|
-// Pull in `std_float` crate into libstd. The contents of
|
||||||
|
-// `std_float` are in a different repository: rust-lang/portable-simd.
|
||||||
|
-#[path = "../../portable-simd/crates/std_float/src/lib.rs"]
|
||||||
|
-#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)]
|
||||||
|
-#[allow(rustdoc::bare_urls)]
|
||||||
|
-#[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
-mod std_float;
|
||||||
|
-
|
||||||
|
-#[doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")]
|
||||||
|
-#[unstable(feature = "portable_simd", issue = "86656")]
|
||||||
|
-pub mod simd {
|
||||||
|
- #[doc(inline)]
|
||||||
|
- pub use crate::std_float::StdFloat;
|
||||||
|
- #[doc(inline)]
|
||||||
|
- pub use core::simd::*;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
#[stable(feature = "futures_api", since = "1.36.0")]
|
||||||
|
pub mod task {
|
||||||
|
//! Types and Traits for working with asynchronous tasks.
|
||||||
|
--
|
||||||
2.26.2.7.g19db9cfb68
|
2.26.2.7.g19db9cfb68
|
||||||
|
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
From 0ffdd8eda8df364391c8ac6e1ce92c73ba9254d4 Mon Sep 17 00:00:00 2001
|
From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001
|
||||||
From: bjorn3 <bjorn3@users.noreply.github.com>
|
From: bjorn3 <bjorn3@users.noreply.github.com>
|
||||||
Date: Fri, 3 Dec 2021 12:16:30 +0100
|
Date: Fri, 3 Dec 2021 12:16:30 +0100
|
||||||
Subject: [PATCH] Disable long running tests
|
Subject: [PATCH] Disable long running tests
|
||||||
|
|
||||||
---
|
---
|
||||||
library/core/tests/slice.rs | 3 +++
|
library/core/tests/slice.rs | 2 ++
|
||||||
1 file changed, 3 insertions(+)
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
|
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
|
||||||
index 2c8f00a..44847ee 100644
|
index 8402833..84592e0 100644
|
||||||
--- a/library/core/tests/slice.rs
|
--- a/library/core/tests/slice.rs
|
||||||
+++ b/library/core/tests/slice.rs
|
+++ b/library/core/tests/slice.rs
|
||||||
@@ -2332,7 +2332,8 @@ macro_rules! empty_max_mut {
|
@@ -2462,6 +2462,7 @@ take_tests! {
|
||||||
};
|
#[cfg(not(miri))] // unused in Miri
|
||||||
}
|
const EMPTY_MAX: &'static [()] = &[(); usize::MAX];
|
||||||
|
|
||||||
+/*
|
+/*
|
||||||
#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations)
|
// can't be a constant due to const mutability rules
|
||||||
take_tests! {
|
#[cfg(not(miri))] // unused in Miri
|
||||||
slice: &[(); usize::MAX], method: take,
|
macro_rules! empty_max_mut {
|
||||||
(take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]),
|
@@ -2485,6 +2486,7 @@ take_tests! {
|
||||||
@@ -2345,3 +2347,4 @@ take_tests! {
|
|
||||||
(take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
|
(take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
|
||||||
(take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
|
(take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
|
||||||
}
|
}
|
||||||
+*/
|
+*/
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_slice_from_ptr_range() {
|
||||||
--
|
--
|
||||||
2.26.2.7.g19db9cfb68
|
2.26.2.7.g19db9cfb68
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#!/bin/bash --verbose
|
#!/bin/bash --verbose
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
rustup component add rust-src rustc-dev llvm-tools-preview
|
|
||||||
./build_sysroot/prepare_sysroot_src.sh
|
./build_sysroot/prepare_sysroot_src.sh
|
||||||
|
@ -1 +1,3 @@
|
|||||||
nightly-2021-12-30
|
[toolchain]
|
||||||
|
channel = "nightly-2022-03-26"
|
||||||
|
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use gccjit::{ToRValue, Type};
|
use gccjit::{ToLValue, ToRValue, Type};
|
||||||
use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
|
use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
|
||||||
|
use rustc_data_structures::stable_set::FxHashSet;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
|
use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
|
||||||
@ -15,9 +16,21 @@ impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_param(&mut self, index: usize) -> Self::Value {
|
fn get_param(&mut self, index: usize) -> Self::Value {
|
||||||
self.cx.current_func.borrow().expect("current func")
|
let func = self.current_func();
|
||||||
.get_param(index as i32)
|
let param = func.get_param(index as i32);
|
||||||
.to_rvalue()
|
let on_stack =
|
||||||
|
if let Some(on_stack_param_indices) = self.on_stack_function_params.borrow().get(&func) {
|
||||||
|
on_stack_param_indices.contains(&index)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if on_stack {
|
||||||
|
param.to_lvalue().get_address(None)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
param.to_rvalue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +100,13 @@ impl GccType for Reg {
|
|||||||
|
|
||||||
pub trait FnAbiGccExt<'gcc, 'tcx> {
|
pub trait FnAbiGccExt<'gcc, 'tcx> {
|
||||||
// TODO(antoyo): return a function pointer type instead?
|
// TODO(antoyo): return a function pointer type instead?
|
||||||
fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool);
|
fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool, FxHashSet<usize>);
|
||||||
fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
|
fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
||||||
fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool) {
|
fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool, FxHashSet<usize>) {
|
||||||
|
let mut on_stack_param_indices = FxHashSet::default();
|
||||||
let args_capacity: usize = self.args.iter().map(|arg|
|
let args_capacity: usize = self.args.iter().map(|arg|
|
||||||
if arg.pad.is_some() {
|
if arg.pad.is_some() {
|
||||||
1
|
1
|
||||||
@ -144,17 +158,22 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
PassMode::Cast(cast) => cast.gcc_type(cx),
|
PassMode::Cast(cast) => cast.gcc_type(cx),
|
||||||
PassMode::Indirect { extra_attrs: None, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
|
PassMode::Indirect { extra_attrs: None, on_stack: true, .. } => {
|
||||||
|
on_stack_param_indices.insert(argument_tys.len());
|
||||||
|
arg.memory_ty(cx)
|
||||||
|
},
|
||||||
|
PassMode::Indirect { extra_attrs: None, on_stack: false, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
|
||||||
};
|
};
|
||||||
argument_tys.push(arg_ty);
|
argument_tys.push(arg_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
(return_ty, argument_tys, self.c_variadic)
|
(return_ty, argument_tys, self.c_variadic, on_stack_param_indices)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
|
fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
|
||||||
let (return_type, params, variadic) = self.gcc_type(cx);
|
let (return_type, params, variadic, on_stack_param_indices) = self.gcc_type(cx);
|
||||||
let pointer_type = cx.context.new_function_pointer_type(None, return_type, ¶ms, variadic);
|
let pointer_type = cx.context.new_function_pointer_type(None, return_type, ¶ms, variadic);
|
||||||
|
cx.on_stack_params.borrow_mut().insert(pointer_type.dyncast_function_ptr_type().expect("function ptr type"), on_stack_param_indices);
|
||||||
pointer_type
|
pointer_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
|
|||||||
if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") {
|
if env::var("CG_GCCJIT_DUMP_MODULE_NAMES").as_deref() == Ok("1") {
|
||||||
println!("Module {}", module.name);
|
println!("Module {}", module.name);
|
||||||
}
|
}
|
||||||
if env::var("CG_GCCJIT_DUMP_MODULE").as_deref() == Ok(&module.name) {
|
if env::var("CG_GCCJIT_DUMP_ALL_MODULES").as_deref() == Ok("1") || env::var("CG_GCCJIT_DUMP_MODULE").as_deref() == Ok(&module.name) {
|
||||||
println!("Dumping reproducer {}", module.name);
|
println!("Dumping reproducer {}", module.name);
|
||||||
let _ = fs::create_dir("/tmp/reproducers");
|
let _ = fs::create_dir("/tmp/reproducers");
|
||||||
// FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
|
// FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
|
||||||
@ -54,6 +54,11 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
|
|||||||
context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name));
|
context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name));
|
||||||
println!("Dumped reproducer {}", module.name);
|
println!("Dumped reproducer {}", module.name);
|
||||||
}
|
}
|
||||||
|
if env::var("CG_GCCJIT_DUMP_TO_FILE").as_deref() == Ok("1") {
|
||||||
|
let _ = fs::create_dir("/tmp/gccjit_dumps");
|
||||||
|
let path = &format!("/tmp/gccjit_dumps/{}.c", module.name);
|
||||||
|
context.dump_to_file(path, true);
|
||||||
|
}
|
||||||
context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
|
context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<GccContext>, u64) {
|
pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_128bit_integers: bool) -> (ModuleCodegen<GccContext>, u64) {
|
||||||
let prof_timer = tcx.prof.generic_activity("codegen_module");
|
let prof_timer = tcx.prof.generic_activity("codegen_module");
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul
|
|||||||
let (module, _) = tcx.dep_graph.with_task(
|
let (module, _) = tcx.dep_graph.with_task(
|
||||||
dep_node,
|
dep_node,
|
||||||
tcx,
|
tcx,
|
||||||
cgu_name,
|
(cgu_name, supports_128bit_integers),
|
||||||
module_codegen,
|
module_codegen,
|
||||||
Some(dep_graph::hash_result),
|
Some(dep_graph::hash_result),
|
||||||
);
|
);
|
||||||
@ -71,7 +71,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul
|
|||||||
// the time we needed for codegenning it.
|
// the time we needed for codegenning it.
|
||||||
let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
|
let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
|
||||||
|
|
||||||
fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<GccContext> {
|
fn module_codegen(tcx: TyCtxt<'_>, (cgu_name, supports_128bit_integers): (Symbol, bool)) -> ModuleCodegen<GccContext> {
|
||||||
let cgu = tcx.codegen_unit(cgu_name);
|
let cgu = tcx.codegen_unit(cgu_name);
|
||||||
// Instantiate monomorphizations without filling out definitions yet...
|
// Instantiate monomorphizations without filling out definitions yet...
|
||||||
//let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
|
//let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
|
||||||
@ -85,6 +85,12 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul
|
|||||||
context.add_command_line_option("-fno-semantic-interposition");
|
context.add_command_line_option("-fno-semantic-interposition");
|
||||||
// NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292).
|
// NOTE: Rust relies on LLVM not doing TBAA (https://github.com/rust-lang/unsafe-code-guidelines/issues/292).
|
||||||
context.add_command_line_option("-fno-strict-aliasing");
|
context.add_command_line_option("-fno-strict-aliasing");
|
||||||
|
|
||||||
|
if tcx.sess.opts.debugging_opts.function_sections.unwrap_or(tcx.sess.target.function_sections) {
|
||||||
|
context.add_command_line_option("-ffunction-sections");
|
||||||
|
context.add_command_line_option("-fdata-sections");
|
||||||
|
}
|
||||||
|
|
||||||
if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") {
|
if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") {
|
||||||
context.set_dump_code_on_compile(true);
|
context.set_dump_code_on_compile(true);
|
||||||
}
|
}
|
||||||
@ -99,8 +105,11 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul
|
|||||||
context.set_keep_intermediates(true);
|
context.set_keep_intermediates(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(bjorn3): Remove once unwinding is properly implemented
|
||||||
|
context.set_allow_unreachable_blocks(true);
|
||||||
|
|
||||||
{
|
{
|
||||||
let cx = CodegenCx::new(&context, cgu, tcx);
|
let cx = CodegenCx::new(&context, cgu, tcx, supports_128bit_integers);
|
||||||
|
|
||||||
let mono_items = cgu.items_in_deterministic_order(tcx);
|
let mono_items = cgu.items_in_deterministic_order(tcx);
|
||||||
for &(mono_item, (linkage, visibility)) in &mono_items {
|
for &(mono_item, (linkage, visibility)) in &mono_items {
|
||||||
|
@ -30,6 +30,7 @@ use rustc_codegen_ssa::traits::{
|
|||||||
OverflowOp,
|
OverflowOp,
|
||||||
StaticBuilderMethods,
|
StaticBuilderMethods,
|
||||||
};
|
};
|
||||||
|
use rustc_data_structures::stable_set::FxHashSet;
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -80,21 +81,21 @@ impl EnumClone for AtomicOrdering {
|
|||||||
|
|
||||||
pub struct Builder<'a: 'gcc, 'gcc, 'tcx> {
|
pub struct Builder<'a: 'gcc, 'gcc, 'tcx> {
|
||||||
pub cx: &'a CodegenCx<'gcc, 'tcx>,
|
pub cx: &'a CodegenCx<'gcc, 'tcx>,
|
||||||
pub block: Option<Block<'gcc>>,
|
pub block: Block<'gcc>,
|
||||||
stack_var_count: Cell<usize>,
|
stack_var_count: Cell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||||
fn with_cx(cx: &'a CodegenCx<'gcc, 'tcx>) -> Self {
|
fn with_cx(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
|
||||||
Builder {
|
Builder {
|
||||||
cx,
|
cx,
|
||||||
block: None,
|
block,
|
||||||
stack_var_count: Cell::new(0),
|
stack_var_count: Cell::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atomic_extremum(&mut self, operation: ExtremumOperation, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
|
fn atomic_extremum(&mut self, operation: ExtremumOperation, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
|
||||||
let size = self.cx.int_width(src.get_type()) / 8;
|
let size = src.get_type().get_size();
|
||||||
|
|
||||||
let func = self.current_func();
|
let func = self.current_func();
|
||||||
|
|
||||||
@ -114,10 +115,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
let after_block = func.new_block("after_while");
|
let after_block = func.new_block("after_while");
|
||||||
self.llbb().end_with_jump(None, while_block);
|
self.llbb().end_with_jump(None, while_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added and compare_exchange doesn't expect this, the current blocks in the
|
// NOTE: since jumps were added and compare_exchange doesn't expect this, the current block in the
|
||||||
// state need to be updated.
|
// state need to be updated.
|
||||||
self.block = Some(while_block);
|
self.switch_to_block(while_block);
|
||||||
*self.cx.current_block.borrow_mut() = Some(while_block);
|
|
||||||
|
|
||||||
let comparison_operator =
|
let comparison_operator =
|
||||||
match operation {
|
match operation {
|
||||||
@ -132,17 +132,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
while_block.end_with_conditional(None, cond, while_block, after_block);
|
while_block.end_with_conditional(None, cond, while_block, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
||||||
// state need to be updated.
|
// state need to be updated.
|
||||||
self.block = Some(after_block);
|
self.switch_to_block(after_block);
|
||||||
*self.cx.current_block.borrow_mut() = Some(after_block);
|
|
||||||
|
|
||||||
return_value.to_rvalue()
|
return_value.to_rvalue()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
|
fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
|
||||||
let size = self.cx.int_width(src.get_type());
|
let size = src.get_type().get_size();
|
||||||
let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size / 8));
|
let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size));
|
||||||
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
|
let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
|
||||||
let failure_order = self.context.new_rvalue_from_int(self.i32_type, failure_order.to_gcc());
|
let failure_order = self.context.new_rvalue_from_int(self.i32_type, failure_order.to_gcc());
|
||||||
let weak = self.context.new_rvalue_from_int(self.bool_type, weak as i32);
|
let weak = self.context.new_rvalue_from_int(self.bool_type, weak as i32);
|
||||||
@ -209,6 +208,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
param_types.push(param);
|
param_types.push(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut on_stack_param_indices = FxHashSet::default();
|
||||||
|
if let Some(indices) = self.on_stack_params.borrow().get(&gcc_func) {
|
||||||
|
on_stack_param_indices = indices.clone();
|
||||||
|
}
|
||||||
|
|
||||||
if all_args_match {
|
if all_args_match {
|
||||||
return Cow::Borrowed(args);
|
return Cow::Borrowed(args);
|
||||||
}
|
}
|
||||||
@ -217,10 +221,15 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(args.iter())
|
.zip(args.iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(_i, (expected_ty, &actual_val))| {
|
.map(|(index, (expected_ty, &actual_val))| {
|
||||||
let actual_ty = actual_val.get_type();
|
let actual_ty = actual_val.get_type();
|
||||||
if expected_ty != actual_ty {
|
if expected_ty != actual_ty {
|
||||||
self.bitcast(actual_val, expected_ty)
|
if on_stack_param_indices.contains(&index) {
|
||||||
|
actual_val.dereference(None).to_rvalue()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.bitcast(actual_val, expected_ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
actual_val
|
actual_val
|
||||||
@ -245,7 +254,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_func(&self) -> Function<'gcc> {
|
pub fn current_func(&self) -> Function<'gcc> {
|
||||||
self.block.expect("block").get_function()
|
self.block.get_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
||||||
@ -256,17 +265,16 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
// gccjit requires to use the result of functions, even when it's not used.
|
// gccjit requires to use the result of functions, even when it's not used.
|
||||||
// That's why we assign the result to a local or call add_eval().
|
// That's why we assign the result to a local or call add_eval().
|
||||||
let return_type = func.get_return_type();
|
let return_type = func.get_return_type();
|
||||||
let current_block = self.current_block.borrow().expect("block");
|
|
||||||
let void_type = self.context.new_type::<()>();
|
let void_type = self.context.new_type::<()>();
|
||||||
let current_func = current_block.get_function();
|
let current_func = self.block.get_function();
|
||||||
if return_type != void_type {
|
if return_type != void_type {
|
||||||
unsafe { RETURN_VALUE_COUNT += 1 };
|
unsafe { RETURN_VALUE_COUNT += 1 };
|
||||||
let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
||||||
current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
|
self.block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
|
||||||
result.to_rvalue()
|
result.to_rvalue()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
current_block.add_eval(None, self.cx.context.new_call(None, func, &args));
|
self.block.add_eval(None, self.cx.context.new_call(None, func, &args));
|
||||||
// Return dummy value when not having return value.
|
// Return dummy value when not having return value.
|
||||||
self.context.new_rvalue_from_long(self.isize_type, 0)
|
self.context.new_rvalue_from_long(self.isize_type, 0)
|
||||||
}
|
}
|
||||||
@ -279,9 +287,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
// That's why we assign the result to a local or call add_eval().
|
// That's why we assign the result to a local or call add_eval().
|
||||||
let gcc_func = func_ptr.get_type().dyncast_function_ptr_type().expect("function ptr");
|
let gcc_func = func_ptr.get_type().dyncast_function_ptr_type().expect("function ptr");
|
||||||
let mut return_type = gcc_func.get_return_type();
|
let mut return_type = gcc_func.get_return_type();
|
||||||
let current_block = self.current_block.borrow().expect("block");
|
|
||||||
let void_type = self.context.new_type::<()>();
|
let void_type = self.context.new_type::<()>();
|
||||||
let current_func = current_block.get_function();
|
let current_func = self.block.get_function();
|
||||||
|
|
||||||
// FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
|
// FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
|
||||||
if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" {
|
if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" {
|
||||||
@ -290,35 +297,34 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
if return_type != void_type {
|
if return_type != void_type {
|
||||||
unsafe { RETURN_VALUE_COUNT += 1 };
|
unsafe { RETURN_VALUE_COUNT += 1 };
|
||||||
let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
let result = current_func.new_local(None, return_type, &format!("ptrReturnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
||||||
current_block.add_assignment(None, result, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
|
self.block.add_assignment(None, result, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
|
||||||
result.to_rvalue()
|
result.to_rvalue()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if gcc_func.get_param_count() == 0 {
|
if gcc_func.get_param_count() == 0 {
|
||||||
// FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
|
// FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
|
||||||
current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[]));
|
self.block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[]));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
|
self.block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
|
||||||
}
|
}
|
||||||
// Return dummy value when not having return value.
|
// Return dummy value when not having return value.
|
||||||
let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed");
|
let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed");
|
||||||
current_block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0));
|
self.block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0));
|
||||||
result.to_rvalue()
|
result.to_rvalue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overflow_call(&mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
pub fn overflow_call(&self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
||||||
// gccjit requires to use the result of functions, even when it's not used.
|
// gccjit requires to use the result of functions, even when it's not used.
|
||||||
// That's why we assign the result to a local.
|
// That's why we assign the result to a local.
|
||||||
let return_type = self.context.new_type::<bool>();
|
let return_type = self.context.new_type::<bool>();
|
||||||
let current_block = self.current_block.borrow().expect("block");
|
let current_func = self.block.get_function();
|
||||||
let current_func = current_block.get_function();
|
|
||||||
// TODO(antoyo): return the new_call() directly? Since the overflow function has no side-effects.
|
// TODO(antoyo): return the new_call() directly? Since the overflow function has no side-effects.
|
||||||
unsafe { RETURN_VALUE_COUNT += 1 };
|
unsafe { RETURN_VALUE_COUNT += 1 };
|
||||||
let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
let result = current_func.new_local(None, return_type, &format!("overflowReturnValue{}", unsafe { RETURN_VALUE_COUNT }));
|
||||||
current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
|
self.block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
|
||||||
result.to_rvalue()
|
result.to_rvalue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,14 +390,11 @@ impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
|
fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
|
||||||
let mut bx = Builder::with_cx(cx);
|
Builder::with_cx(cx, block)
|
||||||
*cx.current_block.borrow_mut() = Some(block);
|
|
||||||
bx.block = Some(block);
|
|
||||||
bx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn llbb(&self) -> Block<'gcc> {
|
fn llbb(&self) -> Block<'gcc> {
|
||||||
self.block.expect("block")
|
self.block
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_block(cx: &'a CodegenCx<'gcc, 'tcx>, func: RValue<'gcc>, name: &str) -> Block<'gcc> {
|
fn append_block(cx: &'a CodegenCx<'gcc, 'tcx>, func: RValue<'gcc>, name: &str) -> Block<'gcc> {
|
||||||
@ -405,8 +408,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn switch_to_block(&mut self, block: Self::BasicBlock) {
|
fn switch_to_block(&mut self, block: Self::BasicBlock) {
|
||||||
*self.cx.current_block.borrow_mut() = Some(block);
|
self.block = block;
|
||||||
self.block = Some(block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret_void(&mut self) {
|
fn ret_void(&mut self) {
|
||||||
@ -441,50 +443,42 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
let on_val = self.const_uint_big(typ, on_val);
|
let on_val = self.const_uint_big(typ, on_val);
|
||||||
gcc_cases.push(self.context.new_case(on_val, on_val, dest));
|
gcc_cases.push(self.context.new_case(on_val, on_val, dest));
|
||||||
}
|
}
|
||||||
self.block.expect("block").end_with_switch(None, value, default_block, &gcc_cases);
|
self.block.end_with_switch(None, value, default_block, &gcc_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invoke(&mut self, _typ: Type<'gcc>, _func: RValue<'gcc>, _args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
fn invoke(&mut self, typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
||||||
let condition = self.context.new_rvalue_from_int(self.bool_type, 0);
|
// TODO(bjorn3): Properly implement unwinding.
|
||||||
|
let call_site = self.call(typ, func, args, None);
|
||||||
|
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
|
||||||
self.llbb().end_with_conditional(None, condition, then, catch);
|
self.llbb().end_with_conditional(None, condition, then, catch);
|
||||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
call_site
|
||||||
|
|
||||||
// TODO(antoyo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unreachable(&mut self) {
|
fn unreachable(&mut self) {
|
||||||
let func = self.context.get_builtin_function("__builtin_unreachable");
|
let func = self.context.get_builtin_function("__builtin_unreachable");
|
||||||
let block = self.block.expect("block");
|
self.block.add_eval(None, self.context.new_call(None, func, &[]));
|
||||||
block.add_eval(None, self.context.new_call(None, func, &[]));
|
let return_type = self.block.get_function().get_return_type();
|
||||||
let return_type = block.get_function().get_return_type();
|
|
||||||
let void_type = self.context.new_type::<()>();
|
let void_type = self.context.new_type::<()>();
|
||||||
if return_type == void_type {
|
if return_type == void_type {
|
||||||
block.end_with_void_return(None)
|
self.block.end_with_void_return(None)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let return_value = self.current_func()
|
let return_value = self.current_func()
|
||||||
.new_local(None, return_type, "unreachableReturn");
|
.new_local(None, return_type, "unreachableReturn");
|
||||||
block.end_with_return(None, return_value)
|
self.block.end_with_return(None, return_value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
fn add(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// FIXME(antoyo): this should not be required.
|
self.gcc_add(a, b)
|
||||||
if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) {
|
|
||||||
b = self.context.new_cast(None, b, a.get_type());
|
|
||||||
}
|
|
||||||
a + b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a + b
|
a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sub(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
fn sub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
if a.get_type() != b.get_type() {
|
self.gcc_sub(a, b)
|
||||||
b = self.context.new_cast(None, b, a.get_type());
|
|
||||||
}
|
|
||||||
a - b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -492,7 +486,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a * b
|
self.gcc_mul(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -500,8 +494,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): convert the arguments to unsigned?
|
self.gcc_udiv(a, b)
|
||||||
a / b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -511,8 +504,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): convert the arguments to signed?
|
self.gcc_sdiv(a, b)
|
||||||
a / b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -529,11 +521,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a % b
|
self.gcc_urem(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a % b
|
self.gcc_srem(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -549,81 +541,33 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
|
self.gcc_shl(a, b)
|
||||||
let a_type = a.get_type();
|
|
||||||
let b_type = b.get_type();
|
|
||||||
if a_type.is_unsigned(self) && b_type.is_signed(self) {
|
|
||||||
let a = self.context.new_cast(None, a, b_type);
|
|
||||||
let result = a << b;
|
|
||||||
self.context.new_cast(None, result, a_type)
|
|
||||||
}
|
|
||||||
else if a_type.is_signed(self) && b_type.is_unsigned(self) {
|
|
||||||
let b = self.context.new_cast(None, b, a_type);
|
|
||||||
a << b
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
a << b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
|
self.gcc_lshr(a, b)
|
||||||
// TODO(antoyo): cast to unsigned to do a logical shift if that does not work.
|
|
||||||
let a_type = a.get_type();
|
|
||||||
let b_type = b.get_type();
|
|
||||||
if a_type.is_unsigned(self) && b_type.is_signed(self) {
|
|
||||||
let a = self.context.new_cast(None, a, b_type);
|
|
||||||
let result = a >> b;
|
|
||||||
self.context.new_cast(None, result, a_type)
|
|
||||||
}
|
|
||||||
else if a_type.is_signed(self) && b_type.is_unsigned(self) {
|
|
||||||
let b = self.context.new_cast(None, b, a_type);
|
|
||||||
a >> b
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
a >> b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): check whether behavior is an arithmetic shift for >> .
|
// TODO(antoyo): check whether behavior is an arithmetic shift for >> .
|
||||||
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
|
// It seems to be if the value is signed.
|
||||||
let a_type = a.get_type();
|
self.gcc_lshr(a, b)
|
||||||
let b_type = b.get_type();
|
|
||||||
if a_type.is_unsigned(self) && b_type.is_signed(self) {
|
|
||||||
let a = self.context.new_cast(None, a, b_type);
|
|
||||||
let result = a >> b;
|
|
||||||
self.context.new_cast(None, result, a_type)
|
|
||||||
}
|
|
||||||
else if a_type.is_signed(self) && b_type.is_unsigned(self) {
|
|
||||||
let b = self.context.new_cast(None, b, a_type);
|
|
||||||
a >> b
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
a >> b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn and(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
fn and(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
if a.get_type() != b.get_type() {
|
self.gcc_and(a, b)
|
||||||
b = self.context.new_cast(None, b, a.get_type());
|
|
||||||
}
|
|
||||||
a & b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn or(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
if a.get_type() != b.get_type() {
|
self.cx.gcc_or(a, b)
|
||||||
b = self.context.new_cast(None, b, a.get_type());
|
|
||||||
}
|
|
||||||
a | b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a ^ b
|
self.gcc_xor(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
|
self.gcc_neg(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -631,14 +575,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
let operation =
|
self.gcc_not(a)
|
||||||
if a.get_type().is_bool() {
|
|
||||||
UnaryOp::LogicalNegate
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
UnaryOp::BitwiseNegate
|
|
||||||
};
|
|
||||||
self.cx.context.new_unary_op(None, operation, a.get_type(), a)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -646,7 +583,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
a + b
|
self.gcc_add(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -655,7 +592,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): should generate poison value?
|
// TODO(antoyo): should generate poison value?
|
||||||
a - b
|
self.gcc_sub(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -687,76 +624,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
|
fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
|
||||||
use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
|
self.gcc_checked_binop(oop, typ, lhs, rhs)
|
||||||
|
|
||||||
let new_kind =
|
|
||||||
match typ.kind() {
|
|
||||||
Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
|
|
||||||
Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
|
|
||||||
t @ (Uint(_) | Int(_)) => t.clone(),
|
|
||||||
_ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO(antoyo): remove duplication with intrinsic?
|
|
||||||
let name =
|
|
||||||
match oop {
|
|
||||||
OverflowOp::Add =>
|
|
||||||
match new_kind {
|
|
||||||
Int(I8) => "__builtin_add_overflow",
|
|
||||||
Int(I16) => "__builtin_add_overflow",
|
|
||||||
Int(I32) => "__builtin_sadd_overflow",
|
|
||||||
Int(I64) => "__builtin_saddll_overflow",
|
|
||||||
Int(I128) => "__builtin_add_overflow",
|
|
||||||
|
|
||||||
Uint(U8) => "__builtin_add_overflow",
|
|
||||||
Uint(U16) => "__builtin_add_overflow",
|
|
||||||
Uint(U32) => "__builtin_uadd_overflow",
|
|
||||||
Uint(U64) => "__builtin_uaddll_overflow",
|
|
||||||
Uint(U128) => "__builtin_add_overflow",
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
OverflowOp::Sub =>
|
|
||||||
match new_kind {
|
|
||||||
Int(I8) => "__builtin_sub_overflow",
|
|
||||||
Int(I16) => "__builtin_sub_overflow",
|
|
||||||
Int(I32) => "__builtin_ssub_overflow",
|
|
||||||
Int(I64) => "__builtin_ssubll_overflow",
|
|
||||||
Int(I128) => "__builtin_sub_overflow",
|
|
||||||
|
|
||||||
Uint(U8) => "__builtin_sub_overflow",
|
|
||||||
Uint(U16) => "__builtin_sub_overflow",
|
|
||||||
Uint(U32) => "__builtin_usub_overflow",
|
|
||||||
Uint(U64) => "__builtin_usubll_overflow",
|
|
||||||
Uint(U128) => "__builtin_sub_overflow",
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
OverflowOp::Mul =>
|
|
||||||
match new_kind {
|
|
||||||
Int(I8) => "__builtin_mul_overflow",
|
|
||||||
Int(I16) => "__builtin_mul_overflow",
|
|
||||||
Int(I32) => "__builtin_smul_overflow",
|
|
||||||
Int(I64) => "__builtin_smulll_overflow",
|
|
||||||
Int(I128) => "__builtin_mul_overflow",
|
|
||||||
|
|
||||||
Uint(U8) => "__builtin_mul_overflow",
|
|
||||||
Uint(U16) => "__builtin_mul_overflow",
|
|
||||||
Uint(U32) => "__builtin_umul_overflow",
|
|
||||||
Uint(U64) => "__builtin_umulll_overflow",
|
|
||||||
Uint(U128) => "__builtin_mul_overflow",
|
|
||||||
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let intrinsic = self.context.get_builtin_function(&name);
|
|
||||||
let res = self.current_func()
|
|
||||||
// TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
|
|
||||||
.new_local(None, rhs.get_type(), "binopResult")
|
|
||||||
.get_address(None);
|
|
||||||
let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
|
|
||||||
(res.dereference(None).to_rvalue(), overflow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> {
|
fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> {
|
||||||
@ -1006,7 +874,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
/* Casts */
|
/* Casts */
|
||||||
fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): check that it indeed truncate the value.
|
// TODO(antoyo): check that it indeed truncate the value.
|
||||||
self.context.new_cast(None, value, dest_ty)
|
self.gcc_int_cast(value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1019,19 +887,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.context.new_cast(None, value, dest_ty)
|
self.gcc_float_to_uint_cast(value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.context.new_cast(None, value, dest_ty)
|
self.gcc_float_to_int_cast(value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.context.new_cast(None, value, dest_ty)
|
self.gcc_uint_to_float_cast(value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.context.new_cast(None, value, dest_ty)
|
self.gcc_int_to_float_cast(value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1044,11 +912,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.cx.ptrtoint(self.block.expect("block"), value, dest_ty)
|
let usize_value = self.cx.const_bitcast(value, self.cx.type_isize());
|
||||||
|
self.intcast(usize_value, dest_ty, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
self.cx.inttoptr(self.block.expect("block"), value, dest_ty)
|
let usize_value = self.intcast(value, self.cx.type_isize(), false);
|
||||||
|
self.cx.const_bitcast(usize_value, dest_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1057,7 +927,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> {
|
fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> {
|
||||||
// NOTE: is_signed is for value, not dest_typ.
|
// NOTE: is_signed is for value, not dest_typ.
|
||||||
self.cx.context.new_cast(None, value, dest_typ)
|
self.gcc_int_cast(value, dest_typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1078,21 +948,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Comparisons */
|
/* Comparisons */
|
||||||
fn icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
|
fn icmp(&mut self, op: IntPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
let left_type = lhs.get_type();
|
self.gcc_icmp(op, lhs, rhs)
|
||||||
let right_type = rhs.get_type();
|
|
||||||
if left_type != right_type {
|
|
||||||
// NOTE: because libgccjit cannot compare function pointers.
|
|
||||||
if left_type.dyncast_function_ptr_type().is_some() && right_type.dyncast_function_ptr_type().is_some() {
|
|
||||||
lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
|
|
||||||
rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
|
|
||||||
}
|
|
||||||
// NOTE: hack because we try to cast a vector type to the same vector type.
|
|
||||||
else if format!("{:?}", left_type) != format!("{:?}", right_type) {
|
|
||||||
rhs = self.context.new_cast(None, rhs, left_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
|
fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1100,22 +957,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Miscellaneous instructions */
|
/* Miscellaneous instructions */
|
||||||
fn memcpy(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
|
fn memcpy(&mut self, dst: RValue<'gcc>, _dst_align: Align, src: RValue<'gcc>, _src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
|
||||||
if flags.contains(MemFlags::NONTEMPORAL) {
|
assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported");
|
||||||
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
|
|
||||||
let val = self.load(src.get_type(), src, src_align);
|
|
||||||
let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val)));
|
|
||||||
self.store_with_flags(val, ptr, dst_align, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let size = self.intcast(size, self.type_size_t(), false);
|
let size = self.intcast(size, self.type_size_t(), false);
|
||||||
let _is_volatile = flags.contains(MemFlags::VOLATILE);
|
let _is_volatile = flags.contains(MemFlags::VOLATILE);
|
||||||
let dst = self.pointercast(dst, self.type_i8p());
|
let dst = self.pointercast(dst, self.type_i8p());
|
||||||
let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
|
let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
|
||||||
let memcpy = self.context.get_builtin_function("memcpy");
|
let memcpy = self.context.get_builtin_function("memcpy");
|
||||||
let block = self.block.expect("block");
|
|
||||||
// TODO(antoyo): handle aligns and is_volatile.
|
// TODO(antoyo): handle aligns and is_volatile.
|
||||||
block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size]));
|
self.block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memmove(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
|
fn memmove(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
|
||||||
@ -1132,20 +982,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
|
let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
|
||||||
|
|
||||||
let memmove = self.context.get_builtin_function("memmove");
|
let memmove = self.context.get_builtin_function("memmove");
|
||||||
let block = self.block.expect("block");
|
|
||||||
// TODO(antoyo): handle is_volatile.
|
// TODO(antoyo): handle is_volatile.
|
||||||
block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size]));
|
self.block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memset(&mut self, ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, _align: Align, flags: MemFlags) {
|
fn memset(&mut self, ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, _align: Align, flags: MemFlags) {
|
||||||
let _is_volatile = flags.contains(MemFlags::VOLATILE);
|
let _is_volatile = flags.contains(MemFlags::VOLATILE);
|
||||||
let ptr = self.pointercast(ptr, self.type_i8p());
|
let ptr = self.pointercast(ptr, self.type_i8p());
|
||||||
let memset = self.context.get_builtin_function("memset");
|
let memset = self.context.get_builtin_function("memset");
|
||||||
let block = self.block.expect("block");
|
|
||||||
// TODO(antoyo): handle align and is_volatile.
|
// TODO(antoyo): handle align and is_volatile.
|
||||||
let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type);
|
let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type);
|
||||||
let size = self.intcast(size, self.type_size_t(), false);
|
let size = self.intcast(size, self.type_size_t(), false);
|
||||||
block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size]));
|
self.block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(&mut self, cond: RValue<'gcc>, then_val: RValue<'gcc>, mut else_val: RValue<'gcc>) -> RValue<'gcc> {
|
fn select(&mut self, cond: RValue<'gcc>, then_val: RValue<'gcc>, mut else_val: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
@ -1159,16 +1007,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
then_block.add_assignment(None, variable, then_val);
|
then_block.add_assignment(None, variable, then_val);
|
||||||
then_block.end_with_jump(None, after_block);
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
if then_val.get_type() != else_val.get_type() {
|
if !then_val.get_type().is_compatible_with(else_val.get_type()) {
|
||||||
else_val = self.context.new_cast(None, else_val, then_val.get_type());
|
else_val = self.context.new_cast(None, else_val, then_val.get_type());
|
||||||
}
|
}
|
||||||
else_block.add_assignment(None, variable, else_val);
|
else_block.add_assignment(None, variable, else_val);
|
||||||
else_block.end_with_jump(None, after_block);
|
else_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
||||||
// state need to be updated.
|
// state need to be updated.
|
||||||
self.block = Some(after_block);
|
self.switch_to_block(after_block);
|
||||||
*self.cx.current_block.borrow_mut() = Some(after_block);
|
|
||||||
|
|
||||||
variable.to_rvalue()
|
variable.to_rvalue()
|
||||||
}
|
}
|
||||||
@ -1264,7 +1111,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup_landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>) -> RValue<'gcc> {
|
fn cleanup_landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
let field1 = self.context.new_field(None, self.u8_type, "landing_pad_field_1");
|
let field1 = self.context.new_field(None, self.u8_type.make_pointer(), "landing_pad_field_1");
|
||||||
let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1");
|
let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1");
|
||||||
let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]);
|
let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]);
|
||||||
self.current_func().new_local(None, struct_type.as_type(), "landing_pad")
|
self.current_func().new_local(None, struct_type.as_type(), "landing_pad")
|
||||||
@ -1275,7 +1122,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&mut self, _exn: RValue<'gcc>) {
|
fn resume(&mut self, _exn: RValue<'gcc>) {
|
||||||
unimplemented!();
|
// TODO(bjorn3): Properly implement unwinding.
|
||||||
|
self.unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup_pad(&mut self, _parent: Option<RValue<'gcc>>, _args: &[RValue<'gcc>]) -> Funclet {
|
fn cleanup_pad(&mut self, _parent: Option<RValue<'gcc>>, _args: &[RValue<'gcc>]) -> Funclet {
|
||||||
@ -1322,7 +1170,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn atomic_rmw(&mut self, op: AtomicRmwBinOp, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
|
fn atomic_rmw(&mut self, op: AtomicRmwBinOp, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
|
||||||
let size = self.cx.int_width(src.get_type()) / 8;
|
let size = src.get_type().get_size();
|
||||||
let name =
|
let name =
|
||||||
match op {
|
match op {
|
||||||
AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size),
|
AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size),
|
||||||
@ -1396,7 +1244,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
// Fix the code in codegen_ssa::base::from_immediate.
|
// Fix the code in codegen_ssa::base::from_immediate.
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
self.context.new_cast(None, value, dest_typ)
|
self.gcc_int_cast(value, dest_typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cx(&self) -> &CodegenCx<'gcc, 'tcx> {
|
fn cx(&self) -> &CodegenCx<'gcc, 'tcx> {
|
||||||
@ -1404,7 +1252,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn do_not_inline(&mut self, _llret: RValue<'gcc>) {
|
fn do_not_inline(&mut self, _llret: RValue<'gcc>) {
|
||||||
unimplemented!();
|
// FIMXE(bjorn3): implement
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_span(&mut self, _span: Span) {}
|
fn set_span(&mut self, _span: Span) {}
|
||||||
@ -1470,7 +1318,7 @@ impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ToGccComp {
|
pub trait ToGccComp {
|
||||||
fn to_gcc_comparison(&self) -> ComparisonOp;
|
fn to_gcc_comparison(&self) -> ComparisonOp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use gccjit::LValue;
|
use gccjit::LValue;
|
||||||
use gccjit::{Block, CType, RValue, Type, ToRValue};
|
use gccjit::{RValue, Type, ToRValue};
|
||||||
use rustc_codegen_ssa::mir::place::PlaceRef;
|
use rustc_codegen_ssa::mir::place::PlaceRef;
|
||||||
use rustc_codegen_ssa::traits::{
|
use rustc_codegen_ssa::traits::{
|
||||||
BaseTypeMethods,
|
BaseTypeMethods,
|
||||||
@ -35,27 +33,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
global
|
global
|
||||||
// TODO(antoyo): set linkage.
|
// TODO(antoyo): set linkage.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inttoptr(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
|
||||||
let func = block.get_function();
|
|
||||||
let local = func.new_local(None, value.get_type(), "intLocal");
|
|
||||||
block.add_assignment(None, local, value);
|
|
||||||
let value_address = local.get_address(None);
|
|
||||||
|
|
||||||
let ptr = self.context.new_cast(None, value_address, dest_ty.make_pointer());
|
|
||||||
ptr.dereference(None).to_rvalue()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptrtoint(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
|
||||||
// TODO(antoyo): when libgccjit allow casting from pointer to int, remove this.
|
|
||||||
let func = block.get_function();
|
|
||||||
let local = func.new_local(None, value.get_type(), "ptrLocal");
|
|
||||||
block.add_assignment(None, local, value);
|
|
||||||
let ptr_address = local.get_address(None);
|
|
||||||
|
|
||||||
let ptr = self.context.new_cast(None, ptr_address, dest_ty.make_pointer());
|
|
||||||
ptr.dereference(None).to_rvalue()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
|
pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
|
||||||
@ -99,29 +76,15 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
|
fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
|
||||||
self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
|
self.gcc_int(typ, int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
|
fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
|
||||||
self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
|
self.gcc_uint(typ, int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
|
fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
|
||||||
if num >> 64 != 0 {
|
self.gcc_uint_big(typ, num)
|
||||||
// FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()?
|
|
||||||
let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
|
|
||||||
let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64);
|
|
||||||
|
|
||||||
let sixty_four = self.context.new_rvalue_from_long(typ, 64);
|
|
||||||
(high << sixty_four) | self.context.new_cast(None, low, typ)
|
|
||||||
}
|
|
||||||
else if typ.is_i128(self) {
|
|
||||||
let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
|
|
||||||
self.context.new_cast(None, num, typ)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.context.new_rvalue_from_long(typ, num as u64 as i64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn const_bool(&self, val: bool) -> RValue<'gcc> {
|
fn const_bool(&self, val: bool) -> RValue<'gcc> {
|
||||||
@ -210,11 +173,8 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let value = self.const_uint_big(self.type_ix(bitsize), data);
|
let value = self.const_uint_big(self.type_ix(bitsize), data);
|
||||||
if layout.value == Pointer {
|
// TODO(bjorn3): assert size is correct
|
||||||
self.inttoptr(self.current_block.borrow().expect("block"), value, ty)
|
self.const_bitcast(value, ty)
|
||||||
} else {
|
|
||||||
self.const_bitcast(value, ty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Scalar::Ptr(ptr, _size) => {
|
Scalar::Ptr(ptr, _size) => {
|
||||||
let (alloc_id, offset) = ptr.into_parts();
|
let (alloc_id, offset) = ptr.into_parts();
|
||||||
@ -418,11 +378,11 @@ impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
||||||
self.unqualified() == cx.context.new_c_type(CType::Int128t)
|
self.unqualified() == cx.i128_type.unqualified()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
||||||
self.unqualified() == cx.context.new_c_type(CType::UInt128t)
|
self.unqualified() == cx.u128_type.unqualified()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use gccjit::{LValue, RValue, ToRValue, Type};
|
use gccjit::{GlobalKind, LValue, RValue, ToRValue, Type};
|
||||||
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
|
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::Node;
|
use rustc_hir::Node;
|
||||||
@ -35,7 +35,12 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
|
|||||||
// following:
|
// following:
|
||||||
for (value, variable) in &*self.const_globals.borrow() {
|
for (value, variable) in &*self.const_globals.borrow() {
|
||||||
if format!("{:?}", value) == format!("{:?}", cv) {
|
if format!("{:?}", value) == format!("{:?}", cv) {
|
||||||
// TODO(antoyo): upgrade alignment.
|
if let Some(global_variable) = self.global_lvalues.borrow().get(variable) {
|
||||||
|
let alignment = align.bits() as i32;
|
||||||
|
if alignment > global_variable.get_alignment() {
|
||||||
|
global_variable.set_alignment(alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
return *variable;
|
return *variable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,11 +170,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
match kind {
|
match kind {
|
||||||
Some(kind) if !self.tcx.sess.fewer_names() => {
|
Some(kind) if !self.tcx.sess.fewer_names() => {
|
||||||
let name = self.generate_local_symbol_name(kind);
|
let name = self.generate_local_symbol_name(kind);
|
||||||
// TODO(antoyo): check if it's okay that TLS is off here.
|
// TODO(antoyo): check if it's okay that no link_section is set.
|
||||||
// TODO(antoyo): check if it's okay that link_section is None here.
|
|
||||||
// TODO(antoyo): set alignment here as well.
|
// TODO(antoyo): set alignment here as well.
|
||||||
let global = self.define_global(&name[..], self.val_ty(cv), false, None);
|
let global = self.declare_private_global(&name[..], self.val_ty(cv));
|
||||||
// TODO(antoyo): set linkage.
|
|
||||||
global
|
global
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -178,11 +181,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
global
|
global
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// FIXME(antoyo): I think the name coming from generate_local_symbol_name() above cannot be used
|
|
||||||
// globally.
|
|
||||||
global.global_set_initializer_rvalue(cv);
|
global.global_set_initializer_rvalue(cv);
|
||||||
// TODO(antoyo): set unnamed address.
|
// TODO(antoyo): set unnamed address.
|
||||||
global.get_address(None)
|
let rvalue = global.get_address(None);
|
||||||
|
self.global_lvalues.borrow_mut().insert(rvalue, global);
|
||||||
|
rvalue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> {
|
pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> {
|
||||||
@ -218,7 +221,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
|
let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
|
||||||
let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section);
|
let global = self.declare_global(
|
||||||
|
&sym,
|
||||||
|
llty,
|
||||||
|
GlobalKind::Exported,
|
||||||
|
is_tls,
|
||||||
|
fn_attrs.link_section,
|
||||||
|
);
|
||||||
|
|
||||||
if !self.tcx.is_reachable_non_generic(def_id) {
|
if !self.tcx.is_reachable_non_generic(def_id) {
|
||||||
// TODO(antoyo): set visibility.
|
// TODO(antoyo): set visibility.
|
||||||
@ -390,6 +399,6 @@ fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &Codeg
|
|||||||
// don't do this then linker errors can be generated where the linker
|
// don't do this then linker errors can be generated where the linker
|
||||||
// complains that one object files has a thread local version of the
|
// complains that one object files has a thread local version of the
|
||||||
// symbol and another one doesn't.
|
// symbol and another one doesn't.
|
||||||
cx.declare_global(&sym, llty, is_tls, attrs.link_section)
|
cx.declare_global(&sym, llty, GlobalKind::Imported, is_tls, attrs.link_section)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
use gccjit::{Block, CType, Context, Function, FunctionType, LValue, RValue, Struct, Type};
|
use gccjit::{Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, RValue, Struct, Type};
|
||||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||||
use rustc_codegen_ssa::traits::{
|
use rustc_codegen_ssa::traits::{
|
||||||
BackendTypes,
|
BackendTypes,
|
||||||
@ -18,7 +18,6 @@ use rustc_target::abi::{call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDat
|
|||||||
use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
|
use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
|
||||||
|
|
||||||
use crate::callee::get_fn;
|
use crate::callee::get_fn;
|
||||||
use crate::declare::mangle_name;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FuncSig<'gcc> {
|
pub struct FuncSig<'gcc> {
|
||||||
@ -31,8 +30,7 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||||||
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
|
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
|
||||||
pub context: &'gcc Context<'gcc>,
|
pub context: &'gcc Context<'gcc>,
|
||||||
|
|
||||||
// TODO(antoyo): First set it to a dummy block to avoid using Option?
|
// TODO(bjorn3): Can this field be removed?
|
||||||
pub current_block: RefCell<Option<Block<'gcc>>>,
|
|
||||||
pub current_func: RefCell<Option<Function<'gcc>>>,
|
pub current_func: RefCell<Option<Function<'gcc>>>,
|
||||||
pub normal_function_addresses: RefCell<FxHashSet<RValue<'gcc>>>,
|
pub normal_function_addresses: RefCell<FxHashSet<RValue<'gcc>>>,
|
||||||
|
|
||||||
@ -62,6 +60,8 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||||||
pub ulonglong_type: Type<'gcc>,
|
pub ulonglong_type: Type<'gcc>,
|
||||||
pub sizet_type: Type<'gcc>,
|
pub sizet_type: Type<'gcc>,
|
||||||
|
|
||||||
|
pub supports_128bit_integers: bool,
|
||||||
|
|
||||||
pub float_type: Type<'gcc>,
|
pub float_type: Type<'gcc>,
|
||||||
pub double_type: Type<'gcc>,
|
pub double_type: Type<'gcc>,
|
||||||
|
|
||||||
@ -81,9 +81,19 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||||||
/// Cache generated vtables
|
/// Cache generated vtables
|
||||||
pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
|
pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
|
||||||
|
|
||||||
|
// TODO(antoyo): improve the SSA API to not require those.
|
||||||
|
// Mapping from function pointer type to indexes of on stack parameters.
|
||||||
|
pub on_stack_params: RefCell<FxHashMap<FunctionPtrType<'gcc>, FxHashSet<usize>>>,
|
||||||
|
// Mapping from function to indexes of on stack parameters.
|
||||||
|
pub on_stack_function_params: RefCell<FxHashMap<Function<'gcc>, FxHashSet<usize>>>,
|
||||||
|
|
||||||
/// Cache of emitted const globals (value -> global)
|
/// Cache of emitted const globals (value -> global)
|
||||||
pub const_globals: RefCell<FxHashMap<RValue<'gcc>, RValue<'gcc>>>,
|
pub const_globals: RefCell<FxHashMap<RValue<'gcc>, RValue<'gcc>>>,
|
||||||
|
|
||||||
|
/// Map from the address of a global variable (rvalue) to the global variable itself (lvalue).
|
||||||
|
/// TODO(antoyo): remove when the rustc API is fixed.
|
||||||
|
pub global_lvalues: RefCell<FxHashMap<RValue<'gcc>, LValue<'gcc>>>,
|
||||||
|
|
||||||
/// Cache of constant strings,
|
/// Cache of constant strings,
|
||||||
pub const_str_cache: RefCell<FxHashMap<Symbol, LValue<'gcc>>>,
|
pub const_str_cache: RefCell<FxHashMap<Symbol, LValue<'gcc>>>,
|
||||||
|
|
||||||
@ -92,7 +102,6 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||||||
|
|
||||||
/// A counter that is used for generating local symbol names
|
/// A counter that is used for generating local symbol names
|
||||||
local_gen_sym_counter: Cell<usize>,
|
local_gen_sym_counter: Cell<usize>,
|
||||||
pub global_gen_sym_counter: Cell<usize>,
|
|
||||||
|
|
||||||
eh_personality: Cell<Option<RValue<'gcc>>>,
|
eh_personality: Cell<Option<RValue<'gcc>>>,
|
||||||
|
|
||||||
@ -107,22 +116,29 @@ pub struct CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||||
pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>, supports_128bit_integers: bool) -> Self {
|
||||||
let check_overflow = tcx.sess.overflow_checks();
|
let check_overflow = tcx.sess.overflow_checks();
|
||||||
// TODO(antoyo): fix this mess. libgccjit seems to return random type when using new_int_type().
|
|
||||||
let isize_type = context.new_c_type(CType::LongLong);
|
let i8_type = context.new_c_type(CType::Int8t);
|
||||||
let usize_type = context.new_c_type(CType::ULongLong);
|
let i16_type = context.new_c_type(CType::Int16t);
|
||||||
let bool_type = context.new_type::<bool>();
|
let i32_type = context.new_c_type(CType::Int32t);
|
||||||
let i8_type = context.new_type::<i8>();
|
let i64_type = context.new_c_type(CType::Int64t);
|
||||||
let i16_type = context.new_type::<i16>();
|
let u8_type = context.new_c_type(CType::UInt8t);
|
||||||
let i32_type = context.new_type::<i32>();
|
let u16_type = context.new_c_type(CType::UInt16t);
|
||||||
let i64_type = context.new_c_type(CType::LongLong);
|
let u32_type = context.new_c_type(CType::UInt32t);
|
||||||
let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
|
let u64_type = context.new_c_type(CType::UInt64t);
|
||||||
let u8_type = context.new_type::<u8>();
|
|
||||||
let u16_type = context.new_type::<u16>();
|
let (i128_type, u128_type) =
|
||||||
let u32_type = context.new_type::<u32>();
|
if supports_128bit_integers {
|
||||||
let u64_type = context.new_c_type(CType::ULongLong);
|
let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?;
|
||||||
let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
|
let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?;
|
||||||
|
(i128_type, u128_type)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let i128_type = context.new_array_type(None, i64_type, 2);
|
||||||
|
let u128_type = context.new_array_type(None, u64_type, 2);
|
||||||
|
(i128_type, u128_type)
|
||||||
|
};
|
||||||
|
|
||||||
let tls_model = to_gcc_tls_mode(tcx.sess.tls_model());
|
let tls_model = to_gcc_tls_mode(tcx.sess.tls_model());
|
||||||
|
|
||||||
@ -136,8 +152,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
let ulonglong_type = context.new_c_type(CType::ULongLong);
|
let ulonglong_type = context.new_c_type(CType::ULongLong);
|
||||||
let sizet_type = context.new_c_type(CType::SizeT);
|
let sizet_type = context.new_c_type(CType::SizeT);
|
||||||
|
|
||||||
assert_eq!(isize_type, i64_type);
|
let isize_type = context.new_c_type(CType::LongLong);
|
||||||
assert_eq!(usize_type, u64_type);
|
let usize_type = context.new_c_type(CType::ULongLong);
|
||||||
|
let bool_type = context.new_type::<bool>();
|
||||||
|
|
||||||
|
// TODO(antoyo): only have those assertions on x86_64.
|
||||||
|
assert_eq!(isize_type.get_size(), i64_type.get_size());
|
||||||
|
assert_eq!(usize_type.get_size(), u64_type.get_size());
|
||||||
|
|
||||||
let mut functions = FxHashMap::default();
|
let mut functions = FxHashMap::default();
|
||||||
let builtins = [
|
let builtins = [
|
||||||
@ -160,7 +181,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
check_overflow,
|
check_overflow,
|
||||||
codegen_unit,
|
codegen_unit,
|
||||||
context,
|
context,
|
||||||
current_block: RefCell::new(None),
|
|
||||||
current_func: RefCell::new(None),
|
current_func: RefCell::new(None),
|
||||||
normal_function_addresses: Default::default(),
|
normal_function_addresses: Default::default(),
|
||||||
functions: RefCell::new(functions),
|
functions: RefCell::new(functions),
|
||||||
@ -187,14 +207,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
ulonglong_type,
|
ulonglong_type,
|
||||||
sizet_type,
|
sizet_type,
|
||||||
|
|
||||||
|
supports_128bit_integers,
|
||||||
|
|
||||||
float_type,
|
float_type,
|
||||||
double_type,
|
double_type,
|
||||||
|
|
||||||
linkage: Cell::new(FunctionType::Internal),
|
linkage: Cell::new(FunctionType::Internal),
|
||||||
instances: Default::default(),
|
instances: Default::default(),
|
||||||
function_instances: Default::default(),
|
function_instances: Default::default(),
|
||||||
|
on_stack_params: Default::default(),
|
||||||
|
on_stack_function_params: Default::default(),
|
||||||
vtables: Default::default(),
|
vtables: Default::default(),
|
||||||
const_globals: Default::default(),
|
const_globals: Default::default(),
|
||||||
|
global_lvalues: Default::default(),
|
||||||
const_str_cache: Default::default(),
|
const_str_cache: Default::default(),
|
||||||
globals: Default::default(),
|
globals: Default::default(),
|
||||||
scalar_types: Default::default(),
|
scalar_types: Default::default(),
|
||||||
@ -203,7 +228,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
struct_types: Default::default(),
|
struct_types: Default::default(),
|
||||||
types_with_fields_to_set: Default::default(),
|
types_with_fields_to_set: Default::default(),
|
||||||
local_gen_sym_counter: Cell::new(0),
|
local_gen_sym_counter: Cell::new(0),
|
||||||
global_gen_sym_counter: Cell::new(0),
|
|
||||||
eh_personality: Cell::new(None),
|
eh_personality: Cell::new(None),
|
||||||
pointee_infos: Default::default(),
|
pointee_infos: Default::default(),
|
||||||
structs_as_pointer: Default::default(),
|
structs_as_pointer: Default::default(),
|
||||||
@ -217,6 +241,41 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
function
|
function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_native_int_type(&self, typ: Type<'gcc>) -> bool {
|
||||||
|
let types = [
|
||||||
|
self.u8_type,
|
||||||
|
self.u16_type,
|
||||||
|
self.u32_type,
|
||||||
|
self.u64_type,
|
||||||
|
self.i8_type,
|
||||||
|
self.i16_type,
|
||||||
|
self.i32_type,
|
||||||
|
self.i64_type,
|
||||||
|
];
|
||||||
|
|
||||||
|
for native_type in types {
|
||||||
|
if native_type.is_compatible_with(typ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.supports_128bit_integers &&
|
||||||
|
(self.u128_type.is_compatible_with(typ) || self.i128_type.is_compatible_with(typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_non_native_int_type(&self, typ: Type<'gcc>) -> bool {
|
||||||
|
!self.supports_128bit_integers &&
|
||||||
|
(self.u128_type.is_compatible_with(typ) || self.i128_type.is_compatible_with(typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_native_int_type_or_bool(&self, typ: Type<'gcc>) -> bool {
|
||||||
|
self.is_native_int_type(typ) || typ == self.bool_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_int_type_or_bool(&self, typ: Type<'gcc>) -> bool {
|
||||||
|
self.is_native_int_type(typ) || self.is_non_native_int_type(typ) || typ == self.bool_type
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sess(&self) -> &Session {
|
pub fn sess(&self) -> &Session {
|
||||||
&self.tcx.sess
|
&self.tcx.sess
|
||||||
}
|
}
|
||||||
@ -450,11 +509,6 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit_name<'tcx>(codegen_unit: &CodegenUnit<'tcx>) -> String {
|
|
||||||
let name = &codegen_unit.name().to_string();
|
|
||||||
mangle_name(&name.replace('-', "_"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
|
fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
|
||||||
match tls_model {
|
match tls_model {
|
||||||
TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic,
|
TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic,
|
||||||
|
@ -5,7 +5,7 @@ use rustc_span::Symbol;
|
|||||||
use rustc_target::abi::call::FnAbi;
|
use rustc_target::abi::call::FnAbi;
|
||||||
|
|
||||||
use crate::abi::FnAbiGccExt;
|
use crate::abi::FnAbiGccExt;
|
||||||
use crate::context::{CodegenCx, unit_name};
|
use crate::context::CodegenCx;
|
||||||
use crate::intrinsic::llvm;
|
use crate::intrinsic::llvm;
|
||||||
|
|
||||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||||
@ -22,15 +22,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
global
|
global
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
self.declare_global(name, ty, is_tls, link_section)
|
self.declare_global(name, ty, GlobalKind::Exported, is_tls, link_section)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> {
|
pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> {
|
||||||
let index = self.global_gen_sym_counter.get();
|
let name = self.generate_local_symbol_name("global");
|
||||||
self.global_gen_sym_counter.set(index + 1);
|
self.context.new_global(None, GlobalKind::Internal, ty, &name)
|
||||||
let name = format!("global_{}_{}", index, unit_name(&self.codegen_unit));
|
|
||||||
self.context.new_global(None, GlobalKind::Exported, ty, &name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> LValue<'gcc> {
|
pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> LValue<'gcc> {
|
||||||
@ -47,8 +45,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
unsafe { std::mem::transmute(func) }
|
unsafe { std::mem::transmute(func) }
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub fn declare_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
|
pub fn declare_global(&self, name: &str, ty: Type<'gcc>, global_kind: GlobalKind, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
|
||||||
let global = self.context.new_global(None, GlobalKind::Exported, ty, name);
|
let global = self.context.new_global(None, global_kind, ty, name);
|
||||||
if is_tls {
|
if is_tls {
|
||||||
global.set_tls_model(self.tls_model);
|
global.set_tls_model(self.tls_model);
|
||||||
}
|
}
|
||||||
@ -82,8 +80,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> RValue<'gcc> {
|
pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> RValue<'gcc> {
|
||||||
let (return_type, params, variadic) = fn_abi.gcc_type(self);
|
let (return_type, params, variadic, on_stack_param_indices) = fn_abi.gcc_type(self);
|
||||||
let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, ¶ms, variadic);
|
let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, ¶ms, variadic);
|
||||||
|
self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices);
|
||||||
// FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
|
// FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
|
||||||
unsafe { std::mem::transmute(func) }
|
unsafe { std::mem::transmute(func) }
|
||||||
}
|
}
|
||||||
|
730
compiler/rustc_codegen_gcc/src/int.rs
Normal file
730
compiler/rustc_codegen_gcc/src/int.rs
Normal file
@ -0,0 +1,730 @@
|
|||||||
|
//! Module to handle integer operations.
|
||||||
|
//! This module exists because some integer types are not supported on some gcc platforms, e.g.
|
||||||
|
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
use gccjit::{ComparisonOp, FunctionType, RValue, ToRValue, Type, UnaryOp, BinaryOp};
|
||||||
|
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||||
|
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp};
|
||||||
|
use rustc_middle::ty::Ty;
|
||||||
|
|
||||||
|
use crate::builder::ToGccComp;
|
||||||
|
use crate::{builder::Builder, common::{SignType, TypeReflection}, context::CodegenCx};
|
||||||
|
|
||||||
|
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||||
|
pub fn gcc_urem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
// 128-bit unsigned %: __umodti3
|
||||||
|
self.multiplicative_operation(BinaryOp::Modulo, "mod", false, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_srem(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
// 128-bit signed %: __modti3
|
||||||
|
self.multiplicative_operation(BinaryOp::Modulo, "mod", true, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_not(&self, a: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let typ = a.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(typ) {
|
||||||
|
let operation =
|
||||||
|
if typ.is_bool() {
|
||||||
|
UnaryOp::LogicalNegate
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UnaryOp::BitwiseNegate
|
||||||
|
};
|
||||||
|
self.cx.context.new_unary_op(None, operation, typ, a)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO(antoyo): use __negdi2 and __negti2 instead?
|
||||||
|
let element_type = typ.dyncast_array().expect("element type");
|
||||||
|
let values = [
|
||||||
|
self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.low(a)),
|
||||||
|
self.cx.context.new_unary_op(None, UnaryOp::BitwiseNegate, element_type, self.high(a)),
|
||||||
|
];
|
||||||
|
self.cx.context.new_array_constructor(None, typ, &values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_neg(&self, a: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
if self.is_native_int_type(a_type) {
|
||||||
|
self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a], "__negti2", false);
|
||||||
|
self.context.new_call(None, func, &[a])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_and(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.cx.bitwise_operation(BinaryOp::BitwiseAnd, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
let a_native = self.is_native_int_type(a_type);
|
||||||
|
let b_native = self.is_native_int_type(b_type);
|
||||||
|
if a_native && b_native {
|
||||||
|
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by a signed number.
|
||||||
|
// TODO(antoyo): cast to unsigned to do a logical shift if that does not work.
|
||||||
|
if a_type.is_signed(self) != b_type.is_signed(self) {
|
||||||
|
let b = self.context.new_cast(None, b, a_type);
|
||||||
|
a >> b
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
a >> b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if a_native && !b_native {
|
||||||
|
self.gcc_lshr(a, self.gcc_int_cast(b, a_type))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// NOTE: we cannot use the lshr builtin because it's calling hi() (to get the most
|
||||||
|
// significant half of the number) which uses lshr.
|
||||||
|
|
||||||
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
||||||
|
|
||||||
|
let func = self.current_func();
|
||||||
|
let then_block = func.new_block("then");
|
||||||
|
let else_block = func.new_block("else");
|
||||||
|
let after_block = func.new_block("after");
|
||||||
|
let b0_block = func.new_block("b0");
|
||||||
|
let actual_else_block = func.new_block("actual_else");
|
||||||
|
|
||||||
|
let result = func.new_local(None, a_type, "shiftResult");
|
||||||
|
|
||||||
|
let sixty_four = self.gcc_int(native_int_type, 64);
|
||||||
|
let sixty_three = self.gcc_int(native_int_type, 63);
|
||||||
|
let zero = self.gcc_zero(native_int_type);
|
||||||
|
let b = self.gcc_int_cast(b, native_int_type);
|
||||||
|
let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero);
|
||||||
|
self.llbb().end_with_conditional(None, condition, then_block, else_block);
|
||||||
|
|
||||||
|
// TODO(antoyo): take endianness into account.
|
||||||
|
let shift_value = self.gcc_sub(b, sixty_four);
|
||||||
|
let high = self.high(a);
|
||||||
|
let sign =
|
||||||
|
if a_type.is_signed(self) {
|
||||||
|
high >> sixty_three
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
zero
|
||||||
|
};
|
||||||
|
let values = [
|
||||||
|
high >> shift_value,
|
||||||
|
sign,
|
||||||
|
];
|
||||||
|
let array_value = self.context.new_array_constructor(None, a_type, &values);
|
||||||
|
then_block.add_assignment(None, result, array_value);
|
||||||
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
|
||||||
|
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
|
||||||
|
|
||||||
|
b0_block.add_assignment(None, result, a);
|
||||||
|
b0_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
let shift_value = self.gcc_sub(sixty_four, b);
|
||||||
|
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
|
||||||
|
let unsigned_type = native_int_type.to_unsigned(&self.cx);
|
||||||
|
let casted_low = self.context.new_cast(None, self.low(a), unsigned_type);
|
||||||
|
let shifted_low = casted_low >> self.context.new_cast(None, b, unsigned_type);
|
||||||
|
let shifted_low = self.context.new_cast(None, shifted_low, native_int_type);
|
||||||
|
let values = [
|
||||||
|
(high << shift_value) | shifted_low,
|
||||||
|
high >> b,
|
||||||
|
];
|
||||||
|
let array_value = self.context.new_array_constructor(None, a_type, &values);
|
||||||
|
actual_else_block.add_assignment(None, result, array_value);
|
||||||
|
actual_else_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
||||||
|
// state need to be updated.
|
||||||
|
self.switch_to_block(after_block);
|
||||||
|
|
||||||
|
result.to_rvalue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additive_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
||||||
|
if a.get_type() != b.get_type() {
|
||||||
|
b = self.context.new_cast(None, b, a.get_type());
|
||||||
|
}
|
||||||
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let signed = a_type.is_compatible_with(self.i128_type);
|
||||||
|
let func_name =
|
||||||
|
match (operation, signed) {
|
||||||
|
(BinaryOp::Plus, true) => "__rust_i128_add",
|
||||||
|
(BinaryOp::Plus, false) => "__rust_u128_add",
|
||||||
|
(BinaryOp::Minus, true) => "__rust_i128_sub",
|
||||||
|
(BinaryOp::Minus, false) => "__rust_u128_sub",
|
||||||
|
_ => unreachable!("unexpected additive operation {:?}", operation),
|
||||||
|
};
|
||||||
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
||||||
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false);
|
||||||
|
self.context.new_call(None, func, &[a, b])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_add(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.additive_operation(BinaryOp::Plus, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_mul(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.multiplicative_operation(BinaryOp::Mult, "mul", true, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_sub(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.additive_operation(BinaryOp::Minus, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiplicative_operation(&self, operation: BinaryOp, operation_name: &str, signed: bool, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
||||||
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let sign =
|
||||||
|
if signed {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"u"
|
||||||
|
};
|
||||||
|
let func_name = format!("__{}{}ti3", sign, operation_name);
|
||||||
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
||||||
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, a_type, &[param_a, param_b], func_name, false);
|
||||||
|
self.context.new_call(None, func, &[a, b])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_sdiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
// TODO(antoyo): check if the types are signed?
|
||||||
|
// 128-bit, signed: __divti3
|
||||||
|
// TODO(antoyo): convert the arguments to signed?
|
||||||
|
self.multiplicative_operation(BinaryOp::Divide, "div", true, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_udiv(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
// 128-bit, unsigned: __udivti3
|
||||||
|
self.multiplicative_operation(BinaryOp::Divide, "div", false, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_checked_binop(&self, oop: OverflowOp, typ: Ty<'_>, lhs: <Self as BackendTypes>::Value, rhs: <Self as BackendTypes>::Value) -> (<Self as BackendTypes>::Value, <Self as BackendTypes>::Value) {
|
||||||
|
use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
|
||||||
|
|
||||||
|
let new_kind =
|
||||||
|
match typ.kind() {
|
||||||
|
Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
|
||||||
|
Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
|
||||||
|
t @ (Uint(_) | Int(_)) => t.clone(),
|
||||||
|
_ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO(antoyo): remove duplication with intrinsic?
|
||||||
|
let name =
|
||||||
|
if self.is_native_int_type(lhs.get_type()) {
|
||||||
|
match oop {
|
||||||
|
OverflowOp::Add =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I8) => "__builtin_add_overflow",
|
||||||
|
Int(I16) => "__builtin_add_overflow",
|
||||||
|
Int(I32) => "__builtin_sadd_overflow",
|
||||||
|
Int(I64) => "__builtin_saddll_overflow",
|
||||||
|
Int(I128) => "__builtin_add_overflow",
|
||||||
|
|
||||||
|
Uint(U8) => "__builtin_add_overflow",
|
||||||
|
Uint(U16) => "__builtin_add_overflow",
|
||||||
|
Uint(U32) => "__builtin_uadd_overflow",
|
||||||
|
Uint(U64) => "__builtin_uaddll_overflow",
|
||||||
|
Uint(U128) => "__builtin_add_overflow",
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
OverflowOp::Sub =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I8) => "__builtin_sub_overflow",
|
||||||
|
Int(I16) => "__builtin_sub_overflow",
|
||||||
|
Int(I32) => "__builtin_ssub_overflow",
|
||||||
|
Int(I64) => "__builtin_ssubll_overflow",
|
||||||
|
Int(I128) => "__builtin_sub_overflow",
|
||||||
|
|
||||||
|
Uint(U8) => "__builtin_sub_overflow",
|
||||||
|
Uint(U16) => "__builtin_sub_overflow",
|
||||||
|
Uint(U32) => "__builtin_usub_overflow",
|
||||||
|
Uint(U64) => "__builtin_usubll_overflow",
|
||||||
|
Uint(U128) => "__builtin_sub_overflow",
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
OverflowOp::Mul =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I8) => "__builtin_mul_overflow",
|
||||||
|
Int(I16) => "__builtin_mul_overflow",
|
||||||
|
Int(I32) => "__builtin_smul_overflow",
|
||||||
|
Int(I64) => "__builtin_smulll_overflow",
|
||||||
|
Int(I128) => "__builtin_mul_overflow",
|
||||||
|
|
||||||
|
Uint(U8) => "__builtin_mul_overflow",
|
||||||
|
Uint(U16) => "__builtin_mul_overflow",
|
||||||
|
Uint(U32) => "__builtin_umul_overflow",
|
||||||
|
Uint(U64) => "__builtin_umulll_overflow",
|
||||||
|
Uint(U128) => "__builtin_mul_overflow",
|
||||||
|
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
match new_kind {
|
||||||
|
Int(I128) | Uint(U128) => {
|
||||||
|
let func_name =
|
||||||
|
match oop {
|
||||||
|
OverflowOp::Add =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I128) => "__rust_i128_addo",
|
||||||
|
Uint(U128) => "__rust_u128_addo",
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
OverflowOp::Sub =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I128) => "__rust_i128_subo",
|
||||||
|
Uint(U128) => "__rust_u128_subo",
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
OverflowOp::Mul =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead?
|
||||||
|
Uint(U128) => "__rust_u128_mulo",
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let a_type = lhs.get_type();
|
||||||
|
let b_type = rhs.get_type();
|
||||||
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
||||||
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
||||||
|
let result_field = self.context.new_field(None, a_type, "result");
|
||||||
|
let overflow_field = self.context.new_field(None, self.bool_type, "overflow");
|
||||||
|
let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]);
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
|
||||||
|
let result = self.context.new_call(None, func, &[lhs, rhs]);
|
||||||
|
let overflow = result.access_field(None, overflow_field);
|
||||||
|
let int_result = result.access_field(None, result_field);
|
||||||
|
return (int_result, overflow);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
match oop {
|
||||||
|
OverflowOp::Mul =>
|
||||||
|
match new_kind {
|
||||||
|
Int(I32) => "__mulosi4",
|
||||||
|
Int(I64) => "__mulodi4",
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unimplemented!("overflow operation for {:?}", new_kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let intrinsic = self.context.get_builtin_function(&name);
|
||||||
|
let res = self.current_func()
|
||||||
|
// TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
|
||||||
|
.new_local(None, rhs.get_type(), "binopResult")
|
||||||
|
.get_address(None);
|
||||||
|
let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
|
||||||
|
(res.dereference(None).to_rvalue(), overflow)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_icmp(&self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = lhs.get_type();
|
||||||
|
let b_type = rhs.get_type();
|
||||||
|
if self.is_non_native_int_type(a_type) || self.is_non_native_int_type(b_type) {
|
||||||
|
let signed = a_type.is_compatible_with(self.i128_type);
|
||||||
|
let sign =
|
||||||
|
if signed {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"u"
|
||||||
|
};
|
||||||
|
let func_name = format!("__{}cmpti2", sign);
|
||||||
|
let param_a = self.context.new_parameter(None, a_type, "a");
|
||||||
|
let param_b = self.context.new_parameter(None, b_type, "b");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, self.int_type, &[param_a, param_b], func_name, false);
|
||||||
|
let cmp = self.context.new_call(None, func, &[lhs, rhs]);
|
||||||
|
let (op, limit) =
|
||||||
|
match op {
|
||||||
|
IntPredicate::IntEQ => {
|
||||||
|
return self.context.new_comparison(None, ComparisonOp::Equals, cmp, self.context.new_rvalue_one(self.int_type));
|
||||||
|
},
|
||||||
|
IntPredicate::IntNE => {
|
||||||
|
return self.context.new_comparison(None, ComparisonOp::NotEquals, cmp, self.context.new_rvalue_one(self.int_type));
|
||||||
|
},
|
||||||
|
IntPredicate::IntUGT => (ComparisonOp::Equals, 2),
|
||||||
|
IntPredicate::IntUGE => (ComparisonOp::GreaterThanEquals, 1),
|
||||||
|
IntPredicate::IntULT => (ComparisonOp::Equals, 0),
|
||||||
|
IntPredicate::IntULE => (ComparisonOp::LessThanEquals, 1),
|
||||||
|
IntPredicate::IntSGT => (ComparisonOp::Equals, 2),
|
||||||
|
IntPredicate::IntSGE => (ComparisonOp::GreaterThanEquals, 1),
|
||||||
|
IntPredicate::IntSLT => (ComparisonOp::Equals, 0),
|
||||||
|
IntPredicate::IntSLE => (ComparisonOp::LessThanEquals, 1),
|
||||||
|
};
|
||||||
|
self.context.new_comparison(None, op, cmp, self.context.new_rvalue_from_int(self.int_type, limit))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let left_type = lhs.get_type();
|
||||||
|
let right_type = rhs.get_type();
|
||||||
|
if left_type != right_type {
|
||||||
|
// NOTE: because libgccjit cannot compare function pointers.
|
||||||
|
if left_type.dyncast_function_ptr_type().is_some() && right_type.dyncast_function_ptr_type().is_some() {
|
||||||
|
lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
|
||||||
|
rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
|
||||||
|
}
|
||||||
|
// NOTE: hack because we try to cast a vector type to the same vector type.
|
||||||
|
else if format!("{:?}", left_type) != format!("{:?}", right_type) {
|
||||||
|
rhs = self.context.new_cast(None, rhs, left_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_xor(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(a_type) && self.is_native_int_type_or_bool(b_type) {
|
||||||
|
a ^ b
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let values = [
|
||||||
|
self.low(a) ^ self.low(b),
|
||||||
|
self.high(a) ^ self.high(b),
|
||||||
|
];
|
||||||
|
self.context.new_array_constructor(None, a_type, &values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
let a_native = self.is_native_int_type(a_type);
|
||||||
|
let b_native = self.is_native_int_type(b_type);
|
||||||
|
if a_native && b_native {
|
||||||
|
// FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
|
||||||
|
if a_type.is_unsigned(self) && b_type.is_signed(self) {
|
||||||
|
let a = self.context.new_cast(None, a, b_type);
|
||||||
|
let result = a << b;
|
||||||
|
self.context.new_cast(None, result, a_type)
|
||||||
|
}
|
||||||
|
else if a_type.is_signed(self) && b_type.is_unsigned(self) {
|
||||||
|
let b = self.context.new_cast(None, b, a_type);
|
||||||
|
a << b
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
a << b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if a_native && !b_native {
|
||||||
|
self.gcc_shl(a, self.gcc_int_cast(b, a_type))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// NOTE: we cannot use the ashl builtin because it's calling widen_hi() which uses ashl.
|
||||||
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
||||||
|
|
||||||
|
let func = self.current_func();
|
||||||
|
let then_block = func.new_block("then");
|
||||||
|
let else_block = func.new_block("else");
|
||||||
|
let after_block = func.new_block("after");
|
||||||
|
let b0_block = func.new_block("b0");
|
||||||
|
let actual_else_block = func.new_block("actual_else");
|
||||||
|
|
||||||
|
let result = func.new_local(None, a_type, "shiftResult");
|
||||||
|
|
||||||
|
let b = self.gcc_int_cast(b, native_int_type);
|
||||||
|
let sixty_four = self.gcc_int(native_int_type, 64);
|
||||||
|
let zero = self.gcc_zero(native_int_type);
|
||||||
|
let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero);
|
||||||
|
self.llbb().end_with_conditional(None, condition, then_block, else_block);
|
||||||
|
|
||||||
|
// TODO(antoyo): take endianness into account.
|
||||||
|
let values = [
|
||||||
|
zero,
|
||||||
|
self.low(a) << (b - sixty_four),
|
||||||
|
];
|
||||||
|
let array_value = self.context.new_array_constructor(None, a_type, &values);
|
||||||
|
then_block.add_assignment(None, result, array_value);
|
||||||
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
let condition = self.gcc_icmp(IntPredicate::IntEQ, b, zero);
|
||||||
|
else_block.end_with_conditional(None, condition, b0_block, actual_else_block);
|
||||||
|
|
||||||
|
b0_block.add_assignment(None, result, a);
|
||||||
|
b0_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
// NOTE: cast low to its unsigned type in order to perform a logical right shift.
|
||||||
|
let unsigned_type = native_int_type.to_unsigned(&self.cx);
|
||||||
|
let casted_low = self.context.new_cast(None, self.low(a), unsigned_type);
|
||||||
|
let shift_value = self.context.new_cast(None, sixty_four - b, unsigned_type);
|
||||||
|
let high_low = self.context.new_cast(None, casted_low >> shift_value, native_int_type);
|
||||||
|
let values = [
|
||||||
|
self.low(a) << b,
|
||||||
|
(self.high(a) << b) | high_low,
|
||||||
|
];
|
||||||
|
|
||||||
|
let array_value = self.context.new_array_constructor(None, a_type, &values);
|
||||||
|
actual_else_block.add_assignment(None, result, array_value);
|
||||||
|
actual_else_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
|
// NOTE: since jumps were added in a place rustc does not expect, the current block in the
|
||||||
|
// state need to be updated.
|
||||||
|
self.switch_to_block(after_block);
|
||||||
|
|
||||||
|
result.to_rvalue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_bswap(&mut self, mut arg: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
||||||
|
let arg_type = arg.get_type();
|
||||||
|
if !self.is_native_int_type(arg_type) {
|
||||||
|
let native_int_type = arg_type.dyncast_array().expect("get element type");
|
||||||
|
let lsb = self.context.new_array_access(None, arg, self.context.new_rvalue_from_int(self.int_type, 0)).to_rvalue();
|
||||||
|
let swapped_lsb = self.gcc_bswap(lsb, width / 2);
|
||||||
|
let swapped_lsb = self.context.new_cast(None, swapped_lsb, native_int_type);
|
||||||
|
let msb = self.context.new_array_access(None, arg, self.context.new_rvalue_from_int(self.int_type, 1)).to_rvalue();
|
||||||
|
let swapped_msb = self.gcc_bswap(msb, width / 2);
|
||||||
|
let swapped_msb = self.context.new_cast(None, swapped_msb, native_int_type);
|
||||||
|
|
||||||
|
// NOTE: we also need to swap the two elements here, in addition to swapping inside
|
||||||
|
// the elements themselves like done above.
|
||||||
|
return self.context.new_array_constructor(None, arg_type, &[swapped_msb, swapped_lsb]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(antoyo): check if it's faster to use string literals and a
|
||||||
|
// match instead of format!.
|
||||||
|
let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width));
|
||||||
|
// FIXME(antoyo): this cast should not be necessary. Remove
|
||||||
|
// when having proper sized integer types.
|
||||||
|
let param_type = bswap.get_param(0).to_rvalue().get_type();
|
||||||
|
if param_type != arg_type {
|
||||||
|
arg = self.bitcast(arg, param_type);
|
||||||
|
}
|
||||||
|
self.cx.context.new_call(None, bswap, &[arg])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||||
|
pub fn gcc_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
|
||||||
|
if self.is_native_int_type_or_bool(typ) {
|
||||||
|
self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// NOTE: set the sign in high.
|
||||||
|
self.from_low_high(typ, int, -(int.is_negative() as i64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
|
||||||
|
if self.is_native_int_type_or_bool(typ) {
|
||||||
|
self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.from_low_high(typ, int as i64, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
|
||||||
|
let low = num as u64;
|
||||||
|
let high = (num >> 64) as u64;
|
||||||
|
if num >> 64 != 0 {
|
||||||
|
// FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()?
|
||||||
|
if self.is_native_int_type(typ) {
|
||||||
|
let low = self.context.new_rvalue_from_long(self.u64_type, low as i64);
|
||||||
|
let high = self.context.new_rvalue_from_long(typ, high as i64);
|
||||||
|
|
||||||
|
let sixty_four = self.context.new_rvalue_from_long(typ, 64);
|
||||||
|
let shift = high << sixty_four;
|
||||||
|
shift | self.context.new_cast(None, low, typ)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.from_low_high(typ, low as i64, high as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if typ.is_i128(self) {
|
||||||
|
let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
|
||||||
|
self.gcc_int_cast(num, typ)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.gcc_uint(typ, num as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_zero(&self, typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
if self.is_native_int_type_or_bool(typ) {
|
||||||
|
self.context.new_rvalue_zero(typ)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.from_low_high(typ, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_int_width(&self, typ: Type<'gcc>) -> u64 {
|
||||||
|
if self.is_native_int_type_or_bool(typ) {
|
||||||
|
typ.get_size() as u64 * 8
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// NOTE: the only unsupported types are u128 and i128.
|
||||||
|
128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bitwise_operation(&self, operation: BinaryOp, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
let a_type = a.get_type();
|
||||||
|
let b_type = b.get_type();
|
||||||
|
let a_native = self.is_native_int_type_or_bool(a_type);
|
||||||
|
let b_native = self.is_native_int_type_or_bool(b_type);
|
||||||
|
if a_native && b_native {
|
||||||
|
if a_type != b_type {
|
||||||
|
b = self.context.new_cast(None, b, a_type);
|
||||||
|
}
|
||||||
|
self.context.new_binary_op(None, operation, a_type, a, b)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert!(!a_native && !b_native, "both types should either be native or non-native for or operation");
|
||||||
|
let native_int_type = a_type.dyncast_array().expect("get element type");
|
||||||
|
let values = [
|
||||||
|
self.context.new_binary_op(None, operation, native_int_type, self.low(a), self.low(b)),
|
||||||
|
self.context.new_binary_op(None, operation, native_int_type, self.high(a), self.high(b)),
|
||||||
|
];
|
||||||
|
self.context.new_array_constructor(None, a_type, &values)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_or(&self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.bitwise_operation(BinaryOp::BitwiseOr, a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(antoyo): can we use https://github.com/rust-lang/compiler-builtins/blob/master/src/int/mod.rs#L379 instead?
|
||||||
|
pub fn gcc_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
let value_type = value.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) {
|
||||||
|
self.context.new_cast(None, value, dest_typ)
|
||||||
|
}
|
||||||
|
else if self.is_native_int_type_or_bool(dest_typ) {
|
||||||
|
self.context.new_cast(None, self.low(value), dest_typ)
|
||||||
|
}
|
||||||
|
else if self.is_native_int_type_or_bool(value_type) {
|
||||||
|
let dest_element_type = dest_typ.dyncast_array().expect("get element type");
|
||||||
|
|
||||||
|
// NOTE: set the sign of the value.
|
||||||
|
let zero = self.context.new_rvalue_zero(value_type);
|
||||||
|
let is_negative = self.context.new_comparison(None, ComparisonOp::LessThan, value, zero);
|
||||||
|
let is_negative = self.gcc_int_cast(is_negative, dest_element_type);
|
||||||
|
let values = [
|
||||||
|
self.context.new_cast(None, value, dest_element_type),
|
||||||
|
self.context.new_unary_op(None, UnaryOp::Minus, dest_element_type, is_negative),
|
||||||
|
];
|
||||||
|
self.context.new_array_constructor(None, dest_typ, &values)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Since u128 and i128 are the only types that can be unsupported, we know the type of
|
||||||
|
// value and the destination type have the same size, so a bitcast is fine.
|
||||||
|
self.context.new_bitcast(None, value, dest_typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_to_float_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
let value_type = value.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(value_type) {
|
||||||
|
return self.context.new_cast(None, value, dest_typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_suffix =
|
||||||
|
match self.type_kind(dest_typ) {
|
||||||
|
TypeKind::Float => "tisf",
|
||||||
|
TypeKind::Double => "tidf",
|
||||||
|
kind => panic!("cannot cast a non-native integer to type {:?}", kind),
|
||||||
|
};
|
||||||
|
let sign =
|
||||||
|
if signed {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"un"
|
||||||
|
};
|
||||||
|
let func_name = format!("__float{}{}", sign, name_suffix);
|
||||||
|
let param = self.context.new_parameter(None, value_type, "n");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false);
|
||||||
|
self.context.new_call(None, func, &[value])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_int_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.int_to_float_cast(true, value, dest_typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_uint_to_float_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.int_to_float_cast(false, value, dest_typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn float_to_int_cast(&self, signed: bool, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
let value_type = value.get_type();
|
||||||
|
if self.is_native_int_type_or_bool(dest_typ) {
|
||||||
|
return self.context.new_cast(None, value, dest_typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name_suffix =
|
||||||
|
match self.type_kind(value_type) {
|
||||||
|
TypeKind::Float => "sfti",
|
||||||
|
TypeKind::Double => "dfti",
|
||||||
|
kind => panic!("cannot cast a {:?} to non-native integer", kind),
|
||||||
|
};
|
||||||
|
let sign =
|
||||||
|
if signed {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"uns"
|
||||||
|
};
|
||||||
|
let func_name = format!("__fix{}{}", sign, name_suffix);
|
||||||
|
let param = self.context.new_parameter(None, value_type, "n");
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, dest_typ, &[param], func_name, false);
|
||||||
|
self.context.new_call(None, func, &[value])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_float_to_int_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.float_to_int_cast(true, value, dest_typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gcc_float_to_uint_cast(&self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.float_to_int_cast(false, value, dest_typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high(&self, value: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, 1))
|
||||||
|
.to_rvalue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn low(&self, value: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
|
self.context.new_array_access(None, value, self.context.new_rvalue_from_int(self.int_type, 0))
|
||||||
|
.to_rvalue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_low_high(&self, typ: Type<'gcc>, low: i64, high: i64) -> RValue<'gcc> {
|
||||||
|
let native_int_type = typ.dyncast_array().expect("get element type");
|
||||||
|
let values = [
|
||||||
|
self.context.new_rvalue_from_long(native_int_type, low),
|
||||||
|
self.context.new_rvalue_from_long(native_int_type, high),
|
||||||
|
];
|
||||||
|
self.context.new_array_constructor(None, typ, &values)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
pub mod llvm;
|
pub mod llvm;
|
||||||
mod simd;
|
mod simd;
|
||||||
|
|
||||||
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
|
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp, FunctionType};
|
||||||
use rustc_codegen_ssa::MemFlags;
|
use rustc_codegen_ssa::MemFlags;
|
||||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||||
use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error};
|
use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error};
|
||||||
@ -175,19 +175,18 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
let arg = args[0].immediate();
|
let arg = args[0].immediate();
|
||||||
let result = func.new_local(None, arg.get_type(), "zeros");
|
let result = func.new_local(None, arg.get_type(), "zeros");
|
||||||
let zero = self.cx.context.new_rvalue_zero(arg.get_type());
|
let zero = self.cx.gcc_zero(arg.get_type());
|
||||||
let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero);
|
let cond = self.gcc_icmp(IntPredicate::IntEQ, arg, zero);
|
||||||
self.llbb().end_with_conditional(None, cond, then_block, else_block);
|
self.llbb().end_with_conditional(None, cond, then_block, else_block);
|
||||||
|
|
||||||
let zero_result = self.cx.context.new_rvalue_from_long(arg.get_type(), width as i64);
|
let zero_result = self.cx.gcc_uint(arg.get_type(), width);
|
||||||
then_block.add_assignment(None, result, zero_result);
|
then_block.add_assignment(None, result, zero_result);
|
||||||
then_block.end_with_jump(None, after_block);
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place
|
// NOTE: since jumps were added in a place
|
||||||
// count_leading_zeroes() does not expect, the current blocks
|
// count_leading_zeroes() does not expect, the current block
|
||||||
// in the state need to be updated.
|
// in the state need to be updated.
|
||||||
*self.current_block.borrow_mut() = Some(else_block);
|
self.switch_to_block(else_block);
|
||||||
self.block = Some(else_block);
|
|
||||||
|
|
||||||
let zeros =
|
let zeros =
|
||||||
match name {
|
match name {
|
||||||
@ -195,13 +194,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
sym::cttz => self.count_trailing_zeroes(width, arg),
|
sym::cttz => self.count_trailing_zeroes(width, arg),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
else_block.add_assignment(None, result, zeros);
|
self.llbb().add_assignment(None, result, zeros);
|
||||||
else_block.end_with_jump(None, after_block);
|
self.llbb().end_with_jump(None, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place rustc does not
|
// NOTE: since jumps were added in a place rustc does not
|
||||||
// expect, the current blocks in the state need to be updated.
|
// expect, the current block in the state need to be updated.
|
||||||
*self.current_block.borrow_mut() = Some(after_block);
|
self.switch_to_block(after_block);
|
||||||
self.block = Some(after_block);
|
|
||||||
|
|
||||||
result.to_rvalue()
|
result.to_rvalue()
|
||||||
}
|
}
|
||||||
@ -217,17 +215,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|||||||
args[0].immediate() // byte swap a u8/i8 is just a no-op
|
args[0].immediate() // byte swap a u8/i8 is just a no-op
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO(antoyo): check if it's faster to use string literals and a
|
self.gcc_bswap(args[0].immediate(), width)
|
||||||
// match instead of format!.
|
|
||||||
let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width));
|
|
||||||
let mut arg = args[0].immediate();
|
|
||||||
// FIXME(antoyo): this cast should not be necessary. Remove
|
|
||||||
// when having proper sized integer types.
|
|
||||||
let param_type = bswap.get_param(0).to_rvalue().get_type();
|
|
||||||
if param_type != arg.get_type() {
|
|
||||||
arg = self.bitcast(arg, param_type);
|
|
||||||
}
|
|
||||||
self.cx.context.new_call(None, bswap, &[arg])
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sym::bitreverse => self.bit_reverse(width, args[0].immediate()),
|
sym::bitreverse => self.bit_reverse(width, args[0].immediate()),
|
||||||
@ -476,17 +464,17 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
|
|||||||
val.to_rvalue()
|
val.to_rvalue()
|
||||||
};
|
};
|
||||||
match self.mode {
|
match self.mode {
|
||||||
PassMode::Ignore => {}
|
PassMode::Ignore => {},
|
||||||
PassMode::Pair(..) => {
|
PassMode::Pair(..) => {
|
||||||
OperandValue::Pair(next(), next()).store(bx, dst);
|
OperandValue::Pair(next(), next()).store(bx, dst);
|
||||||
}
|
},
|
||||||
PassMode::Indirect { extra_attrs: Some(_), .. } => {
|
PassMode::Indirect { extra_attrs: Some(_), .. } => {
|
||||||
OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
|
OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
|
||||||
}
|
},
|
||||||
PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(_) => {
|
PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(_) => {
|
||||||
let next_arg = next();
|
let next_arg = next();
|
||||||
self.store(bx, next_arg.to_rvalue(), dst);
|
self.store(bx, next_arg, dst);
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,7 +514,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
let value =
|
let value =
|
||||||
if result_type.is_signed(self.cx) {
|
if result_type.is_signed(self.cx) {
|
||||||
self.context.new_cast(None, value, typ)
|
self.gcc_int_cast(value, typ)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
value
|
value
|
||||||
@ -673,30 +661,33 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
},
|
},
|
||||||
128 => {
|
128 => {
|
||||||
// TODO(antoyo): find a more efficient implementation?
|
// TODO(antoyo): find a more efficient implementation?
|
||||||
let sixty_four = self.context.new_rvalue_from_long(typ, 64);
|
let sixty_four = self.gcc_int(typ, 64);
|
||||||
let high = self.context.new_cast(None, value >> sixty_four, self.u64_type);
|
let right_shift = self.gcc_lshr(value, sixty_four);
|
||||||
let low = self.context.new_cast(None, value, self.u64_type);
|
let high = self.gcc_int_cast(right_shift, self.u64_type);
|
||||||
|
let low = self.gcc_int_cast(value, self.u64_type);
|
||||||
|
|
||||||
let reversed_high = self.bit_reverse(64, high);
|
let reversed_high = self.bit_reverse(64, high);
|
||||||
let reversed_low = self.bit_reverse(64, low);
|
let reversed_low = self.bit_reverse(64, low);
|
||||||
|
|
||||||
let new_low = self.context.new_cast(None, reversed_high, typ);
|
let new_low = self.gcc_int_cast(reversed_high, typ);
|
||||||
let new_high = self.context.new_cast(None, reversed_low, typ) << sixty_four;
|
let new_high = self.shl(self.gcc_int_cast(reversed_low, typ), sixty_four);
|
||||||
|
|
||||||
new_low | new_high
|
self.gcc_or(new_low, new_high)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
panic!("cannot bit reverse with width = {}", width);
|
panic!("cannot bit reverse with width = {}", width);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.context.new_cast(None, result, result_type)
|
self.gcc_int_cast(result, result_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
fn count_leading_zeroes(&mut self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
// TODO(antoyo): use width?
|
// TODO(antoyo): use width?
|
||||||
let arg_type = arg.get_type();
|
let arg_type = arg.get_type();
|
||||||
let count_leading_zeroes =
|
let count_leading_zeroes =
|
||||||
|
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
|
||||||
|
// instead of using is_uint().
|
||||||
if arg_type.is_uint(&self.cx) {
|
if arg_type.is_uint(&self.cx) {
|
||||||
"__builtin_clz"
|
"__builtin_clz"
|
||||||
}
|
}
|
||||||
@ -712,9 +703,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
let result = self.current_func()
|
let result = self.current_func()
|
||||||
.new_local(None, array_type, "count_loading_zeroes_results");
|
.new_local(None, array_type, "count_loading_zeroes_results");
|
||||||
|
|
||||||
let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
|
let sixty_four = self.const_uint(arg_type, 64);
|
||||||
let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
|
let shift = self.lshr(arg, sixty_four);
|
||||||
let low = self.context.new_cast(None, arg, self.u64_type);
|
let high = self.gcc_int_cast(shift, self.u64_type);
|
||||||
|
let low = self.gcc_int_cast(arg, self.u64_type);
|
||||||
|
|
||||||
let zero = self.context.new_rvalue_zero(self.usize_type);
|
let zero = self.context.new_rvalue_zero(self.usize_type);
|
||||||
let one = self.context.new_rvalue_one(self.usize_type);
|
let one = self.context.new_rvalue_one(self.usize_type);
|
||||||
@ -723,17 +715,18 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
let clzll = self.context.get_builtin_function("__builtin_clzll");
|
let clzll = self.context.get_builtin_function("__builtin_clzll");
|
||||||
|
|
||||||
let first_elem = self.context.new_array_access(None, result, zero);
|
let first_elem = self.context.new_array_access(None, result, zero);
|
||||||
let first_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[high]), arg_type);
|
let first_value = self.gcc_int_cast(self.context.new_call(None, clzll, &[high]), arg_type);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, first_elem, first_value);
|
.add_assignment(None, first_elem, first_value);
|
||||||
|
|
||||||
let second_elem = self.context.new_array_access(None, result, one);
|
let second_elem = self.context.new_array_access(None, result, one);
|
||||||
let second_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[low]), arg_type) + sixty_four;
|
let cast = self.gcc_int_cast(self.context.new_call(None, clzll, &[low]), arg_type);
|
||||||
|
let second_value = self.add(cast, sixty_four);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, second_elem, second_value);
|
.add_assignment(None, second_elem, second_value);
|
||||||
|
|
||||||
let third_elem = self.context.new_array_access(None, result, two);
|
let third_elem = self.context.new_array_access(None, result, two);
|
||||||
let third_value = self.context.new_rvalue_from_long(arg_type, 128);
|
let third_value = self.const_uint(arg_type, 128);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, third_elem, third_value);
|
.add_assignment(None, third_elem, third_value);
|
||||||
|
|
||||||
@ -749,13 +742,13 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
let res = self.context.new_array_access(None, result, index);
|
let res = self.context.new_array_access(None, result, index);
|
||||||
|
|
||||||
return self.context.new_cast(None, res, arg_type);
|
return self.gcc_int_cast(res.to_rvalue(), arg_type);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clz");
|
let count_leading_zeroes = self.context.get_builtin_function("__builtin_clzll");
|
||||||
let arg = self.context.new_cast(None, arg, self.uint_type);
|
let arg = self.context.new_cast(None, arg, self.ulonglong_type);
|
||||||
let diff = self.int_width(self.uint_type) - self.int_width(arg_type);
|
let diff = self.ulonglong_type.get_size() as i64 - arg_type.get_size() as i64;
|
||||||
let diff = self.context.new_rvalue_from_long(self.int_type, diff);
|
let diff = self.context.new_rvalue_from_long(self.int_type, diff * 8);
|
||||||
let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff;
|
let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff;
|
||||||
return self.context.new_cast(None, res, arg_type);
|
return self.context.new_cast(None, res, arg_type);
|
||||||
};
|
};
|
||||||
@ -764,18 +757,20 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
self.context.new_cast(None, res, arg_type)
|
self.context.new_cast(None, res, arg_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_trailing_zeroes(&self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
fn count_trailing_zeroes(&mut self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
let result_type = arg.get_type();
|
let result_type = arg.get_type();
|
||||||
let arg =
|
let arg =
|
||||||
if result_type.is_signed(self.cx) {
|
if result_type.is_signed(self.cx) {
|
||||||
let new_type = result_type.to_unsigned(self.cx);
|
let new_type = result_type.to_unsigned(self.cx);
|
||||||
self.context.new_cast(None, arg, new_type)
|
self.gcc_int_cast(arg, new_type)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
arg
|
arg
|
||||||
};
|
};
|
||||||
let arg_type = arg.get_type();
|
let arg_type = arg.get_type();
|
||||||
let (count_trailing_zeroes, expected_type) =
|
let (count_trailing_zeroes, expected_type) =
|
||||||
|
// TODO(antoyo): write a new function Type::is_compatible_with(&Type) and use it here
|
||||||
|
// instead of using is_uint().
|
||||||
if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) {
|
if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) {
|
||||||
// NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
|
// NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
|
||||||
("__builtin_ctz", self.cx.uint_type)
|
("__builtin_ctz", self.cx.uint_type)
|
||||||
@ -792,9 +787,10 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
let result = self.current_func()
|
let result = self.current_func()
|
||||||
.new_local(None, array_type, "count_loading_zeroes_results");
|
.new_local(None, array_type, "count_loading_zeroes_results");
|
||||||
|
|
||||||
let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
|
let sixty_four = self.gcc_int(arg_type, 64);
|
||||||
let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
|
let shift = self.gcc_lshr(arg, sixty_four);
|
||||||
let low = self.context.new_cast(None, arg, self.u64_type);
|
let high = self.gcc_int_cast(shift, self.u64_type);
|
||||||
|
let low = self.gcc_int_cast(arg, self.u64_type);
|
||||||
|
|
||||||
let zero = self.context.new_rvalue_zero(self.usize_type);
|
let zero = self.context.new_rvalue_zero(self.usize_type);
|
||||||
let one = self.context.new_rvalue_one(self.usize_type);
|
let one = self.context.new_rvalue_one(self.usize_type);
|
||||||
@ -803,17 +799,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
let ctzll = self.context.get_builtin_function("__builtin_ctzll");
|
let ctzll = self.context.get_builtin_function("__builtin_ctzll");
|
||||||
|
|
||||||
let first_elem = self.context.new_array_access(None, result, zero);
|
let first_elem = self.context.new_array_access(None, result, zero);
|
||||||
let first_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[low]), arg_type);
|
let first_value = self.gcc_int_cast(self.context.new_call(None, ctzll, &[low]), arg_type);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, first_elem, first_value);
|
.add_assignment(None, first_elem, first_value);
|
||||||
|
|
||||||
let second_elem = self.context.new_array_access(None, result, one);
|
let second_elem = self.context.new_array_access(None, result, one);
|
||||||
let second_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[high]), arg_type) + sixty_four;
|
let second_value = self.gcc_add(self.gcc_int_cast(self.context.new_call(None, ctzll, &[high]), arg_type), sixty_four);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, second_elem, second_value);
|
.add_assignment(None, second_elem, second_value);
|
||||||
|
|
||||||
let third_elem = self.context.new_array_access(None, result, two);
|
let third_elem = self.context.new_array_access(None, result, two);
|
||||||
let third_value = self.context.new_rvalue_from_long(arg_type, 128);
|
let third_value = self.gcc_int(arg_type, 128);
|
||||||
self.llbb()
|
self.llbb()
|
||||||
.add_assignment(None, third_elem, third_value);
|
.add_assignment(None, third_elem, third_value);
|
||||||
|
|
||||||
@ -829,10 +825,20 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
let res = self.context.new_array_access(None, result, index);
|
let res = self.context.new_array_access(None, result, index);
|
||||||
|
|
||||||
return self.context.new_cast(None, res, result_type);
|
return self.gcc_int_cast(res.to_rvalue(), result_type);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unimplemented!("count_trailing_zeroes for {:?}", arg_type);
|
let count_trailing_zeroes = self.context.get_builtin_function("__builtin_ctzll");
|
||||||
|
let arg_size = arg_type.get_size();
|
||||||
|
let casted_arg = self.context.new_cast(None, arg, self.ulonglong_type);
|
||||||
|
let byte_diff = self.ulonglong_type.get_size() as i64 - arg_size as i64;
|
||||||
|
let diff = self.context.new_rvalue_from_long(self.int_type, byte_diff * 8);
|
||||||
|
let mask = self.context.new_rvalue_from_long(arg_type, -1); // To get the value with all bits set.
|
||||||
|
let masked = mask & self.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, arg);
|
||||||
|
let cond = self.context.new_comparison(None, ComparisonOp::Equals, masked, mask);
|
||||||
|
let diff = diff * self.context.new_cast(None, cond, self.int_type);
|
||||||
|
let res = self.context.new_call(None, count_trailing_zeroes, &[casted_arg]) - diff;
|
||||||
|
return self.context.new_cast(None, res, result_type);
|
||||||
};
|
};
|
||||||
let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
|
let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
|
||||||
let arg =
|
let arg =
|
||||||
@ -846,18 +852,14 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
self.context.new_cast(None, res, result_type)
|
self.context.new_cast(None, res, result_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_width(&self, typ: Type<'gcc>) -> i64 {
|
fn pop_count(&mut self, value: RValue<'gcc>) -> RValue<'gcc> {
|
||||||
self.cx.int_width(typ) as i64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_count(&self, value: RValue<'gcc>) -> RValue<'gcc> {
|
|
||||||
// TODO(antoyo): use the optimized version with fewer operations.
|
// TODO(antoyo): use the optimized version with fewer operations.
|
||||||
let result_type = value.get_type();
|
let result_type = value.get_type();
|
||||||
let value_type = result_type.to_unsigned(self.cx);
|
let value_type = result_type.to_unsigned(self.cx);
|
||||||
|
|
||||||
let value =
|
let value =
|
||||||
if result_type.is_signed(self.cx) {
|
if result_type.is_signed(self.cx) {
|
||||||
self.context.new_cast(None, value, value_type)
|
self.gcc_int_cast(value, value_type)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
value
|
value
|
||||||
@ -867,13 +869,14 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
// TODO(antoyo): implement in the normal algorithm below to have a more efficient
|
// TODO(antoyo): implement in the normal algorithm below to have a more efficient
|
||||||
// implementation (that does not require a call to __popcountdi2).
|
// implementation (that does not require a call to __popcountdi2).
|
||||||
let popcount = self.context.get_builtin_function("__builtin_popcountll");
|
let popcount = self.context.get_builtin_function("__builtin_popcountll");
|
||||||
let sixty_four = self.context.new_rvalue_from_long(value_type, 64);
|
let sixty_four = self.gcc_int(value_type, 64);
|
||||||
let high = self.context.new_cast(None, value >> sixty_four, self.cx.ulonglong_type);
|
let right_shift = self.gcc_lshr(value, sixty_four);
|
||||||
|
let high = self.gcc_int_cast(right_shift, self.cx.ulonglong_type);
|
||||||
let high = self.context.new_call(None, popcount, &[high]);
|
let high = self.context.new_call(None, popcount, &[high]);
|
||||||
let low = self.context.new_cast(None, value, self.cx.ulonglong_type);
|
let low = self.gcc_int_cast(value, self.cx.ulonglong_type);
|
||||||
let low = self.context.new_call(None, popcount, &[low]);
|
let low = self.context.new_call(None, popcount, &[low]);
|
||||||
let res = high + low;
|
let res = high + low;
|
||||||
return self.context.new_cast(None, res, result_type);
|
return self.gcc_int_cast(res, result_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First step.
|
// First step.
|
||||||
@ -935,13 +938,14 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
// Algorithm from: https://blog.regehr.org/archives/1063
|
// Algorithm from: https://blog.regehr.org/archives/1063
|
||||||
fn rotate_left(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
fn rotate_left(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
||||||
let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
|
let max = self.const_uint(shift.get_type(), width);
|
||||||
let shift = shift % max;
|
let shift = self.urem(shift, max);
|
||||||
let lhs = self.shl(value, shift);
|
let lhs = self.shl(value, shift);
|
||||||
|
let result_neg = self.neg(shift);
|
||||||
let result_and =
|
let result_and =
|
||||||
self.and(
|
self.and(
|
||||||
self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
|
result_neg,
|
||||||
self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
|
self.const_uint(shift.get_type(), width - 1),
|
||||||
);
|
);
|
||||||
let rhs = self.lshr(value, result_and);
|
let rhs = self.lshr(value, result_and);
|
||||||
self.or(lhs, rhs)
|
self.or(lhs, rhs)
|
||||||
@ -949,13 +953,14 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
|
|
||||||
// Algorithm from: https://blog.regehr.org/archives/1063
|
// Algorithm from: https://blog.regehr.org/archives/1063
|
||||||
fn rotate_right(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
fn rotate_right(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
|
||||||
let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
|
let max = self.const_uint(shift.get_type(), width);
|
||||||
let shift = shift % max;
|
let shift = self.urem(shift, max);
|
||||||
let lhs = self.lshr(value, shift);
|
let lhs = self.lshr(value, shift);
|
||||||
|
let result_neg = self.neg(shift);
|
||||||
let result_and =
|
let result_and =
|
||||||
self.and(
|
self.and(
|
||||||
self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
|
result_neg,
|
||||||
self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
|
self.const_uint(shift.get_type(), width - 1),
|
||||||
);
|
);
|
||||||
let rhs = self.shl(value, result_and);
|
let rhs = self.shl(value, result_and);
|
||||||
self.or(lhs, rhs)
|
self.or(lhs, rhs)
|
||||||
@ -995,9 +1000,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
|
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place rustc does not
|
// NOTE: since jumps were added in a place rustc does not
|
||||||
// expect, the current blocks in the state need to be updated.
|
// expect, the current block in the state need to be updated.
|
||||||
*self.current_block.borrow_mut() = Some(after_block);
|
self.switch_to_block(after_block);
|
||||||
self.block = Some(after_block);
|
|
||||||
|
|
||||||
res.to_rvalue()
|
res.to_rvalue()
|
||||||
}
|
}
|
||||||
@ -1015,39 +1019,59 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
|
fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
|
||||||
if signed {
|
if signed {
|
||||||
// Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
// Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
|
||||||
let func_name =
|
|
||||||
match width {
|
|
||||||
8 => "__builtin_sub_overflow",
|
|
||||||
16 => "__builtin_sub_overflow",
|
|
||||||
32 => "__builtin_ssub_overflow",
|
|
||||||
64 => "__builtin_ssubll_overflow",
|
|
||||||
128 => "__builtin_sub_overflow",
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let overflow_func = self.context.get_builtin_function(func_name);
|
|
||||||
let result_type = lhs.get_type();
|
let result_type = lhs.get_type();
|
||||||
let func = self.current_func.borrow().expect("func");
|
let func = self.current_func.borrow().expect("func");
|
||||||
let res = func.new_local(None, result_type, "saturating_diff");
|
let res = func.new_local(None, result_type, "saturating_diff");
|
||||||
let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
|
let supports_native_type = self.is_native_int_type(result_type);
|
||||||
|
let overflow =
|
||||||
|
if supports_native_type {
|
||||||
|
let func_name =
|
||||||
|
match width {
|
||||||
|
8 => "__builtin_sub_overflow",
|
||||||
|
16 => "__builtin_sub_overflow",
|
||||||
|
32 => "__builtin_ssub_overflow",
|
||||||
|
64 => "__builtin_ssubll_overflow",
|
||||||
|
128 => "__builtin_sub_overflow",
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let overflow_func = self.context.get_builtin_function(func_name);
|
||||||
|
self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let func_name =
|
||||||
|
match width {
|
||||||
|
128 => "__rust_i128_subo",
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let param_a = self.context.new_parameter(None, result_type, "a");
|
||||||
|
let param_b = self.context.new_parameter(None, result_type, "b");
|
||||||
|
let result_field = self.context.new_field(None, result_type, "result");
|
||||||
|
let overflow_field = self.context.new_field(None, self.bool_type, "overflow");
|
||||||
|
let return_type = self.context.new_struct_type(None, "result_overflow", &[result_field, overflow_field]);
|
||||||
|
let func = self.context.new_function(None, FunctionType::Extern, return_type.as_type(), &[param_a, param_b], func_name, false);
|
||||||
|
let result = self.context.new_call(None, func, &[lhs, rhs]);
|
||||||
|
let overflow = result.access_field(None, overflow_field);
|
||||||
|
let int_result = result.access_field(None, result_field);
|
||||||
|
self.llbb().add_assignment(None, res, int_result);
|
||||||
|
overflow
|
||||||
|
};
|
||||||
|
|
||||||
let then_block = func.new_block("then");
|
let then_block = func.new_block("then");
|
||||||
let after_block = func.new_block("after");
|
let after_block = func.new_block("after");
|
||||||
|
|
||||||
let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
|
// NOTE: convert the type to unsigned to have an unsigned shift.
|
||||||
let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
|
let unsigned_type = result_type.to_unsigned(&self.cx);
|
||||||
let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
|
let shifted = self.gcc_lshr(self.gcc_int_cast(lhs, unsigned_type), self.gcc_int(unsigned_type, width as i64 - 1));
|
||||||
self.context.new_rvalue_from_int(unsigned_type, 0)
|
let uint_max = self.gcc_not(self.gcc_int(unsigned_type, 0));
|
||||||
);
|
let int_max = self.gcc_lshr(uint_max, self.gcc_int(unsigned_type, 1));
|
||||||
let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
|
then_block.add_assignment(None, res, self.gcc_int_cast(self.gcc_add(shifted, int_max), result_type));
|
||||||
then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
|
|
||||||
then_block.end_with_jump(None, after_block);
|
then_block.end_with_jump(None, after_block);
|
||||||
|
|
||||||
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
|
self.llbb().end_with_conditional(None, overflow, then_block, after_block);
|
||||||
|
|
||||||
// NOTE: since jumps were added in a place rustc does not
|
// NOTE: since jumps were added in a place rustc does not
|
||||||
// expect, the current blocks in the state need to be updated.
|
// expect, the current block in the state need to be updated.
|
||||||
*self.current_block.borrow_mut() = Some(after_block);
|
self.switch_to_block(after_block);
|
||||||
self.block = Some(after_block);
|
|
||||||
|
|
||||||
res.to_rvalue()
|
res.to_rvalue()
|
||||||
}
|
}
|
||||||
@ -1062,7 +1086,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
|
fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
|
||||||
if bx.sess().panic_strategy() == PanicStrategy::Abort {
|
// NOTE: the `|| true` here is to use the panic=abort strategy with panic=unwind too
|
||||||
|
if bx.sess().panic_strategy() == PanicStrategy::Abort || true {
|
||||||
|
// TODO(bjorn3): Properly implement unwinding and remove the `|| true` once this is done.
|
||||||
bx.call(bx.type_void(), try_func, &[data], None);
|
bx.call(bx.type_void(), try_func, &[data], None);
|
||||||
// Return 0 unconditionally from the intrinsic call;
|
// Return 0 unconditionally from the intrinsic call;
|
||||||
// we can never unwind.
|
// we can never unwind.
|
||||||
|
@ -163,5 +163,26 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>,
|
|||||||
simd_xor: Uint, Int => xor;
|
simd_xor: Uint, Int => xor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! arith_unary {
|
||||||
|
($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
|
||||||
|
$(if name == sym::$name {
|
||||||
|
match in_elem.kind() {
|
||||||
|
$($(ty::$p(_))|* => {
|
||||||
|
return Ok(bx.$call(args[0].immediate()))
|
||||||
|
})*
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
require!(false,
|
||||||
|
"unsupported operation on `{}` with element `{}`",
|
||||||
|
in_ty,
|
||||||
|
in_elem)
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arith_unary! {
|
||||||
|
simd_neg: Int => neg, Float => fneg;
|
||||||
|
}
|
||||||
|
|
||||||
unimplemented!("simd {}", name);
|
unimplemented!("simd {}", name);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
* TODO(antoyo): implement equality in libgccjit based on https://zpz.github.io/blog/overloading-equality-operator-in-cpp-class-hierarchy/ (for type equality?)
|
||||||
* TODO(antoyo): support #[inline] attributes.
|
* TODO(antoyo): support #[inline] attributes.
|
||||||
* TODO(antoyo): support LTO.
|
* TODO(antoyo): support LTO (gcc's equivalent to Thin LTO is enabled by -fwhopr: https://stackoverflow.com/questions/64954525/does-gcc-have-thin-lto).
|
||||||
*
|
*
|
||||||
* TODO(antoyo): remove the patches.
|
* TODO(antoyo): remove the patches.
|
||||||
*/
|
*/
|
||||||
@ -21,6 +22,7 @@ extern crate rustc_middle;
|
|||||||
extern crate rustc_session;
|
extern crate rustc_session;
|
||||||
extern crate rustc_span;
|
extern crate rustc_span;
|
||||||
extern crate rustc_target;
|
extern crate rustc_target;
|
||||||
|
extern crate tempfile;
|
||||||
|
|
||||||
// This prevents duplicating functions and statics that are already part of the host rustc process.
|
// This prevents duplicating functions and statics that are already part of the host rustc process.
|
||||||
#[allow(unused_extern_crates)]
|
#[allow(unused_extern_crates)]
|
||||||
@ -40,15 +42,16 @@ mod context;
|
|||||||
mod coverageinfo;
|
mod coverageinfo;
|
||||||
mod debuginfo;
|
mod debuginfo;
|
||||||
mod declare;
|
mod declare;
|
||||||
|
mod int;
|
||||||
mod intrinsic;
|
mod intrinsic;
|
||||||
mod mono_item;
|
mod mono_item;
|
||||||
mod type_;
|
mod type_;
|
||||||
mod type_of;
|
mod type_of;
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use gccjit::{Context, OptimizationLevel};
|
use gccjit::{Context, OptimizationLevel, CType};
|
||||||
use rustc_ast::expand::allocator::AllocatorKind;
|
use rustc_ast::expand::allocator::AllocatorKind;
|
||||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
|
use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
|
||||||
use rustc_codegen_ssa::base::codegen_crate;
|
use rustc_codegen_ssa::base::codegen_crate;
|
||||||
@ -61,10 +64,12 @@ use rustc_errors::{ErrorGuaranteed, Handler};
|
|||||||
use rustc_metadata::EncodedMetadata;
|
use rustc_metadata::EncodedMetadata;
|
||||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_session::config::{Lto, OptLevel, OutputFilenames};
|
use rustc_session::config::{Lto, OptLevel, OutputFilenames};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_span::fatal_error::FatalError;
|
use rustc_span::fatal_error::FatalError;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
pub struct PrintOnPanic<F: Fn() -> String>(pub F);
|
pub struct PrintOnPanic<F: Fn() -> String>(pub F);
|
||||||
|
|
||||||
@ -77,13 +82,29 @@ impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GccCodegenBackend;
|
pub struct GccCodegenBackend {
|
||||||
|
supports_128bit_integers: Arc<Mutex<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl CodegenBackend for GccCodegenBackend {
|
impl CodegenBackend for GccCodegenBackend {
|
||||||
fn init(&self, sess: &Session) {
|
fn init(&self, sess: &Session) {
|
||||||
if sess.lto() != Lto::No {
|
if sess.lto() != Lto::No {
|
||||||
sess.warn("LTO is not supported. You may get a linker error.");
|
sess.warn("LTO is not supported. You may get a linker error.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let temp_dir = TempDir::new().expect("cannot create temporary directory");
|
||||||
|
let temp_file = temp_dir.into_path().join("result.asm");
|
||||||
|
let check_context = Context::default();
|
||||||
|
check_context.set_print_errors_to_stderr(false);
|
||||||
|
let _int128_ty = check_context.new_c_type(CType::UInt128t);
|
||||||
|
// NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
|
||||||
|
check_context.compile_to_file(gccjit::OutputKind::Assembler, temp_file.to_str().expect("path to str"));
|
||||||
|
*self.supports_128bit_integers.lock().expect("lock") = check_context.get_last_error() == Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn provide(&self, providers: &mut Providers) {
|
||||||
|
// FIXME(antoyo) compute list of enabled features from cli flags
|
||||||
|
providers.global_backend_features = |_tcx, ()| vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
|
fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
|
||||||
@ -129,7 +150,7 @@ impl ExtraBackendMethods for GccCodegenBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
|
fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
|
||||||
base::compile_codegen_unit(tcx, cgu_name)
|
base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
|
fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel, _features: &[String]) -> TargetMachineFactoryFn<Self> {
|
||||||
@ -237,7 +258,9 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||||||
/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
|
/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
|
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
|
||||||
Box::new(GccCodegenBackend)
|
Box::new(GccCodegenBackend {
|
||||||
|
supports_128bit_integers: Arc::new(Mutex::new(false)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
|
fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
|
||||||
|
@ -7,7 +7,6 @@ use rustc_middle::bug;
|
|||||||
use rustc_middle::ty::layout::TyAndLayout;
|
use rustc_middle::ty::layout::TyAndLayout;
|
||||||
use rustc_target::abi::{AddressSpace, Align, Integer, Size};
|
use rustc_target::abi::{AddressSpace, Align, Integer, Size};
|
||||||
|
|
||||||
use crate::common::TypeReflection;
|
|
||||||
use crate::context::CodegenCx;
|
use crate::context::CodegenCx;
|
||||||
use crate::type_of::LayoutGccExt;
|
use crate::type_of::LayoutGccExt;
|
||||||
|
|
||||||
@ -119,9 +118,15 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
|
fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
|
||||||
if typ.is_integral() {
|
if self.is_int_type_or_bool(typ) {
|
||||||
TypeKind::Integer
|
TypeKind::Integer
|
||||||
}
|
}
|
||||||
|
else if typ.is_compatible_with(self.float_type) {
|
||||||
|
TypeKind::Float
|
||||||
|
}
|
||||||
|
else if typ.is_compatible_with(self.double_type) {
|
||||||
|
TypeKind::Double
|
||||||
|
}
|
||||||
else if typ.dyncast_vector().is_some() {
|
else if typ.dyncast_vector().is_some() {
|
||||||
TypeKind::Vector
|
TypeKind::Vector
|
||||||
}
|
}
|
||||||
@ -175,24 +180,7 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn int_width(&self, typ: Type<'gcc>) -> u64 {
|
fn int_width(&self, typ: Type<'gcc>) -> u64 {
|
||||||
if typ.is_i8(self) || typ.is_u8(self) {
|
self.gcc_int_width(typ)
|
||||||
8
|
|
||||||
}
|
|
||||||
else if typ.is_i16(self) || typ.is_u16(self) {
|
|
||||||
16
|
|
||||||
}
|
|
||||||
else if typ.is_i32(self) || typ.is_u32(self) {
|
|
||||||
32
|
|
||||||
}
|
|
||||||
else if typ.is_i64(self) || typ.is_u64(self) {
|
|
||||||
64
|
|
||||||
}
|
|
||||||
else if typ.is_i128(self) || typ.is_u128(self) {
|
|
||||||
128
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic!("Cannot get width of int type {:?}", typ);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn val_ty(&self, value: RValue<'gcc>) -> Type<'gcc> {
|
fn val_ty(&self, value: RValue<'gcc>) -> Type<'gcc> {
|
||||||
|
@ -251,7 +251,9 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
|||||||
ty::Ref(..) | ty::RawPtr(_) => {
|
ty::Ref(..) | ty::RawPtr(_) => {
|
||||||
return self.field(cx, index).gcc_type(cx, true);
|
return self.field(cx, index).gcc_type(cx, true);
|
||||||
}
|
}
|
||||||
ty::Adt(def, _) if def.is_box() => {
|
// only wide pointer boxes are handled as pointers
|
||||||
|
// thin pointer boxes with scalar allocators are handled by the general logic below
|
||||||
|
ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
|
||||||
let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
|
let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
|
||||||
return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
|
return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -f ./gcc_path ]; then
|
if [ -f ./gcc_path ]; then
|
||||||
export GCC_PATH=$(cat gcc_path)
|
export GCC_PATH=$(cat gcc_path)
|
||||||
else
|
else
|
||||||
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
||||||
@ -14,14 +14,26 @@ fi
|
|||||||
export LD_LIBRARY_PATH="$GCC_PATH"
|
export LD_LIBRARY_PATH="$GCC_PATH"
|
||||||
export LIBRARY_PATH="$GCC_PATH"
|
export LIBRARY_PATH="$GCC_PATH"
|
||||||
|
|
||||||
|
features=
|
||||||
|
|
||||||
|
if [[ "$1" == "--features" ]]; then
|
||||||
|
shift
|
||||||
|
features="--features $1"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$1" == "--release" ]]; then
|
if [[ "$1" == "--release" ]]; then
|
||||||
export CHANNEL='release'
|
export CHANNEL='release'
|
||||||
CARGO_INCREMENTAL=1 cargo rustc --release
|
CARGO_INCREMENTAL=1 cargo rustc --release $features
|
||||||
shift
|
shift
|
||||||
else
|
else
|
||||||
echo $LD_LIBRARY_PATH
|
echo $LD_LIBRARY_PATH
|
||||||
export CHANNEL='debug'
|
export CHANNEL='debug'
|
||||||
cargo rustc
|
cargo rustc $features
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$1" == "--build" ]]; then
|
||||||
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source config.sh
|
source config.sh
|
||||||
@ -145,7 +157,7 @@ function test_rustc() {
|
|||||||
echo
|
echo
|
||||||
echo "[TEST] rust-lang/rust"
|
echo "[TEST] rust-lang/rust"
|
||||||
|
|
||||||
rust_toolchain=$(cat rust-toolchain)
|
rust_toolchain=$(cat rust-toolchain | grep channel | sed 's/channel = "\(.*\)"/\1/')
|
||||||
|
|
||||||
git clone https://github.com/rust-lang/rust.git || true
|
git clone https://github.com/rust-lang/rust.git || true
|
||||||
cd rust
|
cd rust
|
||||||
@ -153,6 +165,24 @@ function test_rustc() {
|
|||||||
git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(')
|
git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(')
|
||||||
export RUSTFLAGS=
|
export RUSTFLAGS=
|
||||||
|
|
||||||
|
git apply - <<EOF
|
||||||
|
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
|
||||||
|
index 887d27fd6dca4..2c2239f2b83d1 100644
|
||||||
|
--- a/src/tools/compiletest/src/header.rs
|
||||||
|
+++ b/src/tools/compiletest/src/header.rs
|
||||||
|
@@ -806,8 +806,8 @@ pub fn make_test_description<R: Read>(
|
||||||
|
cfg: Option<&str>,
|
||||||
|
) -> test::TestDesc {
|
||||||
|
let mut ignore = false;
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
- let ignore_message: Option<String> = None;
|
||||||
|
+ let ignore_message: Option<&str> = None;
|
||||||
|
let mut should_fail = false;
|
||||||
|
|
||||||
|
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
rm config.toml || true
|
rm config.toml || true
|
||||||
|
|
||||||
cat > config.toml <<EOF
|
cat > config.toml <<EOF
|
||||||
@ -175,13 +205,12 @@ EOF
|
|||||||
|
|
||||||
git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
|
git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
|
||||||
|
|
||||||
rm -r src/test/ui/{abi*,extern/,llvm-asm/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true
|
rm -r src/test/ui/{abi*,extern/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true
|
||||||
for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do
|
for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do
|
||||||
rm $test
|
rm $test
|
||||||
done
|
done
|
||||||
git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs
|
git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs
|
||||||
git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs
|
git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs
|
||||||
rm src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs || true # TODO(antoyo): Enable back this test if I ever implement the llvm_asm! macro.
|
|
||||||
|
|
||||||
RUSTC_ARGS="-Zpanic-abort-tests -Csymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort"
|
RUSTC_ARGS="-Zpanic-abort-tests -Csymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort"
|
||||||
|
|
||||||
@ -206,6 +235,14 @@ case $1 in
|
|||||||
clean_ui_tests
|
clean_ui_tests
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
"--std-tests")
|
||||||
|
std_tests
|
||||||
|
;;
|
||||||
|
|
||||||
|
"--build-sysroot")
|
||||||
|
build_sysroot
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
clean
|
clean
|
||||||
mini_tests
|
mini_tests
|
||||||
|
@ -51,7 +51,7 @@ mod libc {
|
|||||||
pub fn fflush(stream: *mut i32) -> i32;
|
pub fn fflush(stream: *mut i32) -> i32;
|
||||||
pub fn printf(format: *const i8, ...) -> i32;
|
pub fn printf(format: *const i8, ...) -> i32;
|
||||||
|
|
||||||
pub static STDOUT: *mut i32;
|
pub static stdout: *mut i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ mod intrinsics {
|
|||||||
pub fn panic(_msg: &str) -> ! {
|
pub fn panic(_msg: &str) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::puts("Panicking\0" as *const str as *const u8);
|
libc::puts("Panicking\0" as *const str as *const u8);
|
||||||
libc::fflush(libc::STDOUT);
|
libc::fflush(libc::stdout);
|
||||||
intrinsics::abort();
|
intrinsics::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
153
compiler/rustc_codegen_gcc/tests/run/int.rs
Normal file
153
compiler/rustc_codegen_gcc/tests/run/int.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Compiler:
|
||||||
|
//
|
||||||
|
// Run-time:
|
||||||
|
// status: 0
|
||||||
|
|
||||||
|
#![feature(arbitrary_self_types, auto_traits, core_intrinsics, lang_items, start, intrinsics)]
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
mod intrinsics {
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn abort() -> !;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Core
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod libc {
|
||||||
|
#[link(name = "c")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn puts(s: *const u8) -> i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
unsafe {
|
||||||
|
core::intrinsics::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Code
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[start]
|
||||||
|
fn main(argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
let var = 134217856_u128;
|
||||||
|
let var2 = 10475372733397991552_u128;
|
||||||
|
let var3 = 193236519889708027473620326106273939584_u128;
|
||||||
|
let var4 = 123236519889708027473620326106273939584_u128;
|
||||||
|
let var5 = 153236519889708027473620326106273939584_u128;
|
||||||
|
let var6 = 18446744073709551616_i128;
|
||||||
|
let var7 = 170141183460469231731687303715884105728_u128;
|
||||||
|
|
||||||
|
// Shifts.
|
||||||
|
assert_eq!(var << (argc as u128 - 1), var);
|
||||||
|
assert_eq!(var << argc as u128, 268435712);
|
||||||
|
assert_eq!(var << (argc + 32) as u128, 1152922604118474752);
|
||||||
|
assert_eq!(var << (argc + 48) as u128, 75557935783508361347072);
|
||||||
|
assert_eq!(var << (argc + 60) as u128, 309485304969250248077606912);
|
||||||
|
assert_eq!(var << (argc + 62) as u128, 1237941219877000992310427648);
|
||||||
|
assert_eq!(var << (argc + 63) as u128, 2475882439754001984620855296);
|
||||||
|
assert_eq!(var << (argc + 80) as u128, 324518863143436548128224745357312);
|
||||||
|
|
||||||
|
assert_eq!(var2 << argc as u128, 20950745466795983104);
|
||||||
|
assert_eq!(var2 << (argc as u128 - 1), var2);
|
||||||
|
assert_eq!(var2 << (argc + 32) as u128, 89982766606709001335848566784);
|
||||||
|
assert_eq!(var2 << (argc + 48) as u128, 5897110592337281111546171672756224);
|
||||||
|
assert_eq!(var2 << (argc + 60) as u128, 24154564986213503432893119171609493504);
|
||||||
|
assert_eq!(var2 << (argc + 62) as u128, 96618259944854013731572476686437974016);
|
||||||
|
assert_eq!(var2 << (argc + 63) as u128, 193236519889708027463144953372875948032);
|
||||||
|
|
||||||
|
assert_eq!(var3 << argc as u128, 46190672858477591483866044780779667712);
|
||||||
|
assert_eq!(var3 << (argc as u128 - 1), var3);
|
||||||
|
assert_eq!(var3 << (argc + 32) as u128, 21267668304951024224840338247585366016);
|
||||||
|
assert_eq!(var3 << (argc + 48) as u128, 1335125106377253154015353231953100800);
|
||||||
|
assert_eq!(var3 << (argc + 60) as u128, 24154564986213503432893119171609493504);
|
||||||
|
assert_eq!(var3 << (argc + 62) as u128, 96618259944854013731572476686437974016);
|
||||||
|
assert_eq!(var3 << (argc + 63) as u128, 193236519889708027463144953372875948032);
|
||||||
|
|
||||||
|
assert_eq!((2220326408_u32 + argc as u32) >> (32 - 6), 33);
|
||||||
|
|
||||||
|
assert_eq!(var >> (argc as u128 - 1), var);
|
||||||
|
assert_eq!(var >> argc as u128, 67108928);
|
||||||
|
assert_eq!(var >> (argc + 32) as u128, 0);
|
||||||
|
assert_eq!(var >> (argc + 48) as u128, 0);
|
||||||
|
assert_eq!(var >> (argc + 60) as u128, 0);
|
||||||
|
assert_eq!(var >> (argc + 62) as u128, 0);
|
||||||
|
assert_eq!(var >> (argc + 63) as u128, 0);
|
||||||
|
|
||||||
|
assert_eq!(var2 >> argc as u128, 5237686366698995776);
|
||||||
|
assert_eq!(var2 >> (argc as u128 - 1), var2);
|
||||||
|
assert_eq!(var2 >> (argc + 32) as u128, 1219493888);
|
||||||
|
assert_eq!(var2 >> (argc + 48) as u128, 18608);
|
||||||
|
assert_eq!(var2 >> (argc + 60) as u128, 4);
|
||||||
|
assert_eq!(var2 >> (argc + 62) as u128, 1);
|
||||||
|
assert_eq!(var2 >> (argc + 63) as u128, 0);
|
||||||
|
|
||||||
|
assert_eq!(var3 >> (argc as u128 - 1), var3);
|
||||||
|
assert_eq!(var3 >> argc as u128, 96618259944854013736810163053136969792);
|
||||||
|
assert_eq!(var3 >> (argc + 32) as u128, 22495691651677250335181635584);
|
||||||
|
assert_eq!(var3 >> (argc + 48) as u128, 343257013727985387194544);
|
||||||
|
assert_eq!(var3 >> (argc + 60) as u128, 83802981867183932420);
|
||||||
|
assert_eq!(var3 >> (argc + 62) as u128, 20950745466795983105);
|
||||||
|
assert_eq!(var3 >> (argc + 63) as u128, 10475372733397991552);
|
||||||
|
assert_eq!(var3 >> (argc + 80) as u128, 79920751444992);
|
||||||
|
|
||||||
|
assert_eq!(var6 >> argc as u128, 9223372036854775808);
|
||||||
|
assert_eq!((var6 - 1) >> argc as u128, 9223372036854775807);
|
||||||
|
assert_eq!(var7 >> argc as u128, 85070591730234615865843651857942052864);
|
||||||
|
|
||||||
|
// Casts
|
||||||
|
assert_eq!((var >> (argc + 32) as u128) as u64, 0);
|
||||||
|
assert_eq!((var >> argc as u128) as u64, 67108928);
|
||||||
|
|
||||||
|
// Addition.
|
||||||
|
assert_eq!(var + argc as u128, 134217857);
|
||||||
|
|
||||||
|
assert_eq!(var2 + argc as u128, 10475372733397991553);
|
||||||
|
assert_eq!(var2 + (var2 + argc as u128) as u128, 20950745466795983105);
|
||||||
|
|
||||||
|
assert_eq!(var3 + argc as u128, 193236519889708027473620326106273939585);
|
||||||
|
|
||||||
|
// Subtraction
|
||||||
|
assert_eq!(var - argc as u128, 134217855);
|
||||||
|
|
||||||
|
assert_eq!(var2 - argc as u128, 10475372733397991551);
|
||||||
|
|
||||||
|
assert_eq!(var3 - argc as u128, 193236519889708027473620326106273939583);
|
||||||
|
|
||||||
|
// Multiplication
|
||||||
|
assert_eq!(var * (argc + 1) as u128, 268435712);
|
||||||
|
assert_eq!(var * (argc as u128 + var2), 1405982069077538020949770368);
|
||||||
|
|
||||||
|
assert_eq!(var2 * (argc + 1) as u128, 20950745466795983104);
|
||||||
|
assert_eq!(var2 * (argc as u128 + var2), 109733433903618109003204073240861360256);
|
||||||
|
|
||||||
|
assert_eq!(var3 * argc as u128, 193236519889708027473620326106273939584);
|
||||||
|
|
||||||
|
assert_eq!(var4 * (argc + 1) as u128, 246473039779416054947240652212547879168);
|
||||||
|
|
||||||
|
assert_eq!(var5 * (argc + 1) as u128, 306473039779416054947240652212547879168);
|
||||||
|
|
||||||
|
// Division.
|
||||||
|
assert_eq!(var / (argc + 1) as u128, 67108928);
|
||||||
|
assert_eq!(var / (argc + 2) as u128, 44739285);
|
||||||
|
|
||||||
|
assert_eq!(var2 / (argc + 1) as u128, 5237686366698995776);
|
||||||
|
assert_eq!(var2 / (argc + 2) as u128, 3491790911132663850);
|
||||||
|
|
||||||
|
assert_eq!(var3 / (argc + 1) as u128, 96618259944854013736810163053136969792);
|
||||||
|
assert_eq!(var3 / (argc + 2) as u128, 64412173296569342491206775368757979861);
|
||||||
|
assert_eq!(var3 / (argc as u128 + var4), 1);
|
||||||
|
assert_eq!(var3 / (argc as u128 + var2), 18446744073709551615);
|
||||||
|
|
||||||
|
assert_eq!(var4 / (argc + 1) as u128, 61618259944854013736810163053136969792);
|
||||||
|
assert_eq!(var4 / (argc + 2) as u128, 41078839963236009157873442035424646528);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
@ -49,7 +49,7 @@ mod libc {
|
|||||||
pub fn puts(s: *const u8) -> i32;
|
pub fn puts(s: *const u8) -> i32;
|
||||||
pub fn fflush(stream: *mut i32) -> i32;
|
pub fn fflush(stream: *mut i32) -> i32;
|
||||||
|
|
||||||
pub static STDOUT: *mut i32;
|
pub static stdout: *mut i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ mod intrinsics {
|
|||||||
pub fn panic(_msg: &str) -> ! {
|
pub fn panic(_msg: &str) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::puts("Panicking\0" as *const str as *const u8);
|
libc::puts("Panicking\0" as *const str as *const u8);
|
||||||
libc::fflush(libc::STDOUT);
|
libc::fflush(libc::stdout);
|
||||||
intrinsics::abort();
|
intrinsics::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ mod libc {
|
|||||||
pub fn fflush(stream: *mut i32) -> i32;
|
pub fn fflush(stream: *mut i32) -> i32;
|
||||||
pub fn printf(format: *const i8, ...) -> i32;
|
pub fn printf(format: *const i8, ...) -> i32;
|
||||||
|
|
||||||
pub static STDOUT: *mut i32;
|
pub static stdout: *mut i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ mod intrinsics {
|
|||||||
pub fn panic(_msg: &str) -> ! {
|
pub fn panic(_msg: &str) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::puts("Panicking\0" as *const str as *const u8);
|
libc::puts("Panicking\0" as *const str as *const u8);
|
||||||
libc::fflush(libc::STDOUT);
|
libc::fflush(libc::stdout);
|
||||||
intrinsics::abort();
|
intrinsics::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ mod libc {
|
|||||||
pub fn puts(s: *const u8) -> i32;
|
pub fn puts(s: *const u8) -> i32;
|
||||||
pub fn fflush(stream: *mut i32) -> i32;
|
pub fn fflush(stream: *mut i32) -> i32;
|
||||||
|
|
||||||
pub static STDOUT: *mut i32;
|
pub static stdout: *mut i32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ mod intrinsics {
|
|||||||
pub fn panic(_msg: &str) -> ! {
|
pub fn panic(_msg: &str) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::puts("Panicking\0" as *const str as *const u8);
|
libc::puts("Panicking\0" as *const str as *const u8);
|
||||||
libc::fflush(libc::STDOUT);
|
libc::fflush(libc::stdout);
|
||||||
intrinsics::abort();
|
intrinsics::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,12 @@
|
|||||||
#[lang = "sized"]
|
#[lang = "sized"]
|
||||||
pub trait Sized {}
|
pub trait Sized {}
|
||||||
|
|
||||||
|
#[lang = "destruct"]
|
||||||
|
pub trait Destruct {}
|
||||||
|
|
||||||
|
#[lang = "drop"]
|
||||||
|
pub trait Drop {}
|
||||||
|
|
||||||
#[lang = "copy"]
|
#[lang = "copy"]
|
||||||
trait Copy {
|
trait Copy {
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user