diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 04368201b4576..27b833f2a5e87 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,8 @@ use rustc_codegen_ssa::traits::*; use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_lint_defs::builtin::DEPRECATED_LLVM_INTRINSIC; -use rustc_middle::mir::BinOp; +use rustc_middle::mir::interpret::{AllocId, Allocation, ErrorHandled, GlobalAlloc, alloc_range}; +use rustc_middle::mir::{self, BinOp}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::offload_meta::OffloadMetadata; use rustc_middle::ty::{self, GenericArgsRef, Instance, SimdAlign, Ty, TyCtxt}; @@ -43,7 +44,7 @@ use crate::diagnostics::{ OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; use crate::intrinsic::ty::typetree::fnc_typetrees; -use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; +use crate::llvm::{self, Attribute, AttributePlace, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -885,6 +886,13 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } + sym::codeview_annotation => { + if self.sess().target.uses_pdb_debuginfo() { + codegen_codeview_annotation(self, instance, span); + } + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } + _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code @@ -3470,3 +3478,139 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } + +fn codegen_codeview_annotation<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + instance: ty::Instance<'tcx>, + span: Span, +) { + // This function lowers the `codeview_annotation` intrinsic, + // which is declared like this: + // + // trait CodeViewAnnotationArgs { + // const ARGS: &[&str]; + // } + // + // fn codeview_annotation() {} + // + // It finds the `T: CodeViewAnnotationArgs` trait bound, locates + // `ARGS`, const evaluates it, reads the strings off the resulting + // allocation and lowers them. + + const ARGS_CONST_NAME: &str = "`CodeViewAnnotationArgs::ARGS`"; + + let tcx = bx.tcx(); + + // Locate `ARGS` + let Some((args_const_def_id, generic_args)) = tcx + .explicit_clauses_of(instance.def_id()) + .instantiate_own(tcx, instance.args) + .filter_map(|(clause, _)| clause.skip_norm_wip().as_trait_clause()) + .find_map(|trait_clause| { + let trait_ref = trait_clause.skip_binder().trait_ref; + tcx.associated_items(trait_ref.def_id) + .in_definition_order() + .find(|item| { + item.tag() == ty::AssocTag::Const + && tcx.item_name(item.def_id).as_str() == "ARGS" + }) + .map(|item| (item.def_id, trait_ref.args)) + }) + else { + span_bug!(span, "could not find {ARGS_CONST_NAME}"); + }; + + // Const evaluate `ARGS` + let args_const_val = match tcx.const_eval_resolve( + bx.typing_env(), + mir::UnevaluatedConst::new(args_const_def_id, generic_args), + span, + ) { + Ok(val) => val, + Err(ErrorHandled::Reported(..)) => return, + Err(ErrorHandled::TooGeneric(_)) => { + span_bug!(span, "{ARGS_CONST_NAME} is too generic") + } + }; + + // Read the strings + let array_alloc = match args_const_val { + mir::ConstValue::Indirect { alloc_id, offset } => read_slice_pointer(tcx, alloc_id, offset) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} slice pointer")), + _ => span_bug!(span, "unexpected {ARGS_CONST_NAME} value: {args_const_val:?}"), + }; + + let strings = array_alloc.into_iter().flat_map(|(array_alloc_id, array_offset, array_len)| { + (0..array_len).map(move |index| { + let elem_size = tcx.data_layout.pointer_size().bytes() * 2; // Multiplying by 2 for fat pointers + let elem_offset = array_offset + Size::from_bytes(index * elem_size); + + let (string_alloc_id, string_offset, string_len) = + match read_slice_pointer(tcx, array_alloc_id, elem_offset) { + Ok(Some(slice)) => slice, + Ok(None) => return &[] as &[u8], + Err(()) => span_bug!(span, "invalid {ARGS_CONST_NAME} string pointer"), + }; + + resolve_alloc(tcx, string_alloc_id) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} string allocation")) + .get_bytes_strip_provenance( + &tcx, + alloc_range(string_offset, Size::from_bytes(string_len)), + ) + .unwrap_or_else(|_| span_bug!(span, "invalid {ARGS_CONST_NAME} string bytes")) + }) + }); + + // Lower the strings + let md_strings: Vec<&Metadata> = strings.map(|s| bx.cx.create_metadata(s)).collect(); + let md_tuple = bx.cx.md_node_in_context(&md_strings); + let md_value = bx.cx.get_metadata_value(md_tuple); + let (fn_ty, intrinsic_fn) = bx.cx.get_intrinsic("llvm.codeview.annotation".into(), &[]); + + bx.call(fn_ty, None, None, intrinsic_fn, ReturnSlot::Direct, &[md_value], None, None); +} + +// Reads the slice pointer stored at `offset` in `alloc_id` and +// returns its pointee's allocation ID, relative byte offset +// and slice length. Returns `None` for an empty slice. +fn read_slice_pointer<'tcx>( + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, + offset: Size, +) -> Result, ()> { + let alloc = resolve_alloc(tcx, alloc_id)?; + let pointer_size = tcx.data_layout.pointer_size(); + + let pointer = alloc + .read_scalar(&tcx, alloc_range(offset, pointer_size), true) + .map_err(|_| ())? + .to_pointer(&tcx); + + let len = alloc + .read_scalar(&tcx, alloc_range(offset + pointer_size, pointer_size), false) + .map_err(|_| ())? + .to_target_usize(&tcx) + .discard_err() + .ok_or(())?; + + if len == 0 { + return Ok(None); + } + + let (provenance, offset) = + pointer.into_pointer_or_addr().map_err(|_| ())?.prov_and_relative_offset(); + + Ok(Some((provenance.alloc_id(), offset, len))) +} + +fn resolve_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId) -> Result<&'tcx Allocation, ()> { + let alloc = match tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => alloc, + GlobalAlloc::Static(def_id) => tcx.eval_static_initializer(def_id).map_err(|_| ())?, + _ => return Err(()), + } + .inner(); + + Ok(alloc) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index ade4f8a4ee8d6..5099b9f86c183 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -125,6 +125,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::cold_path | sym::gpu_launch_sized_workgroup_mem | sym::breakpoint + | sym::codeview_annotation | sym::amdgpu_dispatch_ptr | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 9cf88e3953d5d..804e84324f0d2 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,7 @@ 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 => (1, 0, vec![], 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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 22d453df065c9..d86dd715d70d4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -663,6 +663,7 @@ symbols! { cmpxchg16b_target_feature, cmse_nonsecure_entry, code, + codeview_annotation, coerce_pointee_validated, coerce_shared, coerce_shared_target, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 3a5ec85f507ec..550e52480301c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -571,6 +571,30 @@ pub const fn prefetch_write_instruction(data: *const T) #[rustc_nounwind] pub fn breakpoint(); +/// A container for the string arguments passed to [`codeview_annotation`]. +#[unstable(feature = "codeview_annotation", issue = "163964")] +pub trait CodeViewAnnotationArgs { + /// The string arguments to `codeview_annotation`. + const ARGS: &[&str]; +} + +/// Emits a call to [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic) +/// which results in [`CodeViewAnnotationArgs::ARGS`] being written to the +/// PDB as an `S_ANNOTATION` record. +/// +/// Works only with targets that use PDB debuginfo and with the LLVM backend. +/// Is a no-op on other targets and backends. +#[unstable(feature = "codeview_annotation", issue = "163964")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[miri::intrinsic_fallback_is_spec] +pub fn codeview_annotation() { + // const block to catch any errors in `T::ARGS` + const { + let _ignore = T::ARGS; + } +} + /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// diff --git a/library/core/src/os/darwin/objc.rs b/library/core/src/os/darwin/objc.rs index 7be07891085a3..4512e3c3ada85 100644 --- a/library/core/src/os/darwin/objc.rs +++ b/library/core/src/os/darwin/objc.rs @@ -74,7 +74,7 @@ pub type SEL = *mut objc_selector; /// /// let string_class = objc::class!("NSString"); /// ``` -#[allow_internal_unstable(rustc_attrs)] +#[allow_internal_unstable(core_os, rustc_attrs)] pub macro class($classname:expr) {{ // Since static Objective-C class references actually end up with multiple definitions // across dylib boundaries, we only expose the value of the static and don't provide a way to @@ -102,7 +102,7 @@ pub macro class($classname:expr) {{ /// let alloc_sel = objc::selector!("alloc"); /// let init_sel = objc::selector!("initWithCString:encoding:"); /// ``` -#[allow_internal_unstable(rustc_attrs)] +#[allow_internal_unstable(core_os, rustc_attrs)] pub macro selector($methname:expr) {{ // Since static Objective-C selector references actually end up with multiple definitions // across dylib boundaries, we only expose the value of the static and don't provide a way to diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs index 8d177dd3947c9..ba86ba7cd0fb5 100644 --- a/library/core/src/os/mod.rs +++ b/library/core/src/os/mod.rs @@ -1,6 +1,5 @@ //! OS-specific functionality. - -#![unstable(feature = "darwin_objc", issue = "145496")] +#![unstable(feature = "core_os", issue = "none")] #![allow(missing_docs)] #[cfg(all( @@ -23,3 +22,6 @@ pub mod darwin {} )))] #[cfg(any(target_vendor = "apple", doc))] pub mod darwin; + +#[cfg(any(target_family = "windows", doc))] +pub mod windows; diff --git a/library/core/src/os/windows.rs b/library/core/src/os/windows.rs new file mode 100644 index 0000000000000..47528f67f6428 --- /dev/null +++ b/library/core/src/os/windows.rs @@ -0,0 +1,39 @@ +//! Platform-specific extensions to `core` for Windows platforms. + +#![unstable(feature = "codeview_annotation", issue = "163964")] +#![doc(cfg(windows))] + +#[doc(inline)] +pub use crate::intrinsics::CodeViewAnnotationArgs; + +/// Writes [`T::ARGS`](CodeViewAnnotationArgs::ARGS) to the PDB as an `S_ANNOTATION` record using +/// [`llvm.codeview.annotation`](https://llvm.org/docs/LangRef.html#llvm-codeview-annotation-intrinsic). +/// +/// This function has an effect only on the `msvc` environment and the LLVM backend. It is a no-op on other +/// environments and backends. +/// +/// # Examples +/// +/// To call `codeview_annotation`, the caller must declare a type +/// implementing [`CodeViewAnnotationArgs`] and pass it as the type +/// parameter to `codeview_annotation`. The string arguments must be +/// specified in `CodeViewAnnotationArgs::ARGS`. +/// +#[cfg_attr(windows, doc = "```")] +#[cfg_attr(not(windows), doc = "```ignore (needs windows)")] +/// #![feature(codeview_annotation, core_os)] +/// use core::os::windows::{codeview_annotation, CodeViewAnnotationArgs}; +/// +/// struct Args; +/// +/// impl CodeViewAnnotationArgs for Args { +/// const ARGS: &[&str] = &["Hello", "World"]; +/// } +/// +/// codeview_annotation::(); +/// ``` +#[inline(always)] +#[unstable(feature = "codeview_annotation", issue = "163964")] +pub fn codeview_annotation() { + crate::intrinsics::codeview_annotation::(); +} diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index a7e032dbf4d4d..507d14765e3fa 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -36,6 +36,10 @@ pub mod process; pub mod raw; pub mod thread; +#[unstable(feature = "codeview_annotation", issue = "163964")] +#[cfg(windows)] +pub use core::os::windows::{CodeViewAnnotationArgs, codeview_annotation}; + /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. diff --git a/src/tools/miri/tests/fail/intrinsics/codeview-annotation.rs b/src/tools/miri/tests/fail/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..023a21e97ca3c --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/codeview-annotation.rs @@ -0,0 +1,17 @@ +// Verifies that `codeview_annotation` produces errors under Miri +// when args fail to evaluate + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::{CodeViewAnnotationArgs, codeview_annotation}; + +struct Args; + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = panic!("panic"); //~ ERROR: evaluation panicked +} + +fn main() { + codeview_annotation::(); +} diff --git a/src/tools/miri/tests/fail/intrinsics/codeview-annotation.stderr b/src/tools/miri/tests/fail/intrinsics/codeview-annotation.stderr new file mode 100644 index 0000000000000..9c8221e326814 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/codeview-annotation.stderr @@ -0,0 +1,24 @@ +error[E0080]: evaluation panicked: panic + --> tests/fail/intrinsics/codeview-annotation.rs:LL:CC + | +LL | impl CodeViewAnnotationArgs for Args { +LL | const ARGS: &[&str] = panic!("panic"); + | ^^^^^^^^^^^^^^^ evaluation of `::ARGS` failed here + +note: erroneous constant encountered + --> RUSTLIB/core/src/intrinsics/mod.rs:LL:CC + | +LL | let _ignore = T::ARGS; + | ^^^^^^^ + +note: erroneous constant encountered + --> RUSTLIB/core/src/intrinsics/mod.rs:LL:CC + | +LL | / const { +LL | | let _ignore = T::ARGS; +LL | | } + | |_____^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/tools/miri/tests/pass/intrinsics/codeview-annotation.rs b/src/tools/miri/tests/pass/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..ab9060237dffb --- /dev/null +++ b/src/tools/miri/tests/pass/intrinsics/codeview-annotation.rs @@ -0,0 +1,16 @@ +// Verifies that `codeview_annotation` works under Miri + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::{CodeViewAnnotationArgs, codeview_annotation}; + +struct Args; + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &["Hello", "World"]; +} + +fn main() { + codeview_annotation::(); +} diff --git a/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs b/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs new file mode 100644 index 0000000000000..1f2833bf54ac3 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview-annotation-non-pdb-targets.rs @@ -0,0 +1,25 @@ +// Verifies that `codeview_annotation` does NOT emit `llvm.codeview.annotation` +// on targets not using PDB debuginfo which is everything except MSVC and UEFI. + +//@ ignore-msvc +//@ ignore-uefi +//@ compile-flags: -C no-prepopulate-passes + +#![crate_type = "lib"] +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::{CodeViewAnnotationArgs, codeview_annotation}; + +struct Args; + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &["string1", "string2", "string3"]; +} + +// CHECK-LABEL: @test_non_pdb +// CHECK-NOT: llvm.codeview.annotation +#[no_mangle] +pub fn test_non_pdb() { + codeview_annotation::(); +} diff --git a/tests/codegen-llvm/intrinsics/codeview-annotation.rs b/tests/codegen-llvm/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..ee52f3dfd91b9 --- /dev/null +++ b/tests/codegen-llvm/intrinsics/codeview-annotation.rs @@ -0,0 +1,197 @@ +// Verifies that codeview_annotation lowers correctly to +// `llvm.codeview.annotation` + +//@ 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)] + +// === Helper macros === +macro_rules! call_intrinsic { + ($args:expr) => {{ + call_codeview_annotation!(std::intrinsics, $args); + }}; +} + +macro_rules! call_api { + ($args:expr) => {{ + call_codeview_annotation!(std::os::windows, $args); + }}; +} + +macro_rules! call_codeview_annotation { + ($($module:ident)::+, $args:expr) => {{ + struct Args; + + impl std::os::windows::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = $args; + } + + $($module)::+::codeview_annotation::(); + }}; +} + +// At OPT0, the API wrapper function is not inlined +// so we must check it exists and calls the intrinsic +// OPT0-LABEL: ; core::os::windows::codeview_annotation:: +// OPT0: define internal void [[API_SINGLE_WRAPPER:@[^(]+]]() +// OPT0: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +// OPT0-LABEL: ; core::os::windows::codeview_annotation:: +// OPT0: define internal void [[API_MULTIPLE_WRAPPER:@[^(]+]]() +// OPT0: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) + +// === Intrinsic tests === +// CHECK-LABEL: @single +// CHECK: call void @llvm.codeview.annotation(metadata [[SINGLE:![0-9]+]]) +#[no_mangle] +pub fn single() { + call_intrinsic!(&["single_string"]); +} + +// CHECK-LABEL: @multiple +// CHECK: call void @llvm.codeview.annotation(metadata [[MULTIPLE:![0-9]+]]) +#[no_mangle] +pub fn multiple() { + call_intrinsic!(&["multi1", "multi2", "multi3"]); +} + +const STR_A: &str = "str_a"; +const STR_B: &str = "str_b"; +const STR_C: &str = "str_c"; + +// CHECK-LABEL: @named_const_elements +// CHECK: call void @llvm.codeview.annotation(metadata [[NAMED_CONST:![0-9]+]]) +#[no_mangle] +pub fn named_const_elements() { + call_intrinsic!(&[STR_A, STR_B, STR_C]); +} + +// CHECK-LABEL: @mixed_named_consts_and_literals +// CHECK: call void @llvm.codeview.annotation(metadata [[MIXED:![0-9]+]]) +#[no_mangle] +pub fn mixed_named_consts_and_literals() { + call_intrinsic!(&[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() { + call_intrinsic!(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() { + call_intrinsic!(&STRS_ARRAY); +} + +static STATIC_STRS_ARRAY: [&str; 3] = ["static1", "static2", "static3"]; + +// CHECK-LABEL: @static_array_ref +// CHECK: call void @llvm.codeview.annotation(metadata [[STATIC_ARRAY:![0-9]+]]) +#[no_mangle] +pub fn static_array_ref() { + call_intrinsic!(&STATIC_STRS_ARRAY); +} + +static STATIC_STRING_BYTES: [u8; 5] = *b"bytes"; +static STATIC_STRING: &str = unsafe { core::str::from_utf8_unchecked(&STATIC_STRING_BYTES) }; + +// CHECK-LABEL: @static_string_element +// CHECK: call void @llvm.codeview.annotation(metadata [[STATIC_STRING:![0-9]+]]) +#[no_mangle] +pub fn static_string_element() { + call_intrinsic!(&["string1", STATIC_STRING, "string3"]); +} + +// CHECK-LABEL: @empty_strings +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_STRINGS:![0-9]+]]) +#[no_mangle] +pub fn empty_strings() { + call_intrinsic!(&["", "", "string1"]); +} + +// CHECK-LABEL: @empty_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_SLICE:![0-9]+]]) +#[no_mangle] +pub fn empty_slice() { + call_intrinsic!(&[]); +} + +const EMPTY_STRS_SLICE: &[&str] = &[]; + +// CHECK-LABEL: @named_empty_slice +// CHECK: call void @llvm.codeview.annotation(metadata [[EMPTY_SLICE]]) +#[no_mangle] +pub fn named_empty_slice() { + call_intrinsic!(EMPTY_STRS_SLICE); +} + +// 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() { + call_intrinsic!(&["dup1", "dup2", "dup3"]); + call_intrinsic!(&["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() { + call_intrinsic!(&["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() { + call_intrinsic!(&["func_dup1", "func_dup2", "func_dup3"]); +} + +// === API tests === +// CHECK-LABEL: @api_single_annotation +// OPT0: call void [[API_SINGLE_WRAPPER]]() +// OPT3: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +#[no_mangle] +pub fn api_single_annotation() { + call_api!(&["intr_single_string"]); +} + +// CHECK-LABEL: @api_multiple_annotations +// OPT0: call void [[API_MULTIPLE_WRAPPER]]() +// OPT3: call void @llvm.codeview.annotation(metadata !{{[0-9]+}}) +#[no_mangle] +pub fn api_multiple_annotations() { + call_api!(&["intr_multi1", "intr_multi2", "intr_multi3"]); +} + +// Metadata definitions are at the end of LLVM IR, so check them here +// CHECK-DAG: [[SINGLE]] = !{!"single_string"} +// CHECK-DAG: [[MULTIPLE]] = !{!"multi1", !"multi2", !"multi3"} +// CHECK-DAG: [[NAMED_CONST]] = !{!"str_a", !"str_b", !"str_c"} +// CHECK-DAG: [[MIXED]] = !{!"str_a", !"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: [[STATIC_ARRAY]] = !{!"static1", !"static2", !"static3"} +// CHECK-DAG: [[STATIC_STRING]] = !{!"string1", !"bytes", !"string3"} +// CHECK-DAG: [[EMPTY_STRINGS]] = !{!"", !"", !"string1"} +// CHECK-DAG: [[EMPTY_SLICE]] = !{} +// CHECK-DAG: [[DUP]] = !{!"dup1", !"dup2", !"dup3"} +// CHECK-DAG: [[FUNC_DUP]] = !{!"func_dup1", !"func_dup2", !"func_dup3"} +// CHECK-DAG: !{{[0-9]+}} = !{!"intr_single_string"} +// CHECK-DAG: !{{[0-9]+}} = !{!"intr_multi1", !"intr_multi2", !"intr_multi3"} 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..f789eb37eb2de --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.rs @@ -0,0 +1,11 @@ +//@ only-windows + +struct Args; + +impl std::os::windows::CodeViewAnnotationArgs for Args { //~ ERROR use of unstable library feature `codeview_annotation` + const ARGS: &[&str] = &["string1", "string2", "string3"]; //~ ERROR use of unstable library feature `codeview_annotation` +} + +fn main() { + std::os::windows::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..588f0238c9196 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-codeview_annotation.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `codeview_annotation` + --> $DIR/feature-gate-codeview_annotation.rs:6:5 + | +LL | const ARGS: &[&str] = &["string1", "string2", "string3"]; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #163964 for more information + = 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:5:6 + | +LL | impl std::os::windows::CodeViewAnnotationArgs for Args { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #163964 for more information + = 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:10:5 + | +LL | std::os::windows::codeview_annotation::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #163964 for more information + = 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: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/intrinsics/codeview-annotation-bad-args.rs b/tests/ui/intrinsics/codeview-annotation-bad-args.rs new file mode 100644 index 0000000000000..ce0174f4bf19d --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation-bad-args.rs @@ -0,0 +1,45 @@ +// Verifies that `codeview_annotation` emits a compile-time error +// when the `CodeViewAnnotationArgs::ARGS` associated constant +// does not const eval successfully. + +//@ build-fail +//@ normalize-stderr: "fn std::intrinsics::codeview_annotation::" -> "fn codeview_annotation::" + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +use std::intrinsics::{CodeViewAnnotationArgs, codeview_annotation}; + +// `CodeViewAnnotationArgs::ARGS` does not const eval successfully +struct Invalid; + +impl CodeViewAnnotationArgs for Invalid { + const ARGS: &[&str] = panic!("panic"); //~ ERROR evaluation panicked: panic [E0080] +} + + +// `CodeViewAnnotationArgs::ARGS` does not const eval successfully +// with generics involved +trait GetName { + const NAME: &str; +} + +impl GetName for i32 { + const NAME: &str = panic!("panic"); //~ ERROR evaluation panicked: panic [E0080] +} + +struct Args(std::marker::PhantomData); + +impl CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &[T::NAME]; +} + +fn emit() { + codeview_annotation::>(); +} + + +fn main() { + codeview_annotation::(); + emit::(); +} diff --git a/tests/ui/intrinsics/codeview-annotation-bad-args.stderr b/tests/ui/intrinsics/codeview-annotation-bad-args.stderr new file mode 100644 index 0000000000000..c02a8a8411537 --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation-bad-args.stderr @@ -0,0 +1,51 @@ +error[E0080]: evaluation panicked: panic + --> $DIR/codeview-annotation-bad-args.rs:17:27 + | +LL | impl CodeViewAnnotationArgs for Invalid { +LL | const ARGS: &[&str] = panic!("panic"); + | ^^^^^^^^^^^^^^^ evaluation of `::ARGS` failed here + +note: erroneous constant encountered + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + +note: erroneous constant encountered + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + +note: the above error was encountered while instantiating `fn codeview_annotation::` + --> $DIR/codeview-annotation-bad-args.rs:43:5 + | +LL | codeview_annotation::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0080]: evaluation panicked: panic + --> $DIR/codeview-annotation-bad-args.rs:28:24 + | +LL | impl GetName for i32 { +LL | const NAME: &str = panic!("panic"); + | ^^^^^^^^^^^^^^^ evaluation of `::NAME` failed here + +note: erroneous constant encountered + --> $DIR/codeview-annotation-bad-args.rs:34:29 + | +LL | const ARGS: &[&str] = &[T::NAME]; + | ^^^^^^^ + +note: erroneous constant encountered + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: erroneous constant encountered + --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: the above error was encountered while instantiating `fn codeview_annotation::>` + --> $DIR/codeview-annotation-bad-args.rs:38:5 + | +LL | codeview_annotation::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/intrinsics/codeview-annotation.rs b/tests/ui/intrinsics/codeview-annotation.rs new file mode 100644 index 0000000000000..55b27a738f01a --- /dev/null +++ b/tests/ui/intrinsics/codeview-annotation.rs @@ -0,0 +1,177 @@ +// Verifies that calls to the `codeview_annotation` API +// and the intrinsic compile successfully + +//@ build-pass +//@ only-windows + +#![feature(codeview_annotation)] +#![feature(core_intrinsics)] + +// === Helper macros === +macro_rules! call_intrinsic { + ($args:expr) => {{ + call_codeview_annotation!(std::intrinsics, $args); + }}; +} + +macro_rules! call_api { + ($args:expr) => {{ + call_codeview_annotation!(std::os::windows, $args); + }}; +} + +macro_rules! call_codeview_annotation { + ($($module:ident)::+, $args:expr) => {{ + struct Args; + + impl $($module)::+::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = $args; + } + + $($module)::+::codeview_annotation::(); + }}; +} + +// === API tests === +fn single() { + call_api!(&["string1"]); +} + +fn multiple() { + call_api!(&["string1", "string2", "string3"]); +} + +const STR_A: &str = "string1"; +const STR_B: &str = "string2"; +const STR_C: &str = "string3"; + +fn named_consts() { + call_api!(&[STR_A, STR_B, STR_C]); +} + +fn mixed_named_consts_and_literals() { + call_api!(&[STR_A, "string2", "string3"]); +} + +const STRS_SLICE: &[&str] = &["string1", "string2", "string3"]; + +fn named_const_slice() { + call_api!(STRS_SLICE); +} + +const STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; + +fn named_const_array_ref() { + call_api!(&STRS_ARRAY); +} + +static STATIC_STRS_ARRAY: [&str; 3] = ["string1", "string2", "string3"]; + +fn static_array_ref() { + call_api!(&STATIC_STRS_ARRAY); +} + +// Data associated with the types of +// some variables passed to codeview_annotation +// e.g. type names +fn consts_associated_with_vars() { + // A trait that lets you assign names to types + trait TypeName { + const NAME: &str; + } + + struct A; + impl TypeName for A { + const NAME: &str = "A"; + } + + struct B; + impl TypeName for B { + const NAME: &str = "B"; + } + + // This function's purpose is to monomorphize and infer + // the types of its arguments and pass their associated + // consts to codeview_annotation + fn emit_annotation(_var1: &T1, _var2: &T2) { + struct Args(std::marker::PhantomData<(T1, T2)>); + + impl std::os::windows::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = &["string", T1::NAME, T2::NAME]; + } + + std::os::windows::codeview_annotation::>(); + } + + // Strings "A" and "B" get passed to + // codeview_annotation for a and b respectively + let a = A; + let b = B; + emit_annotation(&a, &b); +} + +// An associated const slice passed as args +// to codeview_annotation +trait HasStrs { + const STRS: &[&str]; +} + +impl HasStrs for i32 { + const STRS: &[&str] = &["string1", "string2", "string3"]; +} + +fn generic_associated_const_slice() { + struct Args(std::marker::PhantomData); + + impl std::os::windows::CodeViewAnnotationArgs for Args { + const ARGS: &[&str] = T::STRS; + } + + std::os::windows::codeview_annotation::>(); +} + +fn empty_strings() { + call_api!(&["", "", "string1"]); +} + +fn empty_slice() { + call_api!(&[]); +} + +const EMPTY_STRS_SLICE: &[&str] = &[]; + +pub fn named_empty_slice() { + call_api!(EMPTY_STRS_SLICE); +} + +// === Intrinsic tests === +fn intrinsic_single() { + call_intrinsic!(&["string1"]); +} + +fn intrinsic_multiple() { + call_intrinsic!(&["string1", "string2", "string3"]); +} + +fn intrinsic_mixed_named_consts_and_literals() { + call_intrinsic!(&[STR_A, "string2", "string3"]); +} + +fn main() { + single(); + multiple(); + named_consts(); + mixed_named_consts_and_literals(); + named_const_slice(); + named_const_array_ref(); + static_array_ref(); + consts_associated_with_vars(); + generic_associated_const_slice::(); + empty_strings(); + empty_slice(); + named_empty_slice(); + + intrinsic_single(); + intrinsic_multiple(); + intrinsic_mixed_named_consts_and_literals(); +}