]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '48d60ab7c505c6c1ebb042eacaafd8dc9f7a9267' into libgccjit-codegen
authorAntoni Boucher <bouanto@zoho.com>
Fri, 17 Sep 2021 21:52:40 +0000 (17:52 -0400)
committerAntoni Boucher <bouanto@zoho.com>
Fri, 17 Sep 2021 21:52:40 +0000 (17:52 -0400)
24 files changed:
compiler/rustc_codegen_gcc/.github/FUNDING.yml [deleted file]
compiler/rustc_codegen_gcc/.github/workflows/main.yml
compiler/rustc_codegen_gcc/.gitignore
compiler/rustc_codegen_gcc/Cargo.lock
compiler/rustc_codegen_gcc/Readme.md
compiler/rustc_codegen_gcc/build.sh
compiler/rustc_codegen_gcc/cargo.sh
compiler/rustc_codegen_gcc/config.sh
compiler/rustc_codegen_gcc/gcc_path [deleted file]
compiler/rustc_codegen_gcc/rust-toolchain
compiler/rustc_codegen_gcc/src/allocator.rs
compiler/rustc_codegen_gcc/src/archive.rs
compiler/rustc_codegen_gcc/src/asm.rs
compiler/rustc_codegen_gcc/src/builder.rs
compiler/rustc_codegen_gcc/src/callee.rs
compiler/rustc_codegen_gcc/src/common.rs
compiler/rustc_codegen_gcc/src/consts.rs
compiler/rustc_codegen_gcc/src/context.rs
compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
compiler/rustc_codegen_gcc/src/lib.rs
compiler/rustc_codegen_gcc/src/mono_item.rs
compiler/rustc_codegen_gcc/src/type_of.rs
compiler/rustc_codegen_gcc/test.sh
compiler/rustc_codegen_gcc/tests/run/asm.rs

diff --git a/compiler/rustc_codegen_gcc/.github/FUNDING.yml b/compiler/rustc_codegen_gcc/.github/FUNDING.yml
deleted file mode 100644 (file)
index cf2dcd4..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-github: antoyo
-patreon: antoyo
index 774a68aa5e1a40e3ef276561f2197ac784364f6d..98bed8ef387ff4fee2f2c513f89f4db1655eeec3 100644 (file)
@@ -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
index d9f0ae05ed40dbea1f3a4e72a3d4dfa24031c5de..1e2f9e3aebb2cd83b6ae296b833efc75c3e10f92 100644 (file)
@@ -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
index 952c03b05c7f4d3f7460cad4147212f994032f9d..f3e07fd08ee8e278975c98c59e3f0a03e4198d0d 100644 (file)
@@ -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",
 ]
index 44cb16334742fc227de254859dde833e0ee18229..1f27d721fabbb4c52ffbb87ff17881a0c4c1e46d 100644 (file)
@@ -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`.
index 9f1228687e27dcdf63d440dbabdd3eb04d162636..17a0d2ab3f0601f92b56bccff8b7ba0adde227d5 100755 (executable)
@@ -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"
index a85865019c1358bbd25421c9d4ed7de1666037c8..1001c522052c800b15d170971d187b6987092288 100755 (executable)
@@ -20,4 +20,4 @@ fi
 cmd=$1
 shift
 
-RUSTDOCFLAGS=$RUSTFLAGS cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
+RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
index bd2d915f589a0828fb13e5b777117e62f335b737..98caeb7407e084fc585bd49733b3882a0b6ec104 100644 (file)
@@ -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 (file)
index e69de29..0000000
index 4e9771311efbdf057464c8c454ce135e66b06d30..a03d26c0467e18aadccbc22c1ba417012ca15538 100644 (file)
@@ -1 +1 @@
-nightly-2021-08-12
+nightly-2021-09-17
index 4e622efcaa0baefbac06673e155d12f5483c7113..6378a31202c1b88b1e2d213b5518e28c422b52a8 100644 (file)
@@ -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];
index 52acf4f2d26e727fa973f0f9d93f82ff8476dc45..d749d763402b810ebd35508cbb5ec1b33ea2dfba 100644 (file)
@@ -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<PathBuf>,
     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_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;
-            }
-
-            // Don't include Rust objects if LTO is enabled
-            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
-                return true;
-            }
+    fn add_archive<F>(&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();
 
-            // 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;
+        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;
+        }
 
-            // 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<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(())
-    }
-}
index e4d57c39de48af670fd153f6394e7041dc9d22b0..a684c34b644101d6ba1a31f25f4ab869063e4ecd 100644 (file)
-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;
 
+
+// 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.
+// 
+
+const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
+const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
+
+
+struct AsmOutOperand<'a, 'tcx, 'gcc> {
+    rust_idx: usize,
+    constraint: &'a str,
+    late: bool,
+    readwrite: bool,
+
+    tmp_var: LValue<'gcc>,
+    out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
+}
+
+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('&');
+        }
+
+        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<PlaceRef<'tcx, RValue<'gcc>>>, mut _inputs: Vec<RValue<'gcc>>, _span: Span) -> bool {
-        // TODO(antoyo)
-        return true;
+    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], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) {
+    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);
 
-        let intel_dialect =
-            match asm_arch {
-                InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => true,
-                _ => false,
-            };
+        // 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;
 
-        // 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() {
+        // 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 { 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::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::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);
+
+                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();
                 }
-                _ => {}
             }
         }
 
-        // All output operands must come before the input operands, hence the 2 loops.
-        for (idx, op) in operands.iter().enumerate() {
+        // 2. Register variables.
+        for (rust_idx, op) in rust_operands.iter().enumerate() {
             match *op {
-                InlineAsmOperandRef::In { .. } | InlineAsmOperandRef::InOut { .. } => {
-                    operand_numbers.insert(idx, current_number);
-                    current_number += 1;
-                },
-                _ => (),
+                // `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
+                }
             }
         }
 
-        // Build the template string
-        let mut template_str = String::new();
+        // 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) => {
-                    if string.contains('%') {
-                        for c in string.chars() {
-                            if c == '%' {
-                                template_str.push_str("%%");
-                            }
-                            else {
-                                template_str.push(c);
-                            }
-                        }
+                    // 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);
                     }
-                    else {
-                        template_str.push_str(string)
+
+                    for s in iter {
+                        template_str.push_str("%%");
+                        template_str.push_str(s);
                     }
                 }
                 InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
-                    match operands[operand_idx] {
-                        InlineAsmOperandRef::Out { reg, place: Some(_), ..  } => {
+                    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);
-                            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 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);
-                            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]));
-                            }
+                            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);
                         }
-                        InlineAsmOperandRef::SymFn { .. }
-                        | InlineAsmOperandRef::SymStatic { .. } => {
-                            unimplemented!();
-                            // Only emit the raw symbol name
-                            //template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx]));
-                        }
                     }
                 }
             }
         }
 
+        if !intel_dialect {
+            template_str.push_str(INTEL_SYNTAX_INS);
+        }
+        
+        // 4. Generate Extended Asm block
+
         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));
+        for op in &outputs {
+            extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
+        }
 
-                    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);
-                    }
+        for op in &inputs {
+            extended_asm.add_input_operand(None, &op.constraint, op.val);
+        }
 
-                    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());
-                }
-                _ => {}
+        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);                
             }
         }
 
-        // 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);
+    }
+}
+
+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!(),
     }
 }
index 0f7db911552a17cd5fd529ee7389222f832bbdd7..5d06d71953c6675408de6caed1b5bd7fe52e4c65 100644 (file)
@@ -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::{
     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<u128>) {
+    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());
         }
index 4ea084ef729d31e7f8139efac8ad8450b72396c6..e402e0e91f13359b73edac08a6853f0083555d8b 100644 (file)
@@ -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;
index 752ba99af9ce3630b4d4deb9c63fdc396fabbed3..a24fe0df911a6db3344d0d256c8e05d7f3b4cc05 100644 (file)
 };
 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<u128> {
         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) => {
index 9b7959503ab1b9543317da8567bdfe3fe0a8d9fc..df13fa79f0696dd1e52ccbeec77adfb9971c7e54 100644 (file)
@@ -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;
index 7ab1ca0d771c1be318398c6315eecdbe897ad183..ef687dd22c6da2c7fd6462dd45021bf2853a7ffd 100644 (file)
 };
 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<Self::Function> {
             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> {
@@ -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)
+        }
     }
 }
 
index a79be7cfc74c8ca850b7ecf4ecb9a14784020a5b..3dc4f61a7ac3cd819f62cfb7300940e6f384e2b0 100644 (file)
@@ -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.
index 6febccff1ffacd6270f1cd61f6099eac1d1b52f9..793e5c48d0a365abe81b907585621c553b93a6dd 100644 (file)
@@ -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<Self::Module>, u64) {
index f9ec933dd3abc24e46075c8a8ff29e016d3530e0..cedeb54f60bf2e0ffa307676cc29ab4799d21fbd 100644 (file)
@@ -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));
index 9302f57b642af1fc0cd3cf70936b0b847476deb8..2ff2ee7b8522651b28b851a90c15cdb84686ce04 100644 (file)
@@ -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
index 06faa98d9b1f045987686d96f3d1d7e2b38931cb..12df1f8af2f0b2be40d274c4e44809fe8365b4fb 100755 (executable)
@@ -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 <<EOF
 [rust]
 codegen-backends = []
+deny-warnings = false
 
 [build]
 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
 
-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
   rm $test
 done
index bd76153e047e4929ac8f908ca3bbfb3c77fdfc55..9c0055b0b6b5ec36fdbbcfc2d7eda5645a3a7b63 100644 (file)
@@ -62,5 +62,92 @@ fn main() {
     }
     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);
 }