diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 114f9d864e735..d778c18a4765f 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -828,7 +828,8 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, NonDivergingIntrinsic::CopyNonOverlapping(..) => span_bug!( span, "Unexpected CopyNonOverlapping, should only appear after lower_intrinsics", - ) + ), + NonDivergingIntrinsic::CodeviewAnnotation(_) => {}, } // Only relevant for mir typeck StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 439aa1a91e068..9fc05b4306a55 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -65,6 +65,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, dst); self.consume_operand(location, count); } + // Doesn't have any language semantics + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) => {} // Only relevant for mir typeck StatementKind::AscribeUserType(..) // Only relevant for liveness and unsafeck diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 47f14dc3df62e..7b22460271dc5 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -691,6 +691,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(..)) | StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 4f483cdc5d6c7..4382230b0134a 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -918,6 +918,8 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: StatementKind::Intrinsic(intrinsic) => match &**intrinsic { // We ignore `assume` intrinsics, they are only useful for optimizations NonDivergingIntrinsic::Assume(_) => {} + // codeview_annotation is supported only by the LLVM backend + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { src, dst, diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index ff8e6744bd32c..40facfc52d207 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -606,6 +606,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( StatementKind::Intrinsic(intrinsic) => match **intrinsic { NonDivergingIntrinsic::CopyNonOverlapping(..) => return None, NonDivergingIntrinsic::Assume(..) => {} + NonDivergingIntrinsic::CodeviewAnnotation(..) => {} }, // conservative handling StatementKind::Assign(_) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 3f1b33c73e638..52e8edc9a0006 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -716,6 +716,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc // FIXME(antoyo): implement. self.context.new_rvalue_from_int(self.int_type, 0) } + + fn codeview_annotation(&mut self, _strings: &[Symbol]) { + // No-op as codeview_annotation is supported only by the LLVM backend. + // GCC has no intrinsic we can lower to + } } impl<'a, 'gcc, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cc6ecee60b0e4..088c88988ccab 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -37,7 +37,7 @@ use crate::declare::declare_raw_fn; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, }; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -834,6 +834,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value { self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) } + + fn codeview_annotation(&mut self, strings: &[Symbol]) { + if !self.cx.sess().target.is_like_msvc { + return; + } + + if strings.is_empty() { + bug!("codeview_annotation with empty strings should not reach codegen"); + } + + let md_strings: Vec<&Metadata> = + strings.iter().map(|s| self.cx.create_metadata(s.as_str().as_bytes())).collect(); + let md_tuple = unsafe { + llvm::LLVMMDNodeInContext2(self.cx.llcx, md_strings.as_ptr(), md_strings.len()) + }; + let md_value = self.cx.get_metadata_value(md_tuple); + let (fn_ty, intrinsic_fn) = self.cx.get_intrinsic("llvm.codeview.annotation".into(), &[]); + + self.call(fn_ty, None, None, intrinsic_fn, &[md_value], None, None); + } } fn catch_unwind_intrinsic<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index bc3ffa24d5289..8d2ff085bd8cb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -94,6 +94,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } + mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation( + ref symbols, + )) => { + bx.codeview_annotation(symbols); + } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } | mir::StatementKind::AscribeUserType(..) diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index d1e6436f6b1eb..e1576ad13a7a0 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -1,5 +1,5 @@ use rustc_middle::ty; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use super::BackendTypes; use crate::mir::operand::OperandRef; @@ -49,4 +49,7 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; + + /// Emit a codeview_annotation with the given strings. + fn codeview_annotation(&mut self, strings: &[Symbol]); } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d5f69d78ea6ae..3cc64d74c5425 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -776,6 +776,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let count = self.eval_operand(count, None)?; self.copy_intrinsic(&src, &dst, &count, /* nonoverlapping */ true) } + NonDivergingIntrinsic::CodeviewAnnotation(_) => interp_ok(()), } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 47420997a509a..d9518bf9f4f44 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -86,6 +86,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ceilf32 | sym::ceilf64 | sym::ceilf128 + | sym::codeview_annotation | sym::cold_path | sym::const_eval_select | sym::contract_check_ensures @@ -293,6 +294,31 @@ pub(crate) fn check_intrinsic_type( sym::amdgpu_dispatch_ptr => (0, 0, vec![], Ty::new_imm_ptr(tcx, tcx.types.unit)), sym::unreachable => (0, 0, vec![], tcx.types.never), sym::breakpoint => (0, 0, vec![], tcx.types.unit), + sym::codeview_annotation => { + let str_ref = Ty::new_imm_ref( + tcx, + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_u32(1), + kind: ty::BoundRegionKind::Anon, + }, + ), + tcx.types.str_, + ); + let slice_ty = Ty::new_slice(tcx, str_ref); + let ref_to_slice = Ty::new_imm_ref( + tcx, + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ), + slice_ty, + ); + (0, 0, vec![ref_to_slice], tcx.types.unit) + } sym::size_of | sym::align_of | sym::variant_count => (1, 0, vec![], tcx.types.usize), sym::size_of_val | sym::align_of_val => { (1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index bf3d595e99436..196d995fd5566 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -870,6 +870,16 @@ impl Display for NonDivergingIntrinsic<'_> { Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => { write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})") } + Self::CodeviewAnnotation(symbols) => { + write!(f, "codeview_annotation(")?; + for (i, sym) in symbols.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{sym:?}")?; + } + write!(f, ")") + } } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 455089f285d17..9863558de0266 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -504,6 +504,16 @@ pub enum NonDivergingIntrinsic<'tcx> { /// **Needs clarification**: Is this typed or not, ie is there a typed load and store involved? /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(CopyNonOverlapping<'tcx>), + + /// Denotes a call to the intrinsic function `codeview_annotation`. + /// + /// Gets lowered to `llvm.codeview.annotation` on LLVM for MSVC targets. + /// Is a no-op on other targets and backends. + CodeviewAnnotation( + #[type_foldable(identity)] + #[type_visitable(ignore)] + Box<[rustc_span::Symbol]>, + ), } /// Describes what kind of retag is to be performed. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 16a8743a6d67b..4198f4ef67006 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -495,6 +495,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} } } StatementKind::BackwardIncompatibleDropHint { place, .. } => { diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 446b2939e3705..b26901b47a065 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -1,16 +1,17 @@ //! See docs in build/expr/mod.rs use rustc_abi::FieldIdx; -use rustc_ast::{AsmMacro, InlineAsmOptions}; +use rustc_ast::{AsmMacro, InlineAsmOptions, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; +use rustc_middle::mir::interpret::GlobalId; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::thir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; -use rustc_span::{DUMMY_SP, Spanned, sym}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::{DUMMY_SP, Spanned, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::{debug, instrument}; @@ -366,12 +367,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None }) } - // Some intrinsics are handled here because they desperately want to avoid introducing - // unnecessary copies. + // Some intrinsics are handled here: + // - write_via_move and write_box_via_move because they desperately want + // to avoid introducing unnecessary copies. + // - codeview_annotation because it is easier to extract its argument here + // than in codegen where it will require walking the MIR. ExprKind::Call { ty, fun, ref args, .. } if let ty::FnDef(def_id, generic_args) = *ty.kind() && let Some(intrinsic) = this.tcx.intrinsic(def_id) - && matches!(intrinsic.name, sym::write_via_move | sym::write_box_via_move) => + && matches!( + intrinsic.name, + sym::write_via_move | sym::write_box_via_move | sym::codeview_annotation + ) => { // We still have to evaluate the callee expression as normal (but we don't care // about its result). @@ -444,6 +451,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } + sym::codeview_annotation => { + // Extract strings from the `&[&str]` argument of codeview_annotation + // and lower them into a CodeviewAnnotation MIR intrinsic + let strings = + args.first().and_then(|&arg_id| this.extract_strs_from_array(arg_id)); + + match strings { + Some(strings) if !strings.is_empty() => { + this.cfg.push( + block, + Statement::new( + this.source_info(expr_span), + StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::CodeviewAnnotation( + strings.into_boxed_slice(), + ), + )), + ), + ); + } + Some(_) => { + this.tcx.dcx().span_err( + expr_span, + "`codeview_annotation` expects a non-empty array", + ); + } + None => { + this.tcx.dcx().span_err( + expr_span, + "`codeview_annotation` expects a const array", + ); + } + } + + // Assign unit to destination + let unit_rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand { + span: expr_span, + user_ty: None, + const_: Const::zero_sized(this.tcx.types.unit), + }))); + this.cfg.push_assign( + block, + this.source_info(expr_span), + destination, + unit_rvalue, + ); + block.unit() + } _ => rustc_middle::bug!(), } } @@ -899,4 +954,109 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => false, } } + + /// Extract strings from a string array expr. + /// + /// Peels through THIR wrapper nodes (Scope, Borrow, PointerCoercion, Deref) + /// to find either an inline array of string literals/named consts, or a + /// named const evaluating to such an array. + fn extract_strs_from_array(&self, expr_id: ExprId) -> Option> { + // Peel away scopes, borrows etc. + let mut id = expr_id; + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } = self.thir[id].kind + { + id = value; + } + + // Extract strings + match self.thir[id].kind { + // Array literal: e.g. ["lit1", STR_CONST] + ExprKind::Array { ref fields } => { + let mut syms = Vec::with_capacity(fields.len()); + for &field_id in fields.iter() { + let mut id = field_id; + while let ExprKind::Scope { value, .. } + | ExprKind::Borrow { arg: value, .. } + | ExprKind::PointerCoercion { source: value, .. } + | ExprKind::Deref { arg: value } = self.thir[id].kind + { + id = value; + } + match self.thir[id].kind { + ExprKind::Literal { lit, neg: false } + if let LitKind::Str(sym, _) = lit.node => + { + syms.push(sym); + } + ExprKind::NamedConst { .. } => { + let mut strs = self.eval_named_const_strs(&self.thir[id])?; + if strs.len() != 1 { + // Each array field is just a single string + return None; + } + syms.append(&mut strs); + } + _ => return None, + } + } + Some(syms) + } + // Named const array: e.g. const ARR_CONST: &[&str] = &["lit1", STR_CONST] + ExprKind::NamedConst { .. } => self.eval_named_const_strs(&self.thir[id]), + _ => None, + } + } + + /// Evaluate a `NamedConst` THIR expression to a list of string symbols. + /// Works for `&str` (returns one symbol), `&[&str]`, `[&str; N]`, etc. + fn eval_named_const_strs(&self, expr: &Expr<'tcx>) -> Option> { + let ExprKind::NamedConst { def_id, args, .. } = expr.kind else { + return None; + }; + + // Can't evaluate consts with unresolved generic params (e.g. T::NAME) + if args.has_non_region_param() { + return None; + } + + let Ok(Ok(valtree)) = self.tcx.const_eval_global_id_for_typeck( + self.typing_env(), + GlobalId { instance: ty::Instance::new_raw(def_id, args), promoted: None }, + expr.span, + ) else { + return None; + }; + + Self::extract_strs_from_value(self.tcx, ty::Value { ty: expr.ty, valtree }) + } + + /// Recursively extract string symbols from a `ty::Value`. + /// Handles `&str`, `&[&str]`, `[&str; N]`, and `&[&str; N]`. + fn extract_strs_from_value(tcx: TyCtxt<'tcx>, value: ty::Value<'tcx>) -> Option> { + match value.ty.kind() { + ty::Ref(_, inner_ty, _) if inner_ty.is_str() => { + let bytes = value.try_to_raw_bytes(tcx)?; + let s = std::str::from_utf8(bytes).ok()?; + Some(vec![Symbol::intern(s)]) + } + ty::Ref(_, inner_ty, _) => Self::extract_strs_from_value( + tcx, + ty::Value { ty: *inner_ty, valtree: value.valtree }, + ), + ty::Array(..) | ty::Slice(_) => { + let elems = value.try_to_branch()?; + let mut syms = Vec::with_capacity(elems.len()); + for c in elems { + let elem_value = c.try_to_value()?; + let mut elem_syms = Self::extract_strs_from_value(tcx, elem_value)?; + syms.append(&mut elem_syms); + } + Some(syms) + } + _ => None, + } + } } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 331c98fc198eb..48cad5bf237f8 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -87,6 +87,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += match **ndi { NonDivergingIntrinsic::Assume(..) => INSTR_COST, NonDivergingIntrinsic::CopyNonOverlapping(..) => CALL_PENALTY, + NonDivergingIntrinsic::CodeviewAnnotation(..) => 0, // codeview_annotation is a no-op at runtime }; } StatementKind::Assign(..) => self.penalty += INSTR_COST, diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 122429276e3c5..c9774da3a16e8 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -202,6 +202,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { }) => { // This statement represents `*dst = *src`, `count` times. } + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} } } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index a1e5d810afc02..26c6b5422fc1f 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -399,6 +399,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(..)) // copy_nonoverlapping takes pointers and mutated the pointed-to value. | StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(..)) + | StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(..)) | StatementKind::AscribeUserType(..) | StatementKind::Coverage(..) | StatementKind::FakeRead(..) diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 656b74e15f727..c6e42f955d3c4 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1540,6 +1540,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({op_cnt_ty} != usize)")) } } + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(symbols)) => { + if symbols.is_empty() { + self.fail(location, "`CodeviewAnnotation` must have at least one string"); + } + } StatementKind::SetDiscriminant { place, .. } => { if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 51757c5827221..3cd35e9e78c1e 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -465,6 +465,7 @@ pub struct CopyNonOverlapping { pub enum NonDivergingIntrinsic { Assume(Operand), CopyNonOverlapping(CopyNonOverlapping), + CodeviewAnnotation(Vec), } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index e1d9cf31036e2..0ccd0e14e4e9a 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -194,6 +194,7 @@ macro_rules! make_mir_visitor { self.visit_operand(dst, location); self.visit_operand(count, location); } + NonDivergingIntrinsic::CodeviewAnnotation(_) => {} }, StatementKind::ConstEvalCounter | StatementKind::Nop => {} } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index d25751c81f3f5..f22145da33e6e 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -510,6 +510,11 @@ impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> { count: copy_non_overlapping.count.stable(tables, cx), }) } + NonDivergingIntrinsic::CodeviewAnnotation(symbols) => { + crate::mir::NonDivergingIntrinsic::CodeviewAnnotation( + symbols.iter().map(|s| s.as_str().to_string()).collect(), + ) + } } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 738c9b975fd00..d8acbfcf92204 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -618,6 +618,7 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, + codeview_annotation, coerce_pointee_validated, coerce_shared, coerce_unsized, diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index a8e01e6e78b4b..1076522af0d1d 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1025,3 +1025,21 @@ pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), } } + +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in `strings` being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with Windows targets and the LLVM backend. +/// Is a no-op on other targets and backends. +#[unstable(feature = "codeview_annotation", issue = "none")] +#[macro_export] +macro_rules! codeview_annotation { + ($($string_:literal),+ $(,)?) => {{ + $crate::intrinsics::codeview_annotation(&[$($string_),+]) + }}; +} + +#[unstable(feature = "codeview_annotation", issue = "none")] +#[doc(inline)] +pub use codeview_annotation; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 128df654e078d..d37ba87122a6c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -336,6 +336,17 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in `strings` being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with Windows targets and the LLVM backend. +/// Is a no-op on other targets and backends. +#[unstable(feature = "codeview_annotation", issue = "none")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn codeview_annotation(strings: &[&str]); + /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1484b8c8bcc45..193424dd05ddc 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -238,8 +238,10 @@ fn check_statement<'tcx>( check_operand(cx, src, span, body, msrv)?; check_operand(cx, count, span, body, msrv) }, + // These are all NOPs - StatementKind::StorageLive(_) + StatementKind::Intrinsic(box NonDivergingIntrinsic::CodeviewAnnotation(_)) + | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Retag { .. } | StatementKind::AscribeUserType(..) diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation.rs b/tests/codegen-llvm/intrinsics/codeview_annotation.rs new file mode 100644 index 0000000000000..01b4e8687ac40 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview_annotation.rs @@ -0,0 +1,117 @@ +// Verifies that codeview_annotation intrinsic lowers correctly +// under various conditions like directly calling the intrinsic, +// calling through the macro, calling with different kinds of +// args and in the presence of duplicate calls. + +//@ only-msvc +//@ revisions: OPT0 OPT3 +//@ [OPT0] compile-flags: -Copt-level=0 +//@ [OPT3] compile-flags: -Copt-level=3 +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// CHECK-LABEL: @intrinsic_single_annotation +// CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) +#[no_mangle] +pub fn intrinsic_single_annotation() { + codeview_annotation(&["intr_single_string"]); +} + +// CHECK-LABEL: @intrinsic_multiple_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[MULTI:![0-9]+]]) +#[no_mangle] +pub fn intrinsic_multiple_annotations() { + codeview_annotation(&["intr_multi1", "intr_multi2", "intr_multi3"]); +} + +// CHECK-LABEL: @macro_single_annotation +// CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_SINGLE:![0-9]+]]) +#[no_mangle] +pub fn macro_single_annotation() { + std::hint::codeview_annotation!("mac_single"); +} + +// CHECK-LABEL: @macro_multiple_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[MACRO_MULTI:![0-9]+]]) +#[no_mangle] +pub fn macro_multiple_annotations() { + std::hint::codeview_annotation!("mac_multi1", "mac_multi2", "mac_multi3"); +} + +const STR_A: &str = "named_const1"; +const STR_B: &str = "named_const2"; +const STR_C: &str = "named_const3"; + +// CHECK-LABEL: @named_const_elements +// CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) +#[no_mangle] +pub fn named_const_elements() { + codeview_annotation(&[STR_A, STR_B, STR_C]); +} + +// CHECK-LABEL: @mixed_named_const_and_literal_elements +// CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) +#[no_mangle] +pub fn mixed_named_const_and_literal_elements() { + codeview_annotation(&[STR_A, "mixed_literal1", "mixed_literal2"]); +} + +const STRS_SLICE: &[&str] = &["slice_element1", "slice_element2", "slice_element3"]; + +// CHECK-LABEL: @named_const_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_SLICE:![0-9]+]]) +#[no_mangle] +pub fn named_const_slice() { + codeview_annotation(STRS_SLICE); +} + +const STRS_ARRAY: [&str; 3] = ["arr_element1", "arr_element2", "arr_element3"]; + +// CHECK-LABEL: @named_const_array_ref +// CHECK: call void @llvm.codeview.annotation(metadata [[CONST_ARRAY:![0-9]+]]) +#[no_mangle] +pub fn named_const_array_ref() { + codeview_annotation(&STRS_ARRAY); +} + +// Multiple annotations with same strings within a single function +// CHECK-LABEL: @duplicate_annotations +// CHECK: call void @llvm.codeview.annotation(metadata [[DUP:![0-9]+]]) +// CHECK: call void @llvm.codeview.annotation(metadata [[DUP]]) +#[no_mangle] +pub fn duplicate_annotations() { + codeview_annotation(&["dup1", "dup2", "dup3"]); + codeview_annotation(&["dup1", "dup2", "dup3"]); +} + +// Multiple annotations with same strings within different functions +// CHECK-LABEL: @duplicate_annotations_func_a +// CHECK: call void @llvm.codeview.annotation(metadata [[FUNC_DUP:![0-9]+]]) +#[no_mangle] +pub fn duplicate_annotations_func_a() { + codeview_annotation(&["func_dup1", "func_dup2", "func_dup3"]); +} + +// CHECK-LABEL: @duplicate_annotations_func_b +// CHECK: call void @llvm.codeview.annotation(metadata [[FUNC_DUP:![0-9]+]]) +#[no_mangle] +pub fn duplicate_annotations_func_b() { + codeview_annotation(&["func_dup1", "func_dup2", "func_dup3"]); +} + +// Metadata definitions are at the end of LLVM IR, so check them here +// CHECK-DAG: [[SINGLE]] = !{!"intr_single_string"} +// CHECK-DAG: [[MULTI]] = !{!"intr_multi1", !"intr_multi2", !"intr_multi3"} +// CHECK-DAG: [[MACRO_SINGLE]] = !{!"mac_single"} +// CHECK-DAG: [[MACRO_MULTI]] = !{!"mac_multi1", !"mac_multi2", !"mac_multi3"} +// CHECK-DAG: [[NAMED_CONST]] = !{!"named_const1", !"named_const2", !"named_const3"} +// CHECK-DAG: [[MIXED]] = !{!"named_const1", !"mixed_literal1", !"mixed_literal2"} +// CHECK-DAG: [[CONST_SLICE]] = !{!"slice_element1", !"slice_element2", !"slice_element3"} +// CHECK-DAG: [[CONST_ARRAY]] = !{!"arr_element1", !"arr_element2", !"arr_element3"} +// CHECK-DAG: [[DUP]] = !{!"dup1", !"dup2", !"dup3"} +// CHECK-DAG: [[FUNC_DUP]] = !{!"func_dup1", !"func_dup2", !"func_dup3"} diff --git a/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs new file mode 100644 index 0000000000000..fe3f72f9eb3a0 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview_annotation_non_msvc.rs @@ -0,0 +1,18 @@ +// Verifies that codeview_annotation does NOT emit `llvm.codeview.annotation` +// on non-MSVC targets. The intrinsic should be silently ignored. + +//@ ignore-msvc +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +// CHECK-LABEL: @test_non_msvc +// CHECK-NOT: codeview.annotation +#[no_mangle] +pub fn test_non_msvc() { + codeview_annotation(&["string1", "string2", "string3"]); +} diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.rs b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs new file mode 100644 index 0000000000000..bfeaf5339c8e9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs @@ -0,0 +1,4 @@ +fn main() { + std::hint::codeview_annotation!("string1", "string2", "string3"); //~ ERROR use of unstable library feature `codeview_annotation` + //~| ERROR use of unstable library feature `codeview_annotation` +} diff --git a/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr new file mode 100644 index 0000000000000..e8e855f200787 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -0,0 +1,22 @@ +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:2:5 + | +LL | std::hint::codeview_annotation!("string1", "string2", "string3"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:2:5 + | +LL | std::hint::codeview_annotation!("string1", "string2", "string3"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(codeview_annotation)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `std::hint::codeview_annotation` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/intrinsics/codeview_annotation.rs b/tests/ui/intrinsics/codeview_annotation.rs new file mode 100644 index 0000000000000..f54ec017ca0ec --- /dev/null +++ b/tests/ui/intrinsics/codeview_annotation.rs @@ -0,0 +1,99 @@ +// Verifies codeview_annotation compile-time behavior: +// - Happy paths: inline literals, macro usage, named const elements, +// mixed const/literal, const slices, const array refs, and const fn usage. +// - Error cases: non-const arguments, function parameters, empty arrays, +// and wrong types. +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::codeview_annotation; + +fn intrinsic_single() { + codeview_annotation(&["string1"]); +} + +fn intrinsic_multiple() { + codeview_annotation(&["string1", "string2", "string3"]); +} + +fn macro_single() { + std::hint::codeview_annotation!("string1"); +} + +fn macro_multiple() { + std::hint::codeview_annotation!("string1", "string2", "string3"); +} + +const STR_A: &str = "string1"; +const STR_B: &str = "string2"; +const STR_C: &str = "string3"; +fn named_const_elements() { + codeview_annotation(&[STR_A, STR_B, STR_C]); +} + +fn mixed_named_const_and_literal_elements() { + codeview_annotation(&[STR_A, "string2", "string3"]); +} + +const STRS_SLICE: &[&str] = &["string1", "string2", "string3"]; +fn named_const_slice() { + codeview_annotation(STRS_SLICE); +} + +const STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; +fn named_const_array_ref() { + codeview_annotation(&STRS_ARRAY); +} + +// Use in const function +const fn annotated_computation(x: u32) -> u32 { + codeview_annotation(&["string1", "string2", "string3"]); + x + 1 +} + +// --- Error cases --- + +fn non_const_arg(strs: &[&str]) { + codeview_annotation(strs); //~ ERROR `codeview_annotation` expects a const array + let s = "string1"; + codeview_annotation(&[s]); //~ ERROR `codeview_annotation` expects a const array +} + +fn empty_array() { + codeview_annotation(&[]); //~ ERROR `codeview_annotation` expects a non-empty array +} + +fn wrong_type() { + codeview_annotation(42); //~ ERROR mismatched types +} + +trait TypeName { + const NAME: &str; +} + +impl TypeName for i32 { + const NAME: &str = "i32"; +} + +fn generics() { + codeview_annotation(&[T::NAME]); //~ ERROR `codeview_annotation` expects a const array +} + + +fn main() { + intrinsic_single(); + intrinsic_multiple(); + macro_single(); + macro_multiple(); + named_const_elements(); + mixed_named_const_and_literal_elements(); + named_const_slice(); + named_const_array_ref(); + let _ = annotated_computation(5); + const _: u32 = annotated_computation(5); + + non_const_arg(&["a"]); + empty_array(); + wrong_type(); + generics::(); +} diff --git a/tests/ui/intrinsics/codeview_annotation.stderr b/tests/ui/intrinsics/codeview_annotation.stderr new file mode 100644 index 0000000000000..aa9e2b620b683 --- /dev/null +++ b/tests/ui/intrinsics/codeview_annotation.stderr @@ -0,0 +1,38 @@ +error[E0308]: mismatched types + --> $DIR/codeview_annotation.rs:67:25 + | +LL | codeview_annotation(42); + | ------------------- ^^ expected `&[&str]`, found integer + | | + | arguments to this function are incorrect + | +note: function defined here + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + +error: `codeview_annotation` expects a const array + --> $DIR/codeview_annotation.rs:57:5 + | +LL | codeview_annotation(strs); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `codeview_annotation` expects a const array + --> $DIR/codeview_annotation.rs:59:5 + | +LL | codeview_annotation(&[s]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `codeview_annotation` expects a non-empty array + --> $DIR/codeview_annotation.rs:63:5 + | +LL | codeview_annotation(&[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `codeview_annotation` expects a const array + --> $DIR/codeview_annotation.rs:79:5 + | +LL | codeview_annotation(&[T::NAME]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`.