From 5d25b8fc45f66cdd1b19c87cce38eda86141dcf8 Mon Sep 17 00:00:00 2001 From: yvt Date: Sat, 23 Apr 2022 23:39:27 +0900 Subject: [PATCH 1/2] Convert inline assembly `sym` operands into GCC input operands This commit updates `::codegen_inline_asm` to convert `sym` operands into `"X" (&func_or_static)` input operands to indicate the dependency on the referenced symbols and prevent them from being eliminated. We follow the suit of the LLVM codegen with a mixture of its differing techniques for `asm!` and `global_asm!`. The codegen module generates input operands for the `sym` operands (as in `asm!` in cg_llvm). However, the codegen module replaces all placeholders with mangled symbol names before passing the assembly template string to the backend (as in `global_asm!` in cg_llvm), which means these input operands are never referenced in the final assembly template string. Unlike the LLVM codegen, the input operand constraint must be `X` instead of `s`. If the `s` constraint is used, GCC will employ checks to make sure that the operand can really be represented by a simple symbolic constant, thus rejecting symbols requiring GOT, etc. to resolve. Such checks are unnecessary for Rust `sym` as it's up to programmers to handle such complex cases, e.g., by manually appending GOT addressing modifiers to the substituted symbol names. Using the `X` constraint doesn't seem to generate any extra code, so this will not compromise the property of naked functions. --- src/asm.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/asm.rs b/src/asm.rs index 8a74c4c07e0c..053a6c595e97 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -13,6 +13,7 @@ use std::borrow::Cow; use crate::builder::Builder; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; +use crate::callee::get_fn; // Rust asm! and GCC Extended Asm semantics differ substantially. @@ -343,9 +344,24 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // processed in the previous pass } - InlineAsmOperandRef::Const { .. } - | InlineAsmOperandRef::SymFn { .. } - | InlineAsmOperandRef::SymStatic { .. } => { + InlineAsmOperandRef::SymFn { instance } => { + inputs.push(AsmInOperand { + constraint: "X".into(), + rust_idx, + val: self.cx.rvalue_as_function(get_fn(self.cx, instance)) + .get_address(None), + }); + } + + InlineAsmOperandRef::SymStatic { def_id } => { + inputs.push(AsmInOperand { + constraint: "X".into(), + rust_idx, + val: self.cx.get_static(def_id).get_address(None), + }); + } + + InlineAsmOperandRef::Const { .. } => { // processed in the previous pass } } From 63ffdfdd1776afa2e82bbd3d2ff8ff7b7f0d5b67 Mon Sep 17 00:00:00 2001 From: yvt Date: Mon, 25 Apr 2022 21:19:42 +0900 Subject: [PATCH 2/2] Add compilation tests with optimization enabled Introduces a new variant of `tests/lib.rs` that compiles the source files in `tests/run` with `-Copt-level=3`. --- Cargo.toml | 8 ++++++-- tests/{lib.rs => lang_tests_common.rs} | 20 +++++++++++++++++++- tests/lang_tests_debug.rs | 5 +++++ tests/lang_tests_release.rs | 5 +++++ tests/run/int_overflow.rs | 17 ++++++++++++++--- 5 files changed, 49 insertions(+), 6 deletions(-) rename tests/{lib.rs => lang_tests_common.rs} (77%) create mode 100644 tests/lang_tests_debug.rs create mode 100644 tests/lang_tests_release.rs diff --git a/Cargo.toml b/Cargo.toml index 86278b469830..211d19a8dc89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,12 @@ license = "MIT OR Apache-2.0" crate-type = ["dylib"] [[test]] -name = "lang_tests" -path = "tests/lib.rs" +name = "lang_tests_debug" +path = "tests/lang_tests_debug.rs" +harness = false +[[test]] +name = "lang_tests_release" +path = "tests/lang_tests_release.rs" harness = false [features] diff --git a/tests/lib.rs b/tests/lang_tests_common.rs similarity index 77% rename from tests/lib.rs rename to tests/lang_tests_common.rs index 8ee35b30bc8e..8e378177e245 100644 --- a/tests/lib.rs +++ b/tests/lang_tests_common.rs @@ -1,3 +1,4 @@ +//! The common code for `tests/lang_tests_*.rs` use std::{ env::{self, current_dir}, path::PathBuf, @@ -7,7 +8,15 @@ use std::{ use lang_tester::LangTester; use tempfile::TempDir; -fn main() { +/// Controls the compile options (e.g., optimization level) used to compile +/// test code. +#[allow(dead_code)] // Each test crate picks one variant +pub enum Profile { + Debug, + Release, +} + +pub fn main_inner(profile: Profile) { let tempdir = TempDir::new().expect("temp dir"); let current_dir = current_dir().expect("current dir"); let current_dir = current_dir.to_str().expect("current dir").to_string(); @@ -42,6 +51,15 @@ fn main() { "-o", exe.to_str().expect("to_str"), path.to_str().expect("to_str"), ]); + match profile { + Profile::Debug => {} + Profile::Release => { + compiler.args(&[ + "-C", "opt-level=3", + "-C", "lto=no", + ]); + } + } // Test command 2: run `tempdir/x`. let runtime = Command::new(exe); vec![("Compiler", compiler), ("Run-time", runtime)] diff --git a/tests/lang_tests_debug.rs b/tests/lang_tests_debug.rs new file mode 100644 index 000000000000..96bd74883ff0 --- /dev/null +++ b/tests/lang_tests_debug.rs @@ -0,0 +1,5 @@ +mod lang_tests_common; + +fn main() { + lang_tests_common::main_inner(lang_tests_common::Profile::Debug); +} diff --git a/tests/lang_tests_release.rs b/tests/lang_tests_release.rs new file mode 100644 index 000000000000..35d5d60c33ee --- /dev/null +++ b/tests/lang_tests_release.rs @@ -0,0 +1,5 @@ +mod lang_tests_common; + +fn main() { + lang_tests_common::main_inner(lang_tests_common::Profile::Release); +} diff --git a/tests/run/int_overflow.rs b/tests/run/int_overflow.rs index 6477b8398280..ea2c5add962a 100644 --- a/tests/run/int_overflow.rs +++ b/tests/run/int_overflow.rs @@ -1,7 +1,7 @@ // Compiler: // // Run-time: -// stdout: Panicking +// stdout: Success // status: signal #![allow(unused_attributes)] @@ -64,7 +64,9 @@ mod intrinsics { #[no_mangle] pub fn panic(_msg: &str) -> ! { unsafe { - libc::puts("Panicking\0" as *const str as *const u8); + // Panicking is expected iff overflow checking is enabled. + #[cfg(debug_assertions)] + libc::puts("Success\0" as *const str as *const u8); libc::fflush(libc::stdout); intrinsics::abort(); } @@ -124,6 +126,15 @@ impl Add for isize { #[start] fn main(mut argc: isize, _argv: *const *const u8) -> isize { let int = 9223372036854775807isize; - let int = int + argc; + let int = int + argc; // overflow + + // If overflow checking is disabled, we should reach here. + #[cfg(not(debug_assertions))] + unsafe { + libc::puts("Success\0" as *const str as *const u8); + libc::fflush(libc::stdout); + intrinsics::abort(); + } + int }