Skip to content
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
148 changes: 146 additions & 2 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<T: CodeViewAnnotationArgs>() {}
//
// 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<Option<(AllocId, Size, u64)>, ()> {
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)
}
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ symbols! {
cmpxchg16b_target_feature,
cmse_nonsecure_entry,
code,
codeview_annotation,
coerce_pointee_validated,
coerce_shared,
coerce_shared_target,
Expand Down
24 changes: 24 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,30 @@ pub const fn prefetch_write_instruction<T, const LOCALITY: i32>(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<T: CodeViewAnnotationArgs>() {
// 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.
///
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/os/darwin/objc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions library/core/src/os/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! OS-specific functionality.

#![unstable(feature = "darwin_objc", issue = "145496")]
#![unstable(feature = "core_os", issue = "none")]
#![allow(missing_docs)]

#[cfg(all(
Expand 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;
39 changes: 39 additions & 0 deletions library/core/src/os/windows.rs
Original file line number Diff line number Diff line change
@@ -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)")]
Comment on lines +22 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, we have

```ignore-windows

but sadly not currently

```only-windows

it would be nice if rustdoc could do that

/// #![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::<Args>();
/// ```
#[inline(always)]
#[unstable(feature = "codeview_annotation", issue = "163964")]
pub fn codeview_annotation<T: CodeViewAnnotationArgs>() {
crate::intrinsics::codeview_annotation::<T>();
}
4 changes: 4 additions & 0 deletions library/std/src/os/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 17 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/codeview-annotation.rs

@RalfJung RalfJung Oct 10, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this into the intrinsics folder.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved

Original file line number Diff line number Diff line change
@@ -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::<Args>();
}
24 changes: 24 additions & 0 deletions src/tools/miri/tests/fail/intrinsics/codeview-annotation.stderr
Original file line number Diff line number Diff line change
@@ -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 as std::intrinsics::CodeViewAnnotationArgs>::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`.
16 changes: 16 additions & 0 deletions src/tools/miri/tests/pass/intrinsics/codeview-annotation.rs
Original file line number Diff line number Diff line change
@@ -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::<Args>();
}
Loading
Loading