Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dedicated definition for intrinsics #125336

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions compiler/rustc_smir/src/rustc_smir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use stable_mir::mir::{BinOp, Body, Place};
use stable_mir::target::{MachineInfo, MachineSize};
use stable_mir::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, UintTy,
VariantDef,
ForeignItemKind, GenericArgs, IntrinsicDef, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind,
UintTy, VariantDef,
};
use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol};
use std::cell::RefCell;
Expand Down Expand Up @@ -307,6 +307,28 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
sig.stable(&mut *tables)
}

fn intrinsic(&self, def: DefId) -> Option<IntrinsicDef> {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let def_id = def.internal(&mut *tables, tcx);
let intrinsic = tcx.intrinsic_raw(def_id);
intrinsic.map(|_| IntrinsicDef(def))
}

fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let def_id = def.0.internal(&mut *tables, tcx);
tcx.intrinsic(def_id).unwrap().name.to_string()
}

fn intrinsic_must_be_overridden(&self, def: IntrinsicDef) -> bool {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let def_id = def.0.internal(&mut *tables, tcx);
tcx.intrinsic_raw(def_id).unwrap().must_be_overridden
}

fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
Expand Down Expand Up @@ -645,16 +667,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
}
}

/// Retrieve the plain intrinsic name of an instance.
///
/// This assumes that the instance is an intrinsic.
fn intrinsic_name(&self, def: InstanceDef) -> Symbol {
let tables = self.0.borrow_mut();
let instance = tables.instances[def];
let intrinsic = tables.tcx.intrinsic(instance.def_id()).unwrap();
intrinsic.name.to_string()
}

fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
Expand Down
15 changes: 12 additions & 3 deletions compiler/stable_mir/src/compiler_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::target::MachineInfo;
use crate::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics,
ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind,
UintTy, VariantDef,
ImplDef, ImplTrait, IntrinsicDef, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty,
TyKind, UintTy, VariantDef,
};
use crate::{
mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind,
Expand Down Expand Up @@ -88,6 +88,16 @@ pub trait Context {
/// Retrieve the function signature for the given generic arguments.
fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig;

/// Retrieve the intrinsic definition if the item corresponds one.
fn intrinsic(&self, item: DefId) -> Option<IntrinsicDef>;

/// Retrieve the plain function name of an intrinsic.
fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol;

/// Returns whether the intrinsic has no meaningful body and all backends
/// need to shim all calls to it.
fn intrinsic_must_be_overridden(&self, def: IntrinsicDef) -> bool;

/// Retrieve the closure signature for the given generic arguments.
fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig;

Expand Down Expand Up @@ -198,7 +208,6 @@ pub trait Context {
fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId>;
fn krate(&self, def_id: DefId) -> Crate;
fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol;
fn intrinsic_name(&self, def: InstanceDef) -> Symbol;

/// Return information about the target machine.
fn target_info(&self) -> MachineInfo;
Expand Down
4 changes: 3 additions & 1 deletion compiler/stable_mir/src/mir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ impl Instance {
/// which is more convenient to match with intrinsic symbols.
pub fn intrinsic_name(&self) -> Option<Symbol> {
match self.kind {
InstanceKind::Intrinsic => Some(with(|context| context.intrinsic_name(self.def))),
InstanceKind::Intrinsic => {
Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name()))
}
InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None,
}
}
Expand Down
35 changes: 35 additions & 0 deletions compiler/stable_mir/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,41 @@ impl FnDef {
pub fn body(&self) -> Option<Body> {
with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0)))
}

/// Get the information of the intrinsic if this function is a definition of one.
pub fn as_intrinsic(&self) -> Option<IntrinsicDef> {
with(|cx| cx.intrinsic(self.def_id()))
}

/// Check if the function is an intrinsic.
#[inline]
pub fn is_intrinsic(&self) -> bool {
self.as_intrinsic().is_some()
}
}

crate_def! {
pub IntrinsicDef;
}

impl IntrinsicDef {
/// Returns the plain name of the intrinsic.
/// e.g., `transmute` for `core::intrinsics::transmute`.
pub fn fn_name(&self) -> Symbol {
with(|cx| cx.intrinsic_name(*self))
}

/// Returns whether the intrinsic has no meaningful body and all backends
/// need to shim all calls to it.
pub fn must_be_overridden(&self) -> bool {
with(|cx| cx.intrinsic_must_be_overridden(*self))
}
}

impl From<IntrinsicDef> for FnDef {
fn from(def: IntrinsicDef) -> Self {
FnDef(def.0)
}
}

crate_def! {
Expand Down
54 changes: 37 additions & 17 deletions tests/ui-fulldeps/stable-mir/check_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//@ 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]
Expand All @@ -23,8 +24,8 @@ 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::{RigidTy, TyKind};
use std::collections::HashSet;
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;
Expand All @@ -39,9 +40,10 @@ fn test_intrinsics() -> ControlFlow<()> {
visitor.visit_body(&main_body);

let calls = visitor.calls;
assert_eq!(calls.len(), 2, "Expected 2 calls, but found: {calls:?}");
for intrinsic in &calls {
check_intrinsic(intrinsic)
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(())
Expand All @@ -53,22 +55,39 @@ fn test_intrinsics() -> ControlFlow<()> {
///
/// If by any chance this test breaks because you changed how an intrinsic is implemented, please
/// update the test to invoke a different intrinsic.
fn check_intrinsic(intrinsic: &Instance) {
assert_eq!(intrinsic.kind, InstanceKind::Intrinsic);
let name = intrinsic.intrinsic_name().unwrap();
if intrinsic.has_body() {
let Some(body) = intrinsic.body() else { unreachable!("Expected a body") };
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");
assert_matches!(name.as_str(), "likely" | "vtable_size");
} else {
assert!(intrinsic.body().is_none());
assert!(instance.body().is_none());
assert_eq!(&name, "size_of_val");
}
}

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" | "size_of_val" => {
assert!(!intrinsic.must_be_overridden());
}
"vtable_size" => {
assert!(intrinsic.must_be_overridden());
}
_ => unreachable!("Unexpected intrinsic: {}", name),
}
}

struct CallsVisitor<'a> {
locals: &'a [LocalDecl],
calls: HashSet<Instance>,
calls: Vec<(FnDef, GenericArgs)>,
}

impl<'a> MirVisitor for CallsVisitor<'a> {
Expand All @@ -77,10 +96,10 @@ impl<'a> MirVisitor for CallsVisitor<'a> {
TerminatorKind::Call { func, .. } => {
let TyKind::RigidTy(RigidTy::FnDef(def, args)) =
func.ty(self.locals).unwrap().kind()
else {
return;
};
self.calls.insert(Instance::resolve(def, &args).unwrap());
else {
return;
};
self.calls.push((def, args.clone()));
}
_ => {}
}
Expand All @@ -106,6 +125,7 @@ fn generate_input(path: &str) -> std::io::Result<()> {
#![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)
}}
Expand Down
Loading