diff --git a/compiler/rustc_codegen_gcc/.github/FUNDING.yml b/compiler/rustc_codegen_gcc/.github/FUNDING.yml deleted file mode 100644 index cf2dcd4d099..00000000000 --- a/compiler/rustc_codegen_gcc/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: antoyo -patreon: antoyo diff --git a/compiler/rustc_codegen_gcc/.github/workflows/main.yml b/compiler/rustc_codegen_gcc/.github/workflows/main.yml index 774a68aa5e1..98bed8ef387 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/main.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/main.yml @@ -31,7 +31,9 @@ jobs: ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0 - 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 - name: Fixup owner of ~/.cargo/ @@ -66,6 +68,7 @@ jobs: run: | ./prepare_build.sh ./build.sh + cargo test ./clean_all.sh - name: Prepare dependencies diff --git a/compiler/rustc_codegen_gcc/.gitignore b/compiler/rustc_codegen_gcc/.gitignore index d9f0ae05ed4..1e2f9e3aebb 100644 --- a/compiler/rustc_codegen_gcc/.gitignore +++ b/compiler/rustc_codegen_gcc/.gitignore @@ -7,11 +7,14 @@ perf.data.old *.events *.string* /build_sysroot/sysroot +/build_sysroot/sysroot_src /build_sysroot/Cargo.lock /build_sysroot/test_target/Cargo.lock /rust +/simple-raytracer /regex gimple* *asm res test-backend +gcc_path diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 952c03b05c7..f3e07fd08ee 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "gccjit" version = "1.0.0" -source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3" dependencies = [ "gccjit_sys", ] @@ -64,7 +64,7 @@ dependencies = [ [[package]] name = "gccjit_sys" version = "0.0.1" -source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3" dependencies = [ "libc 0.1.12", ] diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index 44cb1633474..1f27d721fab 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -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`.** ```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 $ ./prepare_build.sh # download and patch sysroot src $ ./build.sh --release @@ -113,6 +113,5 @@ p loc->m_line ### 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`. - * Add `~/.rustup/toolchains/debug-current/lib/rustlib/x86_64-unknown-linux-gnu/lib` to `LD_LIBRARY_PATH`. diff --git a/compiler/rustc_codegen_gcc/build.sh b/compiler/rustc_codegen_gcc/build.sh index 9f1228687e2..17a0d2ab3f0 100755 --- a/compiler/rustc_codegen_gcc/build.sh +++ b/compiler/rustc_codegen_gcc/build.sh @@ -3,7 +3,12 @@ #set -x 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 LIBRARY_PATH="$GCC_PATH" diff --git a/compiler/rustc_codegen_gcc/cargo.sh b/compiler/rustc_codegen_gcc/cargo.sh index a85865019c1..1001c522052 100755 --- a/compiler/rustc_codegen_gcc/cargo.sh +++ b/compiler/rustc_codegen_gcc/cargo.sh @@ -20,4 +20,4 @@ fi cmd=$1 shift -RUSTDOCFLAGS=$RUSTFLAGS cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@ +RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@ diff --git a/compiler/rustc_codegen_gcc/config.sh b/compiler/rustc_codegen_gcc/config.sh index bd2d915f589..98caeb7407e 100644 --- a/compiler/rustc_codegen_gcc/config.sh +++ b/compiler/rustc_codegen_gcc/config.sh @@ -2,7 +2,12 @@ set -e 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` if [[ "$unamestr" == 'Linux' ]]; then @@ -30,7 +35,7 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then 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 if [[ `uname` == 'Darwin' ]]; then diff --git a/compiler/rustc_codegen_gcc/gcc_path b/compiler/rustc_codegen_gcc/gcc_path deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 4e9771311ef..a03d26c0467 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1 +1 @@ -nightly-2021-08-12 +nightly-2021-09-17 diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 4e622efcaa0..6378a31202c 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -6,7 +6,7 @@ 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 usize = match tcx.sess.target.pointer_width { @@ -77,6 +77,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: Alloc else { 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]; diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 52acf4f2d26..d749d763402 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -2,16 +2,15 @@ use std::path::{Path, PathBuf}; use rustc_session::Session; -use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; -use rustc_codegen_ssa::METADATA_FILENAME; +use rustc_codegen_ssa::back::archive::ArchiveBuilder; + use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_middle::middle::cstore::DllImport; -use rustc_span::symbol::Symbol; + struct ArchiveConfig<'a> { sess: &'a Session, dst: PathBuf, - lib_search_paths: Vec, use_native_ar: bool, use_gnu_style_archive: bool, } @@ -35,11 +34,9 @@ pub struct ArArchiveBuilder<'a> { impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self { - use rustc_codegen_ssa::back::link::archive_search_paths; let config = ArchiveConfig { sess, dst: output.to_path_buf(), - lib_search_paths: archive_search_paths(sess), use_native_ar: false, // FIXME test for linux and System V derivatives instead use_gnu_style_archive: sess.target.options.archive_format == "gnu", @@ -94,47 +91,27 @@ fn add_file(&mut self, file: &Path) { )); } - fn add_native_library(&mut self, name: Symbol, verbatim: bool) { - let location = find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess); - self.add_archive(location.clone(), |_| false) - .unwrap_or_else(|e| { - panic!( - "failed to add native library {}: {}", - location.to_string_lossy(), - e - ); - }); - } + fn add_archive(&mut self, archive_path: &Path, 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(); - fn add_rlib( - &mut self, - rlib: &Path, - name: &str, - lto: bool, - skip_objects: bool, - ) -> std::io::Result<()> { - let obj_start = name.to_owned(); - - self.add_archive(rlib.to_owned(), move |fname: &str| { - // Ignore metadata files, no matter the name. - if fname == METADATA_FILENAME { - return true; + let mut i = 0; + while let Some(entry) = archive.next_entry() { + let entry = entry?; + let file_name = String::from_utf8(entry.header().identifier().to_vec()) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?; + if !skip(&file_name) { + self.entries + .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i })); } + i += 1; + } - // Don't include Rust objects if LTO is enabled - if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { - 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; - }) + self.src_archives.push((archive_path.to_owned(), archive)); + Ok(()) } fn update_symbols(&mut self) { @@ -239,32 +216,3 @@ fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], unimplemented!(); } } - -impl<'a> ArArchiveBuilder<'a> { - fn add_archive(&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(()) - } -} diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index e4d57c39de4..a684c34b644 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -1,254 +1,542 @@ -use gccjit::{RValue, ToRValue, Type}; +use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -use rustc_data_structures::fx::FxHashMap; + use rustc_hir::LlvmInlineAsmInner; -use rustc_middle::bug; +use rustc_middle::{bug, ty::Instance}; use rustc_span::Span; use rustc_target::asm::*; +use std::borrow::Cow; + use crate::builder::Builder; use crate::context::CodegenCx; 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>>, mut _inputs: Vec>, _span: Span) -> bool { - // TODO(antoyo) - return true; - } - fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { - let asm_arch = self.tcx.sess.asm_arch.unwrap(); +// Rust asm! and GCC Extended Asm semantics differ substantially. +// +// 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 = - match asm_arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => true, - _ => false, - }; +const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t"; +const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix"; - // 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 - // clobber). - continue; - }, - }; - let var = self.current_func().new_local(None, ty, "output_register"); - operand_numbers.insert(idx, current_number); - current_number += 1; - output_vars.insert(idx, var); - } - InlineAsmOperandRef::InOut { out_place, .. } => { - let ty = - match out_place { - Some(place) => place.layout.gcc_type(self.cx, false), - None => { - // NOTE: if no output value, we should not create one. - continue; - }, - }; - operand_numbers.insert(idx, current_number); - current_number += 1; - let var = self.current_func().new_local(None, ty, "output_register"); - output_vars.insert(idx, var); - } - _ => {} - } +struct AsmOutOperand<'a, 'tcx, 'gcc> { + rust_idx: usize, + constraint: &'a str, + late: bool, + readwrite: bool, + + tmp_var: LValue<'gcc>, + out_place: Option>> +} + +struct AsmInOperand<'a, 'tcx> { + rust_idx: usize, + constraint: Cow<'a, str>, + val: RValue<'tcx> +} + +impl AsmOutOperand<'_, '_, '_> { + fn to_constraint(&self) -> String { + let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1); + + let sign = if self.readwrite { '+' } else { '=' }; + res.push(sign); + if !self.late { + res.push('&'); } - // All output operands must come before the input operands, hence the 2 loops. - for (idx, op) in operands.iter().enumerate() { - 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); - } - } + res.push_str(&self.constraint); + res } } +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>>, _inputs: Vec>, 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. -// TODO(antoyo): return &'static str instead? -fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { - match reg { +fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { + let constraint = match reg { // For vector registers LLVM wants the register name to match the type size. InlineAsmRegOrRegClass::Reg(reg) => { // TODO(antoyo): add support for vector register. - let constraint = - match reg.name() { - "ax" => "a", - "bx" => "b", - "cx" => "c", - "dx" => "d", - "si" => "S", - "di" => "D", - // TODO(antoyo): for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 - // 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() + match reg.name() { + "ax" => "a", + "bx" => "b", + "cx" => "c", + "dx" => "d", + "si" => "S", + "di" => "D", + // For registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + name => return ConstraintOrRegister::Register(name), + } }, InlineAsmRegOrRegClass::RegClass(reg) => match reg { 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_nonzero) => 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::freg) => unimplemented!(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", + InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), + InlineAsmRegClass::X86( + X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg, + ) => unreachable!("clobber-only"), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("GCC backend does not support SPIR-V") } + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(), InlineAsmRegClass::Err => unreachable!(), } - .to_string(), - } + }; + + ConstraintOrRegister::Constraint(constraint) } /// 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_nonzero) => cx.type_i32(), 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::freg) => 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) => { 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!(), } } @@ -379,7 +681,7 @@ fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[Gl match operands[operand_idx] { GlobalAsmOperandRef::Const { ref string } => { // 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. 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::X86(X86InlineAsmRegClass::reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { - None if arch == InlineAsmArch::X86_64 => Some('q'), - None => Some('k'), + None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') }, Some('l') => Some('b'), Some('h') => Some('h'), Some('x') => Some('w'), @@ -440,17 +741,28 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option Some('r') => Some('q'), _ => unreachable!(), }, - InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None, + InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) { + (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), + (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), + (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::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") }, + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(), InlineAsmRegClass::Err => unreachable!(), } } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 0f7db911552..5d06d71953c 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::cell::Cell; use std::convert::TryFrom; -use std::ops::{Deref, Range}; +use std::ops::Deref; use gccjit::FunctionType; use gccjit::{ @@ -31,16 +31,16 @@ StaticBuilderMethods, }; 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::def_id::DefId; use rustc_target::abi::{ self, Align, HasDataLayout, - LayoutOf, Size, TargetDataLayout, + WrappingRange, }; use rustc_target::spec::{HasTargetSpec, Target}; @@ -338,12 +338,12 @@ fn data_layout(&self) -> &TargetDataLayout { } } -impl<'tcx> LayoutOf for Builder<'_, '_, 'tcx> { - type Ty = Ty<'tcx>; - type TyAndLayout = TyAndLayout<'tcx>; +impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { + type LayoutOfResult = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - self.cx.layout_of(ty) + #[inline] + 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 @@ fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: let vr = scalar.valid_range.clone(); match scalar.value { abi::Int(..) => { - let range = scalar.valid_range_exclusive(bx); - if range.start != range.end { - bx.range_metadata(load, range); + if !scalar.is_always_valid(bx) { + bx.range_metadata(load, scalar.valid_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); } _ => {} @@ -894,7 +893,7 @@ fn write_operand_repeatedly(mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, c next_bx } - fn range_metadata(&mut self, _load: RValue<'gcc>, _range: Range) { + fn range_metadata(&mut self, _load: RValue<'gcc>, _range: WrappingRange) { // TODO(antoyo) } @@ -1378,7 +1377,7 @@ fn from_immediate(&mut self, val: Self::Value) -> Self::Value { } } - 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() { return self.trunc(val, self.cx().type_i1()); } diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 4ea084ef729..e402e0e91f1 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -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.has_escaping_bound_vars()); - assert!(!instance.substs.has_param_types_or_consts()); if let Some(&func) = cx.instances.borrow().get(&instance) { return func; diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 752ba99af9c..a24fe0df911 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -12,10 +12,11 @@ }; use rustc_middle::bug; use rustc_middle::mir::Mutability; -use rustc_middle::ty::{layout::TyAndLayout, ScalarInt}; -use rustc_mir::interpret::{Allocation, GlobalAlloc, Scalar}; +use rustc_middle::ty::ScalarInt; +use rustc_middle::ty::layout::{TyAndLayout, LayoutOf}; +use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar}; 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::context::CodegenCx; @@ -212,7 +213,7 @@ fn const_to_opt_u128(&self, _v: RValue<'gcc>, _sign_ext: bool) -> Option { 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() }; match cv { Scalar::Int(ScalarInt::ZST) => { diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 9b7959503ab..df13fa79f06 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -6,10 +6,11 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::MonoItem; 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::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::context::CodegenCx; @@ -182,6 +183,10 @@ fn codegen_static(&self, def_id: DefId, is_mutable: bool) { fn add_used_global(&self, _global: RValue<'gcc>) { // TODO(antoyo) } + + fn add_compiler_used_global(&self, _global: RValue<'gcc>) { + // TODO(antoyo) + } } 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)), &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(), )); next_offset = offset + pointer_size; diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 7ab1ca0d771..ef687dd22c6 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -18,13 +18,13 @@ }; use rustc_data_structures::base_n; 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::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_span::{Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx}; +use rustc_span::{Span, Symbol}; +use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, Target, TlsModel}; use crate::callee::get_fn; @@ -395,6 +395,14 @@ fn declare_c_main(&self, fn_type: Self::Type) -> Option { None } } + + fn compiler_used_statics(&self) -> &RefCell>> { + unimplemented!() + } + + fn create_compiler_used_variable(&self) { + unimplemented!() + } } impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> { @@ -415,22 +423,16 @@ fn target_spec(&self) -> &Target { } } -impl<'gcc, 'tcx> LayoutOf for CodegenCx<'gcc, 'tcx> { - type Ty = Ty<'tcx>; - type TyAndLayout = TyAndLayout<'tcx>; +impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { + type LayoutOfResult = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { - self.spanned_layout_of(ty, DUMMY_SP) - } - - fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyAndLayout { - self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap_or_else(|e| { - if let LayoutError::SizeOverflow(_) = e { - self.sess().span_fatal(span, &e.to_string()) - } else { - bug!("failed to get layout for `{}`: {}", ty, e) - } - }) + #[inline] + 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()) + } else { + span_bug!(span, "failed to get layout for `{}`: {}", ty, err) + } } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a79be7cfc74..3dc4f61a7ac 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -10,8 +10,9 @@ use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods}; use rustc_middle::bug; use rustc_middle::ty::{self, Instance, Ty}; +use rustc_middle::ty::layout::LayoutOf; 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::spec::PanicStrategy; @@ -176,7 +177,7 @@ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'t let result = func.new_local(None, arg.get_type(), "zeros"); let zero = self.cx.context.new_rvalue_zero(arg.get_type()); 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); then_block.add_assignment(None, result, zero_result); @@ -307,6 +308,19 @@ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'t } } + 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_") => { match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { Ok(llval) => llval, @@ -935,7 +949,7 @@ fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type)); 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 // expect, the current blocks in the state need to be updated. @@ -985,7 +999,7 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type)); 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 // expect, the current blocks in the state need to be updated. diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 6febccff1ff..793e5c48d0a 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -18,7 +18,6 @@ extern crate rustc_hir; extern crate rustc_metadata; extern crate rustc_middle; -extern crate rustc_mir; extern crate rustc_session; extern crate rustc_span; extern crate rustc_symbol_mangling; @@ -144,8 +143,8 @@ fn write_compressed_metadata<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: &EncodedM 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) { - unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) } + 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, module_name, kind, has_alloc_error_handler) } } fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen, u64) { diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index f9ec933dd3a..cedeb54f60b 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -2,9 +2,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::mono::{Linkage, Visibility}; 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_target::abi::LayoutOf; use rustc_target::abi::call::FnAbi; use crate::base; @@ -31,7 +30,7 @@ fn predefine_static(&self, def_id: DefId, _linkage: Linkage, _visibility: Visibi } 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, &[]); self.linkage.set(base::linkage_to_gcc(linkage)); diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 9302f57b642..2ff2ee7b852 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -4,9 +4,9 @@ use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods}; use rustc_middle::bug; 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_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 crate::abi::{FnAbiGccExt, GccType}; @@ -308,7 +308,7 @@ fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option< 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); result diff --git a/compiler/rustc_codegen_gcc/test.sh b/compiler/rustc_codegen_gcc/test.sh index 06faa98d9b1..12df1f8af2f 100755 --- a/compiler/rustc_codegen_gcc/test.sh +++ b/compiler/rustc_codegen_gcc/test.sh @@ -4,7 +4,12 @@ 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 LIBRARY_PATH="$GCC_PATH" @@ -141,6 +146,7 @@ rm config.toml || true cat > config.toml < 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); }