Merge commit '48d60ab7c5
' into libgccjit-codegen
This commit is contained in:
commit
df487317d0
24 changed files with 774 additions and 387 deletions
|
@ -1,2 +0,0 @@
|
||||||
github: antoyo
|
|
||||||
patreon: antoyo
|
|
|
@ -31,7 +31,9 @@ jobs:
|
||||||
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 LIBRARY_PATH
|
||||||
run: echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
run: |
|
||||||
|
echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
|
||||||
|
echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $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/
|
||||||
|
@ -66,6 +68,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./prepare_build.sh
|
./prepare_build.sh
|
||||||
./build.sh
|
./build.sh
|
||||||
|
cargo test
|
||||||
./clean_all.sh
|
./clean_all.sh
|
||||||
|
|
||||||
- name: Prepare dependencies
|
- name: Prepare dependencies
|
||||||
|
|
3
compiler/rustc_codegen_gcc/.gitignore
vendored
3
compiler/rustc_codegen_gcc/.gitignore
vendored
|
@ -7,11 +7,14 @@ perf.data.old
|
||||||
*.events
|
*.events
|
||||||
*.string*
|
*.string*
|
||||||
/build_sysroot/sysroot
|
/build_sysroot/sysroot
|
||||||
|
/build_sysroot/sysroot_src
|
||||||
/build_sysroot/Cargo.lock
|
/build_sysroot/Cargo.lock
|
||||||
/build_sysroot/test_target/Cargo.lock
|
/build_sysroot/test_target/Cargo.lock
|
||||||
/rust
|
/rust
|
||||||
|
/simple-raytracer
|
||||||
/regex
|
/regex
|
||||||
gimple*
|
gimple*
|
||||||
*asm
|
*asm
|
||||||
res
|
res
|
||||||
test-backend
|
test-backend
|
||||||
|
gcc_path
|
||||||
|
|
|
@ -56,7 +56,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gccjit"
|
name = "gccjit"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea"
|
source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gccjit_sys",
|
"gccjit_sys",
|
||||||
]
|
]
|
||||||
|
@ -64,7 +64,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#0572117c7ffdfcb0e6c6526d45266c3f34796bea"
|
source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.12",
|
"libc 0.1.12",
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,7 +19,7 @@ You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already i
|
||||||
**Put the path to your custom build of libgccjit in the file `gcc_path`.**
|
**Put the path to your custom build of libgccjit in the file `gcc_path`.**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/antoyo/rustc_codegen_gcc.git
|
$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git
|
||||||
$ cd rustc_codegen_gcc
|
$ cd rustc_codegen_gcc
|
||||||
$ ./prepare_build.sh # download and patch sysroot src
|
$ ./prepare_build.sh # download and patch sysroot src
|
||||||
$ ./build.sh --release
|
$ ./build.sh --release
|
||||||
|
@ -113,6 +113,5 @@ p loc->m_line
|
||||||
|
|
||||||
### How to use a custom-build rustc
|
### How to use a custom-build rustc
|
||||||
|
|
||||||
* Build the stage1 compiler (`rustup toolchain link debug-current stage2 build/x86_64-unknown-linux-gnu/stage1`).
|
* Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`).
|
||||||
* Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`.
|
* Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`.
|
||||||
* Add `~/.rustup/toolchains/debug-current/lib/rustlib/x86_64-unknown-linux-gnu/lib` to `LD_LIBRARY_PATH`.
|
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
#set -x
|
#set -x
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export GCC_PATH=$(cat gcc_path)
|
if [ -f ./gcc_path ]; then
|
||||||
|
export GCC_PATH=$(cat gcc_path)
|
||||||
|
else
|
||||||
|
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="$GCC_PATH"
|
export LD_LIBRARY_PATH="$GCC_PATH"
|
||||||
export LIBRARY_PATH="$GCC_PATH"
|
export LIBRARY_PATH="$GCC_PATH"
|
||||||
|
|
|
@ -20,4 +20,4 @@ fi
|
||||||
cmd=$1
|
cmd=$1
|
||||||
shift
|
shift
|
||||||
|
|
||||||
RUSTDOCFLAGS=$RUSTFLAGS cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
|
RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
|
||||||
|
|
|
@ -2,7 +2,12 @@ set -e
|
||||||
|
|
||||||
export CARGO_INCREMENTAL=0
|
export CARGO_INCREMENTAL=0
|
||||||
|
|
||||||
export GCC_PATH=$(cat gcc_path)
|
if [ -f ./gcc_path ]; then
|
||||||
|
export GCC_PATH=$(cat gcc_path)
|
||||||
|
else
|
||||||
|
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
unamestr=`uname`
|
unamestr=`uname`
|
||||||
if [[ "$unamestr" == 'Linux' ]]; then
|
if [[ "$unamestr" == 'Linux' ]]; then
|
||||||
|
@ -30,7 +35,7 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot'
|
export RUSTFLAGS="$linker -Cpanic=abort -Cdebuginfo=2 -Clto=off -Zpanic-abort-tests -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot"
|
||||||
|
|
||||||
# FIXME(antoyo): remove once the atomic shim is gone
|
# FIXME(antoyo): remove once the atomic shim is gone
|
||||||
if [[ `uname` == 'Darwin' ]]; then
|
if [[ `uname` == 'Darwin' ]]; then
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
nightly-2021-08-12
|
nightly-2021-09-17
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_span::symbol::sym;
|
||||||
|
|
||||||
use crate::GccContext;
|
use crate::GccContext;
|
||||||
|
|
||||||
pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: AllocatorKind, has_alloc_error_handler: bool) {
|
pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
|
||||||
let context = &mods.context;
|
let context = &mods.context;
|
||||||
let usize =
|
let usize =
|
||||||
match tcx.sess.target.pointer_width {
|
match tcx.sess.target.pointer_width {
|
||||||
|
@ -77,6 +77,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: Alloc
|
||||||
else {
|
else {
|
||||||
block.end_with_void_return(None);
|
block.end_with_void_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
|
||||||
|
// as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
|
||||||
}
|
}
|
||||||
|
|
||||||
let types = [usize, usize];
|
let types = [usize, usize];
|
||||||
|
|
|
@ -2,16 +2,15 @@ use std::fs::File;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
|
use rustc_codegen_ssa::back::archive::ArchiveBuilder;
|
||||||
use rustc_codegen_ssa::METADATA_FILENAME;
|
|
||||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||||
use rustc_middle::middle::cstore::DllImport;
|
use rustc_middle::middle::cstore::DllImport;
|
||||||
use rustc_span::symbol::Symbol;
|
|
||||||
|
|
||||||
struct ArchiveConfig<'a> {
|
struct ArchiveConfig<'a> {
|
||||||
sess: &'a Session,
|
sess: &'a Session,
|
||||||
dst: PathBuf,
|
dst: PathBuf,
|
||||||
lib_search_paths: Vec<PathBuf>,
|
|
||||||
use_native_ar: bool,
|
use_native_ar: bool,
|
||||||
use_gnu_style_archive: bool,
|
use_gnu_style_archive: bool,
|
||||||
}
|
}
|
||||||
|
@ -35,11 +34,9 @@ pub struct ArArchiveBuilder<'a> {
|
||||||
|
|
||||||
impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
|
impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
|
||||||
fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
|
fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
|
||||||
use rustc_codegen_ssa::back::link::archive_search_paths;
|
|
||||||
let config = ArchiveConfig {
|
let config = ArchiveConfig {
|
||||||
sess,
|
sess,
|
||||||
dst: output.to_path_buf(),
|
dst: output.to_path_buf(),
|
||||||
lib_search_paths: archive_search_paths(sess),
|
|
||||||
use_native_ar: false,
|
use_native_ar: false,
|
||||||
// FIXME test for linux and System V derivatives instead
|
// FIXME test for linux and System V derivatives instead
|
||||||
use_gnu_style_archive: sess.target.options.archive_format == "gnu",
|
use_gnu_style_archive: sess.target.options.archive_format == "gnu",
|
||||||
|
@ -94,47 +91,27 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_native_library(&mut self, name: Symbol, verbatim: bool) {
|
fn add_archive<F>(&mut self, archive_path: &Path, mut skip: F) -> std::io::Result<()>
|
||||||
let location = find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess);
|
where
|
||||||
self.add_archive(location.clone(), |_| false)
|
F: FnMut(&str) -> bool + 'static,
|
||||||
.unwrap_or_else(|e| {
|
{
|
||||||
panic!(
|
let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
|
||||||
"failed to add native library {}: {}",
|
let archive_index = self.src_archives.len();
|
||||||
location.to_string_lossy(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_rlib(
|
let mut i = 0;
|
||||||
&mut self,
|
while let Some(entry) = archive.next_entry() {
|
||||||
rlib: &Path,
|
let entry = entry?;
|
||||||
name: &str,
|
let file_name = String::from_utf8(entry.header().identifier().to_vec())
|
||||||
lto: bool,
|
.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
|
||||||
skip_objects: bool,
|
if !skip(&file_name) {
|
||||||
) -> std::io::Result<()> {
|
self.entries
|
||||||
let obj_start = name.to_owned();
|
.push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
|
||||||
|
|
||||||
self.add_archive(rlib.to_owned(), move |fname: &str| {
|
|
||||||
// Ignore metadata files, no matter the name.
|
|
||||||
if fname == METADATA_FILENAME {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't include Rust objects if LTO is enabled
|
self.src_archives.push((archive_path.to_owned(), archive));
|
||||||
if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
|
Ok(())
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise if this is *not* a rust object and we're skipping
|
|
||||||
// objects then skip this file
|
|
||||||
if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ok, don't skip this
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_symbols(&mut self) {
|
fn update_symbols(&mut self) {
|
||||||
|
@ -239,32 +216,3 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArArchiveBuilder<'a> {
|
|
||||||
fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()>
|
|
||||||
where
|
|
||||||
F: FnMut(&str) -> bool + 'static,
|
|
||||||
{
|
|
||||||
let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
|
|
||||||
let archive_index = self.src_archives.len();
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
while let Some(entry) = archive.next_entry() {
|
|
||||||
let entry = entry.unwrap();
|
|
||||||
let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap();
|
|
||||||
if !skip(&file_name) {
|
|
||||||
self.entries.push((
|
|
||||||
file_name,
|
|
||||||
ArchiveEntry::FromArchive {
|
|
||||||
archive_index,
|
|
||||||
entry_index: i,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.src_archives.push((archive_path, archive));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,254 +1,542 @@
|
||||||
use gccjit::{RValue, ToRValue, Type};
|
use gccjit::{LValue, RValue, ToRValue, Type};
|
||||||
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||||
use rustc_codegen_ssa::mir::operand::OperandValue;
|
use rustc_codegen_ssa::mir::operand::OperandValue;
|
||||||
use rustc_codegen_ssa::mir::place::PlaceRef;
|
use rustc_codegen_ssa::mir::place::PlaceRef;
|
||||||
use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
|
use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
|
||||||
use rustc_hir::LlvmInlineAsmInner;
|
use rustc_hir::LlvmInlineAsmInner;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::{bug, ty::Instance};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::asm::*;
|
use rustc_target::asm::*;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::builder::Builder;
|
use crate::builder::Builder;
|
||||||
use crate::context::CodegenCx;
|
use crate::context::CodegenCx;
|
||||||
use crate::type_of::LayoutGccExt;
|
use crate::type_of::LayoutGccExt;
|
||||||
|
|
||||||
impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
|
||||||
fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, mut _inputs: Vec<RValue<'gcc>>, _span: Span) -> bool {
|
|
||||||
// TODO(antoyo)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) {
|
// Rust asm! and GCC Extended Asm semantics differ substantially.
|
||||||
let asm_arch = self.tcx.sess.asm_arch.unwrap();
|
//
|
||||||
|
// 1. Rust asm operands go along as one list of operands. Operands themselves indicate
|
||||||
|
// if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
|
||||||
|
// both "in" and "out" (`inout(reg)`).
|
||||||
|
//
|
||||||
|
// GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
|
||||||
|
// this means that all "out" operands must go before "in" operands. "In" and "out" operands
|
||||||
|
// cannot interleave.
|
||||||
|
//
|
||||||
|
// 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
|
||||||
|
// because the asm template refers to operands by index.
|
||||||
|
//
|
||||||
|
// Mapping from Rust to GCC index would be 1-1 if it wasn't for...
|
||||||
|
//
|
||||||
|
// 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
|
||||||
|
// Contrary, Rust expresses clobbers through "out" operands that aren't tied to
|
||||||
|
// a variable (`_`), and such "clobbers" do have index.
|
||||||
|
//
|
||||||
|
// 4. Furthermore, GCC Extended Asm does not support explicit register constraints
|
||||||
|
// (like `out("eax")`) directly, offering so-called "local register variables"
|
||||||
|
// as a workaround. These variables need to be declared and initialized *before*
|
||||||
|
// the Extended Asm block but *after* normal local variables
|
||||||
|
// (see comment in `codegen_inline_asm` for explanation).
|
||||||
|
//
|
||||||
|
// With that in mind, let's see how we translate Rust syntax to GCC
|
||||||
|
// (from now on, `CC` stands for "constraint code"):
|
||||||
|
//
|
||||||
|
// * `out(reg_class) var` -> translated to output operand: `"=CC"(var)`
|
||||||
|
// * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)`
|
||||||
|
// * `in(reg_class) var` -> translated to input operand: `"CC"(var)`
|
||||||
|
//
|
||||||
|
// * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
|
||||||
|
//
|
||||||
|
// * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
|
||||||
|
//
|
||||||
|
// * `inout(reg_class) in_var => out_var` -> translated to two operands:
|
||||||
|
// output: `"=CC"(in_var)`
|
||||||
|
// input: `"num"(out_var)` where num is the GCC index
|
||||||
|
// of the corresponding output operand
|
||||||
|
//
|
||||||
|
// * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
|
||||||
|
// where "tmp" is a temporary unused variable
|
||||||
|
//
|
||||||
|
// * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
|
||||||
|
// with `"r"(var)` constraint,
|
||||||
|
// and one register variable assigned to the desired register.
|
||||||
|
//
|
||||||
|
|
||||||
let intel_dialect =
|
const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
|
||||||
match asm_arch {
|
const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
|
||||||
InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Collect the types of output operands
|
|
||||||
// FIXME(antoyo): we do this here instead of later because of a bug in libgccjit where creating the
|
|
||||||
// variable after the extended asm expression causes a segfault:
|
|
||||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100380
|
|
||||||
let mut output_vars = FxHashMap::default();
|
|
||||||
let mut operand_numbers = FxHashMap::default();
|
|
||||||
let mut current_number = 0;
|
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
|
||||||
match *op {
|
|
||||||
InlineAsmOperandRef::Out { place, .. } => {
|
|
||||||
let ty =
|
|
||||||
match place {
|
|
||||||
Some(place) => place.layout.gcc_type(self.cx, false),
|
|
||||||
None => {
|
|
||||||
// If the output is discarded, we don't really care what
|
|
||||||
// type is used. We're just using this to tell GCC to
|
|
||||||
// reserve the register.
|
|
||||||
//dummy_output_type(self.cx, reg.reg_class())
|
|
||||||
|
|
||||||
// NOTE: if no output value, we should not create one (it will be a
|
struct AsmOutOperand<'a, 'tcx, 'gcc> {
|
||||||
// clobber).
|
rust_idx: usize,
|
||||||
continue;
|
constraint: &'a str,
|
||||||
},
|
late: bool,
|
||||||
};
|
readwrite: bool,
|
||||||
let var = self.current_func().new_local(None, ty, "output_register");
|
|
||||||
operand_numbers.insert(idx, current_number);
|
tmp_var: LValue<'gcc>,
|
||||||
current_number += 1;
|
out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
|
||||||
output_vars.insert(idx, var);
|
}
|
||||||
}
|
|
||||||
InlineAsmOperandRef::InOut { out_place, .. } => {
|
struct AsmInOperand<'a, 'tcx> {
|
||||||
let ty =
|
rust_idx: usize,
|
||||||
match out_place {
|
constraint: Cow<'a, str>,
|
||||||
Some(place) => place.layout.gcc_type(self.cx, false),
|
val: RValue<'tcx>
|
||||||
None => {
|
}
|
||||||
// NOTE: if no output value, we should not create one.
|
|
||||||
continue;
|
impl AsmOutOperand<'_, '_, '_> {
|
||||||
},
|
fn to_constraint(&self) -> String {
|
||||||
};
|
let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1);
|
||||||
operand_numbers.insert(idx, current_number);
|
|
||||||
current_number += 1;
|
let sign = if self.readwrite { '+' } else { '=' };
|
||||||
let var = self.current_func().new_local(None, ty, "output_register");
|
res.push(sign);
|
||||||
output_vars.insert(idx, var);
|
if !self.late {
|
||||||
}
|
res.push('&');
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All output operands must come before the input operands, hence the 2 loops.
|
res.push_str(&self.constraint);
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
res
|
||||||
match *op {
|
|
||||||
InlineAsmOperandRef::In { .. } | InlineAsmOperandRef::InOut { .. } => {
|
|
||||||
operand_numbers.insert(idx, current_number);
|
|
||||||
current_number += 1;
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the template string
|
|
||||||
let mut template_str = String::new();
|
|
||||||
for piece in template {
|
|
||||||
match *piece {
|
|
||||||
InlineAsmTemplatePiece::String(ref string) => {
|
|
||||||
if string.contains('%') {
|
|
||||||
for c in string.chars() {
|
|
||||||
if c == '%' {
|
|
||||||
template_str.push_str("%%");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
template_str.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
template_str.push_str(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
|
|
||||||
match operands[operand_idx] {
|
|
||||||
InlineAsmOperandRef::Out { reg, place: Some(_), .. } => {
|
|
||||||
let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
|
|
||||||
if let Some(modifier) = modifier {
|
|
||||||
template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx]));
|
|
||||||
} else {
|
|
||||||
template_str.push_str(&format!("%{}", operand_numbers[&operand_idx]));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
InlineAsmOperandRef::Out { place: None, .. } => {
|
|
||||||
unimplemented!("Out None");
|
|
||||||
},
|
|
||||||
InlineAsmOperandRef::In { reg, .. }
|
|
||||||
| InlineAsmOperandRef::InOut { reg, .. } => {
|
|
||||||
let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
|
|
||||||
if let Some(modifier) = modifier {
|
|
||||||
template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx]));
|
|
||||||
} else {
|
|
||||||
template_str.push_str(&format!("%{}", operand_numbers[&operand_idx]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InlineAsmOperandRef::Const { ref string } => {
|
|
||||||
// Const operands get injected directly into the template
|
|
||||||
template_str.push_str(string);
|
|
||||||
}
|
|
||||||
InlineAsmOperandRef::SymFn { .. }
|
|
||||||
| InlineAsmOperandRef::SymStatic { .. } => {
|
|
||||||
unimplemented!();
|
|
||||||
// Only emit the raw symbol name
|
|
||||||
//template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let block = self.llbb();
|
|
||||||
let template_str =
|
|
||||||
if intel_dialect {
|
|
||||||
template_str
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// FIXME(antoyo): this might break the "m" memory constraint:
|
|
||||||
// https://stackoverflow.com/a/9347957/389119
|
|
||||||
// TODO(antoyo): only set on x86 platforms.
|
|
||||||
format!(".att_syntax noprefix\n\t{}\n\t.intel_syntax noprefix", template_str)
|
|
||||||
};
|
|
||||||
let extended_asm = block.add_extended_asm(None, &template_str);
|
|
||||||
|
|
||||||
// Collect the types of output operands
|
|
||||||
let mut output_types = vec![];
|
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
|
||||||
match *op {
|
|
||||||
InlineAsmOperandRef::Out { reg, late, place } => {
|
|
||||||
let ty =
|
|
||||||
match place {
|
|
||||||
Some(place) => place.layout.gcc_type(self.cx, false),
|
|
||||||
None => {
|
|
||||||
// If the output is discarded, we don't really care what
|
|
||||||
// type is used. We're just using this to tell GCC to
|
|
||||||
// reserve the register.
|
|
||||||
dummy_output_type(self.cx, reg.reg_class())
|
|
||||||
},
|
|
||||||
};
|
|
||||||
output_types.push(ty);
|
|
||||||
let prefix = if late { "=" } else { "=&" };
|
|
||||||
let constraint = format!("{}{}", prefix, reg_to_gcc(reg));
|
|
||||||
|
|
||||||
if place.is_some() {
|
|
||||||
let var = output_vars[&idx];
|
|
||||||
extended_asm.add_output_operand(None, &constraint, var);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// NOTE: reg.to_string() returns the register name with quotes around it so
|
|
||||||
// remove them.
|
|
||||||
extended_asm.add_clobber(reg.to_string().trim_matches('"'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
|
|
||||||
let ty =
|
|
||||||
match out_place {
|
|
||||||
Some(out_place) => out_place.layout.gcc_type(self.cx, false),
|
|
||||||
None => dummy_output_type(self.cx, reg.reg_class())
|
|
||||||
};
|
|
||||||
output_types.push(ty);
|
|
||||||
// TODO(antoyo): prefix of "+" for reading and writing?
|
|
||||||
let prefix = if late { "=" } else { "=&" };
|
|
||||||
let constraint = format!("{}{}", prefix, reg_to_gcc(reg));
|
|
||||||
|
|
||||||
if out_place.is_some() {
|
|
||||||
let var = output_vars[&idx];
|
|
||||||
// TODO(antoyo): also specify an output operand when out_place is none: that would
|
|
||||||
// be the clobber but clobbers do not support general constraint like reg;
|
|
||||||
// they only support named registers.
|
|
||||||
// Not sure how we can do this. And the LLVM backend does not seem to add a
|
|
||||||
// clobber.
|
|
||||||
extended_asm.add_output_operand(None, &constraint, var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let constraint = reg_to_gcc(reg);
|
|
||||||
extended_asm.add_input_operand(None, &constraint, in_value.immediate());
|
|
||||||
}
|
|
||||||
InlineAsmOperandRef::In { reg, value } => {
|
|
||||||
let constraint = reg_to_gcc(reg);
|
|
||||||
extended_asm.add_input_operand(None, &constraint, value.immediate());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write results to outputs
|
|
||||||
for (idx, op) in operands.iter().enumerate() {
|
|
||||||
if let InlineAsmOperandRef::Out { place: Some(place), .. }
|
|
||||||
| InlineAsmOperandRef::InOut { out_place: Some(place), .. } = *op
|
|
||||||
{
|
|
||||||
OperandValue::Immediate(output_vars[&idx].to_rvalue()).store(self, place);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ConstraintOrRegister {
|
||||||
|
Constraint(&'static str),
|
||||||
|
Register(&'static str)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
|
fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, _inputs: Vec<RValue<'gcc>>, span: Span) -> bool {
|
||||||
|
self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`")
|
||||||
|
.help("consider using the `asm!` macro instead")
|
||||||
|
.emit();
|
||||||
|
|
||||||
|
// We return `true` even if we've failed to generate the asm
|
||||||
|
// because we want to suppress the "malformed inline assembly" error
|
||||||
|
// generated by the frontend.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) {
|
||||||
|
let asm_arch = self.tcx.sess.asm_arch.unwrap();
|
||||||
|
let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
|
||||||
|
let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
|
||||||
|
let intel_dialect = is_x86 && !options.contains(InlineAsmOptions::ATT_SYNTAX);
|
||||||
|
|
||||||
|
// GCC index of an output operand equals its position in the array
|
||||||
|
let mut outputs = vec![];
|
||||||
|
|
||||||
|
// GCC index of an input operand equals its position in the array
|
||||||
|
// added to `outputs.len()`
|
||||||
|
let mut inputs = vec![];
|
||||||
|
|
||||||
|
// Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
|
||||||
|
let mut clobbers = vec![];
|
||||||
|
|
||||||
|
// We're trying to preallocate space for the template
|
||||||
|
let mut constants_len = 0;
|
||||||
|
|
||||||
|
// There are rules we must adhere to if we want GCC to do the right thing:
|
||||||
|
//
|
||||||
|
// * Every local variable that the asm block uses as an output must be declared *before*
|
||||||
|
// the asm block.
|
||||||
|
// * There must be no instructions whatsoever between the register variables and the asm.
|
||||||
|
//
|
||||||
|
// Therefore, the backend must generate the instructions strictly in this order:
|
||||||
|
//
|
||||||
|
// 1. Output variables.
|
||||||
|
// 2. Register variables.
|
||||||
|
// 3. The asm block.
|
||||||
|
//
|
||||||
|
// We also must make sure that no input operands are emitted before output operands.
|
||||||
|
//
|
||||||
|
// This is why we work in passes, first emitting local vars, then local register vars.
|
||||||
|
// Also, we don't emit any asm operands immediately; we save them to
|
||||||
|
// the one of the buffers to be emitted later.
|
||||||
|
|
||||||
|
// 1. Normal variables (and saving operands to buffers).
|
||||||
|
for (rust_idx, op) in rust_operands.iter().enumerate() {
|
||||||
|
match *op {
|
||||||
|
InlineAsmOperandRef::Out { reg, late, place } => {
|
||||||
|
use ConstraintOrRegister::*;
|
||||||
|
|
||||||
|
let (constraint, ty) = match (reg_to_gcc(reg), place) {
|
||||||
|
(Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx, false)),
|
||||||
|
// When `reg` is a class and not an explicit register but the out place is not specified,
|
||||||
|
// we need to create an unused output variable to assign the output to. This var
|
||||||
|
// needs to be of a type that's "compatible" with the register class, but specific type
|
||||||
|
// doesn't matter.
|
||||||
|
(Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
|
||||||
|
(Register(_), Some(_)) => {
|
||||||
|
// left for the next pass
|
||||||
|
continue
|
||||||
|
},
|
||||||
|
(Register(reg_name), None) => {
|
||||||
|
clobbers.push(reg_name);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let tmp_var = self.current_func().new_local(None, ty, "output_register");
|
||||||
|
outputs.push(AsmOutOperand {
|
||||||
|
constraint,
|
||||||
|
rust_idx,
|
||||||
|
late,
|
||||||
|
readwrite: false,
|
||||||
|
tmp_var,
|
||||||
|
out_place: place
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::In { reg, value } => {
|
||||||
|
if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
|
||||||
|
inputs.push(AsmInOperand {
|
||||||
|
constraint: Cow::Borrowed(constraint),
|
||||||
|
rust_idx,
|
||||||
|
val: value.immediate()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// left for the next pass
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
|
||||||
|
let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
|
||||||
|
constraint
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// left for the next pass
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rustc frontend guarantees that input and output types are "compatible",
|
||||||
|
// so we can just use input var's type for the output variable.
|
||||||
|
//
|
||||||
|
// This decision is also backed by the fact that LLVM needs in and out
|
||||||
|
// values to be of *exactly the same type*, not just "compatible".
|
||||||
|
// I'm not sure if GCC is so picky too, but better safe than sorry.
|
||||||
|
let ty = in_value.layout.gcc_type(self.cx, false);
|
||||||
|
let tmp_var = self.current_func().new_local(None, ty, "output_register");
|
||||||
|
|
||||||
|
// If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
|
||||||
|
// it to one "readwrite (+) output variable", otherwise we translate it to two
|
||||||
|
// "out and tied in" vars as described above.
|
||||||
|
let readwrite = out_place.is_none();
|
||||||
|
outputs.push(AsmOutOperand {
|
||||||
|
constraint,
|
||||||
|
rust_idx,
|
||||||
|
late,
|
||||||
|
readwrite,
|
||||||
|
tmp_var,
|
||||||
|
out_place,
|
||||||
|
});
|
||||||
|
|
||||||
|
if !readwrite {
|
||||||
|
let out_gcc_idx = outputs.len() - 1;
|
||||||
|
let constraint = Cow::Owned(out_gcc_idx.to_string());
|
||||||
|
|
||||||
|
inputs.push(AsmInOperand {
|
||||||
|
constraint,
|
||||||
|
rust_idx,
|
||||||
|
val: in_value.immediate()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::Const { ref string } => {
|
||||||
|
constants_len += string.len() + att_dialect as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::SymFn { instance } => {
|
||||||
|
constants_len += self.tcx.symbol_name(instance).name.len();
|
||||||
|
}
|
||||||
|
InlineAsmOperandRef::SymStatic { def_id } => {
|
||||||
|
constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Register variables.
|
||||||
|
for (rust_idx, op) in rust_operands.iter().enumerate() {
|
||||||
|
match *op {
|
||||||
|
// `out("explicit register") var`
|
||||||
|
InlineAsmOperandRef::Out { reg, late, place } => {
|
||||||
|
if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
|
||||||
|
let out_place = if let Some(place) = place {
|
||||||
|
place
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// processed in the previous pass
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = out_place.layout.gcc_type(self.cx, false);
|
||||||
|
let tmp_var = self.current_func().new_local(None, ty, "output_register");
|
||||||
|
tmp_var.set_register_name(reg_name);
|
||||||
|
|
||||||
|
outputs.push(AsmOutOperand {
|
||||||
|
constraint: "r".into(),
|
||||||
|
rust_idx,
|
||||||
|
late,
|
||||||
|
readwrite: false,
|
||||||
|
tmp_var,
|
||||||
|
out_place: Some(out_place)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// processed in the previous pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// `in("explicit register") var`
|
||||||
|
InlineAsmOperandRef::In { reg, value } => {
|
||||||
|
if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
|
||||||
|
let ty = value.layout.gcc_type(self.cx, false);
|
||||||
|
let reg_var = self.current_func().new_local(None, ty, "input_register");
|
||||||
|
reg_var.set_register_name(reg_name);
|
||||||
|
self.llbb().add_assignment(None, reg_var, value.immediate());
|
||||||
|
|
||||||
|
inputs.push(AsmInOperand {
|
||||||
|
constraint: "r".into(),
|
||||||
|
rust_idx,
|
||||||
|
val: reg_var.to_rvalue()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// processed in the previous pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// `inout("explicit register") in_var => out_var`
|
||||||
|
InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
|
||||||
|
if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
|
||||||
|
let out_place = if let Some(place) = out_place {
|
||||||
|
place
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// processed in the previous pass
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
|
||||||
|
// See explanation in the first pass.
|
||||||
|
let ty = in_value.layout.gcc_type(self.cx, false);
|
||||||
|
let tmp_var = self.current_func().new_local(None, ty, "output_register");
|
||||||
|
tmp_var.set_register_name(reg_name);
|
||||||
|
|
||||||
|
outputs.push(AsmOutOperand {
|
||||||
|
constraint: "r".into(),
|
||||||
|
rust_idx,
|
||||||
|
late,
|
||||||
|
readwrite: false,
|
||||||
|
tmp_var,
|
||||||
|
out_place: Some(out_place)
|
||||||
|
});
|
||||||
|
|
||||||
|
let constraint = Cow::Owned((outputs.len() - 1).to_string());
|
||||||
|
inputs.push(AsmInOperand {
|
||||||
|
constraint,
|
||||||
|
rust_idx,
|
||||||
|
val: in_value.immediate()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// processed in the previous pass
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::Const { .. }
|
||||||
|
| InlineAsmOperandRef::SymFn { .. }
|
||||||
|
| InlineAsmOperandRef::SymStatic { .. } => {
|
||||||
|
// processed in the previous pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Build the template string
|
||||||
|
|
||||||
|
let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
|
||||||
|
if !intel_dialect {
|
||||||
|
template_str.push_str(ATT_SYNTAX_INS);
|
||||||
|
}
|
||||||
|
|
||||||
|
for piece in template {
|
||||||
|
match *piece {
|
||||||
|
InlineAsmTemplatePiece::String(ref string) => {
|
||||||
|
// TODO(@Commeownist): switch to `Iterator::intersperse` once it's stable
|
||||||
|
let mut iter = string.split('%');
|
||||||
|
if let Some(s) = iter.next() {
|
||||||
|
template_str.push_str(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
for s in iter {
|
||||||
|
template_str.push_str("%%");
|
||||||
|
template_str.push_str(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
|
||||||
|
let mut push_to_template = |modifier, gcc_idx| {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
template_str.push('%');
|
||||||
|
if let Some(modifier) = modifier {
|
||||||
|
template_str.push(modifier);
|
||||||
|
}
|
||||||
|
write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
|
||||||
|
};
|
||||||
|
|
||||||
|
match rust_operands[operand_idx] {
|
||||||
|
InlineAsmOperandRef::Out { reg, .. } => {
|
||||||
|
let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
|
||||||
|
let gcc_index = outputs.iter()
|
||||||
|
.position(|op| operand_idx == op.rust_idx)
|
||||||
|
.expect("wrong rust index");
|
||||||
|
push_to_template(modifier, gcc_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::In { reg, .. } => {
|
||||||
|
let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
|
||||||
|
let in_gcc_index = inputs.iter()
|
||||||
|
.position(|op| operand_idx == op.rust_idx)
|
||||||
|
.expect("wrong rust index");
|
||||||
|
let gcc_index = in_gcc_index + outputs.len();
|
||||||
|
push_to_template(modifier, gcc_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::InOut { reg, .. } => {
|
||||||
|
let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
|
||||||
|
|
||||||
|
// The input register is tied to the output, so we can just use the index of the output register
|
||||||
|
let gcc_index = outputs.iter()
|
||||||
|
.position(|op| operand_idx == op.rust_idx)
|
||||||
|
.expect("wrong rust index");
|
||||||
|
push_to_template(modifier, gcc_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::SymFn { instance } => {
|
||||||
|
let name = self.tcx.symbol_name(instance).name;
|
||||||
|
template_str.push_str(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::SymStatic { def_id } => {
|
||||||
|
// TODO(@Commeownist): This may not be sufficient for all kinds of statics.
|
||||||
|
// Some statics may need the `@plt` suffix, like thread-local vars.
|
||||||
|
let instance = Instance::mono(self.tcx, def_id);
|
||||||
|
let name = self.tcx.symbol_name(instance).name;
|
||||||
|
template_str.push_str(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineAsmOperandRef::Const { ref string } => {
|
||||||
|
// Const operands get injected directly into the template
|
||||||
|
if att_dialect {
|
||||||
|
template_str.push('$');
|
||||||
|
}
|
||||||
|
template_str.push_str(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !intel_dialect {
|
||||||
|
template_str.push_str(INTEL_SYNTAX_INS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Generate Extended Asm block
|
||||||
|
|
||||||
|
let block = self.llbb();
|
||||||
|
let extended_asm = block.add_extended_asm(None, &template_str);
|
||||||
|
|
||||||
|
for op in &outputs {
|
||||||
|
extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
for op in &inputs {
|
||||||
|
extended_asm.add_input_operand(None, &op.constraint, op.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
for clobber in clobbers.iter() {
|
||||||
|
extended_asm.add_clobber(clobber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
|
||||||
|
// TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
|
||||||
|
// on all architectures. For instance, what about FP stack?
|
||||||
|
extended_asm.add_clobber("cc");
|
||||||
|
}
|
||||||
|
if !options.contains(InlineAsmOptions::NOMEM) {
|
||||||
|
extended_asm.add_clobber("memory");
|
||||||
|
}
|
||||||
|
if !options.contains(InlineAsmOptions::PURE) {
|
||||||
|
extended_asm.set_volatile_flag(true);
|
||||||
|
}
|
||||||
|
if !options.contains(InlineAsmOptions::NOSTACK) {
|
||||||
|
// TODO(@Commeownist): figure out how to align stack
|
||||||
|
}
|
||||||
|
if options.contains(InlineAsmOptions::NORETURN) {
|
||||||
|
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
|
||||||
|
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
|
||||||
|
self.call(self.type_void(), builtin_unreachable, &[], None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write results to outputs.
|
||||||
|
//
|
||||||
|
// We need to do this because:
|
||||||
|
// 1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
|
||||||
|
// (especially with current `rustc_backend_ssa` API).
|
||||||
|
// 2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
|
||||||
|
//
|
||||||
|
// Instead, we generate a temporary output variable for each output operand, and then this loop,
|
||||||
|
// generates `out_place = tmp_var;` assignments if out_place exists.
|
||||||
|
for op in &outputs {
|
||||||
|
if let Some(place) = op.out_place {
|
||||||
|
OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
|
||||||
|
let len: usize = template.iter().map(|piece| {
|
||||||
|
match *piece {
|
||||||
|
InlineAsmTemplatePiece::String(ref string) => {
|
||||||
|
string.len()
|
||||||
|
}
|
||||||
|
InlineAsmTemplatePiece::Placeholder { .. } => {
|
||||||
|
// '%' + 1 char modifier + 1 char index
|
||||||
|
3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
// increase it by 5% to account for possible '%' signs that'll be duplicated
|
||||||
|
// I pulled the number out of blue, but should be fair enough
|
||||||
|
// as the upper bound
|
||||||
|
let mut res = (len as f32 * 1.05) as usize + constants_len;
|
||||||
|
|
||||||
|
if att_dialect {
|
||||||
|
res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a register class to a GCC constraint code.
|
/// Converts a register class to a GCC constraint code.
|
||||||
// TODO(antoyo): return &'static str instead?
|
fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
|
||||||
fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String {
|
let constraint = match reg {
|
||||||
match reg {
|
|
||||||
// For vector registers LLVM wants the register name to match the type size.
|
// For vector registers LLVM wants the register name to match the type size.
|
||||||
InlineAsmRegOrRegClass::Reg(reg) => {
|
InlineAsmRegOrRegClass::Reg(reg) => {
|
||||||
// TODO(antoyo): add support for vector register.
|
// TODO(antoyo): add support for vector register.
|
||||||
let constraint =
|
match reg.name() {
|
||||||
match reg.name() {
|
"ax" => "a",
|
||||||
"ax" => "a",
|
"bx" => "b",
|
||||||
"bx" => "b",
|
"cx" => "c",
|
||||||
"cx" => "c",
|
"dx" => "d",
|
||||||
"dx" => "d",
|
"si" => "S",
|
||||||
"si" => "S",
|
"di" => "D",
|
||||||
"di" => "D",
|
// For registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
|
||||||
// TODO(antoyo): for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
|
name => return ConstraintOrRegister::Register(name),
|
||||||
// TODO(antoyo): in this case though, it's a clobber, so it should work as r11.
|
}
|
||||||
// Recent nightly supports clobber() syntax, so update to it. It does not seem
|
|
||||||
// like it's implemented yet.
|
|
||||||
name => name, // FIXME(antoyo): probably wrong.
|
|
||||||
};
|
|
||||||
constraint.to_string()
|
|
||||||
},
|
},
|
||||||
InlineAsmRegOrRegClass::RegClass(reg) => match reg {
|
InlineAsmRegOrRegClass::RegClass(reg) => match reg {
|
||||||
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
|
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
|
||||||
|
@ -275,26 +563,34 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String {
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(),
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(),
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(),
|
||||||
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
|
||||||
|
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
|
||||||
|
unreachable!("clobber-only")
|
||||||
|
},
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(),
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
|
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => unimplemented!(),
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(),
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
|
||||||
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => unimplemented!(),
|
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(),
|
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
|
||||||
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
|
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
|
||||||
|
InlineAsmRegClass::X86(
|
||||||
|
X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
|
||||||
|
) => unreachable!("clobber-only"),
|
||||||
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
||||||
bug!("GCC backend does not support SPIR-V")
|
bug!("GCC backend does not support SPIR-V")
|
||||||
}
|
}
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
|
||||||
InlineAsmRegClass::Err => unreachable!(),
|
InlineAsmRegClass::Err => unreachable!(),
|
||||||
}
|
}
|
||||||
.to_string(),
|
};
|
||||||
}
|
|
||||||
|
ConstraintOrRegister::Constraint(constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type to use for outputs that are discarded. It doesn't really matter what
|
/// Type to use for outputs that are discarded. It doesn't really matter what
|
||||||
|
@ -329,6 +625,10 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
|
||||||
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
|
||||||
|
InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
|
||||||
|
| InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
|
||||||
|
unreachable!("clobber-only")
|
||||||
|
},
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
|
||||||
|
@ -345,6 +645,8 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
|
||||||
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
||||||
bug!("LLVM backend does not support SPIR-V")
|
bug!("LLVM backend does not support SPIR-V")
|
||||||
},
|
},
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
|
||||||
InlineAsmRegClass::Err => unreachable!(),
|
InlineAsmRegClass::Err => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,7 +681,7 @@ impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> {
|
||||||
match operands[operand_idx] {
|
match operands[operand_idx] {
|
||||||
GlobalAsmOperandRef::Const { ref string } => {
|
GlobalAsmOperandRef::Const { ref string } => {
|
||||||
// Const operands get injected directly into the
|
// Const operands get injected directly into the
|
||||||
// template. Note that we don't need to escape $
|
// template. Note that we don't need to escape %
|
||||||
// here unlike normal inline assembly.
|
// here unlike normal inline assembly.
|
||||||
template_str.push_str(string);
|
template_str.push_str(string);
|
||||||
}
|
}
|
||||||
|
@ -431,8 +733,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
|
||||||
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
|
InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
|
||||||
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
|
| InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
|
||||||
None if arch == InlineAsmArch::X86_64 => Some('q'),
|
None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') },
|
||||||
None => Some('k'),
|
|
||||||
Some('l') => Some('b'),
|
Some('l') => Some('b'),
|
||||||
Some('h') => Some('h'),
|
Some('h') => Some('h'),
|
||||||
Some('x') => Some('w'),
|
Some('x') => Some('w'),
|
||||||
|
@ -440,17 +741,28 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option
|
||||||
Some('r') => Some('q'),
|
Some('r') => Some('q'),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None,
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(),
|
InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg)
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
|
| InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg)
|
||||||
| InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
|
| InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) {
|
||||||
| InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(),
|
(X86InlineAsmRegClass::xmm_reg, None) => Some('x'),
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
|
(X86InlineAsmRegClass::ymm_reg, None) => Some('t'),
|
||||||
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
|
(X86InlineAsmRegClass::zmm_reg, None) => Some('g'),
|
||||||
|
(_, Some('x')) => Some('x'),
|
||||||
|
(_, Some('y')) => Some('t'),
|
||||||
|
(_, Some('z')) => Some('g'),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
|
||||||
|
InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
|
||||||
|
unreachable!("clobber-only")
|
||||||
|
}
|
||||||
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
|
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
|
||||||
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
|
||||||
bug!("LLVM backend does not support SPIR-V")
|
bug!("LLVM backend does not support SPIR-V")
|
||||||
},
|
},
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
|
||||||
|
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
|
||||||
InlineAsmRegClass::Err => unreachable!(),
|
InlineAsmRegClass::Err => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::Deref;
|
||||||
|
|
||||||
use gccjit::FunctionType;
|
use gccjit::FunctionType;
|
||||||
use gccjit::{
|
use gccjit::{
|
||||||
|
@ -31,16 +31,16 @@ use rustc_codegen_ssa::traits::{
|
||||||
StaticBuilderMethods,
|
StaticBuilderMethods,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, TyAndLayout};
|
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_target::abi::{
|
use rustc_target::abi::{
|
||||||
self,
|
self,
|
||||||
Align,
|
Align,
|
||||||
HasDataLayout,
|
HasDataLayout,
|
||||||
LayoutOf,
|
|
||||||
Size,
|
Size,
|
||||||
TargetDataLayout,
|
TargetDataLayout,
|
||||||
|
WrappingRange,
|
||||||
};
|
};
|
||||||
use rustc_target::spec::{HasTargetSpec, Target};
|
use rustc_target::spec::{HasTargetSpec, Target};
|
||||||
|
|
||||||
|
@ -338,12 +338,12 @@ impl HasDataLayout for Builder<'_, '_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> LayoutOf for Builder<'_, '_, 'tcx> {
|
impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
|
||||||
type Ty = Ty<'tcx>;
|
type LayoutOfResult = TyAndLayout<'tcx>;
|
||||||
type TyAndLayout = TyAndLayout<'tcx>;
|
|
||||||
|
|
||||||
fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout {
|
#[inline]
|
||||||
self.cx.layout_of(ty)
|
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||||
|
self.cx.handle_layout_err(err, span, ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,12 +818,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
let vr = scalar.valid_range.clone();
|
let vr = scalar.valid_range.clone();
|
||||||
match scalar.value {
|
match scalar.value {
|
||||||
abi::Int(..) => {
|
abi::Int(..) => {
|
||||||
let range = scalar.valid_range_exclusive(bx);
|
if !scalar.is_always_valid(bx) {
|
||||||
if range.start != range.end {
|
bx.range_metadata(load, scalar.valid_range);
|
||||||
bx.range_metadata(load, range);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abi::Pointer if vr.start() < vr.end() && !vr.contains(&0) => {
|
abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
|
||||||
bx.nonnull_metadata(load);
|
bx.nonnull_metadata(load);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -894,7 +893,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
next_bx
|
next_bx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn range_metadata(&mut self, _load: RValue<'gcc>, _range: Range<u128>) {
|
fn range_metadata(&mut self, _load: RValue<'gcc>, _range: WrappingRange) {
|
||||||
// TODO(antoyo)
|
// TODO(antoyo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1378,7 +1377,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &abi::Scalar) -> Self::Value {
|
fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value {
|
||||||
if scalar.is_bool() {
|
if scalar.is_bool() {
|
||||||
return self.trunc(val, self.cx().type_i1());
|
return self.trunc(val, self.cx().type_i1());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>)
|
||||||
|
|
||||||
assert!(!instance.substs.needs_infer());
|
assert!(!instance.substs.needs_infer());
|
||||||
assert!(!instance.substs.has_escaping_bound_vars());
|
assert!(!instance.substs.has_escaping_bound_vars());
|
||||||
assert!(!instance.substs.has_param_types_or_consts());
|
|
||||||
|
|
||||||
if let Some(&func) = cx.instances.borrow().get(&instance) {
|
if let Some(&func) = cx.instances.borrow().get(&instance) {
|
||||||
return func;
|
return func;
|
||||||
|
|
|
@ -12,10 +12,11 @@ use rustc_codegen_ssa::traits::{
|
||||||
};
|
};
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty::{layout::TyAndLayout, ScalarInt};
|
use rustc_middle::ty::ScalarInt;
|
||||||
use rustc_mir::interpret::{Allocation, GlobalAlloc, Scalar};
|
use rustc_middle::ty::layout::{TyAndLayout, LayoutOf};
|
||||||
|
use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar};
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_target::abi::{self, HasDataLayout, LayoutOf, Pointer, Size};
|
use rustc_target::abi::{self, HasDataLayout, Pointer, Size};
|
||||||
|
|
||||||
use crate::consts::const_alloc_to_gcc;
|
use crate::consts::const_alloc_to_gcc;
|
||||||
use crate::context::CodegenCx;
|
use crate::context::CodegenCx;
|
||||||
|
@ -212,7 +213,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> {
|
fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> {
|
||||||
let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() };
|
let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() };
|
||||||
match cv {
|
match cv {
|
||||||
Scalar::Int(ScalarInt::ZST) => {
|
Scalar::Int(ScalarInt::ZST) => {
|
||||||
|
|
|
@ -6,10 +6,11 @@ use rustc_middle::{bug, span_bug};
|
||||||
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||||
use rustc_middle::mir::mono::MonoItem;
|
use rustc_middle::mir::mono::MonoItem;
|
||||||
use rustc_middle::ty::{self, Instance, Ty};
|
use rustc_middle::ty::{self, Instance, Ty};
|
||||||
use rustc_mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
|
use rustc_middle::mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_target::abi::{self, Align, HasDataLayout, LayoutOf, Primitive, Size};
|
use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
|
||||||
|
|
||||||
use crate::base;
|
use crate::base;
|
||||||
use crate::context::CodegenCx;
|
use crate::context::CodegenCx;
|
||||||
|
@ -182,6 +183,10 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
|
||||||
fn add_used_global(&self, _global: RValue<'gcc>) {
|
fn add_used_global(&self, _global: RValue<'gcc>) {
|
||||||
// TODO(antoyo)
|
// TODO(antoyo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_compiler_used_global(&self, _global: RValue<'gcc>) {
|
||||||
|
// TODO(antoyo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||||
|
@ -350,7 +355,7 @@ pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: &Alloca
|
||||||
interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
|
||||||
&cx.tcx,
|
&cx.tcx,
|
||||||
),
|
),
|
||||||
&abi::Scalar { value: Primitive::Pointer, valid_range: 0..=!0 },
|
abi::Scalar { value: Primitive::Pointer, valid_range: WrappingRange { start: 0, end: !0 } },
|
||||||
cx.type_i8p(),
|
cx.type_i8p(),
|
||||||
));
|
));
|
||||||
next_offset = offset + pointer_size;
|
next_offset = offset + pointer_size;
|
||||||
|
|
|
@ -18,13 +18,13 @@ use rustc_codegen_ssa::traits::{
|
||||||
};
|
};
|
||||||
use rustc_data_structures::base_n;
|
use rustc_data_structures::base_n;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_middle::bug;
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::mir::mono::CodegenUnit;
|
use rustc_middle::mir::mono::CodegenUnit;
|
||||||
use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout};
|
use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::{Span, Symbol, DUMMY_SP};
|
use rustc_span::{Span, Symbol};
|
||||||
use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||||
use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
|
use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
|
||||||
|
|
||||||
use crate::callee::get_fn;
|
use crate::callee::get_fn;
|
||||||
|
@ -395,6 +395,14 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compiler_used_statics(&self) -> &RefCell<Vec<RValue<'gcc>>> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_compiler_used_variable(&self) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
|
@ -415,22 +423,16 @@ impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> LayoutOf for CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
type Ty = Ty<'tcx>;
|
type LayoutOfResult = TyAndLayout<'tcx>;
|
||||||
type TyAndLayout = TyAndLayout<'tcx>;
|
|
||||||
|
|
||||||
fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout {
|
#[inline]
|
||||||
self.spanned_layout_of(ty, DUMMY_SP)
|
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
|
||||||
}
|
if let LayoutError::SizeOverflow(_) = err {
|
||||||
|
self.sess().span_fatal(span, &err.to_string())
|
||||||
fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyAndLayout {
|
} else {
|
||||||
self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap_or_else(|e| {
|
span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
|
||||||
if let LayoutError::SizeOverflow(_) = e {
|
}
|
||||||
self.sess().span_fatal(span, &e.to_string())
|
|
||||||
} else {
|
|
||||||
bug!("failed to get layout for `{}`: {}", ty, e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@ use rustc_codegen_ssa::mir::place::PlaceRef;
|
||||||
use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
|
use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::{self, Instance, Ty};
|
use rustc_middle::ty::{self, Instance, Ty};
|
||||||
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_span::{Span, Symbol, symbol::kw, sym};
|
use rustc_span::{Span, Symbol, symbol::kw, sym};
|
||||||
use rustc_target::abi::{HasDataLayout, LayoutOf};
|
use rustc_target::abi::HasDataLayout;
|
||||||
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
|
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
|
||||||
use rustc_target::spec::PanicStrategy;
|
use rustc_target::spec::PanicStrategy;
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
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.context.new_rvalue_zero(arg.get_type());
|
||||||
let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero);
|
let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero);
|
||||||
self.block.expect("block").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.context.new_rvalue_from_long(arg.get_type(), width as i64);
|
||||||
then_block.add_assignment(None, result, zero_result);
|
then_block.add_assignment(None, result, zero_result);
|
||||||
|
@ -307,6 +308,19 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sym::black_box => {
|
||||||
|
args[0].val.store(self, result);
|
||||||
|
|
||||||
|
let block = self.llbb();
|
||||||
|
let extended_asm = block.add_extended_asm(None, "");
|
||||||
|
extended_asm.add_input_operand(None, "r", result.llval);
|
||||||
|
extended_asm.add_clobber("memory");
|
||||||
|
extended_asm.set_volatile_flag(true);
|
||||||
|
|
||||||
|
// We have copied the value to `result` already.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_ if name_str.starts_with("simd_") => {
|
_ if name_str.starts_with("simd_") => {
|
||||||
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
|
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
|
||||||
Ok(llval) => llval,
|
Ok(llval) => llval,
|
||||||
|
@ -935,7 +949,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||||
then_block.add_assignment(None, res, self.context.new_cast(None, 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.block.expect("block").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 blocks in the state need to be updated.
|
||||||
|
@ -985,7 +999,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||||
then_block.add_assignment(None, res, self.context.new_cast(None, 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.block.expect("block").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 blocks in the state need to be updated.
|
||||||
|
|
|
@ -18,7 +18,6 @@ extern crate rustc_errors;
|
||||||
extern crate rustc_hir;
|
extern crate rustc_hir;
|
||||||
extern crate rustc_metadata;
|
extern crate rustc_metadata;
|
||||||
extern crate rustc_middle;
|
extern crate rustc_middle;
|
||||||
extern crate rustc_mir;
|
|
||||||
extern crate rustc_session;
|
extern crate rustc_session;
|
||||||
extern crate rustc_span;
|
extern crate rustc_span;
|
||||||
extern crate rustc_symbol_mangling;
|
extern crate rustc_symbol_mangling;
|
||||||
|
@ -144,8 +143,8 @@ impl ExtraBackendMethods for GccCodegenBackend {
|
||||||
base::write_compressed_metadata(tcx, metadata, gcc_module)
|
base::write_compressed_metadata(tcx, metadata, gcc_module)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, kind: AllocatorKind, has_alloc_error_handler: bool) {
|
fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
|
||||||
unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) }
|
unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) }
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
|
@ -2,9 +2,8 @@ use rustc_codegen_ssa::traits::PreDefineMethods;
|
||||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||||
use rustc_middle::mir::mono::{Linkage, Visibility};
|
use rustc_middle::mir::mono::{Linkage, Visibility};
|
||||||
use rustc_middle::ty::{self, Instance, TypeFoldable};
|
use rustc_middle::ty::{self, Instance, TypeFoldable};
|
||||||
use rustc_middle::ty::layout::FnAbiExt;
|
use rustc_middle::ty::layout::{FnAbiExt, LayoutOf};
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_target::abi::LayoutOf;
|
|
||||||
use rustc_target::abi::call::FnAbi;
|
use rustc_target::abi::call::FnAbi;
|
||||||
|
|
||||||
use crate::base;
|
use crate::base;
|
||||||
|
@ -31,7 +30,7 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
|
fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
|
||||||
assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types_or_consts());
|
assert!(!instance.substs.needs_infer());
|
||||||
|
|
||||||
let fn_abi = FnAbi::of_instance(self, instance, &[]);
|
let fn_abi = FnAbi::of_instance(self, instance, &[]);
|
||||||
self.linkage.set(base::linkage_to_gcc(linkage));
|
self.linkage.set(base::linkage_to_gcc(linkage));
|
||||||
|
|
|
@ -4,9 +4,9 @@ use gccjit::{Struct, Type};
|
||||||
use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods};
|
use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods};
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::{self, Ty, TypeFoldable};
|
use rustc_middle::ty::{self, Ty, TypeFoldable};
|
||||||
use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout};
|
use rustc_middle::ty::layout::{FnAbiExt, LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, LayoutOf, Pointer, PointeeInfo, Size, TyAndLayoutMethods, Variants};
|
use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, Pointer, PointeeInfo, Size, TyAbiInterface, Variants};
|
||||||
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
|
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
|
||||||
|
|
||||||
use crate::abi::{FnAbiGccExt, GccType};
|
use crate::abi::{FnAbiGccExt, GccType};
|
||||||
|
@ -308,7 +308,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
|
||||||
return pointee;
|
return pointee;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = Ty::pointee_info_at(*self, cx, offset);
|
let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
|
||||||
|
|
||||||
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
|
||||||
result
|
result
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export GCC_PATH=$(cat gcc_path)
|
if [ -f ./gcc_path ]; then
|
||||||
|
export GCC_PATH=$(cat gcc_path)
|
||||||
|
else
|
||||||
|
echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="$GCC_PATH"
|
export LD_LIBRARY_PATH="$GCC_PATH"
|
||||||
export LIBRARY_PATH="$GCC_PATH"
|
export LIBRARY_PATH="$GCC_PATH"
|
||||||
|
@ -141,6 +146,7 @@ rm config.toml || true
|
||||||
cat > config.toml <<EOF
|
cat > config.toml <<EOF
|
||||||
[rust]
|
[rust]
|
||||||
codegen-backends = []
|
codegen-backends = []
|
||||||
|
deny-warnings = false
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
cargo = "$(which cargo)"
|
cargo = "$(which cargo)"
|
||||||
|
@ -157,7 +163,7 @@ done
|
||||||
|
|
||||||
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/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true
|
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
|
||||||
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
|
||||||
|
|
|
@ -62,5 +62,92 @@ fn main() {
|
||||||
}
|
}
|
||||||
assert_eq!(x, 43);
|
assert_eq!(x, 43);
|
||||||
|
|
||||||
|
// check inout(reg_class) x
|
||||||
|
let mut x: u64 = 42;
|
||||||
|
unsafe {
|
||||||
|
asm!("add {0}, {0}",
|
||||||
|
inout(reg) x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 84);
|
||||||
|
|
||||||
|
// check inout("reg") x
|
||||||
|
let mut x: u64 = 42;
|
||||||
|
unsafe {
|
||||||
|
asm!("add r11, r11",
|
||||||
|
inout("r11") x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 84);
|
||||||
|
|
||||||
|
// check a mix of
|
||||||
|
// in("reg")
|
||||||
|
// inout(class) x => y
|
||||||
|
// inout (class) x
|
||||||
|
let x: u64 = 702;
|
||||||
|
let y: u64 = 100;
|
||||||
|
let res: u64;
|
||||||
|
let mut rem: u64 = 0;
|
||||||
|
unsafe {
|
||||||
|
asm!("div r11",
|
||||||
|
in("r11") y,
|
||||||
|
inout("eax") x => res,
|
||||||
|
inout("edx") rem,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(res, 7);
|
||||||
|
assert_eq!(rem, 2);
|
||||||
|
|
||||||
|
// check const
|
||||||
|
let mut x: u64 = 42;
|
||||||
|
unsafe {
|
||||||
|
asm!("add {}, {}",
|
||||||
|
inout(reg) x,
|
||||||
|
const 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 43);
|
||||||
|
|
||||||
|
// check const (ATT syntax)
|
||||||
|
let mut x: u64 = 42;
|
||||||
|
unsafe {
|
||||||
|
asm!("add {}, {}",
|
||||||
|
const 1,
|
||||||
|
inout(reg) x,
|
||||||
|
options(att_syntax)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 43);
|
||||||
|
|
||||||
|
// check sym fn
|
||||||
|
extern "C" fn foo() -> u64 { 42 }
|
||||||
|
let x: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("call {}", sym foo, lateout("rax") x);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 42);
|
||||||
|
|
||||||
|
// check sym fn (ATT syntax)
|
||||||
|
let x: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("call {}", sym foo, lateout("rax") x, options(att_syntax));
|
||||||
|
}
|
||||||
|
assert_eq!(x, 42);
|
||||||
|
|
||||||
|
// check sym static
|
||||||
|
static FOO: u64 = 42;
|
||||||
|
let x: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("mov {1}, qword ptr [rip + {0}]", sym FOO, lateout(reg) x);
|
||||||
|
}
|
||||||
|
assert_eq!(x, 42);
|
||||||
|
|
||||||
|
// check sym static (ATT syntax)
|
||||||
|
let x: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("movq {0}(%rip), {1}", sym FOO, lateout(reg) x, options(att_syntax));
|
||||||
|
}
|
||||||
|
assert_eq!(x, 42);
|
||||||
|
|
||||||
assert_eq!(unsafe { add_asm(40, 2) }, 42);
|
assert_eq!(unsafe { add_asm(40, 2) }, 42);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue