| //@ run-pass |
| //! Test information regarding intrinsics and ensure we can retrieve the fallback body if it exists. |
| //! |
| //! This tests relies on the intrinsics implementation, and requires one intrinsic with and one |
| //! without a body. It doesn't matter which intrinsic is called here, and feel free to update that |
| //! if needed. |
| |
| //@ ignore-stage1 |
| //@ ignore-cross-compile |
| //@ ignore-remote |
| //@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 |
| |
| #![feature(rustc_private)] |
| #![feature(assert_matches)] |
| |
| extern crate rustc_hir; |
| #[macro_use] |
| extern crate rustc_smir; |
| extern crate rustc_driver; |
| extern crate rustc_interface; |
| extern crate stable_mir; |
| |
| use rustc_smir::rustc_internal; |
| use stable_mir::mir::mono::{Instance, InstanceKind}; |
| use stable_mir::mir::visit::{Location, MirVisitor}; |
| use stable_mir::mir::{LocalDecl, Terminator, TerminatorKind}; |
| use stable_mir::ty::{FnDef, GenericArgs, RigidTy, TyKind}; |
| use std::assert_matches::assert_matches; |
| use std::convert::TryFrom; |
| use std::io::Write; |
| use std::ops::ControlFlow; |
| |
| /// This function tests that we can correctly get type information from binary operations. |
| fn test_intrinsics() -> ControlFlow<()> { |
| // Find items in the local crate. |
| let main_def = stable_mir::all_local_items()[0]; |
| let main_instance = Instance::try_from(main_def).unwrap(); |
| let main_body = main_instance.body().unwrap(); |
| let mut visitor = CallsVisitor { locals: main_body.locals(), calls: Default::default() }; |
| visitor.visit_body(&main_body); |
| |
| let calls = visitor.calls; |
| assert_eq!(calls.len(), 3, "Expected 3 calls, but found: {calls:?}"); |
| for (fn_def, args) in calls.into_iter() { |
| check_instance(&Instance::resolve(fn_def, &args).unwrap()); |
| check_def(fn_def); |
| } |
| |
| ControlFlow::Continue(()) |
| } |
| |
| /// This check is unfortunately tight to the implementation of intrinsics. |
| /// |
| /// We want to ensure that StableMIR can handle intrinsics with and without fallback body. |
| /// |
| /// If by any chance this test breaks because you changed how an intrinsic is implemented, please |
| /// update the test to invoke a different intrinsic. |
| /// |
| /// In StableMIR, we only expose intrinsic body if they are not marked with |
| /// `rustc_intrinsic_must_be_overridden`. |
| fn check_instance(instance: &Instance) { |
| assert_eq!(instance.kind, InstanceKind::Intrinsic); |
| let name = instance.intrinsic_name().unwrap(); |
| if instance.has_body() { |
| let Some(body) = instance.body() else { unreachable!("Expected a body") }; |
| assert!(!body.blocks.is_empty()); |
| assert_eq!(&name, "likely"); |
| } else { |
| assert!(instance.body().is_none()); |
| assert_matches!(name.as_str(), "size_of_val" | "vtable_size"); |
| } |
| } |
| |
| fn check_def(fn_def: FnDef) { |
| assert!(fn_def.is_intrinsic()); |
| let intrinsic = fn_def.as_intrinsic().unwrap(); |
| assert_eq!(fn_def, intrinsic.into()); |
| |
| let name = intrinsic.fn_name(); |
| match name.as_str() { |
| "likely" => { |
| assert!(!intrinsic.must_be_overridden()); |
| assert!(fn_def.has_body()); |
| } |
| "vtable_size" | "size_of_val" => { |
| assert!(intrinsic.must_be_overridden()); |
| assert!(!fn_def.has_body()); |
| } |
| _ => unreachable!("Unexpected intrinsic: {}", name), |
| } |
| } |
| |
| struct CallsVisitor<'a> { |
| locals: &'a [LocalDecl], |
| calls: Vec<(FnDef, GenericArgs)>, |
| } |
| |
| impl<'a> MirVisitor for CallsVisitor<'a> { |
| fn visit_terminator(&mut self, term: &Terminator, _loc: Location) { |
| match &term.kind { |
| TerminatorKind::Call { func, .. } => { |
| let TyKind::RigidTy(RigidTy::FnDef(def, args)) = |
| func.ty(self.locals).unwrap().kind() |
| else { |
| return; |
| }; |
| self.calls.push((def, args.clone())); |
| } |
| _ => {} |
| } |
| } |
| } |
| |
| /// This test will generate and analyze a dummy crate using the stable mir. |
| /// For that, it will first write the dummy crate into a file. |
| /// Then it will create a `StableMir` using custom arguments and then |
| /// it will run the compiler. |
| fn main() { |
| let path = "binop_input.rs"; |
| generate_input(&path).unwrap(); |
| let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; |
| run!(args, test_intrinsics).unwrap(); |
| } |
| |
| fn generate_input(path: &str) -> std::io::Result<()> { |
| let mut file = std::fs::File::create(path)?; |
| write!( |
| file, |
| r#" |
| #![feature(core_intrinsics)] |
| use std::intrinsics::*; |
| pub fn use_intrinsics(init: bool) -> bool {{ |
| let vtable_sz = unsafe {{ vtable_size(0 as *const ()) }}; |
| let sz = unsafe {{ size_of_val("hi") }}; |
| likely(init && sz == 2) |
| }} |
| "# |
| )?; |
| Ok(()) |
| } |