From 591584e71f7d8a613d586066c8b01c1eecf08f35 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Tue, 26 May 2020 23:48:36 +0300 Subject: [PATCH 01/18] Add tests for 'impl Default for [T; N]' --- src/libcore/tests/array.rs | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index c2a816f0a7d90..41855a9a8cbab 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -241,3 +241,44 @@ fn iterator_drops() { } assert_eq!(i.get(), 5); } + +#[test] +fn array_default_impl_avoids_leaks_on_panic() { + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Debug)] + struct Bomb(usize); + + impl Default for Bomb { + fn default() -> Bomb { + if COUNTER.load(Relaxed) == 3 { + panic!("bomb limit exceeded"); + } + + COUNTER.fetch_add(1, Relaxed); + Bomb(COUNTER.load(Relaxed)) + } + } + + impl Drop for Bomb { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default()); + let panic_msg = match res { + Ok(_) => unreachable!(), + Err(p) => p.downcast::<&'static str>().unwrap(), + }; + assert_eq!(*panic_msg, "bomb limit exceeded"); + // check that all bombs are successfully dropped + assert_eq!(COUNTER.load(Relaxed), 0); +} + +#[test] +fn empty_array_is_always_default() { + struct DoesNotImplDefault; + + let _arr = <[DoesNotImplDefault; 0]>::default(); +} From 3313bf62ac45fab2c39e49c788423153754087a9 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Thu, 28 May 2020 20:45:21 +0300 Subject: [PATCH 02/18] Skip leak test on targets without panic=unwind --- src/libcore/tests/array.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index 41855a9a8cbab..4bc44e98fc802 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -242,7 +242,14 @@ fn iterator_drops() { assert_eq!(i.get(), 5); } +// This test does not work on targets without panic=unwind support. +// To work around this problem, test is marked is should_panic, so it will +// be automagically skipped on unsuitable targets, such as +// wasm32-unknown-unkown. +// +// It means that we use panic for indicating success. #[test] +#[should_panic(expected = "test succeeded")] fn array_default_impl_avoids_leaks_on_panic() { use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; static COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -274,6 +281,7 @@ fn array_default_impl_avoids_leaks_on_panic() { assert_eq!(*panic_msg, "bomb limit exceeded"); // check that all bombs are successfully dropped assert_eq!(COUNTER.load(Relaxed), 0); + panic!("test succeeded") } #[test] From 64a6de25eaa4ec1a251862d81392bc9e22704c21 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 15 Jun 2020 13:15:47 +0000 Subject: [PATCH 03/18] Join mutiple lines if it is more readable --- src/libcore/panic.rs | 9 +++------ src/libcore/panicking.rs | 3 +-- src/librustc_middle/ty/context.rs | 3 +-- src/libstd/panicking.rs | 13 +++---------- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 1ffd493d8130b..c7009b76e8148 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -39,8 +39,7 @@ pub struct PanicInfo<'a> { impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -55,8 +54,7 @@ impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -244,8 +242,7 @@ impl<'a> Location<'a> { impl<'a> Location<'a> { #![unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 3ed5e65e11c62..766c69a5f9420 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -22,8 +22,7 @@ #![allow(dead_code, missing_docs)] #![unstable( feature = "core_panic", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs index d5be3508d2d80..de2e1046f1cbb 100644 --- a/src/librustc_middle/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -189,8 +189,7 @@ fn validate_hir_id_for_typeck_tables( if hir_id.owner != hir_owner { ty::tls::with(|tcx| { bug!( - "node {} with HirId::owner {:?} cannot be placed in \ - TypeckTables with hir_owner {:?}", + "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}", tcx.hir().node_to_string(hir_id), hir_id.owner, hir_owner diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 6a120f930ab15..d22ac1d538584 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -201,8 +201,7 @@ fn default_hook(info: &PanicInfo<'_>) { if FIRST_PANIC.swap(false, Ordering::SeqCst) { let _ = writeln!( err, - "note: run with `RUST_BACKTRACE=1` \ - environment variable to display a backtrace" + "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace" ); } } @@ -454,10 +453,7 @@ fn rust_panic_with_hook( // process real quickly as we don't want to try calling it again as it'll // probably just panic again. if panics > 2 { - util::dumb_print(format_args!( - "thread panicked while processing \ - panic. aborting.\n" - )); + util::dumb_print(format_args!("thread panicked while processing panic. aborting.\n")); intrinsics::abort() } @@ -489,10 +485,7 @@ fn rust_panic_with_hook( // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the thread cleanly. - util::dumb_print(format_args!( - "thread panicked while panicking. \ - aborting.\n" - )); + util::dumb_print(format_args!("thread panicked while panicking. aborting.\n")); intrinsics::abort() } From 9e510085ecaedaee86b44410a4b3e4c85d97d6e0 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Mon, 15 Jun 2020 15:19:02 +0200 Subject: [PATCH 04/18] Complete the std::time documentation to warn about the inconsistencies between OS --- src/libstd/time.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libstd/time.rs b/src/libstd/time.rs index c36e78b1d004e..c58168bd446d7 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -60,6 +60,21 @@ pub use core::time::Duration; /// } /// ``` /// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let max_nanoseconds = u64::MAX / 1_000_000_000; +/// let duration = Duration::new(max_nanoseconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// /// # Underlying System calls /// Currently, the following system calls are being used to get the current time using `now()`: /// From fe7456ce94b8edd549176d004a4435e1132c9c36 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 15 Jun 2020 14:17:58 +0000 Subject: [PATCH 05/18] Use track caller for bug! macro --- src/librustc_middle/macros.rs | 18 +++++++++++------- src/librustc_middle/util/bug.rs | 21 +++++++++------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/librustc_middle/macros.rs b/src/librustc_middle/macros.rs index 88ddd96eec8f5..a5482b7bdcfeb 100644 --- a/src/librustc_middle/macros.rs +++ b/src/librustc_middle/macros.rs @@ -1,16 +1,20 @@ #[macro_export] macro_rules! bug { - () => ( bug!("impossible case reached") ); - ($($message:tt)*) => ({ - $crate::util::bug::bug_fmt(file!(), line!(), format_args!($($message)*)) - }) + () => ( $crate::bug!("impossible case reached") ); + ($msg:expr) => ({ $crate::util::bug::bug_fmt(::std::format_args!($msg)) }); + ($msg:expr,) => ({ $crate::bug!($msg) }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) + }); } #[macro_export] macro_rules! span_bug { - ($span:expr, $($message:tt)*) => ({ - $crate::util::bug::span_bug_fmt(file!(), line!(), $span, format_args!($($message)*)) - }) + ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); + ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) }); + ($span:expr, $fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) + }); } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_middle/util/bug.rs b/src/librustc_middle/util/bug.rs index 54cd8a29f9474..9c3a97d8332f1 100644 --- a/src/librustc_middle/util/bug.rs +++ b/src/librustc_middle/util/bug.rs @@ -3,34 +3,31 @@ use crate::ty::{tls, TyCtxt}; use rustc_span::{MultiSpan, Span}; use std::fmt; +use std::panic::Location; #[cold] #[inline(never)] -pub fn bug_fmt(file: &'static str, line: u32, args: fmt::Arguments<'_>) -> ! { +#[track_caller] +pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! { // this wrapper mostly exists so I don't have to write a fully // qualified path of None:: inside the bug!() macro definition - opt_span_bug_fmt(file, line, None::, args); + opt_span_bug_fmt(None::, args, Location::caller()); } #[cold] #[inline(never)] -pub fn span_bug_fmt>( - file: &'static str, - line: u32, - span: S, - args: fmt::Arguments<'_>, -) -> ! { - opt_span_bug_fmt(file, line, Some(span), args); +#[track_caller] +pub fn span_bug_fmt>(span: S, args: fmt::Arguments<'_>) -> ! { + opt_span_bug_fmt(Some(span), args, Location::caller()); } fn opt_span_bug_fmt>( - file: &'static str, - line: u32, span: Option, args: fmt::Arguments<'_>, + location: &Location<'_>, ) -> ! { tls::with_opt(move |tcx| { - let msg = format!("{}:{}: {}", file, line, args); + let msg = format!("{}: {}", location, args); match (tcx, span) { (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg), (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), From e857696cf8c3b4a4381d00424f165c10f93a9ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 14 Jun 2020 21:36:25 -0700 Subject: [PATCH 06/18] Tweak "non-primitive cast" error - Suggest borrowing expression if it would allow cast to work. - Suggest using `::from()` when appropriate. - Minor tweak to `;` typo suggestion. Partily address #47136. --- src/libcore/convert/mod.rs | 1 + src/librustc_parse/parser/diagnostics.rs | 2 +- src/librustc_span/symbol.rs | 1 + src/librustc_typeck/check/cast.rs | 107 ++++++++++++++---- src/test/ui/cast/cast-from-nil.stderr | 4 +- src/test/ui/cast/cast-to-bare-fn.stderr | 8 +- src/test/ui/cast/cast-to-nil.stderr | 4 +- ...-to-unsized-trait-object-suggestion.stderr | 2 +- src/test/ui/closures/closure-no-fn-3.stderr | 4 +- .../ui/coercion/coerce-to-bang-cast.stderr | 8 +- .../const-eval/const-eval-overflow-4b.stderr | 2 +- src/test/ui/error-codes/E0604.stderr | 2 +- src/test/ui/error-codes/E0605.stderr | 8 +- src/test/ui/error-festival.stderr | 6 +- src/test/ui/fat-ptr-cast.stderr | 4 +- src/test/ui/issues/issue-10991.stderr | 4 +- src/test/ui/issues/issue-16048.rs | 4 +- src/test/ui/issues/issue-16048.stderr | 8 +- src/test/ui/issues/issue-17441.stderr | 2 +- src/test/ui/issues/issue-22289.stderr | 7 +- src/test/ui/issues/issue-22312.rs | 2 +- src/test/ui/issues/issue-22312.stderr | 7 +- src/test/ui/issues/issue-2995.stderr | 4 +- src/test/ui/issues/issue-45730.stderr | 18 +-- .../ui/mismatched_types/cast-rfc0401.stderr | 22 +--- .../ui/mismatched_types/issue-26480.stderr | 3 +- src/test/ui/nonscalar-cast.fixed | 16 +++ src/test/ui/nonscalar-cast.rs | 8 ++ src/test/ui/nonscalar-cast.stderr | 6 +- .../ui/order-dependent-cast-inference.stderr | 6 +- .../ui/tag-variant-cast-non-nullary.fixed | 20 ++++ src/test/ui/tag-variant-cast-non-nullary.rs | 11 ++ .../ui/tag-variant-cast-non-nullary.stderr | 6 +- .../never_reveal_concrete_type.stderr | 4 +- .../uninhabited/uninhabited-enum-cast.stderr | 4 +- 35 files changed, 204 insertions(+), 121 deletions(-) create mode 100644 src/test/ui/nonscalar-cast.fixed create mode 100644 src/test/ui/tag-variant-cast-non-nullary.fixed diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index eef9ee7cb0093..8ff1ced53b071 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -374,6 +374,7 @@ pub trait Into: Sized { /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from /// [book]: ../../book/ch09-00-error-handling.html +#[rustc_diagnostic_item = "from_trait"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( all(_Self = "&str", T = "std::string::String"), diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index 660a63841bcef..dafc0a9e048a4 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -961,7 +961,7 @@ impl<'a> Parser<'a> { self.bump(); let sp = self.prev_token.span; self.struct_span_err(sp, &msg) - .span_suggestion(sp, "change this to `;`", ";".to_string(), appl) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) .emit(); return Ok(()); } else if self.look_ahead(0, |t| { diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index d165409696eca..9925e631c5c20 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -343,6 +343,7 @@ symbols! { from_method, from_ok, from_usize, + from_trait, fundamental, future, Future, diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 46d6706cbf429..bad009f4039af 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -43,6 +43,7 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; use rustc_session::lint; use rustc_session::Session; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; @@ -333,10 +334,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { "only `u8` can be cast as `char`, not `{}`", self.expr_ty ) + .span_label(self.span, "invalid cast") .emit(); } CastError::NonScalar => { - type_error_struct!( + let mut err = type_error_struct!( fcx.tcx.sess, self.span, self.expr_ty, @@ -344,12 +346,75 @@ impl<'a, 'tcx> CastCheck<'tcx> { "non-primitive cast: `{}` as `{}`", self.expr_ty, fcx.ty_to_string(self.cast_ty) - ) - .note( - "an `as` expression can only be used to convert between \ - primitive types. Consider using the `From` trait", - ) - .emit(); + ); + let mut sugg = None; + if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } + } + if let Some(sugg) = sugg { + err.span_label(self.span, "invalid cast"); + err.span_suggestion_verbose( + self.expr.span.shrink_to_lo(), + "borrow the value for the cast to be valid", + sugg, + Applicability::MachineApplicable, + ); + } else if !matches!( + self.cast_ty.kind, + ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) + ) { + let mut label = true; + // Check `impl From for self.cast_ty {}` for accurate suggestion: + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { + let ty = fcx.resolve_vars_if_possible(&self.cast_ty); + // Erase regions to avoid panic in `prove_value` when calling + // `type_implements_trait`. + let ty = fcx.tcx.erase_regions(&ty); + let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); + let expr_ty = fcx.tcx.erase_regions(&expr_ty); + let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); + // Check for infer types because cases like `Option<{integer}>` would + // panic otherwise. + if !expr_ty.has_infer_types() + && fcx.tcx.type_implements_trait(( + from_trait, + ty, + ty_params, + fcx.param_env, + )) + { + label = false; + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + let msg = "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object"; + if label { + err.span_label(self.span, msg); + } else { + err.note(msg); + } + } else { + err.span_label(self.span, "invalid cast"); + } + err.emit(); } CastError::SizedUnsizedCast => { use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; @@ -370,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let mut err = struct_span_err!( fcx.tcx.sess, - self.span, + if unknown_cast_to { self.cast_span } else { self.span }, E0641, "cannot cast {} a pointer of an unknown kind", if unknown_cast_to { "to" } else { "from" } ); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); if unknown_cast_to { - err.span_suggestion_short( - self.cast_span, - "consider giving more type information", - String::new(), - Applicability::Unspecified, + err.span_label(self.cast_span, "needs more type information"); + err.note( + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } else { + err.span_label( + self.span, + "the type information given here is insufficient to check whether \ + the pointer cast is valid", ); } err.emit(); @@ -438,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(s) => { err.span_suggestion( self.cast_span, - "try casting to a `Box` instead", + "you can cast to a `Box` instead", format!("Box<{}>", s), Applicability::MachineApplicable, ); } Err(_) => { - err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr)); + err.span_help( + self.cast_span, + &format!("you might have meant `Box<{}>`", tstr), + ); } } } diff --git a/src/test/ui/cast/cast-from-nil.stderr b/src/test/ui/cast/cast-from-nil.stderr index c8e3628a7ded8..dab133cfb4b67 100644 --- a/src/test/ui/cast/cast-from-nil.stderr +++ b/src/test/ui/cast/cast-from-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32` --> $DIR/cast-from-nil.rs:2:21 | LL | fn main() { let u = (assert!(true) as u32); } - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-bare-fn.stderr b/src/test/ui/cast/cast-to-bare-fn.stderr index 84933dca929a4..d97b0c5f8aadc 100644 --- a/src/test/ui/cast/cast-to-bare-fn.stderr +++ b/src/test/ui/cast/cast-to-bare-fn.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize --> $DIR/cast-to-bare-fn.rs:5:13 | LL | let x = foo as extern "C" fn() -> isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` --> $DIR/cast-to-bare-fn.rs:7:13 | LL | let y = v as extern "Rust" fn(isize) -> (isize, isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to 2 previous errors diff --git a/src/test/ui/cast/cast-to-nil.stderr b/src/test/ui/cast/cast-to-nil.stderr index 478f6b69dafc8..29a9baffd71d7 100644 --- a/src/test/ui/cast/cast-to-nil.stderr +++ b/src/test/ui/cast/cast-to-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()` --> $DIR/cast-to-nil.rs:2:21 | LL | fn main() { let u = 0u32 as (); } - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr index ffa02533d8b66..9b86f8d4def86 100644 --- a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr @@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma LL | Box::new(1) as dyn Send; | ^^^^^^^^^^^^^^^-------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr index ab6056b65473e..4b3b4be798fc1 100644 --- a/src/test/ui/closures/closure-no-fn-3.stderr +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b --> $DIR/closure-no-fn-3.rs:6:27 | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/coercion/coerce-to-bang-cast.stderr b/src/test/ui/coercion/coerce-to-bang-cast.stderr index ff30ebc09c63a..d3adbd5158dbb 100644 --- a/src/test/ui/coercion/coerce-to-bang-cast.stderr +++ b/src/test/ui/coercion/coerce-to-bang-cast.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:6:13 | LL | let y = {return; 22} as !; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `i32` as `!` --> $DIR/coerce-to-bang-cast.rs:11:13 | LL | let y = 22 as !; - | ^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr index 5b2c4116c4b1d..e4d256c0ad192 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:25:13 | LL | : [u32; 5i8 as char as usize] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ invalid cast error: aborting due to 3 previous errors diff --git a/src/test/ui/error-codes/E0604.stderr b/src/test/ui/error-codes/E0604.stderr index 5861bdcb7a953..18835310bd5e8 100644 --- a/src/test/ui/error-codes/E0604.stderr +++ b/src/test/ui/error-codes/E0604.stderr @@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/E0604.rs:2:5 | LL | 1u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index 95e899db8b7e9..f23d2008e0b5f 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/E0605.rs:3:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/E0605.rs:6:5 | LL | v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index 7f524230ef006..905195d4ad963 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/error-festival.rs:25:5 | LL | 0u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/error-festival.rs:29:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0054]: cannot cast as `bool` --> $DIR/error-festival.rs:33:24 diff --git a/src/test/ui/fat-ptr-cast.stderr b/src/test/ui/fat-ptr-cast.stderr index 93e1471838f72..56d5a26beb04e 100644 --- a/src/test/ui/fat-ptr-cast.stderr +++ b/src/test/ui/fat-ptr-cast.stderr @@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize` --> $DIR/fat-ptr-cast.rs:14:5 | LL | b as usize; - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const [i32]` as `usize` is invalid --> $DIR/fat-ptr-cast.rs:15:5 diff --git a/src/test/ui/issues/issue-10991.stderr b/src/test/ui/issues/issue-10991.stderr index f12539b47cf44..5b8a182338693 100644 --- a/src/test/ui/issues/issue-10991.stderr +++ b/src/test/ui/issues/issue-10991.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize` --> $DIR/issue-10991.rs:3:14 | LL | let _t = nil as usize; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16048.rs b/src/test/ui/issues/issue-16048.rs index 7d24f3a40a742..eaf6acff26bf3 100644 --- a/src/test/ui/issues/issue-16048.rs +++ b/src/test/ui/issues/issue-16048.rs @@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> { } impl<'a> NoLifetime for Foo<'a> { - fn get<'p, T : Test<'a>>(&self) -> T { + fn get<'p, T: Test<'a> + From>>(&self) -> T { //~^ ERROR E0195 //~| NOTE lifetimes do not match method in trait return *self as T; //~^ ERROR non-primitive cast: `Foo<'a>` as `T` - //~| NOTE an `as` expression can only be used to convert between primitive types. + //~| NOTE an `as` expression can only be used to convert between primitive types } } diff --git a/src/test/ui/issues/issue-16048.stderr b/src/test/ui/issues/issue-16048.stderr index a137bcdf1915e..73610942d7a7e 100644 --- a/src/test/ui/issues/issue-16048.stderr +++ b/src/test/ui/issues/issue-16048.stderr @@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra LL | fn get<'p, T : Test<'p>>(&self) -> T; | ------------------ lifetimes in impl do not match this method in trait ... -LL | fn get<'p, T : Test<'a>>(&self) -> T { - | ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait +LL | fn get<'p, T: Test<'a> + From>>(&self) -> T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait error[E0605]: non-primitive cast: `Foo<'a>` as `T` --> $DIR/issue-16048.rs:24:16 | LL | return *self as T; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17441.stderr b/src/test/ui/issues/issue-17441.stderr index 0ab035515a051..b63a3995d255d 100644 --- a/src/test/ui/issues/issue-17441.stderr +++ b/src/test/ui/issues/issue-17441.stderr @@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box` as `dyn std::fmt::D LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^------------------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug` --> $DIR/issue-17441.rs:8:16 diff --git a/src/test/ui/issues/issue-22289.stderr b/src/test/ui/issues/issue-22289.stderr index cc7ace30cabef..4c35deb1fbe4e 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)` --> $DIR/issue-22289.rs:2:5 | LL | 0 as &dyn std::any::Any; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | &0 as &dyn std::any::Any; + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22312.rs b/src/test/ui/issues/issue-22312.rs index 250fec2588702..4e359b3412a71 100644 --- a/src/test/ui/issues/issue-22312.rs +++ b/src/test/ui/issues/issue-22312.rs @@ -1,6 +1,6 @@ use std::ops::Index; -pub trait Array2D: Index { +pub trait Array2D: Index + Sized { fn rows(&self) -> usize; fn columns(&self) -> usize; fn get<'a>(&'a self, y: usize, x: usize) -> Option<&'a >::Output> { diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index fc32fd376b75a..28564b074633b 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `Self` as `&dyn std::ops::Index $DIR/issue-22312.rs:11:24 | LL | let indexer = &(*self as &dyn Index>::Output>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | let indexer = &(&*self as &dyn Index>::Output>); + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-2995.stderr b/src/test/ui/issues/issue-2995.stderr index c316780d5f6a5..9f5968399a37d 100644 --- a/src/test/ui/issues/issue-2995.stderr +++ b/src/test/ui/issues/issue-2995.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize` --> $DIR/issue-2995.rs:2:22 | LL | let _q: &isize = p as &isize; - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-45730.stderr b/src/test/ui/issues/issue-45730.stderr index d4ddba52df14a..d00f3d91b49da 100644 --- a/src/test/ui/issues/issue-45730.stderr +++ b/src/test/ui/issues/issue-45730.stderr @@ -1,30 +1,24 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:3:23 + --> $DIR/issue-45730.rs:3:28 | LL | let x: *const _ = 0 as _; - | ^^^^^- - | | - | help: consider giving more type information + | ^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:5:23 + --> $DIR/issue-45730.rs:5:28 | LL | let x: *const _ = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:8:13 + --> $DIR/issue-45730.rs:8:44 | LL | let x = 0 as *const i32 as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ - | | - | help: consider giving more type information + | ^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index f94dfd100a6f4..95936de218b8f 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -24,41 +24,31 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 | LL | let _ = v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:30:13 | LL | let _ = v as E; - | ^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `fn()` --> $DIR/cast-rfc0401.rs:31:13 | LL | let _ = v as fn(); - | ^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `*const u8` as `(u32,)` --> $DIR/cast-rfc0401.rs:32:13 | LL | let _ = v as (u32,); - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:33:13 | LL | let _ = Some(&v) as *const u8; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const u8` as `f32` is invalid --> $DIR/cast-rfc0401.rs:35:13 @@ -102,7 +92,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/cast-rfc0401.rs:41:13 | LL | let _ = 0x61u32 as char; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ invalid cast error[E0606]: casting `bool` as `f32` is invalid --> $DIR/cast-rfc0401.rs:43:13 diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 69a9d03e474ba..d39b0a3207763 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -17,12 +17,11 @@ error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:22:19 | LL | ($x:expr) => ($x as ()) - | ^^^^^^^^ + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object ... LL | cast!(2); | --------- in this macro invocation | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/nonscalar-cast.fixed b/src/test/ui/nonscalar-cast.fixed new file mode 100644 index 0000000000000..0a4b98469b2b6 --- /dev/null +++ b/src/test/ui/nonscalar-cast.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#[derive(Debug)] +struct Foo { + x: isize +} + +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + +fn main() { + println!("{}", isize::from(Foo { x: 1 })); //~ non-primitive cast: `Foo` as `isize` [E0605] +} diff --git a/src/test/ui/nonscalar-cast.rs b/src/test/ui/nonscalar-cast.rs index 7e6f1fd038fb7..59fcf09666b24 100644 --- a/src/test/ui/nonscalar-cast.rs +++ b/src/test/ui/nonscalar-cast.rs @@ -1,8 +1,16 @@ +// run-rustfix + #[derive(Debug)] struct Foo { x: isize } +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + fn main() { println!("{}", Foo { x: 1 } as isize); //~ non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/src/test/ui/nonscalar-cast.stderr b/src/test/ui/nonscalar-cast.stderr index 9338688b037ff..2a7037121876d 100644 --- a/src/test/ui/nonscalar-cast.stderr +++ b/src/test/ui/nonscalar-cast.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `Foo` as `isize` - --> $DIR/nonscalar-cast.rs:7:20 + --> $DIR/nonscalar-cast.rs:15:20 | LL | println!("{}", Foo { x: 1 } as isize); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(Foo { x: 1 })` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/order-dependent-cast-inference.stderr b/src/test/ui/order-dependent-cast-inference.stderr index ad50b415869dd..9f4ac0fea36ef 100644 --- a/src/test/ui/order-dependent-cast-inference.stderr +++ b/src/test/ui/order-dependent-cast-inference.stderr @@ -1,10 +1,8 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/order-dependent-cast-inference.rs:5:17 + --> $DIR/order-dependent-cast-inference.rs:5:22 | LL | let mut y = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid diff --git a/src/test/ui/tag-variant-cast-non-nullary.fixed b/src/test/ui/tag-variant-cast-non-nullary.fixed new file mode 100644 index 0000000000000..53e68c2ac6af6 --- /dev/null +++ b/src/test/ui/tag-variant-cast-non-nullary.fixed @@ -0,0 +1,20 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] +enum NonNullary { + Nullary, + Other(isize), +} + +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + +fn main() { + let v = NonNullary::Nullary; + let val = isize::from(v); //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] +} diff --git a/src/test/ui/tag-variant-cast-non-nullary.rs b/src/test/ui/tag-variant-cast-non-nullary.rs index bb34e82cdca37..0d0c6188ad114 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.rs +++ b/src/test/ui/tag-variant-cast-non-nullary.rs @@ -1,8 +1,19 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] enum NonNullary { Nullary, Other(isize), } +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + fn main() { let v = NonNullary::Nullary; let val = v as isize; //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] diff --git a/src/test/ui/tag-variant-cast-non-nullary.stderr b/src/test/ui/tag-variant-cast-non-nullary.stderr index 87ec20f20d789..ae2f5a7aead55 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.stderr +++ b/src/test/ui/tag-variant-cast-non-nullary.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize` - --> $DIR/tag-variant-cast-non-nullary.rs:8:15 + --> $DIR/tag-variant-cast-non-nullary.rs:19:15 | LL | let val = v as isize; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr index 70c99c944d654..360633bba622b 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr @@ -16,9 +16,7 @@ error[E0605]: non-primitive cast: `NoReveal` as `&'static str` --> $DIR/never_reveal_concrete_type.rs:14:13 | LL | let _ = x as &'static str; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr index a39af7832f8c9..a9f10dfec994a 100644 --- a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr +++ b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `E` as `isize` --> $DIR/uninhabited-enum-cast.rs:4:20 | LL | println!("{}", (e as isize).to_string()); - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error From b34a41797248fb36dd690d6013a9a238391437d0 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 15 Jun 2020 15:02:57 -0400 Subject: [PATCH 07/18] Add more info to `x.py build --help` on default value for `-j JOBS`. --- src/bootstrap/flags.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index cfaa43f397095..3726115357436 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -149,7 +149,12 @@ To learn more about a subcommand, run `./x.py -h`", "N", ); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); - opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); + let j_msg = format!( + "number of jobs to run in parallel; \ + defaults to {} (this host's logical CPU count)", + num_cpus::get() + ); + opts.optopt("j", "jobs", &j_msg, "JOBS"); opts.optflag("h", "help", "print this help message"); opts.optopt( "", From 71c54db3dc7110861b6bf072ab823626d1eed242 Mon Sep 17 00:00:00 2001 From: ivan tkachenko Date: Mon, 15 Jun 2020 22:08:56 +0300 Subject: [PATCH 08/18] Fix typo in docs of std::mem --- src/libcore/mem/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index d1f5cb44913db..8bce980cadd1e 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -129,7 +129,7 @@ pub use crate::intrinsics::transmute; /// erring on the side of (double-)dropping. /// /// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the -/// ownership to `s` - the final step of interacting with `v` to dispoe of it without +/// ownership to `s` — the final step of interacting with `v` to dispose of it without /// running its destructor is entirely avoided. /// /// [drop]: fn.drop.html From 5068ae1ca05b2be0c2a98206a58d894aa620b312 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 3 Jun 2020 21:19:34 -0700 Subject: [PATCH 09/18] [WIP] injects llvm intrinsic instrprof.increment for coverage reports This initial version only injects counters at the top of each function. Rust Coverage will require injecting additional counters at each conditional code branch. --- src/libcore/intrinsics.rs | 9 + src/librustc_codegen_llvm/builder.rs | 27 ++ src/librustc_codegen_llvm/context.rs | 2 + src/librustc_codegen_llvm/intrinsic.rs | 21 + src/librustc_codegen_llvm/llvm/ffi.rs | 1 + src/librustc_codegen_ssa/back/write.rs | 6 + src/librustc_codegen_ssa/mir/block.rs | 6 +- src/librustc_codegen_ssa/traits/builder.rs | 8 + src/librustc_codegen_ssa/traits/intrinsic.rs | 1 + src/librustc_hir/lang_items.rs | 2 + src/librustc_interface/tests.rs | 1 + src/librustc_middle/mir/mono.rs | 1 + src/librustc_middle/ty/instance.rs | 7 + src/librustc_middle/ty/mod.rs | 1 + src/librustc_middle/ty/structural_impls.rs | 11 +- src/librustc_mir/interpret/terminator.rs | 3 + src/librustc_mir/monomorphize/collector.rs | 5 +- src/librustc_mir/monomorphize/partitioning.rs | 2 + src/librustc_mir/shim.rs | 3 + .../transform/instrument_coverage.rs | 100 +++++ src/librustc_mir/transform/mod.rs | 3 + src/librustc_session/options.rs | 3 + src/librustc_span/symbol.rs | 1 + src/librustc_ty/instance.rs | 4 + src/rustllvm/RustWrapper.cpp | 6 + .../codegen/coverage-experiments/Cargo.lock | 5 + .../codegen/coverage-experiments/Cargo.toml | 103 +++++ .../README-THIS-IS-TEMPORARY.md | 157 ++++++++ .../src/coverage_injection_test.rs | 335 ++++++++++++++++ .../src/coverage_injection_test2.rs | 320 ++++++++++++++++ .../src/coverage_injection_test_alt.rs | 362 ++++++++++++++++++ .../coverage-experiments/src/drop_trait.rs | 25 ++ .../src/drop_trait_with_comments_prints.rs | 53 +++ .../codegen/coverage-experiments/src/for.rs | 41 ++ .../src/for_with_comments.rs | 24 ++ .../codegen/coverage-experiments/src/if.rs | 80 ++++ .../src/if_with_comments.rs | 39 ++ .../src/increment_intrinsic.rs | 11 + .../coverage-experiments/src/just_main.rs | 3 + .../coverage-experiments/src/lazy_boolean.rs | 17 + .../src/loop_break_value.rs | 15 + .../codegen/coverage-experiments/src/match.rs | 22 ++ .../src/match_with_increment.rs | 305 +++++++++++++++ .../src/match_with_increment_alt.rs | 296 ++++++++++++++ .../src/match_without_increment.mir | 0 .../src/match_without_increment.rs | 5 + .../src/match_without_increment_alt.mir | 0 ..._mark_err_status_handling_with_comments.rs | 24 ++ .../codegen/coverage-experiments/src/while.rs | 23 ++ .../coverage-experiments/src/while_clean.rs | 6 + .../src/while_early_return.rs | 10 + .../src/while_with_comments.rs | 51 +++ 52 files changed, 2561 insertions(+), 5 deletions(-) create mode 100644 src/librustc_mir/transform/instrument_coverage.rs create mode 100644 src/test/codegen/coverage-experiments/Cargo.lock create mode 100644 src/test/codegen/coverage-experiments/Cargo.toml create mode 100644 src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test.rs create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs create mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs create mode 100644 src/test/codegen/coverage-experiments/src/drop_trait.rs create mode 100644 src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs create mode 100644 src/test/codegen/coverage-experiments/src/for.rs create mode 100644 src/test/codegen/coverage-experiments/src/for_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/if.rs create mode 100644 src/test/codegen/coverage-experiments/src/if_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/increment_intrinsic.rs create mode 100644 src/test/codegen/coverage-experiments/src/just_main.rs create mode 100644 src/test/codegen/coverage-experiments/src/lazy_boolean.rs create mode 100644 src/test/codegen/coverage-experiments/src/loop_break_value.rs create mode 100644 src/test/codegen/coverage-experiments/src/match.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.mir create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.rs create mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir create mode 100644 src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs create mode 100644 src/test/codegen/coverage-experiments/src/while.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_clean.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_early_return.rs create mode 100644 src/test/codegen/coverage-experiments/src/while_with_comments.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 85076a573b528..abb35e838ea28 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,6 +1943,15 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } +#[cfg(not(bootstrap))] +#[cfg_attr(not(bootstrap), lang = "count_code_region")] +pub fn count_code_region(_index: u32) { + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump + unsafe { + abort() + } +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index f5ae9824df894..ba285b5ef38d1 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } + fn instrprof_increment( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + num_counters: &'ll Value, + index: &'ll Value, + ) -> &'ll Value { + debug!( + "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", + fn_name, hash, num_counters, index + ); + + let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) }; + let args = &[fn_name, hash, num_counters, index]; + let args = self.check_call("call", llfn, args); + + unsafe { + llvm::LLVMRustBuildCall( + self.llbuilder, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + None, + ) + } + } + fn call( &mut self, llfn: &'ll Value, diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 4c810a37d4180..7ff5ac5cbdc10 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -749,6 +749,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 1e6d2e3dbb74e..7fddda99185b4 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -7,6 +7,8 @@ use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; use crate::value::Value; +use log::debug; + use rustc_ast::ast; use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; use rustc_codegen_ssa::common::span_invalid_monomorphization_error; @@ -21,6 +23,7 @@ use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::{self, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_span::Symbol; use rustc_target::abi::{self, HasDataLayout, LayoutOf, Primitive}; use rustc_target::spec::PanicStrategy; @@ -86,6 +89,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { args: &[OperandRef<'tcx, &'ll Value>], llresult: &'ll Value, span: Span, + caller_instance: ty::Instance<'tcx>, ) { let tcx = self.tcx; let callee_ty = instance.monomorphic_ty(tcx); @@ -136,6 +140,23 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let llfn = self.get_intrinsic(&("llvm.debugtrap")); self.call(llfn, &[], None) } + "count_code_region" => { + if let ty::InstanceDef::Item(fn_def_id) = caller_instance.def { + let caller_fn_path = tcx.def_path_str(fn_def_id); + debug!( + "count_code_region to llvm.instrprof.increment(fn_name={})", + caller_fn_path + ); + + let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path)); + let index = args[0].immediate(); + let hash = self.const_u64(1234); + let num_counters = self.const_u32(1); + self.instrprof_increment(fn_name, hash, num_counters, index) + } else { + bug!("intrinsic count_code_region: no src.instance"); + } + } "va_start" => self.va_start(args[0].immediate()), "va_end" => self.va_end(args[0].immediate()), "va_copy" => { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 54cf99e1c6d6c..372fb17573a4b 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -1360,6 +1360,7 @@ extern "C" { // Miscellaneous instructions pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; + pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value; pub fn LLVMRustBuildCall( B: &Builder<'a>, Fn: &'a Value, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index c118e5ebdb72d..49054765b9dae 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -175,6 +175,12 @@ impl ModuleConfig { if sess.opts.debugging_opts.profile && !is_compiler_builtins { passes.push("insert-gcov-profiling".to_owned()); } + + // The rustc option `-Zinstrument_coverage` injects intrinsic calls to + // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. + if sess.opts.debugging_opts.instrument_coverage { + passes.push("instrprof".to_owned()); + } passes }, vec![] diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index ef59ad486eefe..d7db657154993 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -566,7 +566,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id).as_str()), + Some(ty::InstanceDef::Intrinsic(def_id)) + | Some(ty::InstanceDef::InjectedCode(def_id)) => { + Some(bx.tcx().item_name(def_id).as_str()) + } _ => None, }; let intrinsic = intrinsic.as_ref().map(|s| &s[..]); @@ -693,6 +696,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &args, dest, terminator.source_info.span, + self.instance, ); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { diff --git a/src/librustc_codegen_ssa/traits/builder.rs b/src/librustc_codegen_ssa/traits/builder.rs index caba7ebef593b..7ffc9f15bffdc 100644 --- a/src/librustc_codegen_ssa/traits/builder.rs +++ b/src/librustc_codegen_ssa/traits/builder.rs @@ -260,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ) -> Self::Value; + fn call( &mut self, llfn: Self::Value, diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 9d48e233de655..f62019498511c 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs @@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { args: &[OperandRef<'tcx, Self::Value>], llresult: Self::Value, span: Span, + caller_instance: ty::Instance<'tcx>, ); fn abort(&mut self); diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 83bada4041963..091ded6d74d0f 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -242,6 +242,8 @@ language_item_table! { StartFnLangItem, "start", start_fn, Target::Fn; + CountCodeRegionFnLangItem, "count_code_region", count_code_region_fn, Target::Fn; + EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 87647f3b0b017..c2a7d1a4a6102 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -548,6 +548,7 @@ fn test_debugging_options_tracking_hash() { tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); tracked!(insert_sideeffect, true); + tracked!(instrument_coverage, true); tracked!(instrument_mcount, true); tracked!(link_only, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index c889dbc0a4498..b2c00849d9f83 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -352,6 +352,7 @@ impl<'tcx> CodegenUnit<'tcx> { InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index 1ce079821a22e..4f88e64c5039a 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -21,6 +21,10 @@ pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), + /// Injected call to a placeholder function that is replaced with + /// For example: `core::intrinsic::count_code_region()` for code coverage. + InjectedCode(DefId), + /// `::method` where `method` receives unsizeable `self: Self`. VtableShim(DefId), @@ -149,6 +153,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) + | InstanceDef::InjectedCode(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) => def_id, @@ -236,6 +241,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), + InstanceDef::InjectedCode(_) => write!(f, " - injected-code"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), @@ -415,6 +421,7 @@ impl<'tcx> Instance<'tcx> { | InstanceDef::FnPtrShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) | InstanceDef::VtableShim(..) => Some(self.substs), diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 93ef73171993c..9b1e717731e82 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -2717,6 +2717,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs index f6f5dfd651612..b6cbd2082a518 100644 --- a/src/librustc_middle/ty/structural_impls.rs +++ b/src/librustc_middle/ty/structural_impls.rs @@ -674,6 +674,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), + ty::InstanceDef::InjectedCode(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ref ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) } @@ -846,6 +847,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { VtableShim(did) => VtableShim(did.fold_with(folder)), ReifyShim(did) => ReifyShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), + InjectedCode(did) => InjectedCode(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), Virtual(did, i) => Virtual(did.fold_with(folder), i), ClosureOnceShim { call_once } => { @@ -861,9 +863,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { use crate::ty::InstanceDef::*; self.substs.visit_with(visitor) || match self.def { - Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } + Item(did) + | VtableShim(did) + | ReifyShim(did) + | Intrinsic(did) + | InjectedCode(did) + | Virtual(did, _) => did.visit_with(visitor), FnPtrShim(did, ty) | CloneShim(did, ty) => { did.visit_with(visitor) || ty.visit_with(visitor) } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index cd7621ea9752b..82fa471b54d73 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -257,6 +257,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); M::call_intrinsic(self, instance, args, ret, unwind) } + ty::InstanceDef::InjectedCode(..) => { + M::call_intrinsic(self, instance, args, ret, unwind) + } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 994d1e69f2e3e..24c4226bb4e94 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -714,7 +714,9 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { + ty::InstanceDef::Virtual(..) + | ty::InstanceDef::Intrinsic(_) + | ty::InstanceDef::InjectedCode(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } @@ -751,6 +753,7 @@ fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) + | ty::InstanceDef::InjectedCode(_) | ty::InstanceDef::CloneShim(..) => return true, }; diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index db1ea72c0a531..7c97b9d611e15 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -322,6 +322,7 @@ fn mono_item_visibility( | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) + | InstanceDef::InjectedCode(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) => return Visibility::Hidden, @@ -717,6 +718,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) + | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) => return None, diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index f95fd9b9e90c5..b4477d9c86d43 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -109,6 +109,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } + ty::InstanceDef::InjectedCode(_) => { + bug!("creating shims from injected code ({:?}) is unsupported", instance) + } }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs new file mode 100644 index 0000000000000..045cd03d1f7da --- /dev/null +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -0,0 +1,100 @@ +use crate::transform::{MirPass, MirSource}; +use rustc_index::vec::Idx; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::*; +use rustc_middle::mir::{Local, LocalDecl}; +use rustc_middle::ty; +use rustc_middle::ty::Ty; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +pub struct InstrumentCoverage; + +/** + * Inserts call to count_code_region() as a placeholder to be replaced during code generation with + * the intrinsic llvm.instrprof.increment. + */ + +// FIXME(richkadel): As a first step, counters are only injected at the top of each function. +// The complete solution will inject counters at each conditional code branch. + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.instrument_coverage { + if let Some(callee_fn_def_id) = tcx.lang_items().count_code_region_fn() { + debug!("instrumenting {:?}", src.def_id()); + instrument_coverage(tcx, callee_fn_def_id, body); + } + } + } +} + +pub fn instrument_coverage<'tcx>( + tcx: TyCtxt<'tcx>, + callee_fn_def_id: DefId, + body: &mut Body<'tcx>, +) { + let span = body.span.shrink_to_lo(); + + let ret_ty = tcx.fn_sig(callee_fn_def_id).output(); + let ret_ty = ret_ty.no_bound_vars().unwrap(); + let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); + + let count_code_region_fn: Operand<'_> = + Operand::function_handle(tcx, callee_fn_def_id, substs, span); + + let index = const_int_operand(tcx, span.clone(), tcx.types.u32, 0); + + let args = vec![index]; + + let source_info = SourceInfo { span: span, scope: OUTERMOST_SOURCE_SCOPE }; + + let new_block = START_BLOCK + body.basic_blocks().len(); + + let next_local = body.local_decls.len(); + let new_temp = Local::new(next_local); + let unit_temp = Place::from(new_temp); + + let storage_live = Statement { source_info, kind: StatementKind::StorageLive(new_temp) }; + let storage_dead = Statement { source_info, kind: StatementKind::StorageDead(new_temp) }; + + let count_code_region_call = TerminatorKind::Call { + func: count_code_region_fn, + args, + destination: Some((unit_temp, new_block)), + cleanup: None, + from_hir_call: false, + }; + + body.local_decls.push(LocalDecl::new(tcx.mk_unit(), body.span)); + body.basic_blocks_mut().push(BasicBlockData { + statements: vec![storage_live], + is_cleanup: false, + terminator: Some(Terminator { source_info, kind: count_code_region_call }), + }); + + body.basic_blocks_mut().swap(START_BLOCK, new_block); + body[new_block].statements.push(storage_dead); + + // FIXME(richkadel): ALSO add each computed Span for each conditional branch to the coverage map + // and provide that map to LLVM to encode in the final binary. +} + +fn const_int_operand<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, + val: u128, +) -> Operand<'tcx> { + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), + }) +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 4240b528a6124..e03ef48f74838 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -28,6 +28,7 @@ pub mod elaborate_drops; pub mod generator; pub mod inline; pub mod instcombine; +pub mod instrument_coverage; pub mod no_landing_pads; pub mod nrvo; pub mod promote_consts; @@ -287,6 +288,8 @@ fn mir_validated( &[&[ // What we need to run borrowck etc. &promote_pass, + // FIXME(richkadel): is this the best place for the InstrumentCoverage pass? + &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("qualify-consts"), ]], ); diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index d22c6ec9d7d01..599ce595e1314 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -876,6 +876,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "fix undefined behavior when a thread doesn't eventually make progress \ (such as entering an empty infinite loop) by inserting llvm.sideeffect \ (default: no)"), + instrument_coverage: bool = (false, parse_bool, [TRACKED], + "instrument the generated code with LLVM code region counters for \ + generating coverage reports (default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index fdeb58b7b7a31..623c279734733 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -240,6 +240,7 @@ symbols! { copy_closures, core, core_intrinsics, + count_code_region, crate_id, crate_in_paths, crate_local, diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index 0acf769168137..d4ceeff324450 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -35,6 +35,10 @@ fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } + ty::FnDef(def_id, _) if Some(def_id) == tcx.lang_items().count_code_region_fn() => { + debug!(" => injected placeholder function to be replaced"); + ty::InstanceDef::InjectedCode(def_id) + } ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { let ty = substs.type_at(0); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 4704622922af0..cdb3a157eab97 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -5,6 +5,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Bitcode/BitcodeWriterPass.h" @@ -1364,6 +1365,11 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles)); } +extern "C" LLVMValueRef LLVMRustGetInstrprofIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, diff --git a/src/test/codegen/coverage-experiments/Cargo.lock b/src/test/codegen/coverage-experiments/Cargo.lock new file mode 100644 index 0000000000000..132469cbb182c --- /dev/null +++ b/src/test/codegen/coverage-experiments/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "coverage_experiments" +version = "0.1.0" diff --git a/src/test/codegen/coverage-experiments/Cargo.toml b/src/test/codegen/coverage-experiments/Cargo.toml new file mode 100644 index 0000000000000..296a8d5c9af2d --- /dev/null +++ b/src/test/codegen/coverage-experiments/Cargo.toml @@ -0,0 +1,103 @@ +[workspace] + +[package] +name = "coverage_experiments" +version = "0.1.0" +license = "BSD-3-Clause" +authors = ["rust-fuchsia@fuchsia.com"] +edition = "2018" + +[[bin]] + +name = "coverage_injection_test" +path = "src/coverage_injection_test.rs" + +[[bin]] + +name = "coverage_injection_test2" +path = "src/coverage_injection_test2.rs" + +[[bin]] + +name = "while" +path = "src/while.rs" + +[[bin]] + +name = "while_clean" +path = "src/while_clean.rs" + +[[bin]] + +name = "while_early_return" +path = "src/while_early_return.rs" + +[[bin]] + +name = "if_with_comments" +path = "src/if_with_comments.rs" + +[[bin]] + +name = "if" +path = "src/if.rs" + +[[bin]] + +name = "increment_intrinsic" +path = "src/increment_intrinsic.rs" + +[[bin]] + +name = "just_main" +path = "src/just_main.rs" + +[[bin]] + +name = "lazy_boolean" +path = "src/lazy_boolean.rs" + +[[bin]] + +name = "match" +path = "src/match.rs" + +[[bin]] + +name = "match_without_increment" +path = "src/match_without_increment.rs" # identical to -Zunpretty=hir output + +[[bin]] + +name = "match_with_increment" +path = "src/match_with_increment.rs" + +[[bin]] + +name = "match_with_increment_alt" +path = "src/match_with_increment_alt.rs" + +[[bin]] + +name = "loop_break_value" +path = "src/loop_break_value.rs" + +[[bin]] + +name = "for_with_comments" +path = "src/for_with_comments.rs" + +[[bin]] + +name = "for" +path = "src/for.rs" + +[[bin]] + +name = "drop_trait" +path = "src/drop_trait.rs" + +#[dependencies] # Should not need to manually add coverage dependencies +#version = "0.1.0" +#path = "../__builtin" # for mod __builtin::coverage + diff --git a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md new file mode 100644 index 0000000000000..3b69c0a406594 --- /dev/null +++ b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md @@ -0,0 +1,157 @@ +# codegen/coverage-experiments +*

THIS DIRECTORY IS TEMPORARY

* + +This directory contains some work-in-progress (WIP) code used for experimental development and +testing of Rust Coverage feature development. + +The code in this directory will be removed, or migrated into product tests, when the Rust +Coverage feature is complete. + +[TOC] + +## Development Notes + +### config.toml + +config.toml probably requires (I should verify that intrinsic `llvm.instrprof.increment` +code generation ONLY works with this config option): + + profiler = true + +## First build + +```shell +./x.py clean +./x.py build -i --stage 1 src/libstd +``` + +## Incremental builds *IF POSSIBLE!* + +```shell +./x.py build -i --stage 1 src/libstd --keep-stage 1 +``` + +*Note: Some changes made for Rust Coverage required the full build (without `--keep-stage 1`), and in some cases, required `./x.py clean` first!. Occassionally I would get errors when building or when compiling a test program with `--Zinstrument-coverage` that work correctly only after a full clean and build.* + +## Compile a test program with LLVM coverage instrumentation + +*Note: This PR is still a work in progress. At the time of this writing, the `llvm.instrprof.increment` intrinsic is injected, and recognized by the LLVM code generation stage, but it does not appear to be included in the final binary. This is not surprising since other steps are still to be implemented, such as generating the coverage map. See the suggested additional `llvm` flags for ways to verify the `llvm` passes at least get the right intrinsic.* + +Suggested debug configuration to confirm Rust coverage features: +```shell +$ export RUSTC_LOG=rustc_codegen_llvm::intrinsic,rustc_mir::transform::instrument_coverage=debug +``` + +Ensure the new compiled `rustc` is used (the path below, relative to the `rust` code repository root, is an example only): + +```shell +$ build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ + src/test/codegen/coverage-experiments/just_main.rs \ + -Zinstrument-coverage +``` + +### About the test programs in coverage-experiments/src/ + +The `coverage-experiments/src/` directory contains some sample (and very simple) Rust programs used to analyze Rust compiler output at various stages, with or without the Rust code coverage compiler option. For now, these are only used for the in-progress development and will be removed at a future date. (These are *not* formal test programs.) + +The src director may also contain some snapshots of mir output from experimentation, particularly if the saved snapshots highlight results that are important to the future development, individually or when compared with other output files. + +Be aware that some of the files and/or comments may be outdated. + +### Additional `llvm` flags (append to the `rustc` command) + +These optional flags generate additional files and/or terminal output. LLVM's `-print-before=all` should show the `instrprof.increment` intrinsic with arguments computed by the experimental Rust coverage feature code: + +```shell + --emit llvm-ir \ + -Zverify-llvm-ir \ + -Zprint-llvm-passes \ + -Csave-temps \ + -Cllvm-args=-print-before-all +``` + +### Additional flags for MIR analysis and transforms + +These optional flags generate a directory with many files representing the MIR as text (`.mir` files) and as a visual graph (`.dot` files) rendered by `graphviz`. (**Some IDEs, such as `VSCode` have `graphviz` extensions.**) + +```shell + -Zdump-mir=main \ + -Zdump-mir-graphviz +``` + +### Flags I've used but appear to be irrelvant to `-Zinstrument-coverage` after all: +```shell + # -Zprofile + # -Ccodegen-units=1 + # -Cinline-threshold=0 + # -Clink-dead-code + # -Coverflow-checks=off +``` + +## Run the test program compiled with code coverage instrumentation (maybe): + +As stated above, at the time of this writing, this work-in-progress seems to generate `llvm.instrprof.increment` intrinsic calls correctly, and are visibile in early `llvm` code generation passes, but are eventually stripped. + +The test program should run as expected, currently does not generate any coverage output. + +*Example:* + +```shell + $ src/test/codegen/coverage-experiments/just_main + hello world! (should be covered) +``` + +### Running the coverage-enabled `rustc` compiler in the `lldb` debugger: + +For example, to verify the intrinsic is codegen'ed, set breakpoint in `lldb` where it validates a certain instruction is the `llvm.instrprof.increment` instruction. + +First, update config.toml for debugging: + +```toml + [llvm] + optimize = false + release-debuginfo = true + + [rust] + debug = true + debuginfo-level = 2 +``` + +*(Note, in case this is relevant after all, I also have the following changes; but I don't think I need them:)* + +```toml + # Add and uncomment these if relevant/useful: + # codegen-units = 0 + # python = '/usr/bin/python3.6' +``` + +Run the compiler with additional flags as needed: + +```shell +lldb \ + build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ + -- \ + src/test/codegen/coverage-experiments/just_main.rs \ + -Zinstrument-coverage \ + -Zdump-mir=main \ + -Zdump-mir-graphviz +``` + +Note the specific line numbers may be different: + +```c++ +(lldb) b lib/Transforms/Instrumentation/InstrProfiling.cpp:418 +(lldb) r + +Process 93855 stopped +* thread #6, name = 'rustc', stop reason = breakpoint 2.1 + frame #0: 0x00007fffedff7738 librustc_driver-5a0990d8d18fb2b4.so`llvm::InstrProfiling::lowerIntrinsics(this=0x00007fffcc001d40, F=0x00007fffe4552198) at InstrProfiling.cpp:418:23 + 415 auto Instr = I++; + 416 InstrProfIncrementInst *Inc = castToIncrementInst(&*Instr); + 417 if (Inc) { +-> 418 lowerIncrement(Inc); + 419 MadeChange = true; + 420 } else if (auto *Ind = dyn_cast(Instr)) { + 421 lowerValueProfileInst(Ind); +(lldb) +``` \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs new file mode 100644 index 0000000000000..231da1dc1a67f --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs @@ -0,0 +1,335 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ +/* */ result +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then +/* */ /// write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ #[inline(always)] +/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { +/* */ __incr_cov(region_loc, /*counter,*/ ()); +/* */ __report(); +/* */ result +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ __incr_cov(from!("fn new()"),Self::new_with_value(31415)) // function-scoped counter index = 0 +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ __incr_cov(from!("fn new_with_value()"),Self { +/* ┃ */ field, +/* ┃ */ }) // function-scoped counter index = 0 +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ __incr_cov(from!("fn call_closure()"),closure(123)) // function-scoped counter index = 0 +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match __incr_cov(from!("fn various"),color) { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : I */ Red => __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"),println!("roses")), +/* : - */ Green => { +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any"),spidey > goblin) { +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - */ } else if __incr_cov(from!("`else if` on this line"),spidey == goblin) { +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - - - */ } else if if __incr_cov(from!("`else if` on this line"),true) { +/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); +/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. +/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) +/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO +/* : : : ┃ */ // IT SHOULD BE: +/* ┏-:---:-------:---< */ return __incr_cov(from!(""),Ok(())); +/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. +/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. +/* : : ┃ - */ } else { +/* : : : I */ __incr_cov(from!("`else`"),false) +/* : : - - */ } { +/* : : ┃ */ println!("wierd science"); +/* : : ┃ */ __incr_cov(from!("block start"),()); +/* : ┃ - */ } else { +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return __incr_cov(from!("`else`"),Ok(())); // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : ┃ - */ // __incr_cov(from!(""),()); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : I */ Blue => __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any"),println!("violets")), +/* ┃ */ } +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)"),condition1) { +/* : ┃ */ println!("before while loop"); +/* : ┃ */ let mut countdown = 10; +/* : ┃ */ __incr_cov(from!("block start"),()); // Must increment before repeated while text expression +/* : : I */ while __incr_cov(from!("while test"), countdown > 0) { // span is just the while test expression +/* : : ┃ */ println!("top of `while` loop"); +/* : : ┃ */ countdown -= 1; +/* : : ┃ */ // __incr_cov(from!("while loop"),()); // Counter not needed, but span is computed as "while test" minus "block start" +/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 +/* : ┃ - */ } +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in __incr_cov(from!("end of while"),0..10) { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if __incr_cov(from!("block start"),index == 8) { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break __incr_cov(from!(""),()); +/* : : : ┃ */ __incr_cov(from!("block start"),()); +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if __incr_cov(from!("block end of `if index == 8`"),condition2) { +/* ┏-:---:---:---< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`"),()); +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ __incr_cov(from!("for loop"),()); +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if __incr_cov(from!("block end of `for` loop"),condition2) { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : - */ } else if __incr_cov(from!("`else`"),condition3) { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ __incr_cov(from!("block start"),false) +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ __incr_cov(from!(""),Ok(())) +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| __incr_cov(from!(""), +/* : : ┃ - */ if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ __incr_cov(from!(""),false) +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ __incr_cov(from!(""),true) +/* : : ┃ - */ } +/* : : - */ ) +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned true"))) +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned false"))) +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ __incr_cov(from!("if condition1"),result) +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ __incr_cov(from!(""),Ok(())) +/* ┃ - */ } +/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ __incr_cov(from!(""),{ +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ }) { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ __incr_cov(from!(""),test_struct.various()) +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs new file mode 100644 index 0000000000000..8f4399ab51d09 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs @@ -0,0 +1,320 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str) { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ let __result = Self::new_with_value(31415); // function-scoped counter index = 0 +/* ┃ */ __incr_cov(from!("fn new()")); +/* ┃ */ __result +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ let __result = Self { +/* ┃ */ field, +/* ┃ */ }; +/* ┃ */ __incr_cov(from!("fn new_with_value()")); // function-scoped counter index = 0 +/* ┃ */ __result +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ let __result = closure(123); +/* ┃ */ __incr_cov(from!("fn call_closure()")); // function-scoped counter index = 0 +/* ┃ */ __result +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match { let __result = color; __incr_cov(from!("fn various")); __result } { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : I */ Red => {println!("roses"); __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"));} +/* : - */ Green => { +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if { let __result = spidey > goblin; __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any")); __result } { +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - */ } else if { let __result = spidey == goblin; __incr_cov(from!("`else if` on this line")); __result } { +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - - - */ } else if if { let __result = true; __incr_cov(from!("`else if` on this line")); __result } { +/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); +/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. +/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) +/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO +/* : : : ┃ */ // IT SHOULD BE: +/* ┏-:---:-------:---< */ return { let __result = Ok(()); __incr_cov(from!("")); __result }; +/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. +/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. +/* : : ┃ - */ } else { +/* : : : I */ { let __result = false; __incr_cov(from!("`else`")); __result } +/* : : - - */ } { +/* : : ┃ */ println!("wierd science"); +/* : : ┃ */ __incr_cov(from!("block start")); +/* : ┃ - */ } else { +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return { let __result = Ok(()); __incr_cov(from!("`else`")); __result }; // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : ┃ - */ // __incr_cov(from!("")); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : I */ Blue => { println!("violets"); __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any")); } +/* ┃ */ } +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if { let __result = condition1; __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)")); __result } { +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in { let __result = 0..10; __incr_cov(from!("block start")); __result } { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if { let __result = index == 8; __incr_cov(from!("block start")); __result } { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break __incr_cov(from!("")); +/* : : : ┃ */ __incr_cov(from!("block start")); +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `if index == 8`")); __result } { +/* ┏-:---:---:---< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`")); +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ __incr_cov(from!("")); +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `for` loop")); __result } { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : - */ } else if { let __result = condition3; __incr_cov(from!("`else`")); __result } { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ { let __result = false; __incr_cov(from!("block start")); __result } +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| { +/* : : ┃ - */ let __result = if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ { let __result = false; __incr_cov(from!("")); __result } +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ { let __result = true; __incr_cov(from!("")); __result } +/* : : ┃ - */ }; +/* : : - */ __incr_cov(from!("")); __result } +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")); __incr_cov(from!("")); __result } +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")); __incr_cov(from!("")); __result } +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ { let __result = result; __incr_cov(from!("if condition1")); __result } +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } +/* ┃ - */ } +/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ { let __result = { +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ }; __incr_cov(from!("")); __result } { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return { let __result = Err(Error::new(ErrorKind::Other, format!("Error status {}", status))); __incr_cov(from!("")); __report(); __result } +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ { let __result = test_struct.various(); __incr_cov(from!("")); __result } +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ { let __result = result; __incr_cov(from!("")); __report(); __result } +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs new file mode 100644 index 0000000000000..20c4835dd882e --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs @@ -0,0 +1,362 @@ +/* */ use std::io::Error; +/* */ use std::io::ErrorKind; +/* */ +/* */ /// Align Rust counter increment with with: +/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) +/* */ /// +/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) +/* */ /// +/* */ /// The first argument is a pointer to a global variable containing the name of the entity +/* */ /// being instrumented. This should generally be the (mangled) function name for a set of +/* */ /// counters. +/* */ /// +/* */ /// The second argument is a hash value that can be used by the consumer of the profile data +/* */ /// to detect changes to the instrumented source, and the third is the number of counters +/* */ /// associated with name. It is an error if hash or num-counters differ between two +/* */ /// instances of instrprof.increment that refer to the same name. +/* */ /// +/* */ /// The last argument refers to which of the counters for name should be incremented. It +/* */ /// should be a value between 0 and num-counters. +/* */ /// +/* */ /// # Arguments +/* */ /// +/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: +/* */ /// +/* */ /// ``` +/* */ /// fn rustc_symbol_mangling::compute_symbol_name( +/* */ /// tcx: TyCtxt<'tcx>, +/* */ /// instance: Instance<'tcx>, +/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, +/* */ /// ) -> String +/* */ /// ``` +/* */ /// +/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" +/* */ /// to control-flow inside the function. +/* */ /// +/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the +/* */ /// function. +/* */ /// +/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of +/* */ /// counters, relative to the source code location, is apparently not expected.) +/* */ /// +/* */ /// # Notes +/* */ /// +/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see +/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). +/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further +/* */ /// lowering. +/* */ /// * num_counters depends on having already identified all counter insertion locations. +/* */ /// * counter_index can be computed at time of counter insertion (incrementally). +/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. +/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { +/* */ } +/* */ +/* */ /// A coverage counter implementation that will work as both an intermediate coverage +/* */ /// counting and reporting implementation at the AST-level only--for debugging and +/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM +/* */ /// intrinsic coverage counter APIs during the lowering process. +/* */ /// +/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics +/* */ /// are enabled, the counter function calls that were injected into the AST serve as +/* */ /// placeholders, to be replaced by an alternative, such as: +/* */ /// +/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or +/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the +/* */ /// `llvm.instrprof.increment()` intrinsic; or +/* */ /// * a similar expression wrapper, with the additional parameters (as defined above +/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the +/* */ /// result of the wrapped expression) +/* */ /// +/* */ /// The first two options would require replacing the inlined wrapper call with something +/* */ /// like: +/* */ /// +/* */ /// ``` +/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } +/* */ /// ``` +/* */ /// +/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then +/* */ /// it may be a perfect opportunity to replace the function with one of these more +/* */ /// direct methods. +/* */ /// +/* */ #[inline(always)] +/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/) { +/* */ // Either call the intermediate non-llvm coverage counter API or +/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. +/* */ +/* */ // let _lock = increment_counter(counter); +/* */ println!("{}", region_loc); +/* */ } +/* */ +/* */ /// Write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ fn __report() { +/* */ println!("WRITE REPORT!"); +/* */ } +/* */ +/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then +/* */ /// write a report identifying each incremented counter and the number of times each counter +/* */ /// was incremented. +/* */ #[inline(always)] +/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { +/* */ __incr_cov(region_loc, /*counter,*/); +/* */ __report(); +/* */ result +/* */ } +/* */ +/* */ macro_rules! from { +/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ macro_rules! to { +/* */ ($to:expr) => { &format!("to: {}\n to: {}:{}:{}", $to, file!(), line!(), column!()) }; +/* */ } +/* */ +/* */ #[derive(Debug)] +/* */ enum TestEnum { +/* */ Red, +/* */ Green, +/* */ Blue, +/* */ } +/* */ +/* */ struct TestStruct { +/* */ field: i32, +/* */ } +/* */ +/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING +/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. +/* */ +/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, +/* */ // BUT MAYBE I SHOULD ASK. +/* */ +/* */ impl TestStruct { +/* - */ fn new() -> Self { +/* ┃ */ __incr_cov(to!("end of fn new()")); // function-scoped counter index = 0 +/* ┃ */ Self::new_with_value(31415) +/* - */ } +/* */ +/* - */ fn new_with_value(field: i32) -> Self { +/* ┃ */ __incr_cov(to!("end of fn new_with_value()")); // function-scoped counter index = 0 +/* ┃ */ Self { +/* ┃ */ field, +/* ┃ */ } +/* - */ } +/* */ +/* */ fn call_closure(&self, closure: F) -> bool +/* */ where +/* */ F: FnOnce( +/* */ i32, +/* */ ) -> bool, +/* - */ { +/* ┃ */ __incr_cov(to!("end of fn call_closure()")); // function-scoped counter index = 0 +/* ┃ */ closure(123) +/* - */ } +/* */ +/* - */ fn various(&self) -> Result<(),Error> { +/* ┃ */ __incr_cov(to!("just before next branch: after `match color`: pattern selection")); +/* ┃ */ use TestEnum::*; +/* ┃ */ let mut color = Red; +/* ┃ */ let _ = color; +/* ┃ */ color = Blue; +/* ┃ */ let _ = color; +/* ┃ */ color = Green; +/* ┃ */ match color { // function-scoped counter index = 0 +/* : */ +/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION +/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` +/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION +/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. +/* : - */ Red => { +/* : ┃ */ __incr_cov(to!("end of matched Red")); +/* : ┃ */ println!("roses"); +/* : - */ } +/* : - */ Green => { +/* : ┃ */ __incr_cov(to!("just before next branch: after `if spidey > goblin`")); +/* : ┃ */ let spidey = 100; +/* : ┃ */ let goblin = 50; +/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ +/* : ┃ */ // println!("what ev"); +/* : ┃ */ // })} +/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. +/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) +/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! +/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) +/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. +/* : ┃ - */ if spidey > goblin { +/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); +/* : : ┃ */ println!("spidey beats goblin"); +/* : : ┃ */ +/* : ┃ - */ } else if { +/* : : : */ // Make sure we can't compute the coverage count here. +/* : : : */ // We know the expression executed if the previous if block DID NOT +/* : : : */ // execute, and either this `else if` block does execute OR any subsequent +/* : : : */ // `else if` or `else` blocks execute, OR none of the blocks in the +/* : : : */ // `if`, `else if` or `else` blocks execute. +/* : : : */ // `if`, `else if` or `else` blocks execute. +/* : : ┃ */ __incr_cov(to!("end of `else if spidey == goblin` expression")); +/* : : ┃ */ spidey == goblin +/* : ┃ - */ } { +/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); +/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), +/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with +/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, +/* : : ┃ */ // so I think simpler to just make it its own coverage region.) +/* : : ┃ */ println!("it's a draw"); +/* : : ┃ */ +/* : ┃ - - - */ } else if { +/* : : ┃ */ __incr_cov(to!("end of `if true`")); +/* : ┃ - - - */ if true { +/* : : : ┃ */ __incr_cov(to!("end of `return Ok(())`")); +/* ┏-:---:-------:---< */ return Ok(()); +/* V : : ┃ - */ } else { +/* : : : ┃ */ // __incr_cov(to!("end of else block")); +/* : : : ┃ */ // computed counter expression +/* : : : ┃ */ false +/* : : : - */ } +/* : : - - - */ } { +/* : : ┃ */ __incr_cov(to!("end of if block")); +/* : : ┃ */ println!("wierd science"); +/* : ┃ - */ } else { +/* : : ┃ */ // __incr_cov(to!("end of `return Ok(())")); +/* : : ┃ */ // counter expression: (start of Green match arm) - (if spidey > goblin) - (previous `} else if {`) +/* : : ┃ */ println!("goblin wins"); +/* ┏-:---:---< */ return Ok(()); // THIS COUNTS LAST STATEMENT IN `else` BLOCK +/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, +/* : : : */ // `break`, or `continue`, also report the outer spans +/* : : : */ // got this far--including this `else` block. Record +/* : : : */ // The start positions for those outer blocks, but: +/* : : : */ // * For the block containing the `return`, `break`, or +/* : : : */ // `continue`, end report the end position is the +/* : : : */ // start of the `return` span (or 1 char before it). +/* : : : */ // * Anything else? +/* : ┃ - */ } +/* : : */ // __incr_cov(to!("end of matched Green")); +/* : : */ // // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` +/* : - */ }, +/* : - */ Blue => { +/* : ┃ */ __incr_cov(to!("end of matched Blue")); +/* : ┃ */ println!("violets"); +/* : - */ } +/* ┃ */ } +/* ┃ */ __incr_cov(to!("just before next branch: after `if condition1` (HIR: 'match condition1')")); +/* ┃ */ +/* ┃ */ let condition1 = true; +/* ┃ */ let condition2 = false; +/* ┃ */ let condition3 = true; +/* ┃ */ +/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); +/* ┃ */ +/* ┃ - */ if condition1 { +/* : ┃ */ println!("before while loop"); +/* : ┃ */ let mut countdown = 10; +/* : ┃ */ // Must increment before repeated while text expression +/* : : I */ while countdown > 0 { // span is just the while test expression +/* : : ┃ */ println!("top of `while` loop"); +/* : : ┃ */ countdown -= 1; +/* : : ┃ */ // // Counter not needed, but span is computed as "while test" minus "block start" +/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 +/* : ┃ - */ } +/* : ┃ */ println!("before for loop"); +/* : ┃ - */ for index in 0..10 { +/* : : ┃ */ println!("top of `for` loop"); +/* : : ┃ - */ if index == 8 { +/* : : : ┃ */ println!("before break"); +/* : : : ┃ */ // note the following is not legal here: +/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" +/* : : : ┃ */ // break +/* : : : ┃ */ +/* : : ┏-----< */ break; +/* : : V : : */ +/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. +/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label +/* : : ┃ - */ } +/* : : ┃ */ println!("after `break` test"); +/* : : ┃ - */ if condition2 { +/* ┏-:---:---:---< */ return Ok(()); +/* V : : ┃ - */ } +/* : : ┃ */ +/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN +/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. +/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END +/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. +/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE +/* : : Λ : ┃ */ +/* : : ┗-----< */ continue; +/* : : ┃ - */ } +/* : : ┃ */ println!("after `continue` test"); +/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? +/* : : ┃ */ +/* : ┃ - */ } +/* : ┃ */ println!("after for loop"); +/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION +/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: +/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) +/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK +/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS +/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, +/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" +/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. +/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND +/* : ┃ */ println!("after result = if ..."); +/* : ┃ - */ if condition2 { +/* : : ┃ */ println!("before first return"); +/* ┏-:---:-------< */ return Ok(()); +/* V : : - */ } else if condition3 { +/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. +/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. +/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION +/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. +/* : : ┃ */ println!("not second return"); +/* ┏-:---:-------< */ return Ok(()); +/* V : : - */ } else { +/* : : ┃ */ println!("not returning"); +/* : : ┃ */ false +/* : : - */ } +/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK +/* : ┃ - */ } { +/* : : ┃ */ println!("branched condition returned true"); +/* : : ┃ */ Ok(()) +/* : ┃ - */ } else if self.call_closure( +/* : : - */ |closure_param| +/* : : ┃ - */ if condition3 { +/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); +/* : : : ┃ */ false +/* : : ┃ - */ } else { +/* : : : ┃ */ println!("in closure, captured condition was false"); +/* : : : ┃ */ true +/* : : ┃ - */ } +/* : : - */ +/* : : - */ ) { +/* : : ┃ */ println!("closure returned true"); +/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")) +/* : ┃ - */ } else { +/* : : ┃ */ println!("closure returned false"); +/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")) +/* : ┃ - */ }; +/* : ┃ */ println!("bottom of function might be skipped if early `return`"); +/* : ┃ */ result +/* ┃ - */ } else { +/* : ┃ */ println!("skipping everything in `various()`"); +/* : ┃ */ Ok(()) +/* ┃ - */ } +/* ┃ - */ // 0 // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED +/* - */ } +/* */ } +/* */ +/* - */ fn main() -> Result<(), std::io::Error> { +/* ┃ */ //let mut status: u8 = 2; +/* ┃ */ let mut status: u8 = 1; +/* : - */ let result = if status < 2 && +/* : ┃ */ { +/* : ┃ */ status -= 1; +/* : ┃ */ status == 0 +/* : - - */ } { +/* : ┃ */ let test_struct = TestStruct::new_with_value(100); +/* : ┃ */ let _ = test_struct.various(); +/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) +/* V : - */ } else { +/* : ┃ */ let test_struct = TestStruct::new(); +/* : ┃ */ test_struct.various() +/* : - */ }; +/* ┃ */ println!("done"); +/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 +/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait.rs b/src/test/codegen/coverage-experiments/src/drop_trait.rs new file mode 100644 index 0000000000000..75400e037e9f0 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/drop_trait.rs @@ -0,0 +1,25 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, result: T) -> T { + result +} + +struct Firework { + _strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + __incr_cov("start of drop()", ()); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { _strength: 1 }; + + if __incr_cov("start of main()", true) { + return __incr_cov("if true", { let _t = Err(1); _t }); + } + + let _tnt = Firework { _strength: 100 }; + Ok(()) +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs new file mode 100644 index 0000000000000..de9f5d5cb4647 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs @@ -0,0 +1,53 @@ +// +// +// +// It's interesting to speculate if there is a way to leverage the Drop trait functionality +// to increment counters when a scope is closed, but I don't think it would help "out of the box". +// +// A `return` or `break` with expression might not need a temp value expression wrapper +// such as `return { let _t = result_expression; __incr_counter(...); _t };` +// +// ... **if** the __incr_counter() was somehow called from a "drop()" trait function. +// +// The problem is, since the drop call is automatic, there is no way to have argument variants +// depending on where the drop() occurs (e.g., from a `return` statement vs. from the end of +// the function). We need 2 different code regions though. +// +// +// +// + +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { + // println!("from: {}", _region_loc); + result +} + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + __incr_cov("start of drop()", ()); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + if __incr_cov("start of main()", true) { + return __incr_cov("if true", { let _t = Err(1); println!("computing return value"); _t }); + } + + let _tnt = Firework { strength: 100 }; + // __incr_cov("after if block", Ok(())) // CAN USE COUNTER EXPRESSION: "start of drop()" - "if true" + Ok(()) +} + +// OUTPUT WHEN RUNNING THIS PROGRAM IS AS EXPECTED: + +// computing return value +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/codegen/coverage-experiments/src/for.rs b/src/test/codegen/coverage-experiments/src/for.rs new file mode 100644 index 0000000000000..3f44c382a1e3f --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/for.rs @@ -0,0 +1,41 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { + result +} + +fn main() { + for countdown in __incr_cov("start", 10..0) { + let _ = countdown; + __incr_cov("top of for", ()); + } +} + +// LOWERED TO HIR: +// +// fn main() { +// { +// let _t = +// match ::std::iter::IntoIterator::into_iter(__incr_cov("start", +// ::std::ops::Range{start: +// 10, +// end: +// 0,})) +// { +// mut iter => +// loop { +// let mut __next; +// match ::std::iter::Iterator::next(&mut iter) { +// ::std::option::Option::Some(val) => +// __next = val, +// ::std::option::Option::None => break , +// } +// let countdown = __next; +// { +// let _ = countdown; +// __incr_cov("top of for", ()); +// } +// }, +// }; +// _t +// } +// } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/for_with_comments.rs b/src/test/codegen/coverage-experiments/src/for_with_comments.rs new file mode 100644 index 0000000000000..03d11b2c230ca --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/for_with_comments.rs @@ -0,0 +1,24 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression +/* : ┃ */ let _ = countdown; +/* : ┃ */ __incr_cov("top of for", ()); +/* ┃ - */ } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/if.rs b/src/test/codegen/coverage-experiments/src/if.rs new file mode 100644 index 0000000000000..ad50f6be19004 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/if.rs @@ -0,0 +1,80 @@ +#![feature(core_intrinsics)] + +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; + +fn main() { + let mut countdown = 10; + if __incr_cov("start", countdown > 0) { + + + // // TEST CALLING INTRINSIC: + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 314 as u32, 31 as u32) }; + // // Results in: + // // LLVM ERROR: Cannot select: intrinsic %llvm.instrprof.increment + // // I may need to pass one or more of the following flags (or equivalent opts) to LLVM to enable this: + // // -fprofile-instr-generate -fcoverage-mapping + + + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + } + + let mut countdown = 10; + if { let _tcov = countdown > 0; __llvm_incr_counter("start", ); _tcov } { + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + } +} + +// NOTE: hir REDUNDANTLY lowers the manually inlined counter in the second if block to: +// +// match { +// let _t = +// { +// let _tcov = countdown > 0; +// __llvm_incr_counter("start"); +// _tcov +// }; +// _t +// } { + +// I don't know if optimization phases will fix this or not. +// Otherwise, a more optimal (but definitely special case) way to handle this would be +// to inject the counter between the hir-introduced temp `_t` assignment and the block result +// line returning `_t`: +// +// match { +// let _t = countdown > 0; +// __llvm_incr_counter("start"); // <-- the only thing inserted for coverage here +// _t +// } +// +// UNFORTUNATELY THIS IS NOT A PATTERN WE CAN ALWAYS LEVERAGE, FOR EXPRESSIONS THAT HAVE VALUES +// WHERE WE NEED TO INJECT THE COUNTER AFTER THE EXPRESSION BUT BEFORE IT IS USED. +// +// IT DOES APPEAR TO BE THE CASE FOR WHILE EXPRESSIONS, (BECOMES loop { match { let _t = condition; _t} { true => {...} _ => break, }}) +// AND IS TRUE FOR IF EXPRESSIONS AS NOTED +// BUT NOT FOR RETURN STATEMENT (and I'm guessing not for loop { break value; } ? ) +// +// AND NOT FOR LAZY BOOLEAN EXPRESSIONS! +// +// AND NOT FOR MATCH EXPRESSIONS IN THE ORIGINAL SOURCE! \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/if_with_comments.rs b/src/test/codegen/coverage-experiments/src/if_with_comments.rs new file mode 100644 index 0000000000000..267e7bca2c5a2 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/if_with_comments.rs @@ -0,0 +1,39 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* ┃ */ let mut countdown = 10; +/* : I */ if __incr_cov("start", countdown > 0) { // span is from start of main() +/* : ┃ */ countdown -= 1; +/* : ┃ */ __incr_cov("if block",()); +/* ┃ - */ } + + let mut countdown = 10; + if __incr_cov("start", countdown > 0) { + countdown -= 1; + __incr_cov("if block",()); + } else if countdown > 5 { // counter expression "start" - "if block" + countdown -= 2; + __incr_cov("else if block",()); + } else { + countdown -= 3; + // __incr_cov("else block",()); // counter expression (countdown > 5 counter expression) - "else if block" + // PLACED AT END OF ELSE BLOCK OR START OF FIRST CONDITIONAL BLOCK, IF ANY (PRESUMING POSSIBLE EARLY EXIT). + // IF WE CAN GUARANTEE NO EARLY EXIT IN THIS BLOCK, THEN AT THE END IS FINE EVEN IF ELSE BLOCK CONTAINS OTHER CONDITIONS. + } + +/* - */ } + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs new file mode 100644 index 0000000000000..d4708cd367ff6 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs @@ -0,0 +1,11 @@ +#![feature(core_intrinsics)] + +pub fn not_instrprof_increment(_hash: u64, _num_counters: u32, _index: u32) { +} + +fn main() { + // COMPARE THIS WITH INTRINSIC INSERTION + //not_instrprof_increment(1234 as u64, 314 as u32, 31 as u32); + + unsafe { core::intrinsics::instrprof_increment(1234 as u64, 314 as u32, 31 as u32) }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/just_main.rs b/src/test/codegen/coverage-experiments/src/just_main.rs new file mode 100644 index 0000000000000..081e5d72a6e0a --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/just_main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("hello world! (should be covered)"); +} diff --git a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs new file mode 100644 index 0000000000000..263277c7cdc4d --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs @@ -0,0 +1,17 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + let a = 1; + let b = 10; + let c = 100; + let _result = __incr_cov("start", a < b) || __incr_cov("or", b < c); + + let _result = { let _t = a < b; __llvm_incr_counter("start"); _t } || { let _t = b < c; __llvm_incr_counter("start"); _t }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/loop_break_value.rs b/src/test/codegen/coverage-experiments/src/loop_break_value.rs new file mode 100644 index 0000000000000..76caa833ec4f8 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/loop_break_value.rs @@ -0,0 +1,15 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + __incr_cov("start", ()); + let _result = loop { + break __incr_cov("top of loop", true); + }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match.rs b/src/test/codegen/coverage-experiments/src/match.rs new file mode 100644 index 0000000000000..afbb20888eab5 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match.rs @@ -0,0 +1,22 @@ +pub fn __llvm_incr_counter(_region_loc: &str) { +} + +#[inline(always)] +pub fn __incr_cov(region_loc: &str, result: T) -> T { + __llvm_incr_counter(region_loc); + result +} + +fn main() { + let a = 1; + let b = 10; + let _result = match a < b { + true => true, + _ => false, + }; + + let _result = match __incr_cov("end of first match", a < b) { + true => __incr_cov("matched true", true), + _ => false, // counter expression "end of first match" - "matched true" + }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment.rs b/src/test/codegen/coverage-experiments/src/match_with_increment.rs new file mode 100644 index 0000000000000..f618b37ed5247 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_with_increment.rs @@ -0,0 +1,305 @@ +#![feature(core_intrinsics)] +//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; + static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; +fn main() { + let a = 1; + let b = 10; + let _result = match { + let _t = a < b; + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; + _t + } { + true => { + let _t = true; + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; + _t + } + _ => false, + }; +} + +/* + +I NEED TO INSERT THE instrprof_increment() CALL: + + 1. JUST BEFORE THE switchInt(_4) (because we haven't counted entering the function main() yet, deferring that to "JUST BEFORE FIRST BRANCH") + 2. SOME TIME AFTER THE switchInt(_4), AND JUST BEFORE ANOTHER BRANCH (in this case, before "goto") + 2.a. NOT BEFORE BOTH GOTO'S AFTER switchInt(_4) (because one can be calculated by counter expression), BUT PERHAPS INSERT A noop PLACEHOLDER + AS A MARKER TO INCLUDE THE COVERAGE REGION AND REFERENCE THE COUNTERS TO BE SUBTRACTED (AND/OR SUMMED)? + + WHY DEFER INSERTING COUNTERS TO "JUST BEFORE FIRST BRANCH"? We can ignore panic/unwind() and only count if the coverage region ACTUALLY + executed in entirety. BUT IS THAT NECESSARY? IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF + PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? + + IS THERE A BENEFIT OF THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? (BECAUSE, WE STILL NEED TO COUNT THE REGION + LEADING UP TO THE EXPRESSION ANYWAY) + +================================================= +================================================= + +To inject an intrinsic after computing a final expression value of a coverage region: + +Replace the following basic block end (last statement plus terminator): + +... ... +StorageLive(_4) +StorageLive(_5) +_5 = _1 +StorageLive(_6) +_6 = _2 +_4 = Lt(move _5, move _6) +StorageDead(_6) +StorageDead(_5) + <------ to insert instrprof_increment() here +FakeRead(ForMatchedPlace, _4) +-------------------------------------------------------------------------------------- +switchInt(_4) + + +================================================= +Insert call to intrinsic with: + +StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call +StorageLive(_5) # Unchanged except _4 is now _5 +StorageLive(_6) # Unchanged except _5 is now _6 +_6 = _1 # Unchanged except _5 is now _6 +StorageLive(_7) # Unchanged except _6 is now _7 +_7 = _2 # Unchanged except _6 is now _7 +_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 +StorageDead(_7) # Unchanged except _6 is now _7 +StorageDead(_6) # Unchanged except _5 is now _6 + +FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? +StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` + # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) + # SEE BELOW + +FakeRead(ForMatchedPlace, _4) # Unchanged +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + +================================================= +Can I skip the extra variable and insert call to intrinsic with: + +StorageLive(_4) # Unchanged +StorageLive(_5) # Unchanged +_5 = _1 # Unchanged +StorageLive(_6) # Unchanged +_6 = _2 # Unchanged +_4 = Lt(move _5, move _6) # Unchanged +StorageDead(_6) # Unchanged +StorageDead(_5) # Unchanged + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + + + + +================================================= +================================================= + +For the second inserted call to instrprof_increment, without that call we have: + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # that is, "NOT false" + +_3 = const true + <------ to insert instrprof_increment() here +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +FakeRead(ForLet, _3) # NOTE: Unused result +StorageDead(_4) +_0 = () +StorageDead(_3) +StorageDead(_2) +StorageDead(_1) +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +return # from main() + + +================================================= +With the call to increment(): + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. +_15 = const true # UNCHANGED except _3 is now _15 +FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) +> _3 = _15 +> StorageDead(_15) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +================================================= +As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: + + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +_3 = const true # UNCHANGED? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs new file mode 100644 index 0000000000000..60586967920cb --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs @@ -0,0 +1,296 @@ +#![feature(core_intrinsics)] +//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; + static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; +fn main() { + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; + let a = 1; + let b = 10; + let _result = match a < b { + true => { + unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; + true + } + _ => false, + }; +} + +/* + +ALTERNATE APPROACH: + + IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF + PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? + + IS THERE A DETRACTOR COMPARED TO THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? + + (BECAUSE, WE STILL NEED TO COUNT THE REGION LEADING UP TO THE EXPRESSION ANYWAY) + +================================================= +================================================= + +To inject an intrinsic after computing a final expression value of a coverage region: + +Replace the following basic block end (last statement plus terminator): + +... ... +StorageLive(_4) +StorageLive(_5) +_5 = _1 +StorageLive(_6) +_6 = _2 +_4 = Lt(move _5, move _6) +StorageDead(_6) +StorageDead(_5) + <------ to insert instrprof_increment() here +FakeRead(ForMatchedPlace, _4) +-------------------------------------------------------------------------------------- +switchInt(_4) + + +================================================= +Insert call to intrinsic with: + +StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call +StorageLive(_5) # Unchanged except _4 is now _5 +StorageLive(_6) # Unchanged except _5 is now _6 +_6 = _1 # Unchanged except _5 is now _6 +StorageLive(_7) # Unchanged except _6 is now _7 +_7 = _2 # Unchanged except _6 is now _7 +_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 +StorageDead(_7) # Unchanged except _6 is now _7 +StorageDead(_6) # Unchanged except _5 is now _6 + +FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? +StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` + # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) + # SEE BELOW + +FakeRead(ForMatchedPlace, _4) # Unchanged +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + +================================================= +Can I skip the extra variable and insert call to intrinsic with: + +StorageLive(_4) # Unchanged +StorageLive(_5) # Unchanged +_5 = _1 # Unchanged +StorageLive(_6) # Unchanged +_6 = _2 # Unchanged +_4 = Lt(move _5, move _6) # Unchanged +StorageDead(_6) # Unchanged +StorageDead(_5) # Unchanged + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" +> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? +> StorageLive(_9) +> StorageLive(_10) +> StorageLive(_11) +> _11 = const {alloc1+0: &&[u8; 6]} +> _10 = &raw const (*(*_11)) +> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_10) +> StorageLive(_12) +> _12 = const 1234u64 +> StorageLive(_13) +> _13 = const 3u32 +> StorageLive(_14) +> _14 = const 0u32 +> -------------------------------------------------------------------------------------- +> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) +> +> -> return +> +> StorageDead(_14) +> StorageDead(_13) +> StorageDead(_12) +> StorageDead(_9) +> StorageDead(_11) +> StorageDead(_8) + +FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) +-------------------------------------------------------------------------------------- +switchInt(_4) # Unchanged + + + + + +================================================= +================================================= + +For the second inserted call to instrprof_increment, without that call we have: + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # that is, "NOT false" + +_3 = const true + <------ to insert instrprof_increment() here +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +FakeRead(ForLet, _3) # NOTE: Unused result +StorageDead(_4) +_0 = () +StorageDead(_3) +StorageDead(_2) +StorageDead(_1) +-------------------------------------------------------------------------------------- +goto + +-> # No label. No condition, and not a "return" + +return # from main() + + +================================================= +With the call to increment(): + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. +_15 = const true # UNCHANGED except _3 is now _15 +FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) +> _3 = _15 +> StorageDead(_15) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +================================================= +As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: + + +-------------------------------------------------------------------------------------- +switchInt(_4) # From above + +-> otherwise # "NOT false" # UNCHANGED + +_3 = const true # UNCHANGED? + +> # ALL NEW AND NECESSARY TO CALL instrprof_increment() +> StorageLive(_16) # pointer to instrprof_increment() function ? +> StorageLive(_17) +> StorageLive(_18) +> StorageLive(_19) +> _19 = const {alloc1+0: &&[u8; 6]} +> _18 = &raw const (*(*_19)) +> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) +> StorageDead(_18) +> StorageLive(_20) +> _20 = const 1234u64 +> StorageLive(_21) +> _21 = const 3u32 +> StorageLive(_22) +> _22 = const 1u32 +> -------------------------------------------------------------------------------------- +> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) +> +> -> return +> +> StorageDead(_22) +> StorageDead(_21) +> StorageDead(_20) +> StorageDead(_17) +> StorageDead(_19) +> StorageDead(_16) + +--------------------------------# UNCHANGED------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +FakeRead(ForLet, _3) # UNCHANGED +StorageDead(_4) # UNCHANGED +_0 = () # UNCHANGED +StorageDead(_3) # UNCHANGED +StorageDead(_2) # UNCHANGED +StorageDead(_1) # UNCHANGED +-------------------------------------------------------------------------------------- +goto # UNCHANGED + +-> # UNCHANGED + +return # from main() # UNCHANGED + +*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.mir b/src/test/codegen/coverage-experiments/src/match_without_increment.mir new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.rs b/src/test/codegen/coverage-experiments/src/match_without_increment.rs new file mode 100644 index 0000000000000..fa85833e05434 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/match_without_increment.rs @@ -0,0 +1,5 @@ +fn main() { + let a = 1; + let b = 10; + let _result = match a < b { true => true, _ => false, }; +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir b/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs new file mode 100644 index 0000000000000..03d11b2c230ca --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs @@ -0,0 +1,24 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression +/* : ┃ */ let _ = countdown; +/* : ┃ */ __incr_cov("top of for", ()); +/* ┃ - */ } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/while.rs b/src/test/codegen/coverage-experiments/src/while.rs new file mode 100644 index 0000000000000..3cb185eda544f --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while.rs @@ -0,0 +1,23 @@ +#[inline(always)] +pub fn __incr_cov(_region_loc: &str, result: T) -> T { + result +} + +fn main() { + let mut countdown = 10; + __incr_cov("block start",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + } + + let mut countdown = 10; + __incr_cov("after first while loop",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + if countdown < 5 { + __incr_cov("top of if countdown < 5",()); + break; + } + countdown -= 2; + } +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_clean.rs b/src/test/codegen/coverage-experiments/src/while_clean.rs new file mode 100644 index 0000000000000..e9ed1efc220d4 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_clean.rs @@ -0,0 +1,6 @@ +fn main() { + let mut countdown = 10; + while countdown > 0 { + countdown -= 1; + } +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_early_return.rs b/src/test/codegen/coverage-experiments/src/while_early_return.rs new file mode 100644 index 0000000000000..35709ffba3a04 --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_early_return.rs @@ -0,0 +1,10 @@ +fn main() -> u8 { // this will lower to HIR but will not compile: `main` can only return types that implement `std::process::Termination` + let mut countdown = 10; + while countdown > 0 { + if false { + return if countdown > 8 { 1 } else { return 2; }; + } + countdown -= 1; + } + 0 +} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_with_comments.rs b/src/test/codegen/coverage-experiments/src/while_with_comments.rs new file mode 100644 index 0000000000000..56417fedf00df --- /dev/null +++ b/src/test/codegen/coverage-experiments/src/while_with_comments.rs @@ -0,0 +1,51 @@ +/* */ #[inline(always)] +/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { +/* */ result +/* */ } +/* */ +/* - */ fn main() { +/* ┃ */ let mut countdown = 10; +/* ┃ */ __incr_cov("block start",()); // Must increment before repeated while text expression +/* : I */ while __incr_cov("while test", countdown > 0) { // span is just the while test expression +/* : ┃ */ countdown -= 1; +/* : ┃ */ // __incr_cov("while loop",()); // Counter not needed, but span is computed as "while test" minus "block start" +/* : ┃ */ // If while criteria is tested 11 times, and the outer block runs only once, 11-1 = 10 +/* : ┃ */ // REMOVING COUNTER ASSUMES NO EARLY RETURN THOUGH. +/* : ┃ */ // I THINK WE CAN ONLY USE THE COUNTER EXPRESSION UP TO FIRST CONDITIONAL BLOCK, IF ANY (if, match, maybe any loop) +/* ┃ - */ } + + let mut countdown = 10; + __incr_cov("after first while loop",()); + while __incr_cov("while test", countdown > 0) { + countdown -= 1; + // if __incr_cov("top of while loop", countdown < 5) { + if countdown < 5 { // "top of while loop" = counter expression "while test" - "after first while loop" + __incr_cov("top of if countdown < 5",()); + break; + } + countdown -= 2; + // __incr_cov("after if countdown < 5 block", ()); + // "after if countdown < 5 block" = counter expression "top of while loop" - "top of if countdown < 5" + // HOWEVER, WE CAN ONLY REMOVE THE COUNTER AND USE COUNTER EXPRESSION IF WE **KNOW** THAT THE BODY OF THE IF + // WILL **ALWAYS** BREAK (OR RETURN, OR CONTINUE?) + // AND THUS WE TREAT THE STATEMENTS FOLLOWING THE IF BLOCK AS IF THEY WERE AN ELSE BLOCK. + // THAT'S A LOT TO ASK. + + // PERHAPS TREAT EARLY RETURNS AS A SPECIAL KIND OF COUNTER AND IF ANY ARE INVOKED BEFORE STATEMENTS AFTER THE BLOCK THAT CONTAINS THEM, + // THEN SUBTRACT THOSE COUNTS FROM THE COUNT BEFORE THE BLOCK (AS WE DO HERE)? (SO ONE SET OF EXPRESSIONS MUST SUM ALL OF THE EARLY + // RETURNS) + } +/* - */ } + + +// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; +// valid types are any of the types for `--pretty`, as well as: +// `expanded`, `expanded,identified`, +// `expanded,hygiene` (with internal representations), +// `everybody_loops` (all function bodies replaced with `loop {}`), +// `hir` (the HIR), `hir,identified`, +// `hir,typed` (HIR with types for each node), +// `hir-tree` (dump the raw HIR), +// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + +// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` From 088037a04414dee0b3a792a6fad193e081ee8e37 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 4 Jun 2020 17:47:21 -0700 Subject: [PATCH 10/18] explained lang_item function body (count_code_region) --- src/libcore/intrinsics.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index abb35e838ea28..0e5af35229ca0 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,6 +1943,12 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } +// Since `count_code_region` is lang_item, it must have a function body that the compiler can use +// to register its DefId with the lang_item entry. This function body is never actually called +// (and is therefore implemented as an aborting stub) because it is replaced with the +// LLVM intrinsic `llvm.instrprof.increment` by +// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. +#[doc(hidden)] #[cfg(not(bootstrap))] #[cfg_attr(not(bootstrap), lang = "count_code_region")] pub fn count_code_region(_index: u32) { From 2c5c2a6bc2f7023ee8ad252d2ee5a45fbfb2de22 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 4 Jun 2020 17:52:27 -0700 Subject: [PATCH 11/18] removed experiments for cleaner github PR --- src/libcore/intrinsics.rs | 17 +- .../codegen/coverage-experiments/Cargo.lock | 5 - .../codegen/coverage-experiments/Cargo.toml | 103 ----- .../README-THIS-IS-TEMPORARY.md | 157 -------- .../src/coverage_injection_test.rs | 335 ---------------- .../src/coverage_injection_test2.rs | 320 ---------------- .../src/coverage_injection_test_alt.rs | 362 ------------------ .../coverage-experiments/src/drop_trait.rs | 25 -- .../src/drop_trait_with_comments_prints.rs | 53 --- .../codegen/coverage-experiments/src/for.rs | 41 -- .../src/for_with_comments.rs | 24 -- .../codegen/coverage-experiments/src/if.rs | 80 ---- .../src/if_with_comments.rs | 39 -- .../src/increment_intrinsic.rs | 11 - .../coverage-experiments/src/just_main.rs | 3 - .../coverage-experiments/src/lazy_boolean.rs | 17 - .../src/loop_break_value.rs | 15 - .../codegen/coverage-experiments/src/match.rs | 22 -- .../src/match_with_increment.rs | 305 --------------- .../src/match_with_increment_alt.rs | 296 -------------- .../src/match_without_increment.mir | 0 .../src/match_without_increment.rs | 5 - .../src/match_without_increment_alt.mir | 0 ..._mark_err_status_handling_with_comments.rs | 24 -- .../codegen/coverage-experiments/src/while.rs | 23 -- .../coverage-experiments/src/while_clean.rs | 6 - .../src/while_early_return.rs | 10 - .../src/while_with_comments.rs | 51 --- 28 files changed, 9 insertions(+), 2340 deletions(-) delete mode 100644 src/test/codegen/coverage-experiments/Cargo.lock delete mode 100644 src/test/codegen/coverage-experiments/Cargo.toml delete mode 100644 src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test.rs delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs delete mode 100644 src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs delete mode 100644 src/test/codegen/coverage-experiments/src/drop_trait.rs delete mode 100644 src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs delete mode 100644 src/test/codegen/coverage-experiments/src/for.rs delete mode 100644 src/test/codegen/coverage-experiments/src/for_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/if.rs delete mode 100644 src/test/codegen/coverage-experiments/src/if_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/increment_intrinsic.rs delete mode 100644 src/test/codegen/coverage-experiments/src/just_main.rs delete mode 100644 src/test/codegen/coverage-experiments/src/lazy_boolean.rs delete mode 100644 src/test/codegen/coverage-experiments/src/loop_break_value.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.mir delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment.rs delete mode 100644 src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir delete mode 100644 src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_clean.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_early_return.rs delete mode 100644 src/test/codegen/coverage-experiments/src/while_with_comments.rs diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 0e5af35229ca0..06a432a26961e 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1943,16 +1943,17 @@ extern "rust-intrinsic" { pub fn miri_start_panic(payload: *mut u8) -> !; } -// Since `count_code_region` is lang_item, it must have a function body that the compiler can use -// to register its DefId with the lang_item entry. This function body is never actually called -// (and is therefore implemented as an aborting stub) because it is replaced with the -// LLVM intrinsic `llvm.instrprof.increment` by -// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. -#[doc(hidden)] +/// Defines the `count_code_region` intrinsic as a `LangItem`. `LangItem`s require a function body +/// to register its DefId with the LangItem entry. The function body is never actually called (and +/// is therefore implemented as an aborting stub) because it is replaced with the LLVM intrinsic +/// `llvm.instrprof.increment` by +/// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. #[cfg(not(bootstrap))] #[cfg_attr(not(bootstrap), lang = "count_code_region")] -pub fn count_code_region(_index: u32) { - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] // remove `unsafe` on bootstrap bump +fn count_code_region(_index: u32) { + // remove `unsafe` (and safety comment) on bootstrap bump + #[cfg_attr(not(bootstrap), allow(unused_unsafe))] + // SAFETY: the `abort` intrinsic has no requirements to be called. unsafe { abort() } diff --git a/src/test/codegen/coverage-experiments/Cargo.lock b/src/test/codegen/coverage-experiments/Cargo.lock deleted file mode 100644 index 132469cbb182c..0000000000000 --- a/src/test/codegen/coverage-experiments/Cargo.lock +++ /dev/null @@ -1,5 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "coverage_experiments" -version = "0.1.0" diff --git a/src/test/codegen/coverage-experiments/Cargo.toml b/src/test/codegen/coverage-experiments/Cargo.toml deleted file mode 100644 index 296a8d5c9af2d..0000000000000 --- a/src/test/codegen/coverage-experiments/Cargo.toml +++ /dev/null @@ -1,103 +0,0 @@ -[workspace] - -[package] -name = "coverage_experiments" -version = "0.1.0" -license = "BSD-3-Clause" -authors = ["rust-fuchsia@fuchsia.com"] -edition = "2018" - -[[bin]] - -name = "coverage_injection_test" -path = "src/coverage_injection_test.rs" - -[[bin]] - -name = "coverage_injection_test2" -path = "src/coverage_injection_test2.rs" - -[[bin]] - -name = "while" -path = "src/while.rs" - -[[bin]] - -name = "while_clean" -path = "src/while_clean.rs" - -[[bin]] - -name = "while_early_return" -path = "src/while_early_return.rs" - -[[bin]] - -name = "if_with_comments" -path = "src/if_with_comments.rs" - -[[bin]] - -name = "if" -path = "src/if.rs" - -[[bin]] - -name = "increment_intrinsic" -path = "src/increment_intrinsic.rs" - -[[bin]] - -name = "just_main" -path = "src/just_main.rs" - -[[bin]] - -name = "lazy_boolean" -path = "src/lazy_boolean.rs" - -[[bin]] - -name = "match" -path = "src/match.rs" - -[[bin]] - -name = "match_without_increment" -path = "src/match_without_increment.rs" # identical to -Zunpretty=hir output - -[[bin]] - -name = "match_with_increment" -path = "src/match_with_increment.rs" - -[[bin]] - -name = "match_with_increment_alt" -path = "src/match_with_increment_alt.rs" - -[[bin]] - -name = "loop_break_value" -path = "src/loop_break_value.rs" - -[[bin]] - -name = "for_with_comments" -path = "src/for_with_comments.rs" - -[[bin]] - -name = "for" -path = "src/for.rs" - -[[bin]] - -name = "drop_trait" -path = "src/drop_trait.rs" - -#[dependencies] # Should not need to manually add coverage dependencies -#version = "0.1.0" -#path = "../__builtin" # for mod __builtin::coverage - diff --git a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md b/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md deleted file mode 100644 index 3b69c0a406594..0000000000000 --- a/src/test/codegen/coverage-experiments/README-THIS-IS-TEMPORARY.md +++ /dev/null @@ -1,157 +0,0 @@ -# codegen/coverage-experiments -*

THIS DIRECTORY IS TEMPORARY

* - -This directory contains some work-in-progress (WIP) code used for experimental development and -testing of Rust Coverage feature development. - -The code in this directory will be removed, or migrated into product tests, when the Rust -Coverage feature is complete. - -[TOC] - -## Development Notes - -### config.toml - -config.toml probably requires (I should verify that intrinsic `llvm.instrprof.increment` -code generation ONLY works with this config option): - - profiler = true - -## First build - -```shell -./x.py clean -./x.py build -i --stage 1 src/libstd -``` - -## Incremental builds *IF POSSIBLE!* - -```shell -./x.py build -i --stage 1 src/libstd --keep-stage 1 -``` - -*Note: Some changes made for Rust Coverage required the full build (without `--keep-stage 1`), and in some cases, required `./x.py clean` first!. Occassionally I would get errors when building or when compiling a test program with `--Zinstrument-coverage` that work correctly only after a full clean and build.* - -## Compile a test program with LLVM coverage instrumentation - -*Note: This PR is still a work in progress. At the time of this writing, the `llvm.instrprof.increment` intrinsic is injected, and recognized by the LLVM code generation stage, but it does not appear to be included in the final binary. This is not surprising since other steps are still to be implemented, such as generating the coverage map. See the suggested additional `llvm` flags for ways to verify the `llvm` passes at least get the right intrinsic.* - -Suggested debug configuration to confirm Rust coverage features: -```shell -$ export RUSTC_LOG=rustc_codegen_llvm::intrinsic,rustc_mir::transform::instrument_coverage=debug -``` - -Ensure the new compiled `rustc` is used (the path below, relative to the `rust` code repository root, is an example only): - -```shell -$ build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - src/test/codegen/coverage-experiments/just_main.rs \ - -Zinstrument-coverage -``` - -### About the test programs in coverage-experiments/src/ - -The `coverage-experiments/src/` directory contains some sample (and very simple) Rust programs used to analyze Rust compiler output at various stages, with or without the Rust code coverage compiler option. For now, these are only used for the in-progress development and will be removed at a future date. (These are *not* formal test programs.) - -The src director may also contain some snapshots of mir output from experimentation, particularly if the saved snapshots highlight results that are important to the future development, individually or when compared with other output files. - -Be aware that some of the files and/or comments may be outdated. - -### Additional `llvm` flags (append to the `rustc` command) - -These optional flags generate additional files and/or terminal output. LLVM's `-print-before=all` should show the `instrprof.increment` intrinsic with arguments computed by the experimental Rust coverage feature code: - -```shell - --emit llvm-ir \ - -Zverify-llvm-ir \ - -Zprint-llvm-passes \ - -Csave-temps \ - -Cllvm-args=-print-before-all -``` - -### Additional flags for MIR analysis and transforms - -These optional flags generate a directory with many files representing the MIR as text (`.mir` files) and as a visual graph (`.dot` files) rendered by `graphviz`. (**Some IDEs, such as `VSCode` have `graphviz` extensions.**) - -```shell - -Zdump-mir=main \ - -Zdump-mir-graphviz -``` - -### Flags I've used but appear to be irrelvant to `-Zinstrument-coverage` after all: -```shell - # -Zprofile - # -Ccodegen-units=1 - # -Cinline-threshold=0 - # -Clink-dead-code - # -Coverflow-checks=off -``` - -## Run the test program compiled with code coverage instrumentation (maybe): - -As stated above, at the time of this writing, this work-in-progress seems to generate `llvm.instrprof.increment` intrinsic calls correctly, and are visibile in early `llvm` code generation passes, but are eventually stripped. - -The test program should run as expected, currently does not generate any coverage output. - -*Example:* - -```shell - $ src/test/codegen/coverage-experiments/just_main - hello world! (should be covered) -``` - -### Running the coverage-enabled `rustc` compiler in the `lldb` debugger: - -For example, to verify the intrinsic is codegen'ed, set breakpoint in `lldb` where it validates a certain instruction is the `llvm.instrprof.increment` instruction. - -First, update config.toml for debugging: - -```toml - [llvm] - optimize = false - release-debuginfo = true - - [rust] - debug = true - debuginfo-level = 2 -``` - -*(Note, in case this is relevant after all, I also have the following changes; but I don't think I need them:)* - -```toml - # Add and uncomment these if relevant/useful: - # codegen-units = 0 - # python = '/usr/bin/python3.6' -``` - -Run the compiler with additional flags as needed: - -```shell -lldb \ - build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - -- \ - src/test/codegen/coverage-experiments/just_main.rs \ - -Zinstrument-coverage \ - -Zdump-mir=main \ - -Zdump-mir-graphviz -``` - -Note the specific line numbers may be different: - -```c++ -(lldb) b lib/Transforms/Instrumentation/InstrProfiling.cpp:418 -(lldb) r - -Process 93855 stopped -* thread #6, name = 'rustc', stop reason = breakpoint 2.1 - frame #0: 0x00007fffedff7738 librustc_driver-5a0990d8d18fb2b4.so`llvm::InstrProfiling::lowerIntrinsics(this=0x00007fffcc001d40, F=0x00007fffe4552198) at InstrProfiling.cpp:418:23 - 415 auto Instr = I++; - 416 InstrProfIncrementInst *Inc = castToIncrementInst(&*Instr); - 417 if (Inc) { --> 418 lowerIncrement(Inc); - 419 MadeChange = true; - 420 } else if (auto *Ind = dyn_cast(Instr)) { - 421 lowerValueProfileInst(Ind); -(lldb) -``` \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs deleted file mode 100644 index 231da1dc1a67f..0000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test.rs +++ /dev/null @@ -1,335 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ -/* */ result -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then -/* */ /// write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ #[inline(always)] -/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { -/* */ __incr_cov(region_loc, /*counter,*/ ()); -/* */ __report(); -/* */ result -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ __incr_cov(from!("fn new()"),Self::new_with_value(31415)) // function-scoped counter index = 0 -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ __incr_cov(from!("fn new_with_value()"),Self { -/* ┃ */ field, -/* ┃ */ }) // function-scoped counter index = 0 -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ __incr_cov(from!("fn call_closure()"),closure(123)) // function-scoped counter index = 0 -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match __incr_cov(from!("fn various"),color) { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : I */ Red => __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"),println!("roses")), -/* : - */ Green => { -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any"),spidey > goblin) { -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - */ } else if __incr_cov(from!("`else if` on this line"),spidey == goblin) { -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - - - */ } else if if __incr_cov(from!("`else if` on this line"),true) { -/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); -/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. -/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) -/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO -/* : : : ┃ */ // IT SHOULD BE: -/* ┏-:---:-------:---< */ return __incr_cov(from!(""),Ok(())); -/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. -/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. -/* : : ┃ - */ } else { -/* : : : I */ __incr_cov(from!("`else`"),false) -/* : : - - */ } { -/* : : ┃ */ println!("wierd science"); -/* : : ┃ */ __incr_cov(from!("block start"),()); -/* : ┃ - */ } else { -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return __incr_cov(from!("`else`"),Ok(())); // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : ┃ - */ // __incr_cov(from!(""),()); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : I */ Blue => __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any"),println!("violets")), -/* ┃ */ } -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)"),condition1) { -/* : ┃ */ println!("before while loop"); -/* : ┃ */ let mut countdown = 10; -/* : ┃ */ __incr_cov(from!("block start"),()); // Must increment before repeated while text expression -/* : : I */ while __incr_cov(from!("while test"), countdown > 0) { // span is just the while test expression -/* : : ┃ */ println!("top of `while` loop"); -/* : : ┃ */ countdown -= 1; -/* : : ┃ */ // __incr_cov(from!("while loop"),()); // Counter not needed, but span is computed as "while test" minus "block start" -/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 -/* : ┃ - */ } -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in __incr_cov(from!("end of while"),0..10) { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if __incr_cov(from!("block start"),index == 8) { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break __incr_cov(from!(""),()); -/* : : : ┃ */ __incr_cov(from!("block start"),()); -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if __incr_cov(from!("block end of `if index == 8`"),condition2) { -/* ┏-:---:---:---< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`"),()); -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ __incr_cov(from!("for loop"),()); -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if __incr_cov(from!("block end of `for` loop"),condition2) { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : - */ } else if __incr_cov(from!("`else`"),condition3) { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return __incr_cov(from!("block start"),Ok(())); -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ __incr_cov(from!("block start"),false) -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ __incr_cov(from!(""),Ok(())) -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| __incr_cov(from!(""), -/* : : ┃ - */ if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ __incr_cov(from!(""),false) -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ __incr_cov(from!(""),true) -/* : : ┃ - */ } -/* : : - */ ) -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned true"))) -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ __incr_cov(from!(""),Err(Error::new(ErrorKind::Other, "Result is error if closure returned false"))) -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ __incr_cov(from!("if condition1"),result) -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ __incr_cov(from!(""),Ok(())) -/* ┃ - */ } -/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ __incr_cov(from!(""),{ -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ }) { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ __incr_cov(from!(""),test_struct.various()) -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs deleted file mode 100644 index 8f4399ab51d09..0000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test2.rs +++ /dev/null @@ -1,320 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str) { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ let __result = Self::new_with_value(31415); // function-scoped counter index = 0 -/* ┃ */ __incr_cov(from!("fn new()")); -/* ┃ */ __result -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ let __result = Self { -/* ┃ */ field, -/* ┃ */ }; -/* ┃ */ __incr_cov(from!("fn new_with_value()")); // function-scoped counter index = 0 -/* ┃ */ __result -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ let __result = closure(123); -/* ┃ */ __incr_cov(from!("fn call_closure()")); // function-scoped counter index = 0 -/* ┃ */ __result -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match { let __result = color; __incr_cov(from!("fn various")); __result } { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : I */ Red => {println!("roses"); __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"));} -/* : - */ Green => { -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if { let __result = spidey > goblin; __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any")); __result } { -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - */ } else if { let __result = spidey == goblin; __incr_cov(from!("`else if` on this line")); __result } { -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - - - */ } else if if { let __result = true; __incr_cov(from!("`else if` on this line")); __result } { -/* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(())); -/* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT. -/* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE) -/* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO -/* : : : ┃ */ // IT SHOULD BE: -/* ┏-:---:-------:---< */ return { let __result = Ok(()); __incr_cov(from!("")); __result }; -/* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`. -/* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT. -/* : : ┃ - */ } else { -/* : : : I */ { let __result = false; __incr_cov(from!("`else`")); __result } -/* : : - - */ } { -/* : : ┃ */ println!("wierd science"); -/* : : ┃ */ __incr_cov(from!("block start")); -/* : ┃ - */ } else { -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return { let __result = Ok(()); __incr_cov(from!("`else`")); __result }; // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : ┃ - */ // __incr_cov(from!("")); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : I */ Blue => { println!("violets"); __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any")); } -/* ┃ */ } -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if { let __result = condition1; __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)")); __result } { -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in { let __result = 0..10; __incr_cov(from!("block start")); __result } { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if { let __result = index == 8; __incr_cov(from!("block start")); __result } { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break __incr_cov(from!("")); -/* : : : ┃ */ __incr_cov(from!("block start")); -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `if index == 8`")); __result } { -/* ┏-:---:---:---< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`")); -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ __incr_cov(from!("")); -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `for` loop")); __result } { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : - */ } else if { let __result = condition3; __incr_cov(from!("`else`")); __result } { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result }; -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ { let __result = false; __incr_cov(from!("block start")); __result } -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| { -/* : : ┃ - */ let __result = if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ { let __result = false; __incr_cov(from!("")); __result } -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ { let __result = true; __incr_cov(from!("")); __result } -/* : : ┃ - */ }; -/* : : - */ __incr_cov(from!("")); __result } -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")); __incr_cov(from!("")); __result } -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")); __incr_cov(from!("")); __result } -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ { let __result = result; __incr_cov(from!("if condition1")); __result } -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result } -/* ┃ - */ } -/* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ { let __result = { -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ }; __incr_cov(from!("")); __result } { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return { let __result = Err(Error::new(ErrorKind::Other, format!("Error status {}", status))); __incr_cov(from!("")); __report(); __result } -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ { let __result = test_struct.various(); __incr_cov(from!("")); __result } -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ { let __result = result; __incr_cov(from!("")); __report(); __result } -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs b/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs deleted file mode 100644 index 20c4835dd882e..0000000000000 --- a/src/test/codegen/coverage-experiments/src/coverage_injection_test_alt.rs +++ /dev/null @@ -1,362 +0,0 @@ -/* */ use std::io::Error; -/* */ use std::io::ErrorKind; -/* */ -/* */ /// Align Rust counter increment with with: -/* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic) -/* */ /// -/* */ /// declare void @llvm.instrprof.increment(i8* , i64 , i32 , i32 ) -/* */ /// -/* */ /// The first argument is a pointer to a global variable containing the name of the entity -/* */ /// being instrumented. This should generally be the (mangled) function name for a set of -/* */ /// counters. -/* */ /// -/* */ /// The second argument is a hash value that can be used by the consumer of the profile data -/* */ /// to detect changes to the instrumented source, and the third is the number of counters -/* */ /// associated with name. It is an error if hash or num-counters differ between two -/* */ /// instances of instrprof.increment that refer to the same name. -/* */ /// -/* */ /// The last argument refers to which of the counters for name should be incremented. It -/* */ /// should be a value between 0 and num-counters. -/* */ /// -/* */ /// # Arguments -/* */ /// -/* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using: -/* */ /// -/* */ /// ``` -/* */ /// fn rustc_symbol_mangling::compute_symbol_name( -/* */ /// tcx: TyCtxt<'tcx>, -/* */ /// instance: Instance<'tcx>, -/* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum, -/* */ /// ) -> String -/* */ /// ``` -/* */ /// -/* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant" -/* */ /// to control-flow inside the function. -/* */ /// -/* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the -/* */ /// function. -/* */ /// -/* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of -/* */ /// counters, relative to the source code location, is apparently not expected.) -/* */ /// -/* */ /// # Notes -/* */ /// -/* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see -/* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name). -/* */ /// * The version hash may be computable from AST analysis, and may not benefit from further -/* */ /// lowering. -/* */ /// * num_counters depends on having already identified all counter insertion locations. -/* */ /// * counter_index can be computed at time of counter insertion (incrementally). -/* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types. -/* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) { -/* */ } -/* */ -/* */ /// A coverage counter implementation that will work as both an intermediate coverage -/* */ /// counting and reporting implementation at the AST-level only--for debugging and -/* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM -/* */ /// intrinsic coverage counter APIs during the lowering process. -/* */ /// -/* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics -/* */ /// are enabled, the counter function calls that were injected into the AST serve as -/* */ /// placeholders, to be replaced by an alternative, such as: -/* */ /// -/* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or -/* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the -/* */ /// `llvm.instrprof.increment()` intrinsic; or -/* */ /// * a similar expression wrapper, with the additional parameters (as defined above -/* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the -/* */ /// result of the wrapped expression) -/* */ /// -/* */ /// The first two options would require replacing the inlined wrapper call with something -/* */ /// like: -/* */ /// -/* */ /// ``` -/* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result } -/* */ /// ``` -/* */ /// -/* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then -/* */ /// it may be a perfect opportunity to replace the function with one of these more -/* */ /// direct methods. -/* */ /// -/* */ #[inline(always)] -/* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/) { -/* */ // Either call the intermediate non-llvm coverage counter API or -/* */ // replace the call to this function with the expanded `__lower_incr_cov()` call. -/* */ -/* */ // let _lock = increment_counter(counter); -/* */ println!("{}", region_loc); -/* */ } -/* */ -/* */ /// Write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ fn __report() { -/* */ println!("WRITE REPORT!"); -/* */ } -/* */ -/* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then -/* */ /// write a report identifying each incremented counter and the number of times each counter -/* */ /// was incremented. -/* */ #[inline(always)] -/* */ pub fn __incr_cov_and_report(region_loc: &str, /*counter: u32,*/ result: T) -> T { -/* */ __incr_cov(region_loc, /*counter,*/); -/* */ __report(); -/* */ result -/* */ } -/* */ -/* */ macro_rules! from { -/* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ macro_rules! to { -/* */ ($to:expr) => { &format!("to: {}\n to: {}:{}:{}", $to, file!(), line!(), column!()) }; -/* */ } -/* */ -/* */ #[derive(Debug)] -/* */ enum TestEnum { -/* */ Red, -/* */ Green, -/* */ Blue, -/* */ } -/* */ -/* */ struct TestStruct { -/* */ field: i32, -/* */ } -/* */ -/* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING -/* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT. -/* */ -/* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS, -/* */ // BUT MAYBE I SHOULD ASK. -/* */ -/* */ impl TestStruct { -/* - */ fn new() -> Self { -/* ┃ */ __incr_cov(to!("end of fn new()")); // function-scoped counter index = 0 -/* ┃ */ Self::new_with_value(31415) -/* - */ } -/* */ -/* - */ fn new_with_value(field: i32) -> Self { -/* ┃ */ __incr_cov(to!("end of fn new_with_value()")); // function-scoped counter index = 0 -/* ┃ */ Self { -/* ┃ */ field, -/* ┃ */ } -/* - */ } -/* */ -/* */ fn call_closure(&self, closure: F) -> bool -/* */ where -/* */ F: FnOnce( -/* */ i32, -/* */ ) -> bool, -/* - */ { -/* ┃ */ __incr_cov(to!("end of fn call_closure()")); // function-scoped counter index = 0 -/* ┃ */ closure(123) -/* - */ } -/* */ -/* - */ fn various(&self) -> Result<(),Error> { -/* ┃ */ __incr_cov(to!("just before next branch: after `match color`: pattern selection")); -/* ┃ */ use TestEnum::*; -/* ┃ */ let mut color = Red; -/* ┃ */ let _ = color; -/* ┃ */ color = Blue; -/* ┃ */ let _ = color; -/* ┃ */ color = Green; -/* ┃ */ match color { // function-scoped counter index = 0 -/* : */ -/* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION -/* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue` -/* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION -/* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`. -/* : - */ Red => { -/* : ┃ */ __incr_cov(to!("end of matched Red")); -/* : ┃ */ println!("roses"); -/* : - */ } -/* : - */ Green => { -/* : ┃ */ __incr_cov(to!("just before next branch: after `if spidey > goblin`")); -/* : ┃ */ let spidey = 100; -/* : ┃ */ let goblin = 50; -/* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{ -/* : ┃ */ // println!("what ev"); -/* : ┃ */ // })} -/* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE. -/* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition) -/* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE! -/* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK) -/* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK. -/* : ┃ - */ if spidey > goblin { -/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); -/* : : ┃ */ println!("spidey beats goblin"); -/* : : ┃ */ -/* : ┃ - */ } else if { -/* : : : */ // Make sure we can't compute the coverage count here. -/* : : : */ // We know the expression executed if the previous if block DID NOT -/* : : : */ // execute, and either this `else if` block does execute OR any subsequent -/* : : : */ // `else if` or `else` blocks execute, OR none of the blocks in the -/* : : : */ // `if`, `else if` or `else` blocks execute. -/* : : : */ // `if`, `else if` or `else` blocks execute. -/* : : ┃ */ __incr_cov(to!("end of `else if spidey == goblin` expression")); -/* : : ┃ */ spidey == goblin -/* : ┃ - */ } { -/* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope")); -/* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case), -/* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with -/* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case, -/* : : ┃ */ // so I think simpler to just make it its own coverage region.) -/* : : ┃ */ println!("it's a draw"); -/* : : ┃ */ -/* : ┃ - - - */ } else if { -/* : : ┃ */ __incr_cov(to!("end of `if true`")); -/* : ┃ - - - */ if true { -/* : : : ┃ */ __incr_cov(to!("end of `return Ok(())`")); -/* ┏-:---:-------:---< */ return Ok(()); -/* V : : ┃ - */ } else { -/* : : : ┃ */ // __incr_cov(to!("end of else block")); -/* : : : ┃ */ // computed counter expression -/* : : : ┃ */ false -/* : : : - */ } -/* : : - - - */ } { -/* : : ┃ */ __incr_cov(to!("end of if block")); -/* : : ┃ */ println!("wierd science"); -/* : ┃ - */ } else { -/* : : ┃ */ // __incr_cov(to!("end of `return Ok(())")); -/* : : ┃ */ // counter expression: (start of Green match arm) - (if spidey > goblin) - (previous `} else if {`) -/* : : ┃ */ println!("goblin wins"); -/* ┏-:---:---< */ return Ok(()); // THIS COUNTS LAST STATEMENT IN `else` BLOCK -/* V : : : */ // COVERAGE NOTE: When counting the span for `return`, -/* : : : */ // `break`, or `continue`, also report the outer spans -/* : : : */ // got this far--including this `else` block. Record -/* : : : */ // The start positions for those outer blocks, but: -/* : : : */ // * For the block containing the `return`, `break`, or -/* : : : */ // `continue`, end report the end position is the -/* : : : */ // start of the `return` span (or 1 char before it). -/* : : : */ // * Anything else? -/* : ┃ - */ } -/* : : */ // __incr_cov(to!("end of matched Green")); -/* : : */ // // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match` -/* : - */ }, -/* : - */ Blue => { -/* : ┃ */ __incr_cov(to!("end of matched Blue")); -/* : ┃ */ println!("violets"); -/* : - */ } -/* ┃ */ } -/* ┃ */ __incr_cov(to!("just before next branch: after `if condition1` (HIR: 'match condition1')")); -/* ┃ */ -/* ┃ */ let condition1 = true; -/* ┃ */ let condition2 = false; -/* ┃ */ let condition3 = true; -/* ┃ */ -/* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field); -/* ┃ */ -/* ┃ - */ if condition1 { -/* : ┃ */ println!("before while loop"); -/* : ┃ */ let mut countdown = 10; -/* : ┃ */ // Must increment before repeated while text expression -/* : : I */ while countdown > 0 { // span is just the while test expression -/* : : ┃ */ println!("top of `while` loop"); -/* : : ┃ */ countdown -= 1; -/* : : ┃ */ // // Counter not needed, but span is computed as "while test" minus "block start" -/* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10 -/* : ┃ - */ } -/* : ┃ */ println!("before for loop"); -/* : ┃ - */ for index in 0..10 { -/* : : ┃ */ println!("top of `for` loop"); -/* : : ┃ - */ if index == 8 { -/* : : : ┃ */ println!("before break"); -/* : : : ┃ */ // note the following is not legal here: -/* : : : ┃ */ // "can only break with a value inside `loop` or breakable block" -/* : : : ┃ */ // break -/* : : : ┃ */ -/* : : ┏-----< */ break; -/* : : V : : */ -/* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression. -/* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label -/* : : ┃ - */ } -/* : : ┃ */ println!("after `break` test"); -/* : : ┃ - */ if condition2 { -/* ┏-:---:---:---< */ return Ok(()); -/* V : : ┃ - */ } -/* : : ┃ */ -/* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN -/* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE. -/* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END -/* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED. -/* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE -/* : : Λ : ┃ */ -/* : : ┗-----< */ continue; -/* : : ┃ - */ } -/* : : ┃ */ println!("after `continue` test"); -/* : : ┃ */ // maybe add a runtime flag for a possible `return` here? -/* : : ┃ */ -/* : ┃ - */ } -/* : ┃ */ println!("after for loop"); -/* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION -/* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES: -/* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK) -/* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK -/* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS -/* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION, -/* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED" -/* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK. -/* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND -/* : ┃ */ println!("after result = if ..."); -/* : ┃ - */ if condition2 { -/* : : ┃ */ println!("before first return"); -/* ┏-:---:-------< */ return Ok(()); -/* V : : - */ } else if condition3 { -/* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE. -/* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION. -/* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION -/* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS. -/* : : ┃ */ println!("not second return"); -/* ┏-:---:-------< */ return Ok(()); -/* V : : - */ } else { -/* : : ┃ */ println!("not returning"); -/* : : ┃ */ false -/* : : - */ } -/* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK -/* : ┃ - */ } { -/* : : ┃ */ println!("branched condition returned true"); -/* : : ┃ */ Ok(()) -/* : ┃ - */ } else if self.call_closure( -/* : : - */ |closure_param| -/* : : ┃ - */ if condition3 { -/* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param); -/* : : : ┃ */ false -/* : : ┃ - */ } else { -/* : : : ┃ */ println!("in closure, captured condition was false"); -/* : : : ┃ */ true -/* : : ┃ - */ } -/* : : - */ -/* : : - */ ) { -/* : : ┃ */ println!("closure returned true"); -/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")) -/* : ┃ - */ } else { -/* : : ┃ */ println!("closure returned false"); -/* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")) -/* : ┃ - */ }; -/* : ┃ */ println!("bottom of function might be skipped if early `return`"); -/* : ┃ */ result -/* ┃ - */ } else { -/* : ┃ */ println!("skipping everything in `various()`"); -/* : ┃ */ Ok(()) -/* ┃ - */ } -/* ┃ - */ // 0 // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED -/* - */ } -/* */ } -/* */ -/* - */ fn main() -> Result<(), std::io::Error> { -/* ┃ */ //let mut status: u8 = 2; -/* ┃ */ let mut status: u8 = 1; -/* : - */ let result = if status < 2 && -/* : ┃ */ { -/* : ┃ */ status -= 1; -/* : ┃ */ status == 0 -/* : - - */ } { -/* : ┃ */ let test_struct = TestStruct::new_with_value(100); -/* : ┃ */ let _ = test_struct.various(); -/* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status)))) -/* V : - */ } else { -/* : ┃ */ let test_struct = TestStruct::new(); -/* : ┃ */ test_struct.various() -/* : - */ }; -/* ┃ */ println!("done"); -/* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0 -/* - */ } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait.rs b/src/test/codegen/coverage-experiments/src/drop_trait.rs deleted file mode 100644 index 75400e037e9f0..0000000000000 --- a/src/test/codegen/coverage-experiments/src/drop_trait.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, result: T) -> T { - result -} - -struct Firework { - _strength: i32, -} - -impl Drop for Firework { - fn drop(&mut self) { - __incr_cov("start of drop()", ()); - } -} - -fn main() -> Result<(),u8> { - let _firecracker = Firework { _strength: 1 }; - - if __incr_cov("start of main()", true) { - return __incr_cov("if true", { let _t = Err(1); _t }); - } - - let _tnt = Firework { _strength: 100 }; - Ok(()) -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs b/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs deleted file mode 100644 index de9f5d5cb4647..0000000000000 --- a/src/test/codegen/coverage-experiments/src/drop_trait_with_comments_prints.rs +++ /dev/null @@ -1,53 +0,0 @@ -// -// -// -// It's interesting to speculate if there is a way to leverage the Drop trait functionality -// to increment counters when a scope is closed, but I don't think it would help "out of the box". -// -// A `return` or `break` with expression might not need a temp value expression wrapper -// such as `return { let _t = result_expression; __incr_counter(...); _t };` -// -// ... **if** the __incr_counter() was somehow called from a "drop()" trait function. -// -// The problem is, since the drop call is automatic, there is no way to have argument variants -// depending on where the drop() occurs (e.g., from a `return` statement vs. from the end of -// the function). We need 2 different code regions though. -// -// -// -// - -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { - // println!("from: {}", _region_loc); - result -} - -struct Firework { - strength: i32, -} - -impl Drop for Firework { - fn drop(&mut self) { - println!("BOOM times {}!!!", self.strength); - __incr_cov("start of drop()", ()); - } -} - -fn main() -> Result<(),u8> { - let _firecracker = Firework { strength: 1 }; - - if __incr_cov("start of main()", true) { - return __incr_cov("if true", { let _t = Err(1); println!("computing return value"); _t }); - } - - let _tnt = Firework { strength: 100 }; - // __incr_cov("after if block", Ok(())) // CAN USE COUNTER EXPRESSION: "start of drop()" - "if true" - Ok(()) -} - -// OUTPUT WHEN RUNNING THIS PROGRAM IS AS EXPECTED: - -// computing return value -// BOOM times 1!!! -// Error: 1 diff --git a/src/test/codegen/coverage-experiments/src/for.rs b/src/test/codegen/coverage-experiments/src/for.rs deleted file mode 100644 index 3f44c382a1e3f..0000000000000 --- a/src/test/codegen/coverage-experiments/src/for.rs +++ /dev/null @@ -1,41 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { - result -} - -fn main() { - for countdown in __incr_cov("start", 10..0) { - let _ = countdown; - __incr_cov("top of for", ()); - } -} - -// LOWERED TO HIR: -// -// fn main() { -// { -// let _t = -// match ::std::iter::IntoIterator::into_iter(__incr_cov("start", -// ::std::ops::Range{start: -// 10, -// end: -// 0,})) -// { -// mut iter => -// loop { -// let mut __next; -// match ::std::iter::Iterator::next(&mut iter) { -// ::std::option::Option::Some(val) => -// __next = val, -// ::std::option::Option::None => break , -// } -// let countdown = __next; -// { -// let _ = countdown; -// __incr_cov("top of for", ()); -// } -// }, -// }; -// _t -// } -// } \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/for_with_comments.rs b/src/test/codegen/coverage-experiments/src/for_with_comments.rs deleted file mode 100644 index 03d11b2c230ca..0000000000000 --- a/src/test/codegen/coverage-experiments/src/for_with_comments.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression -/* : ┃ */ let _ = countdown; -/* : ┃ */ __incr_cov("top of for", ()); -/* ┃ - */ } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/if.rs b/src/test/codegen/coverage-experiments/src/if.rs deleted file mode 100644 index ad50f6be19004..0000000000000 --- a/src/test/codegen/coverage-experiments/src/if.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![feature(core_intrinsics)] - -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; - -fn main() { - let mut countdown = 10; - if __incr_cov("start", countdown > 0) { - - - // // TEST CALLING INTRINSIC: - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 314 as u32, 31 as u32) }; - // // Results in: - // // LLVM ERROR: Cannot select: intrinsic %llvm.instrprof.increment - // // I may need to pass one or more of the following flags (or equivalent opts) to LLVM to enable this: - // // -fprofile-instr-generate -fcoverage-mapping - - - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - } - - let mut countdown = 10; - if { let _tcov = countdown > 0; __llvm_incr_counter("start", ); _tcov } { - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - } -} - -// NOTE: hir REDUNDANTLY lowers the manually inlined counter in the second if block to: -// -// match { -// let _t = -// { -// let _tcov = countdown > 0; -// __llvm_incr_counter("start"); -// _tcov -// }; -// _t -// } { - -// I don't know if optimization phases will fix this or not. -// Otherwise, a more optimal (but definitely special case) way to handle this would be -// to inject the counter between the hir-introduced temp `_t` assignment and the block result -// line returning `_t`: -// -// match { -// let _t = countdown > 0; -// __llvm_incr_counter("start"); // <-- the only thing inserted for coverage here -// _t -// } -// -// UNFORTUNATELY THIS IS NOT A PATTERN WE CAN ALWAYS LEVERAGE, FOR EXPRESSIONS THAT HAVE VALUES -// WHERE WE NEED TO INJECT THE COUNTER AFTER THE EXPRESSION BUT BEFORE IT IS USED. -// -// IT DOES APPEAR TO BE THE CASE FOR WHILE EXPRESSIONS, (BECOMES loop { match { let _t = condition; _t} { true => {...} _ => break, }}) -// AND IS TRUE FOR IF EXPRESSIONS AS NOTED -// BUT NOT FOR RETURN STATEMENT (and I'm guessing not for loop { break value; } ? ) -// -// AND NOT FOR LAZY BOOLEAN EXPRESSIONS! -// -// AND NOT FOR MATCH EXPRESSIONS IN THE ORIGINAL SOURCE! \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/if_with_comments.rs b/src/test/codegen/coverage-experiments/src/if_with_comments.rs deleted file mode 100644 index 267e7bca2c5a2..0000000000000 --- a/src/test/codegen/coverage-experiments/src/if_with_comments.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* ┃ */ let mut countdown = 10; -/* : I */ if __incr_cov("start", countdown > 0) { // span is from start of main() -/* : ┃ */ countdown -= 1; -/* : ┃ */ __incr_cov("if block",()); -/* ┃ - */ } - - let mut countdown = 10; - if __incr_cov("start", countdown > 0) { - countdown -= 1; - __incr_cov("if block",()); - } else if countdown > 5 { // counter expression "start" - "if block" - countdown -= 2; - __incr_cov("else if block",()); - } else { - countdown -= 3; - // __incr_cov("else block",()); // counter expression (countdown > 5 counter expression) - "else if block" - // PLACED AT END OF ELSE BLOCK OR START OF FIRST CONDITIONAL BLOCK, IF ANY (PRESUMING POSSIBLE EARLY EXIT). - // IF WE CAN GUARANTEE NO EARLY EXIT IN THIS BLOCK, THEN AT THE END IS FINE EVEN IF ELSE BLOCK CONTAINS OTHER CONDITIONS. - } - -/* - */ } - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs b/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs deleted file mode 100644 index d4708cd367ff6..0000000000000 --- a/src/test/codegen/coverage-experiments/src/increment_intrinsic.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(core_intrinsics)] - -pub fn not_instrprof_increment(_hash: u64, _num_counters: u32, _index: u32) { -} - -fn main() { - // COMPARE THIS WITH INTRINSIC INSERTION - //not_instrprof_increment(1234 as u64, 314 as u32, 31 as u32); - - unsafe { core::intrinsics::instrprof_increment(1234 as u64, 314 as u32, 31 as u32) }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/just_main.rs b/src/test/codegen/coverage-experiments/src/just_main.rs deleted file mode 100644 index 081e5d72a6e0a..0000000000000 --- a/src/test/codegen/coverage-experiments/src/just_main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("hello world! (should be covered)"); -} diff --git a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs b/src/test/codegen/coverage-experiments/src/lazy_boolean.rs deleted file mode 100644 index 263277c7cdc4d..0000000000000 --- a/src/test/codegen/coverage-experiments/src/lazy_boolean.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - let a = 1; - let b = 10; - let c = 100; - let _result = __incr_cov("start", a < b) || __incr_cov("or", b < c); - - let _result = { let _t = a < b; __llvm_incr_counter("start"); _t } || { let _t = b < c; __llvm_incr_counter("start"); _t }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/loop_break_value.rs b/src/test/codegen/coverage-experiments/src/loop_break_value.rs deleted file mode 100644 index 76caa833ec4f8..0000000000000 --- a/src/test/codegen/coverage-experiments/src/loop_break_value.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - __incr_cov("start", ()); - let _result = loop { - break __incr_cov("top of loop", true); - }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match.rs b/src/test/codegen/coverage-experiments/src/match.rs deleted file mode 100644 index afbb20888eab5..0000000000000 --- a/src/test/codegen/coverage-experiments/src/match.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub fn __llvm_incr_counter(_region_loc: &str) { -} - -#[inline(always)] -pub fn __incr_cov(region_loc: &str, result: T) -> T { - __llvm_incr_counter(region_loc); - result -} - -fn main() { - let a = 1; - let b = 10; - let _result = match a < b { - true => true, - _ => false, - }; - - let _result = match __incr_cov("end of first match", a < b) { - true => __incr_cov("matched true", true), - _ => false, // counter expression "end of first match" - "matched true" - }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment.rs b/src/test/codegen/coverage-experiments/src/match_with_increment.rs deleted file mode 100644 index f618b37ed5247..0000000000000 --- a/src/test/codegen/coverage-experiments/src/match_with_increment.rs +++ /dev/null @@ -1,305 +0,0 @@ -#![feature(core_intrinsics)] -//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; - static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; -fn main() { - let a = 1; - let b = 10; - let _result = match { - let _t = a < b; - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; - _t - } { - true => { - let _t = true; - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; - _t - } - _ => false, - }; -} - -/* - -I NEED TO INSERT THE instrprof_increment() CALL: - - 1. JUST BEFORE THE switchInt(_4) (because we haven't counted entering the function main() yet, deferring that to "JUST BEFORE FIRST BRANCH") - 2. SOME TIME AFTER THE switchInt(_4), AND JUST BEFORE ANOTHER BRANCH (in this case, before "goto") - 2.a. NOT BEFORE BOTH GOTO'S AFTER switchInt(_4) (because one can be calculated by counter expression), BUT PERHAPS INSERT A noop PLACEHOLDER - AS A MARKER TO INCLUDE THE COVERAGE REGION AND REFERENCE THE COUNTERS TO BE SUBTRACTED (AND/OR SUMMED)? - - WHY DEFER INSERTING COUNTERS TO "JUST BEFORE FIRST BRANCH"? We can ignore panic/unwind() and only count if the coverage region ACTUALLY - executed in entirety. BUT IS THAT NECESSARY? IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF - PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? - - IS THERE A BENEFIT OF THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? (BECAUSE, WE STILL NEED TO COUNT THE REGION - LEADING UP TO THE EXPRESSION ANYWAY) - -================================================= -================================================= - -To inject an intrinsic after computing a final expression value of a coverage region: - -Replace the following basic block end (last statement plus terminator): - -... ... -StorageLive(_4) -StorageLive(_5) -_5 = _1 -StorageLive(_6) -_6 = _2 -_4 = Lt(move _5, move _6) -StorageDead(_6) -StorageDead(_5) - <------ to insert instrprof_increment() here -FakeRead(ForMatchedPlace, _4) --------------------------------------------------------------------------------------- -switchInt(_4) - - -================================================= -Insert call to intrinsic with: - -StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call -StorageLive(_5) # Unchanged except _4 is now _5 -StorageLive(_6) # Unchanged except _5 is now _6 -_6 = _1 # Unchanged except _5 is now _6 -StorageLive(_7) # Unchanged except _6 is now _7 -_7 = _2 # Unchanged except _6 is now _7 -_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 -StorageDead(_7) # Unchanged except _6 is now _7 -StorageDead(_6) # Unchanged except _5 is now _6 - -FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? -StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` - # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) - # SEE BELOW - -FakeRead(ForMatchedPlace, _4) # Unchanged --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - -================================================= -Can I skip the extra variable and insert call to intrinsic with: - -StorageLive(_4) # Unchanged -StorageLive(_5) # Unchanged -_5 = _1 # Unchanged -StorageLive(_6) # Unchanged -_6 = _2 # Unchanged -_4 = Lt(move _5, move _6) # Unchanged -StorageDead(_6) # Unchanged -StorageDead(_5) # Unchanged - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - - - - -================================================= -================================================= - -For the second inserted call to instrprof_increment, without that call we have: - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # that is, "NOT false" - -_3 = const true - <------ to insert instrprof_increment() here --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -FakeRead(ForLet, _3) # NOTE: Unused result -StorageDead(_4) -_0 = () -StorageDead(_3) -StorageDead(_2) -StorageDead(_1) --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -return # from main() - - -================================================= -With the call to increment(): - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. -_15 = const true # UNCHANGED except _3 is now _15 -FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) -> _3 = _15 -> StorageDead(_15) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -================================================= -As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: - - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -_3 = const true # UNCHANGED? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs b/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs deleted file mode 100644 index 60586967920cb..0000000000000 --- a/src/test/codegen/coverage-experiments/src/match_with_increment_alt.rs +++ /dev/null @@ -1,296 +0,0 @@ -#![feature(core_intrinsics)] -//static TEST_FUNC_NAME: &'static [u8; 7] = b"main()\0"; - static TEST_FUNC_NAME: &'static [u8; 6] = b"main()"; -fn main() { - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 0 as u32) }; - let a = 1; - let b = 10; - let _result = match a < b { - true => { - unsafe { core::intrinsics::instrprof_increment(TEST_FUNC_NAME as *const u8, 1234 as u64, 3 as u32, 1 as u32) }; - true - } - _ => false, - }; -} - -/* - -ALTERNATE APPROACH: - - IS IT MUCH EASIER TO INSERT COUNTERS AT THE TOP OF A REGION THAT MUST EXECUTE IN ENTIRETY IF - PANIC DOES NOT OCCUR? AND WHAT IF WE ADD SUPPORT FOR PANIC UNWIND (later)? - - IS THERE A DETRACTOR COMPARED TO THE DEFERRED APPROACH WHEN CONSIDERING EXPRESSIONS MAY HAVE EARLY RETURNS? - - (BECAUSE, WE STILL NEED TO COUNT THE REGION LEADING UP TO THE EXPRESSION ANYWAY) - -================================================= -================================================= - -To inject an intrinsic after computing a final expression value of a coverage region: - -Replace the following basic block end (last statement plus terminator): - -... ... -StorageLive(_4) -StorageLive(_5) -_5 = _1 -StorageLive(_6) -_6 = _2 -_4 = Lt(move _5, move _6) -StorageDead(_6) -StorageDead(_5) - <------ to insert instrprof_increment() here -FakeRead(ForMatchedPlace, _4) --------------------------------------------------------------------------------------- -switchInt(_4) - - -================================================= -Insert call to intrinsic with: - -StorageLive(_4) # _4 is now meant for deferred FakeRead(ForMatchdPlace, _4) in BasicBlock after increment() call -StorageLive(_5) # Unchanged except _4 is now _5 -StorageLive(_6) # Unchanged except _5 is now _6 -_6 = _1 # Unchanged except _5 is now _6 -StorageLive(_7) # Unchanged except _6 is now _7 -_7 = _2 # Unchanged except _6 is now _7 -_5 = Lt(move _6, move _7) # Unchanged except _4, _5, _6 is now _5, _6, _7 -StorageDead(_7) # Unchanged except _6 is now _7 -StorageDead(_6) # Unchanged except _5 is now _6 - -FakeRead(ForLet, _5) # CHANGED ForMatchedPlace to ForLet - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -_4 = _5 # ARE THESE LINES REDUNDANT? CAN I JUST PASS _5 DIRECTLY TO FakeRead()? -StorageDead(_5) # DROP "_t" temp result of `let _t = a < b` - # (NOTE THAT IF SO, I CAN REMOVE _5 altogether, and use _4, which coincidentally makes less changes) - # SEE BELOW - -FakeRead(ForMatchedPlace, _4) # Unchanged --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - -================================================= -Can I skip the extra variable and insert call to intrinsic with: - -StorageLive(_4) # Unchanged -StorageLive(_5) # Unchanged -_5 = _1 # Unchanged -StorageLive(_6) # Unchanged -_6 = _2 # Unchanged -_4 = Lt(move _5, move _6) # Unchanged -StorageDead(_6) # Unchanged -StorageDead(_5) # Unchanged - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> FakeRead(ForLet, _4) # Save the post-increment result in temp "_t" -> StorageLive(_8) # ?? stores function pointer to instrprof_increment function? -> StorageLive(_9) -> StorageLive(_10) -> StorageLive(_11) -> _11 = const {alloc1+0: &&[u8; 6]} -> _10 = &raw const (*(*_11)) -> _9 = move _10 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_10) -> StorageLive(_12) -> _12 = const 1234u64 -> StorageLive(_13) -> _13 = const 3u32 -> StorageLive(_14) -> _14 = const 0u32 -> -------------------------------------------------------------------------------------- -> _8 = const std::intrinsics::instrprof_increment(move _9, move _12, move _13, move _14) -> -> -> return -> -> StorageDead(_14) -> StorageDead(_13) -> StorageDead(_12) -> StorageDead(_9) -> StorageDead(_11) -> StorageDead(_8) - -FakeRead(ForMatchedPlace, _4) # Unchanged (PREVIOUSLY USED IN FakeRead(ForLet), is that OK?) --------------------------------------------------------------------------------------- -switchInt(_4) # Unchanged - - - - - -================================================= -================================================= - -For the second inserted call to instrprof_increment, without that call we have: - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # that is, "NOT false" - -_3 = const true - <------ to insert instrprof_increment() here --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -FakeRead(ForLet, _3) # NOTE: Unused result -StorageDead(_4) -_0 = () -StorageDead(_3) -StorageDead(_2) -StorageDead(_1) --------------------------------------------------------------------------------------- -goto - --> # No label. No condition, and not a "return" - -return # from main() - - -================================================= -With the call to increment(): - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -StorageLive(_15) # CHANGED! Allocated new storage (_15) for the result of match, if true. -_15 = const true # UNCHANGED except _3 is now _15 -FakeRead(ForLet, _15) # CHANGED! Assign value to temporary (to be assigned to _3 later) ... Do I need to do this? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) -> _3 = _15 -> StorageDead(_15) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -================================================= -As before, can I skip the extra variable (_15) and insert the call to intrinsic with _3 directly?: - - --------------------------------------------------------------------------------------- -switchInt(_4) # From above - --> otherwise # "NOT false" # UNCHANGED - -_3 = const true # UNCHANGED? - -> # ALL NEW AND NECESSARY TO CALL instrprof_increment() -> StorageLive(_16) # pointer to instrprof_increment() function ? -> StorageLive(_17) -> StorageLive(_18) -> StorageLive(_19) -> _19 = const {alloc1+0: &&[u8; 6]} -> _18 = &raw const (*(*_19)) -> _17 = move _18 as *const u8 (Pointer(ArrayToPointer)) -> StorageDead(_18) -> StorageLive(_20) -> _20 = const 1234u64 -> StorageLive(_21) -> _21 = const 3u32 -> StorageLive(_22) -> _22 = const 1u32 -> -------------------------------------------------------------------------------------- -> _16 = const std::intrinsics::instrprof_increment(move _17, move _20, move _21, move _22) -> -> -> return -> -> StorageDead(_22) -> StorageDead(_21) -> StorageDead(_20) -> StorageDead(_17) -> StorageDead(_19) -> StorageDead(_16) - ---------------------------------# UNCHANGED------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -FakeRead(ForLet, _3) # UNCHANGED -StorageDead(_4) # UNCHANGED -_0 = () # UNCHANGED -StorageDead(_3) # UNCHANGED -StorageDead(_2) # UNCHANGED -StorageDead(_1) # UNCHANGED --------------------------------------------------------------------------------------- -goto # UNCHANGED - --> # UNCHANGED - -return # from main() # UNCHANGED - -*/ \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.mir b/src/test/codegen/coverage-experiments/src/match_without_increment.mir deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment.rs b/src/test/codegen/coverage-experiments/src/match_without_increment.rs deleted file mode 100644 index fa85833e05434..0000000000000 --- a/src/test/codegen/coverage-experiments/src/match_without_increment.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - let a = 1; - let b = 10; - let _result = match a < b { true => true, _ => false, }; -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir b/src/test/codegen/coverage-experiments/src/match_without_increment_alt.mir deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs b/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs deleted file mode 100644 index 03d11b2c230ca..0000000000000 --- a/src/test/codegen/coverage-experiments/src/question_mark_err_status_handling_with_comments.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* : I */ for countdown in __incr_cov("start", 10..0) { // span is just the while test expression -/* : ┃ */ let _ = countdown; -/* : ┃ */ __incr_cov("top of for", ()); -/* ┃ - */ } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` diff --git a/src/test/codegen/coverage-experiments/src/while.rs b/src/test/codegen/coverage-experiments/src/while.rs deleted file mode 100644 index 3cb185eda544f..0000000000000 --- a/src/test/codegen/coverage-experiments/src/while.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[inline(always)] -pub fn __incr_cov(_region_loc: &str, result: T) -> T { - result -} - -fn main() { - let mut countdown = 10; - __incr_cov("block start",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - } - - let mut countdown = 10; - __incr_cov("after first while loop",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - if countdown < 5 { - __incr_cov("top of if countdown < 5",()); - break; - } - countdown -= 2; - } -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_clean.rs b/src/test/codegen/coverage-experiments/src/while_clean.rs deleted file mode 100644 index e9ed1efc220d4..0000000000000 --- a/src/test/codegen/coverage-experiments/src/while_clean.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - let mut countdown = 10; - while countdown > 0 { - countdown -= 1; - } -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_early_return.rs b/src/test/codegen/coverage-experiments/src/while_early_return.rs deleted file mode 100644 index 35709ffba3a04..0000000000000 --- a/src/test/codegen/coverage-experiments/src/while_early_return.rs +++ /dev/null @@ -1,10 +0,0 @@ -fn main() -> u8 { // this will lower to HIR but will not compile: `main` can only return types that implement `std::process::Termination` - let mut countdown = 10; - while countdown > 0 { - if false { - return if countdown > 8 { 1 } else { return 2; }; - } - countdown -= 1; - } - 0 -} \ No newline at end of file diff --git a/src/test/codegen/coverage-experiments/src/while_with_comments.rs b/src/test/codegen/coverage-experiments/src/while_with_comments.rs deleted file mode 100644 index 56417fedf00df..0000000000000 --- a/src/test/codegen/coverage-experiments/src/while_with_comments.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* */ #[inline(always)] -/* */ pub fn __incr_cov(_region_loc: &str, /*index: u32,*/ result: T) -> T { -/* */ result -/* */ } -/* */ -/* - */ fn main() { -/* ┃ */ let mut countdown = 10; -/* ┃ */ __incr_cov("block start",()); // Must increment before repeated while text expression -/* : I */ while __incr_cov("while test", countdown > 0) { // span is just the while test expression -/* : ┃ */ countdown -= 1; -/* : ┃ */ // __incr_cov("while loop",()); // Counter not needed, but span is computed as "while test" minus "block start" -/* : ┃ */ // If while criteria is tested 11 times, and the outer block runs only once, 11-1 = 10 -/* : ┃ */ // REMOVING COUNTER ASSUMES NO EARLY RETURN THOUGH. -/* : ┃ */ // I THINK WE CAN ONLY USE THE COUNTER EXPRESSION UP TO FIRST CONDITIONAL BLOCK, IF ANY (if, match, maybe any loop) -/* ┃ - */ } - - let mut countdown = 10; - __incr_cov("after first while loop",()); - while __incr_cov("while test", countdown > 0) { - countdown -= 1; - // if __incr_cov("top of while loop", countdown < 5) { - if countdown < 5 { // "top of while loop" = counter expression "while test" - "after first while loop" - __incr_cov("top of if countdown < 5",()); - break; - } - countdown -= 2; - // __incr_cov("after if countdown < 5 block", ()); - // "after if countdown < 5 block" = counter expression "top of while loop" - "top of if countdown < 5" - // HOWEVER, WE CAN ONLY REMOVE THE COUNTER AND USE COUNTER EXPRESSION IF WE **KNOW** THAT THE BODY OF THE IF - // WILL **ALWAYS** BREAK (OR RETURN, OR CONTINUE?) - // AND THUS WE TREAT THE STATEMENTS FOLLOWING THE IF BLOCK AS IF THEY WERE AN ELSE BLOCK. - // THAT'S A LOT TO ASK. - - // PERHAPS TREAT EARLY RETURNS AS A SPECIAL KIND OF COUNTER AND IF ANY ARE INVOKED BEFORE STATEMENTS AFTER THE BLOCK THAT CONTAINS THEM, - // THEN SUBTRACT THOSE COUNTS FROM THE COUNT BEFORE THE BLOCK (AS WE DO HERE)? (SO ONE SET OF EXPRESSIONS MUST SUM ALL OF THE EARLY - // RETURNS) - } -/* - */ } - - -// -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; -// valid types are any of the types for `--pretty`, as well as: -// `expanded`, `expanded,identified`, -// `expanded,hygiene` (with internal representations), -// `everybody_loops` (all function bodies replaced with `loop {}`), -// `hir` (the HIR), `hir,identified`, -// `hir,typed` (HIR with types for each node), -// `hir-tree` (dump the raw HIR), -// `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) - -// argument to `pretty` must be one of `normal`, `expanded`, `identified`, or `expanded,identified` From d2cd59a0315809afa58df0196c34b33ee0a8c161 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 5 Jun 2020 09:14:45 -0700 Subject: [PATCH 12/18] Add case for count_code_region() extern lang_item As suggested in PR feedback: https://github.com/rust-lang/rust/pull/73011#discussion_r435728923 This allows count_code_region() to be handled like a normal intrinsic so the InstanceDef::InjectedCode variant is no longer needed. --- src/libcore/intrinsics.rs | 21 ++++++------------- src/librustc_codegen_ssa/mir/block.rs | 5 +---- src/librustc_middle/mir/mono.rs | 1 - src/librustc_middle/ty/instance.rs | 7 ------- src/librustc_middle/ty/mod.rs | 1 - src/librustc_middle/ty/structural_impls.rs | 11 +++------- src/librustc_mir/interpret/terminator.rs | 3 --- src/librustc_mir/monomorphize/collector.rs | 5 +---- src/librustc_mir/monomorphize/partitioning.rs | 2 -- src/librustc_mir/shim.rs | 3 --- src/librustc_passes/weak_lang_items.rs | 16 ++++++++++++-- src/librustc_ty/instance.rs | 4 ---- src/librustc_typeck/check/intrinsic.rs | 2 ++ 13 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 06a432a26961e..7ce5814d39a02 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1941,22 +1941,13 @@ extern "rust-intrinsic" { /// /// Perma-unstable: do not use. pub fn miri_start_panic(payload: *mut u8) -> !; -} -/// Defines the `count_code_region` intrinsic as a `LangItem`. `LangItem`s require a function body -/// to register its DefId with the LangItem entry. The function body is never actually called (and -/// is therefore implemented as an aborting stub) because it is replaced with the LLVM intrinsic -/// `llvm.instrprof.increment` by -/// `rustc_codegen_llvm::intrinsic::IntrinsicCallMethods::codegen_intrinsic_call()`. -#[cfg(not(bootstrap))] -#[cfg_attr(not(bootstrap), lang = "count_code_region")] -fn count_code_region(_index: u32) { - // remove `unsafe` (and safety comment) on bootstrap bump - #[cfg_attr(not(bootstrap), allow(unused_unsafe))] - // SAFETY: the `abort` intrinsic has no requirements to be called. - unsafe { - abort() - } + /// Internal placeholder for injecting code coverage counters when the "instrument-coverage" + /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code + /// generation. + #[cfg(not(bootstrap))] + #[cfg_attr(not(bootstrap), lang = "count_code_region")] + pub fn count_code_region(_index: u32); } // Some functions are defined here because they accidentally got made diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index d7db657154993..665ef77090987 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -566,10 +566,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) - | Some(ty::InstanceDef::InjectedCode(def_id)) => { - Some(bx.tcx().item_name(def_id).as_str()) - } + Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id).as_str()), _ => None, }; let intrinsic = intrinsic.as_ref().map(|s| &s[..]); diff --git a/src/librustc_middle/mir/mono.rs b/src/librustc_middle/mir/mono.rs index b2c00849d9f83..c889dbc0a4498 100644 --- a/src/librustc_middle/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -352,7 +352,6 @@ impl<'tcx> CodegenUnit<'tcx> { InstanceDef::VtableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs index 4f88e64c5039a..1ce079821a22e 100644 --- a/src/librustc_middle/ty/instance.rs +++ b/src/librustc_middle/ty/instance.rs @@ -21,10 +21,6 @@ pub enum InstanceDef<'tcx> { Item(DefId), Intrinsic(DefId), - /// Injected call to a placeholder function that is replaced with - /// For example: `core::intrinsic::count_code_region()` for code coverage. - InjectedCode(DefId), - /// `::method` where `method` receives unsizeable `self: Self`. VtableShim(DefId), @@ -153,7 +149,6 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) - | InstanceDef::InjectedCode(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) => def_id, @@ -241,7 +236,6 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::InjectedCode(_) => write!(f, " - injected-code"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), @@ -421,7 +415,6 @@ impl<'tcx> Instance<'tcx> { | InstanceDef::FnPtrShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::ReifyShim(..) | InstanceDef::Virtual(..) | InstanceDef::VtableShim(..) => Some(self.substs), diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 9b1e717731e82..93ef73171993c 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -2717,7 +2717,6 @@ impl<'tcx> TyCtxt<'tcx> { ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs index b6cbd2082a518..f6f5dfd651612 100644 --- a/src/librustc_middle/ty/structural_impls.rs +++ b/src/librustc_middle/ty/structural_impls.rs @@ -674,7 +674,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), - ty::InstanceDef::InjectedCode(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), ty::InstanceDef::FnPtrShim(def_id, ref ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) } @@ -847,7 +846,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { VtableShim(did) => VtableShim(did.fold_with(folder)), ReifyShim(did) => ReifyShim(did.fold_with(folder)), Intrinsic(did) => Intrinsic(did.fold_with(folder)), - InjectedCode(did) => InjectedCode(did.fold_with(folder)), FnPtrShim(did, ty) => FnPtrShim(did.fold_with(folder), ty.fold_with(folder)), Virtual(did, i) => Virtual(did.fold_with(folder), i), ClosureOnceShim { call_once } => { @@ -863,12 +861,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { use crate::ty::InstanceDef::*; self.substs.visit_with(visitor) || match self.def { - Item(did) - | VtableShim(did) - | ReifyShim(did) - | Intrinsic(did) - | InjectedCode(did) - | Virtual(did, _) => did.visit_with(visitor), + Item(did) | VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + did.visit_with(visitor) + } FnPtrShim(did, ty) | CloneShim(did, ty) => { did.visit_with(visitor) || ty.visit_with(visitor) } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 82fa471b54d73..cd7621ea9752b 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -257,9 +257,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); M::call_intrinsic(self, instance, args, ret, unwind) } - ty::InstanceDef::InjectedCode(..) => { - M::call_intrinsic(self, instance, args, ret, unwind) - } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 24c4226bb4e94..994d1e69f2e3e 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -714,9 +714,7 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceDef::Virtual(..) - | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::InjectedCode(_) => { + ty::InstanceDef::Virtual(..) | ty::InstanceDef::Intrinsic(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } @@ -753,7 +751,6 @@ fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::InjectedCode(_) | ty::InstanceDef::CloneShim(..) => return true, }; diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 7c97b9d611e15..db1ea72c0a531 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -322,7 +322,6 @@ fn mono_item_visibility( | InstanceDef::FnPtrShim(..) | InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) - | InstanceDef::InjectedCode(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) => return Visibility::Hidden, @@ -718,7 +717,6 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Intrinsic(..) - | ty::InstanceDef::InjectedCode(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) => return None, diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index b4477d9c86d43..f95fd9b9e90c5 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -109,9 +109,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::Intrinsic(_) => { bug!("creating shims from intrinsics ({:?}) is unsupported", instance) } - ty::InstanceDef::InjectedCode(_) => { - bug!("creating shims from injected code ({:?}) is unsupported", instance) - } }; debug!("make_shim({:?}) = untransformed {:?}", instance, result); diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index 96ec23692df51..f2f07b5d4fb26 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -5,10 +5,12 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::lang_items; +use rustc_hir::lang_items::ITEM_REFS; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; use rustc_middle::middle::lang_items::whitelisted; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -70,11 +72,21 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } impl<'a, 'tcx> Context<'a, 'tcx> { - fn register(&mut self, name: Symbol, span: Span) { + fn register(&mut self, name: Symbol, span: Span, hir_id: hir::HirId) { if let Some(&item) = WEAK_ITEMS_REFS.get(&name) { if self.items.require(item).is_err() { self.items.missing.push(item); } + } else if name == sym::count_code_region { + // `core::intrinsics::code_count_region()` is (currently) the only `extern` lang item + // that is never actually linked. It is not a `weak_lang_item` that can be registered + // when used, and should be registered here instead. + if let Some((item_index, _)) = ITEM_REFS.get(&*name.as_str()).cloned() { + if self.items.items[item_index].is_none() { + let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); + self.items.items[item_index] = Some(item_def_id); + } + } } else { struct_span_err!(self.tcx.sess, span, E0264, "unknown external lang item: `{}`", name) .emit(); @@ -91,7 +103,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) { - self.register(lang_item, i.span); + self.register(lang_item, i.span, i.hir_id); } intravisit::walk_foreign_item(self, i) } diff --git a/src/librustc_ty/instance.rs b/src/librustc_ty/instance.rs index d4ceeff324450..0acf769168137 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -35,10 +35,6 @@ fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } - ty::FnDef(def_id, _) if Some(def_id) == tcx.lang_items().count_code_region_fn() => { - debug!(" => injected placeholder function to be replaced"); - ty::InstanceDef::InjectedCode(def_id) - } ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { let ty = substs.type_at(0); diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index bded2c695c9db..3ec6973a17d56 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -347,6 +347,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { return; } + "count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()), + ref other => { struct_span_err!( tcx.sess, From e4df7e70466611a49d3ff6c49d162b2045173449 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 5 Jun 2020 09:49:31 -0700 Subject: [PATCH 13/18] Update src/libcore/intrinsics.rs Co-authored-by: bjorn3 --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 7ce5814d39a02..3806d3ae25487 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1946,7 +1946,7 @@ extern "rust-intrinsic" { /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code /// generation. #[cfg(not(bootstrap))] - #[cfg_attr(not(bootstrap), lang = "count_code_region")] + #[lang = "count_code_region"] pub fn count_code_region(_index: u32); } From 7e49a9ec59f7950efa9950b65c10f9b3f3a4b6b2 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Sun, 7 Jun 2020 19:35:15 -0700 Subject: [PATCH 14/18] moved to post_borrowck_cleanup & used MirPatch --- src/librustc_mir/interpret/intrinsics.rs | 1 + .../transform/instrument_coverage.rs | 112 +++++++++--------- src/librustc_mir/transform/mod.rs | 6 +- src/librustc_session/options.rs | 4 +- 4 files changed, 65 insertions(+), 58 deletions(-) diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 47e5b8b4fcec4..4d8120794f885 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -389,6 +389,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.copy_op(self.operand_index(args[0], index)?, dest)?; } + sym::count_code_region => (), _ => return Ok(false), } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 045cd03d1f7da..0604caadaea38 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -1,8 +1,7 @@ use crate::transform::{MirPass, MirSource}; -use rustc_index::vec::Idx; +use crate::util::patch::MirPatch; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; -use rustc_middle::mir::{Local, LocalDecl}; use rustc_middle::ty; use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; @@ -16,69 +15,62 @@ pub struct InstrumentCoverage; * the intrinsic llvm.instrprof.increment. */ -// FIXME(richkadel): As a first step, counters are only injected at the top of each function. -// The complete solution will inject counters at each conditional code branch. - impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.instrument_coverage { - if let Some(callee_fn_def_id) = tcx.lang_items().count_code_region_fn() { - debug!("instrumenting {:?}", src.def_id()); - instrument_coverage(tcx, callee_fn_def_id, body); - } + debug!("instrumenting {:?}", src.def_id()); + instrument_coverage(tcx, body); } } } -pub fn instrument_coverage<'tcx>( - tcx: TyCtxt<'tcx>, - callee_fn_def_id: DefId, - body: &mut Body<'tcx>, -) { +// The first counter (start of the function) is index zero. +const INIT_FUNCTION_COUNTER: u128 = 0; + +/// Injects calls to placeholder function `count_code_region()`. +// FIXME(richkadel): As a first step, counters are only injected at the top of each function. +// The complete solution will inject counters at each conditional code branch. +pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = body.span.shrink_to_lo(); - let ret_ty = tcx.fn_sig(callee_fn_def_id).output(); + let count_code_region_fn = + function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); + let counter_index = const_int_operand(tcx, span, tcx.types.u32, INIT_FUNCTION_COUNTER); + + let mut patch = MirPatch::new(body); + + let new_block = patch.new_block(placeholder_block(SourceInfo::outermost(body.span))); + let next_block = START_BLOCK; + + let temp = patch.new_temp(tcx.mk_unit(), body.span); + patch.patch_terminator( + new_block, + TerminatorKind::Call { + func: count_code_region_fn, + args: vec![counter_index], + // new_block will swapped with the next_block, after applying patch + destination: Some((Place::from(temp), new_block)), + cleanup: None, + from_hir_call: false, + }, + ); + + patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp)); + patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp)); + + patch.apply(body); + + // To insert the `new_block` in front of the first block in the counted branch (for example, + // the START_BLOCK, at the top of the function), just swap the indexes, leaving the rest of the + // graph unchanged. + body.basic_blocks_mut().swap(next_block, new_block); +} + +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, span: Span, fn_def_id: DefId) -> Operand<'tcx> { + let ret_ty = tcx.fn_sig(fn_def_id).output(); let ret_ty = ret_ty.no_bound_vars().unwrap(); let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); - - let count_code_region_fn: Operand<'_> = - Operand::function_handle(tcx, callee_fn_def_id, substs, span); - - let index = const_int_operand(tcx, span.clone(), tcx.types.u32, 0); - - let args = vec![index]; - - let source_info = SourceInfo { span: span, scope: OUTERMOST_SOURCE_SCOPE }; - - let new_block = START_BLOCK + body.basic_blocks().len(); - - let next_local = body.local_decls.len(); - let new_temp = Local::new(next_local); - let unit_temp = Place::from(new_temp); - - let storage_live = Statement { source_info, kind: StatementKind::StorageLive(new_temp) }; - let storage_dead = Statement { source_info, kind: StatementKind::StorageDead(new_temp) }; - - let count_code_region_call = TerminatorKind::Call { - func: count_code_region_fn, - args, - destination: Some((unit_temp, new_block)), - cleanup: None, - from_hir_call: false, - }; - - body.local_decls.push(LocalDecl::new(tcx.mk_unit(), body.span)); - body.basic_blocks_mut().push(BasicBlockData { - statements: vec![storage_live], - is_cleanup: false, - terminator: Some(Terminator { source_info, kind: count_code_region_call }), - }); - - body.basic_blocks_mut().swap(START_BLOCK, new_block); - body[new_block].statements.push(storage_dead); - - // FIXME(richkadel): ALSO add each computed Span for each conditional branch to the coverage map - // and provide that map to LLVM to encode in the final binary. + Operand::function_handle(tcx, fn_def_id, substs, span) } fn const_int_operand<'tcx>( @@ -98,3 +90,15 @@ fn const_int_operand<'tcx>( literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), }) } + +fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> { + BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + // this gets overwritten by the counter Call + kind: TerminatorKind::Unreachable, + }), + is_cleanup: false, + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index e03ef48f74838..956ddd2051bac 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -288,8 +288,6 @@ fn mir_validated( &[&[ // What we need to run borrowck etc. &promote_pass, - // FIXME(richkadel): is this the best place for the InstrumentCoverage pass? - &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("qualify-consts"), ]], ); @@ -340,6 +338,10 @@ fn run_post_borrowck_cleanup_passes<'tcx>( // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, // but before optimizations begin. &add_retag::AddRetag, + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("elaborate-drops"), ]; diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 599ce595e1314..2d231359057fd 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -877,8 +877,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (such as entering an empty infinite loop) by inserting llvm.sideeffect \ (default: no)"), instrument_coverage: bool = (false, parse_bool, [TRACKED], - "instrument the generated code with LLVM code region counters for \ - generating coverage reports (default: no)"), + "instrument the generated code with LLVM code region counters to \ + (in the future) generate coverage reports (experimental; default: no)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], From 46ebd57c42439b3aedcb160f70b022a4f59f4afa Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 8 Jun 2020 16:20:26 -0700 Subject: [PATCH 15/18] moved instrument_coverage pass, optimized scalar, added FIXME --- src/librustc_codegen_llvm/intrinsic.rs | 5 ++++ .../transform/instrument_coverage.rs | 27 ++++++++++++------- src/librustc_mir/transform/mod.rs | 8 +++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 7fddda99185b4..95465939070a0 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -148,6 +148,11 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { caller_fn_path ); + // FIXME(richkadel): (1) Replace raw function name with mangled function name; + // (2) Replace hardcoded `1234` in `hash` with a computed hash (as discussed in) + // the MCP (compiler-team/issues/278); and replace the hardcoded `1` for + // `num_counters` with the actual number of counters per function (when the + // changes are made to inject more than one counter per function). let (fn_name, _len_val) = self.const_str(Symbol::intern(&caller_fn_path)); let index = args[0].immediate(); let hash = self.const_u64(1234); diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 0604caadaea38..27abe813b067d 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -7,6 +7,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::Span; +use rustc_target::abi; pub struct InstrumentCoverage; @@ -25,7 +26,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { } // The first counter (start of the function) is index zero. -const INIT_FUNCTION_COUNTER: u128 = 0; +const INIT_FUNCTION_COUNTER: u32 = 0; /// Injects calls to placeholder function `count_code_region()`. // FIXME(richkadel): As a first step, counters are only injected at the top of each function. @@ -35,7 +36,8 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let count_code_region_fn = function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); - let counter_index = const_int_operand(tcx, span, tcx.types.u32, INIT_FUNCTION_COUNTER); + let counter_index = + const_int_operand(tcx, span, tcx.types.u32, Scalar::from_u32(INIT_FUNCTION_COUNTER)); let mut patch = MirPatch::new(body); @@ -77,17 +79,24 @@ fn const_int_operand<'tcx>( tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>, - val: u128, + val: Scalar, ) -> Operand<'tcx> { - let param_env_and_ty = ty::ParamEnv::empty().and(ty); - let size = tcx - .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); Operand::Constant(box Constant { span, user_ty: None, - literal: ty::Const::from_scalar(tcx, Scalar::from_uint(val, size), ty), + literal: ty::Const::from_scalar(tcx, val, ty), }) } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 956ddd2051bac..846ed1f86d8d6 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -289,6 +289,10 @@ fn mir_validated( // What we need to run borrowck etc. &promote_pass, &simplify::SimplifyCfg::new("qualify-consts"), + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, ]], ); @@ -338,10 +342,6 @@ fn run_post_borrowck_cleanup_passes<'tcx>( // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, // but before optimizations begin. &add_retag::AddRetag, - // If the `instrument-coverage` option is enabled, analyze the CFG, identify each - // conditional branch, construct a coverage map to be passed to LLVM, and inject counters - // where needed. - &instrument_coverage::InstrumentCoverage, &simplify::SimplifyCfg::new("elaborate-drops"), ]; From 20aba8f634c13fa2bb1b043b51a074769dc06f66 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 10 Jun 2020 09:54:02 -0700 Subject: [PATCH 16/18] added test, Operand::const_from_scalar, require_lang_item, & comments Addresses feedback from @oli-obk (Thanks!) --- src/librustc_middle/mir/mod.rs | 28 +++++++ src/librustc_mir/interpret/intrinsics.rs | 1 + .../transform/instrument_coverage.rs | 52 ++++-------- src/test/mir-opt/instrument_coverage.rs | 19 +++++ .../rustc.bar.InstrumentCoverage.diff | 41 ++++++++++ .../rustc.main.InstrumentCoverage.diff | 82 +++++++++++++++++++ 6 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 src/test/mir-opt/instrument_coverage.rs create mode 100644 src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff create mode 100644 src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff diff --git a/src/librustc_middle/mir/mod.rs b/src/librustc_middle/mir/mod.rs index 27848684706d6..11ae2cf72c462 100644 --- a/src/librustc_middle/mir/mod.rs +++ b/src/librustc_middle/mir/mod.rs @@ -29,6 +29,7 @@ use rustc_macros::HashStable; use rustc_serialize::{Decodable, Encodable}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; use rustc_target::asm::InlineAsmRegOrRegClass; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter, Write}; @@ -2218,6 +2219,33 @@ impl<'tcx> Operand<'tcx> { }) } + /// Convenience helper to make a literal-like constant from a given scalar value. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_scalar( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + val: Scalar, + span: Span, + ) -> Operand<'tcx> { + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, val, ty), + }) + } + pub fn to_copy(&self) -> Self { match *self { Operand::Copy(_) | Operand::Constant(_) => self.clone(), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 4d8120794f885..ac28ccd181520 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -389,6 +389,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); self.copy_op(self.operand_index(args[0], index)?, dest)?; } + // FIXME(#73156): Handle source code coverage in const eval sym::count_code_region => (), _ => return Ok(false), } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index 27abe813b067d..fda7ad731fa27 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -1,21 +1,17 @@ use crate::transform::{MirPass, MirSource}; use crate::util::patch::MirPatch; +use rustc_hir::lang_items; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::ty; -use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::Span; -use rustc_target::abi; +/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with +/// the intrinsic llvm.instrprof.increment. pub struct InstrumentCoverage; -/** - * Inserts call to count_code_region() as a placeholder to be replaced during code generation with - * the intrinsic llvm.instrprof.increment. - */ - impl<'tcx> MirPass<'tcx> for InstrumentCoverage { fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.instrument_coverage { @@ -34,10 +30,17 @@ const INIT_FUNCTION_COUNTER: u32 = 0; pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = body.span.shrink_to_lo(); - let count_code_region_fn = - function_handle(tcx, span, tcx.lang_items().count_code_region_fn().unwrap()); - let counter_index = - const_int_operand(tcx, span, tcx.types.u32, Scalar::from_u32(INIT_FUNCTION_COUNTER)); + let count_code_region_fn = function_handle( + tcx, + tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None), + span, + ); + let counter_index = Operand::const_from_scalar( + tcx, + tcx.types.u32, + Scalar::from_u32(INIT_FUNCTION_COUNTER), + span, + ); let mut patch = MirPatch::new(body); @@ -68,38 +71,13 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { body.basic_blocks_mut().swap(next_block, new_block); } -fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, span: Span, fn_def_id: DefId) -> Operand<'tcx> { +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> { let ret_ty = tcx.fn_sig(fn_def_id).output(); let ret_ty = ret_ty.no_bound_vars().unwrap(); let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); Operand::function_handle(tcx, fn_def_id, substs, span) } -fn const_int_operand<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - ty: Ty<'tcx>, - val: Scalar, -) -> Operand<'tcx> { - debug_assert!({ - let param_env_and_ty = ty::ParamEnv::empty().and(ty); - let type_size = tcx - .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) - .size; - let scalar_size = abi::Size::from_bytes(match val { - Scalar::Raw { size, .. } => size, - _ => panic!("Invalid scalar type {:?}", val), - }); - scalar_size == type_size - }); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::from_scalar(tcx, val, ty), - }) -} - fn placeholder_block<'tcx>(source_info: SourceInfo) -> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs new file mode 100644 index 0000000000000..e8c723b528a1a --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.rs @@ -0,0 +1,19 @@ +// Test that the initial version of Rust coverage injects count_code_region() placeholder calls, +// at the top of each function. The placeholders are later converted into LLVM instrprof.increment +// intrinsics, during codegen. + +// compile-flags: -Zinstrument-coverage +// EMIT_MIR rustc.main.InstrumentCoverage.diff +// EMIT_MIR rustc.bar.InstrumentCoverage.diff +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff new file mode 100644 index 0000000000000..d23bb93d951dc --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff @@ -0,0 +1,41 @@ +- // MIR for `bar` before InstrumentCoverage ++ // MIR for `bar` after InstrumentCoverage + + fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:17:13: 17:17 ++ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 + + bb0: { ++ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:17:1: 17:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ } ++ ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/instrument_coverage.rs:17:1: 19:2 ++ } ++ ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:18:5: 18:9 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:18:5: 18:9 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + return; // scope 0 at $DIR/instrument_coverage.rs:19:2: 19:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff new file mode 100644 index 0000000000000..d5d0f82495d1a --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:8:11: 8:11 + let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:10:18: 12:10 ++ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + + bb0: { +- falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:8:1: 8:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + } + + bb1: { + StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + _2 = const bar() -> [return: bb2, unwind: bb6]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:12: 10:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + + bb2: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb3: { + falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb4: { + _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:10:9: 12:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + + bb5: { + _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:11:13: 11:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 + return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 + } + + bb6 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 ++ } ++ ++ bb7: { ++ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + } + } + From 163e5854562f5274f092d66318a5c805e18d83c5 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 10 Jun 2020 12:48:30 -0700 Subject: [PATCH 17/18] updated mir-opt test due to other recent changes to MIR --- .../rustc.main.InstrumentCoverage.diff | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff index d5d0f82495d1a..095246580409e 100644 --- a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -9,7 +9,7 @@ + let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 bb0: { -- falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 +- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 + StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + // ty::Const @@ -28,7 +28,7 @@ bb1: { StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - _2 = const bar() -> [return: bb2, unwind: bb6]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 // ty::Const // + ty: fn() -> bool {bar} // + val: Value(Scalar()) @@ -37,16 +37,20 @@ // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } } - bb2: { - FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 - switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + bb2 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 } bb3: { - falseEdges -> [real: bb5, imaginary: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:10:12: 10:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 } bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 + } + + bb5: { _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:10:9: 12:10 // ty::Const // + ty: () @@ -58,7 +62,7 @@ goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 } - bb5: { + bb6: { _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:13: 11:18 // ty::Const // + ty: () @@ -68,15 +72,11 @@ // + literal: Const { ty: (), val: Value(Scalar()) } StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:13:5: 13:6 return; // scope 0 at $DIR/instrument_coverage.rs:14:2: 14:2 - } - - bb6 (cleanup): { - resume; // scope 0 at $DIR/instrument_coverage.rs:8:1: 14:2 + } + + bb7: { + StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 -+ falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:9:5: 13:6 } } From 98685a4bf2ef50c6d6a64ef3867a29994d5a4a25 Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 15 Jun 2020 17:08:13 -0700 Subject: [PATCH 18/18] Add new `fn_span` to TerminatorKind::Call instance --- src/librustc_mir/transform/instrument_coverage.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs index fda7ad731fa27..c36614938e10f 100644 --- a/src/librustc_mir/transform/instrument_coverage.rs +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -57,6 +57,7 @@ pub fn instrument_coverage<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { destination: Some((Place::from(temp), new_block)), cleanup: None, from_hir_call: false, + fn_span: span, }, );