Skip to content
Draft
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
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ fn codegen_regular_intrinsic_call<'tcx>(

fx.bcx.ins().debugtrap();
}
sym::codeview_annotation => {
intrinsic_args!(fx, args => (); intrinsic);
}
sym::copy => {
intrinsic_args!(fx, args => (src, dst, count); intrinsic);
let src = src.load_scalar(fx);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
sym::breakpoint => {
unimplemented!();
}
sym::codeview_annotation => {
return IntrinsicResult::Operand(OperandValue::ZeroSized);
}
sym::va_arg => {
unimplemented!();
}
Expand Down
150 changes: 148 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_hir as hir;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::find_attr;
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, TypingEnv};
Expand Down Expand Up @@ -46,7 +47,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 @@ -894,6 +895,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 @@ -3417,3 +3425,141 @@ 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())
.filter_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))
})
.next()
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::Slice { alloc_id, meta } => Some((alloc_id, Size::ZERO, meta)),
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, &[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 @@ -126,6 +126,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 @@ -306,6 +307,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 @@ -635,6 +635,7 @@ symbols! {
cmpxchg16b_target_feature,
cmse_nonsecure_entry,
code,
codeview_annotation,
coerce_pointee_validated,
coerce_shared,
coerce_shared_target,
Expand Down
38 changes: 38 additions & 0 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1028,3 +1028,41 @@ pub const fn prefetch_read_instruction<T>(ptr: *const T, locality: Locality) {
Locality::L1 => intrinsics::prefetch_read_instruction::<T, { Locality::L1.to_llvm() }>(ptr),
}
}

/// A container for the string arguments passed to [`codeview_annotation`].
#[unstable(feature = "codeview_annotation", issue = "none")]
pub trait CodeViewAnnotationArgs {
/// The string arguments to `codeview_annotation`.
const ARGS: &[&str];
}

/// 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 works only on targets that use PDB debug information (such as `msvc`) and the LLVM
/// backend. It is a no-op on other targets 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`.
///
/// ```
/// #![feature(codeview_annotation)]
/// use std::hint::{codeview_annotation, CodeViewAnnotationArgs};
///
/// struct Args;
///
/// impl CodeViewAnnotationArgs for Args {
/// const ARGS: &[&str] = &["Hello", "World"];
/// }
///
/// codeview_annotation::<Args>();
/// ```
#[inline(always)]
#[unstable(feature = "codeview_annotation", issue = "none")]
pub fn codeview_annotation<T: CodeViewAnnotationArgs>() {
crate::intrinsics::codeview_annotation::<T>();
}
12 changes: 12 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,18 @@ pub const fn prefetch_write_instruction<T, const LOCALITY: i32>(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 [`crate::hint::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 = "none")]
#[rustc_intrinsic]
#[rustc_nounwind]
#[miri::intrinsic_fallback_is_spec]
pub fn codeview_annotation<T: crate::hint::CodeViewAnnotationArgs>() {}

/// Magic intrinsic that derives its meaning from attributes
/// attached to the function.
///
Expand Down
16 changes: 16 additions & 0 deletions src/tools/miri/tests/pass/codeview-annotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Verifies that `codeview_annotation` is a no-op under Miri
// and that Miri does not try to const eval `CodeViewAnnotationArgs::ARGS`

#![feature(codeview_annotation)]

use std::hint::{CodeViewAnnotationArgs, codeview_annotation};

struct Args;

impl CodeViewAnnotationArgs for Args {
const ARGS: &[&str] = panic!("Panic triggered if Miri tries to evaluate annotation args");
}

fn main() {
codeview_annotation::<Args>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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)]
use std::hint::{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::<Args>();
}
Loading
Loading