use crate::clang::cpp_codegen::*;
use crate::clang::*;
use crate::x86::*;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86CxxPlatform {
LinuxGnu,
LinuxMusl,
Darwin,
FreeBSD,
WindowsMSVC,
WindowsMinGW,
Cygwin,
Android,
Fuchsia,
GenericElf,
BareMetal,
}
impl X86CxxPlatform {
pub fn from_triple(triple: &str) -> Self {
let t = triple.to_lowercase();
if t.contains("windows") || t.contains("msvc") || t.contains("mingw32") {
if t.contains("mingw") {
X86CxxPlatform::WindowsMinGW
} else if t.contains("cygwin") {
X86CxxPlatform::Cygwin
} else {
X86CxxPlatform::WindowsMSVC
}
} else if t.contains("apple") || t.contains("darwin") || t.contains("macos") {
X86CxxPlatform::Darwin
} else if t.contains("freebsd") {
X86CxxPlatform::FreeBSD
} else if t.contains("android") {
X86CxxPlatform::Android
} else if t.contains("fuchsia") {
X86CxxPlatform::Fuchsia
} else if t.contains("musl") {
X86CxxPlatform::LinuxMusl
} else if t.contains("linux") || t.contains("gnu") {
X86CxxPlatform::LinuxGnu
} else if t.contains("none") || t.contains("bare") || t.contains("elf") {
X86CxxPlatform::BareMetal
} else {
X86CxxPlatform::GenericElf
}
}
pub fn uses_itanium_abi(&self) -> bool {
!matches!(self, X86CxxPlatform::WindowsMSVC)
}
pub fn uses_msvc_abi(&self) -> bool {
matches!(self, X86CxxPlatform::WindowsMSVC)
}
pub fn default_stdlib(&self) -> &'static str {
match self {
X86CxxPlatform::LinuxGnu => "libstdc++",
X86CxxPlatform::LinuxMusl | X86CxxPlatform::FreeBSD => "libc++",
X86CxxPlatform::Darwin => "libc++",
X86CxxPlatform::WindowsMSVC => "msvcp140",
X86CxxPlatform::WindowsMinGW => "libstdc++",
X86CxxPlatform::Cygwin => "libstdc++",
X86CxxPlatform::Android => "libc++",
X86CxxPlatform::Fuchsia => "libc++",
X86CxxPlatform::GenericElf => "libc++",
X86CxxPlatform::BareMetal => "libc++",
}
}
pub fn sso_capacity(&self) -> usize {
match self {
X86CxxPlatform::Darwin
| X86CxxPlatform::FreeBSD
| X86CxxPlatform::Android
| X86CxxPlatform::Fuchsia => 22,
_ => 15,
}
}
pub fn string_size_64(&self) -> usize {
match self {
X86CxxPlatform::Darwin
| X86CxxPlatform::FreeBSD
| X86CxxPlatform::Android
| X86CxxPlatform::Fuchsia => 24, _ => 32, }
}
}
impl fmt::Display for X86CxxPlatform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CxxPlatform::LinuxGnu => write!(f, "linux-gnu"),
X86CxxPlatform::LinuxMusl => write!(f, "linux-musl"),
X86CxxPlatform::Darwin => write!(f, "darwin"),
X86CxxPlatform::FreeBSD => write!(f, "freebsd"),
X86CxxPlatform::WindowsMSVC => write!(f, "windows-msvc"),
X86CxxPlatform::WindowsMinGW => write!(f, "windows-mingw"),
X86CxxPlatform::Cygwin => write!(f, "cygwin"),
X86CxxPlatform::Android => write!(f, "android"),
X86CxxPlatform::Fuchsia => write!(f, "fuchsia"),
X86CxxPlatform::GenericElf => write!(f, "generic-elf"),
X86CxxPlatform::BareMetal => write!(f, "bare-metal"),
}
}
}
pub struct X86LibCxx {
pub platform: X86CxxPlatform,
pub is_64bit: bool,
pub triple: String,
pub header_mapping: X86CxxHeaderMapping,
pub type_lowering: X86CxxTypeLowering,
pub abi_lowering: X86CxxABILowering,
pub exception_handling: X86CxxExceptionHandling,
pub rtti: X86CxxRTTI,
pub templates: X86CxxTemplates,
pub cpp_standard: CppStandard,
pub stdlib_kind: CXXStdLib,
}
impl X86LibCxx {
pub fn new(
triple: &str,
is_64bit: bool,
cpp_standard: CppStandard,
stdlib_kind: CXXStdLib,
) -> Self {
let platform = X86CxxPlatform::from_triple(triple);
let header_mapping = X86CxxHeaderMapping::new(platform, stdlib_kind);
let type_lowering = X86CxxTypeLowering::new(platform, is_64bit);
let abi_lowering = X86CxxABILowering::new(platform, is_64bit, triple);
let exception_handling = X86CxxExceptionHandling::new(platform, is_64bit, triple);
let rtti = X86CxxRTTI::new(platform, is_64bit, triple);
let templates = X86CxxTemplates::new(platform, is_64bit);
X86LibCxx {
platform,
is_64bit,
triple: triple.to_string(),
header_mapping,
type_lowering,
abi_lowering,
exception_handling,
rtti,
templates,
cpp_standard,
stdlib_kind,
}
}
pub fn initialize(&mut self) {
self.header_mapping.build_index();
self.type_lowering.initialize();
self.abi_lowering.initialize();
self.exception_handling.initialize();
self.rtti.initialize();
self.templates.initialize();
}
pub fn resolve_header(&self, header: &str) -> Option<&str> {
self.header_mapping.resolve(header)
}
pub fn get_stdlib_type(&self, type_name: &str) -> Option<X86LibCxxType> {
self.type_lowering.get_type(type_name)
}
pub fn lower_abi(&self, feature: &X86CxxABIFeature) -> X86CxxABIResult {
self.abi_lowering.lower(feature)
}
pub fn get_personality_fn(&self) -> &str {
self.exception_handling.personality_function()
}
pub fn has_rtti(&self, type_name: &str) -> bool {
self.rtti.has_type_info(type_name)
}
pub fn instantiate_template(
&mut self,
template_name: &str,
args: &[X86CxxTemplateArg],
) -> Result<String, String> {
self.templates.instantiate(template_name, args)
}
}
pub struct X86CxxHeaderMapping {
platform: X86CxxPlatform,
stdlib_kind: CXXStdLib,
headers: HashMap<String, CxxHeaderInfo>,
symbol_index: HashMap<String, String>,
include_prefix: String,
}
#[derive(Debug, Clone)]
pub struct CxxHeaderInfo {
pub name: String,
pub path: String,
pub depends_on: Vec<String>,
pub depended_by: Vec<String>,
pub provides: Vec<String>,
pub is_core: bool,
pub is_standard: bool,
pub introduced_in: Option<CppStandard>,
pub iwyu_mappings: Vec<(String, String)>,
}
#[derive(Debug, Clone)]
pub struct IwyuMapping {
pub symbol: String,
pub header: String,
pub priority: u32,
}
impl X86CxxHeaderMapping {
pub fn new(platform: X86CxxPlatform, stdlib_kind: CXXStdLib) -> Self {
let include_prefix = match platform {
X86CxxPlatform::Darwin => "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
X86CxxPlatform::WindowsMSVC => "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/include",
_ => "/usr/include/c++/v1",
}.to_string();
X86CxxHeaderMapping {
platform,
stdlib_kind,
headers: HashMap::new(),
symbol_index: HashMap::new(),
include_prefix,
}
}
pub fn build_index(&mut self) {
self.headers.clear();
self.symbol_index.clear();
self.add_header(CxxHeaderInfo {
name: "cstddef".into(),
path: "cstddef".into(),
depends_on: vec![],
depended_by: vec!["cstdlib".into(), "new".into()],
provides: vec![
"std::size_t".into(),
"std::ptrdiff_t".into(),
"std::nullptr_t".into(),
"std::max_align_t".into(),
"std::byte".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "cstdlib".into(),
path: "cstdlib".into(),
depends_on: vec!["cstddef".into()],
depended_by: vec!["algorithm".into()],
provides: vec![
"std::malloc".into(),
"std::free".into(),
"std::abort".into(),
"std::exit".into(),
"std::atexit".into(),
"std::at_quick_exit".into(),
"std::system".into(),
"std::getenv".into(),
"std::div".into(),
"std::ldiv".into(),
"std::lldiv".into(),
"std::abs".into(),
"std::labs".into(),
"std::llabs".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "cstdint".into(),
path: "cstdint".into(),
depends_on: vec![],
depended_by: vec!["cinttypes".into()],
provides: vec![
"std::int8_t".into(),
"std::int16_t".into(),
"std::int32_t".into(),
"std::int64_t".into(),
"std::uint8_t".into(),
"std::uint16_t".into(),
"std::uint32_t".into(),
"std::uint64_t".into(),
"std::int_fast8_t".into(),
"std::int_fast16_t".into(),
"std::int_fast32_t".into(),
"std::int_fast64_t".into(),
"std::intmax_t".into(),
"std::uintmax_t".into(),
"std::intptr_t".into(),
"std::uintptr_t".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "utility".into(),
path: "utility".into(),
depends_on: vec!["type_traits".into(), "cstddef".into()],
depended_by: vec![
"tuple".into(),
"optional".into(),
"variant".into(),
"pair".into(),
"memory".into(),
],
provides: vec![
"std::move".into(),
"std::forward".into(),
"std::swap".into(),
"std::exchange".into(),
"std::declval".into(),
"std::index_sequence".into(),
"std::integer_sequence".into(),
"std::in_place_t".into(),
"std::pair".into(),
"std::make_pair".into(),
"std::piecewise_construct_t".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::pair".into(), "utility".into()),
("std::swap".into(), "utility".into()),
("std::move".into(), "utility".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "type_traits".into(),
path: "type_traits".into(),
depends_on: vec!["cstddef".into()],
depended_by: vec![
"utility".into(),
"memory".into(),
"tuple".into(),
"optional".into(),
"variant".into(),
"any".into(),
],
provides: vec![
"std::is_same".into(),
"std::is_void".into(),
"std::is_integral".into(),
"std::is_floating_point".into(),
"std::is_pointer".into(),
"std::is_array".into(),
"std::is_reference".into(),
"std::is_const".into(),
"std::is_volatile".into(),
"std::is_trivial".into(),
"std::is_standard_layout".into(),
"std::is_pod".into(),
"std::is_trivially_copyable".into(),
"std::is_empty".into(),
"std::is_polymorphic".into(),
"std::is_abstract".into(),
"std::is_final".into(),
"std::is_signed".into(),
"std::is_unsigned".into(),
"std::is_constructible".into(),
"std::is_default_constructible".into(),
"std::is_copy_constructible".into(),
"std::is_move_constructible".into(),
"std::is_assignable".into(),
"std::is_destructible".into(),
"std::is_swappable".into(),
"std::is_nothrow_constructible".into(),
"std::is_base_of".into(),
"std::is_convertible".into(),
"std::remove_cv".into(),
"std::remove_reference".into(),
"std::remove_pointer".into(),
"std::add_pointer".into(),
"std::add_lvalue_reference".into(),
"std::add_rvalue_reference".into(),
"std::decay".into(),
"std::enable_if".into(),
"std::conditional".into(),
"std::common_type".into(),
"std::underlying_type".into(),
"std::result_of".into(),
"std::invoke_result".into(),
"std::void_t".into(),
"std::bool_constant".into(),
"std::integral_constant".into(),
"std::true_type".into(),
"std::false_type".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "vector".into(),
path: "vector".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"algorithm".into(),
"stdexcept".into(),
"iterator".into(),
],
depended_by: vec!["stack".into(), "queue".into(), "priority_queue".into()],
provides: vec![
"std::vector".into(),
"std::vector<bool>".into(),
"std::pmr::vector".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::vector".into(), "vector".into()),
("std::vector<bool>".into(), "vector".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "array".into(),
path: "array".into(),
depends_on: vec!["cstddef".into(), "type_traits".into(), "iterator".into()],
depended_by: vec![],
provides: vec!["std::array".into(), "std::to_array".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![("std::array".into(), "array".into())],
});
self.add_header(CxxHeaderInfo {
name: "deque".into(),
path: "deque".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"algorithm".into(),
"iterator".into(),
],
depended_by: vec!["stack".into(), "queue".into()],
provides: vec!["std::deque".into(), "std::pmr::deque".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::deque".into(), "deque".into())],
});
self.add_header(CxxHeaderInfo {
name: "list".into(),
path: "list".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"algorithm".into(),
"iterator".into(),
],
depended_by: vec![],
provides: vec!["std::list".into(), "std::pmr::list".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::list".into(), "list".into())],
});
self.add_header(CxxHeaderInfo {
name: "forward_list".into(),
path: "forward_list".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"iterator".into(),
],
depended_by: vec![],
provides: vec!["std::forward_list".into(), "std::pmr::forward_list".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![("std::forward_list".into(), "forward_list".into())],
});
self.add_header(CxxHeaderInfo {
name: "map".into(),
path: "map".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"utility".into(),
"iterator".into(),
"functional".into(),
],
depended_by: vec![],
provides: vec![
"std::map".into(),
"std::multimap".into(),
"std::pmr::map".into(),
"std::pmr::multimap".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::map".into(), "map".into()),
("std::multimap".into(), "map".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "set".into(),
path: "set".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"utility".into(),
"iterator".into(),
"functional".into(),
],
depended_by: vec![],
provides: vec![
"std::set".into(),
"std::multiset".into(),
"std::pmr::set".into(),
"std::pmr::multiset".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::set".into(), "set".into()),
("std::multiset".into(), "set".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "unordered_map".into(),
path: "unordered_map".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"utility".into(),
"iterator".into(),
"functional".into(),
],
depended_by: vec![],
provides: vec![
"std::unordered_map".into(),
"std::unordered_multimap".into(),
"std::pmr::unordered_map".into(),
"std::pmr::unordered_multimap".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::unordered_map".into(), "unordered_map".into()),
("std::unordered_multimap".into(), "unordered_map".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "unordered_set".into(),
path: "unordered_set".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"utility".into(),
"iterator".into(),
"functional".into(),
],
depended_by: vec![],
provides: vec![
"std::unordered_set".into(),
"std::unordered_multiset".into(),
"std::pmr::unordered_set".into(),
"std::pmr::unordered_multiset".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::unordered_set".into(), "unordered_set".into()),
("std::unordered_multiset".into(), "unordered_set".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "stack".into(),
path: "stack".into(),
depends_on: vec![
"deque".into(),
"vector".into(),
"type_traits".into(),
"utility".into(),
],
depended_by: vec![],
provides: vec!["std::stack".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::stack".into(), "stack".into())],
});
self.add_header(CxxHeaderInfo {
name: "queue".into(),
path: "queue".into(),
depends_on: vec![
"deque".into(),
"vector".into(),
"type_traits".into(),
"utility".into(),
],
depended_by: vec!["priority_queue".into()],
provides: vec!["std::queue".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::queue".into(), "queue".into())],
});
self.add_header(CxxHeaderInfo {
name: "priority_queue".into(),
path: "queue".into(), depends_on: vec!["vector".into(), "functional".into(), "algorithm".into()],
depended_by: vec![],
provides: vec!["std::priority_queue".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::priority_queue".into(), "queue".into())],
});
self.add_header(CxxHeaderInfo {
name: "string".into(),
path: "string".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"memory".into(),
"algorithm".into(),
"stdexcept".into(),
"iterator".into(),
"iosfwd".into(),
"cstring".into(),
"cstdlib".into(),
"cwchar".into(),
],
depended_by: vec!["string_view".into(), "regex".into(), "filesystem".into()],
provides: vec![
"std::string".into(),
"std::wstring".into(),
"std::u8string".into(),
"std::u16string".into(),
"std::u32string".into(),
"std::pmr::string".into(),
"std::char_traits".into(),
"std::basic_string".into(),
"std::stoi".into(),
"std::stol".into(),
"std::stoll".into(),
"std::stoul".into(),
"std::stoull".into(),
"std::stof".into(),
"std::stod".into(),
"std::stold".into(),
"std::to_string".into(),
"std::to_wstring".into(),
"std::hash<std::string>".into(),
"std::operator\"\"s".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::string".into(), "string".into()),
("std::wstring".into(), "string".into()),
("std::basic_string".into(), "string".into()),
("std::to_string".into(), "string".into()),
("std::stoi".into(), "string".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "string_view".into(),
path: "string_view".into(),
depends_on: vec![
"cstddef".into(),
"type_traits".into(),
"iterator".into(),
"stdexcept".into(),
"charconv".into(),
],
depended_by: vec!["format".into()],
provides: vec![
"std::string_view".into(),
"std::wstring_view".into(),
"std::u8string_view".into(),
"std::u16string_view".into(),
"std::u32string_view".into(),
"std::basic_string_view".into(),
"std::hash<std::string_view>".into(),
"std::operator\"\"sv".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![
("std::string_view".into(), "string_view".into()),
("std::basic_string_view".into(), "string_view".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "memory".into(),
path: "memory".into(),
depends_on: vec![
"type_traits".into(),
"utility".into(),
"cstddef".into(),
"new".into(),
"functional".into(),
],
depended_by: vec![
"vector".into(),
"list".into(),
"deque".into(),
"map".into(),
"set".into(),
"unordered_map".into(),
"unordered_set".into(),
"optional".into(),
"variant".into(),
"any".into(),
"shared_mutex".into(),
],
provides: vec![
"std::unique_ptr".into(),
"std::shared_ptr".into(),
"std::weak_ptr".into(),
"std::make_unique".into(),
"std::make_shared".into(),
"std::allocate_shared".into(),
"std::enable_shared_from_this".into(),
"std::owner_less".into(),
"std::allocator".into(),
"std::allocator_traits".into(),
"std::pmr::polymorphic_allocator".into(),
"std::pmr::memory_resource".into(),
"std::pmr::new_delete_resource".into(),
"std::pmr::monotonic_buffer_resource".into(),
"std::pmr::synchronized_pool_resource".into(),
"std::pmr::unsynchronized_pool_resource".into(),
"std::uninitialized_copy".into(),
"std::uninitialized_fill".into(),
"std::uninitialized_move".into(),
"std::destroy".into(),
"std::destroy_n".into(),
"std::construct_at".into(),
"std::destroy_at".into(),
"std::addressof".into(),
"std::align".into(),
"std::assume_aligned".into(),
"std::to_address".into(),
"std::pointer_traits".into(),
"std::allocator_arg_t".into(),
"std::uses_allocator".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::unique_ptr".into(), "memory".into()),
("std::shared_ptr".into(), "memory".into()),
("std::weak_ptr".into(), "memory".into()),
("std::make_unique".into(), "memory".into()),
("std::make_shared".into(), "memory".into()),
("std::allocator".into(), "memory".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "memory_resource".into(),
path: "memory_resource".into(),
depends_on: vec!["cstddef".into(), "new".into()],
depended_by: vec![],
provides: vec![
"std::pmr::memory_resource".into(),
"std::pmr::polymorphic_allocator".into(),
"std::pmr::new_delete_resource".into(),
"std::pmr::null_memory_resource".into(),
"std::pmr::monotonic_buffer_resource".into(),
"std::pmr::synchronized_pool_resource".into(),
"std::pmr::unsynchronized_pool_resource".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![("std::pmr::memory_resource".into(), "memory_resource".into())],
});
self.add_header(CxxHeaderInfo {
name: "scoped_allocator".into(),
path: "scoped_allocator".into(),
depends_on: vec!["memory".into()],
depended_by: vec![],
provides: vec!["std::scoped_allocator_adaptor".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "iterator".into(),
path: "iterator".into(),
depends_on: vec![
"cstddef".into(),
"type_traits".into(),
"utility".into(),
"concepts".into(),
],
depended_by: vec![
"algorithm".into(),
"vector".into(),
"list".into(),
"deque".into(),
"map".into(),
"set".into(),
"unordered_map".into(),
"unordered_set".into(),
],
provides: vec![
"std::iterator_traits".into(),
"std::iterator".into(),
"std::input_iterator_tag".into(),
"std::output_iterator_tag".into(),
"std::forward_iterator_tag".into(),
"std::bidirectional_iterator_tag".into(),
"std::random_access_iterator_tag".into(),
"std::contiguous_iterator_tag".into(),
"std::advance".into(),
"std::distance".into(),
"std::next".into(),
"std::prev".into(),
"std::reverse_iterator".into(),
"std::make_reverse_iterator".into(),
"std::move_iterator".into(),
"std::make_move_iterator".into(),
"std::back_insert_iterator".into(),
"std::back_inserter".into(),
"std::front_insert_iterator".into(),
"std::front_inserter".into(),
"std::insert_iterator".into(),
"std::inserter".into(),
"std::begin".into(),
"std::end".into(),
"std::cbegin".into(),
"std::cend".into(),
"std::rbegin".into(),
"std::rend".into(),
"std::crbegin".into(),
"std::crend".into(),
"std::size".into(),
"std::empty".into(),
"std::data".into(),
"std::istream_iterator".into(),
"std::ostream_iterator".into(),
"std::istreambuf_iterator".into(),
"std::ostreambuf_iterator".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::iterator_traits".into(), "iterator".into()),
("std::input_iterator_tag".into(), "iterator".into()),
("std::reverse_iterator".into(), "iterator".into()),
("std::begin".into(), "iterator".into()),
("std::end".into(), "iterator".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "algorithm".into(),
path: "algorithm".into(),
depends_on: vec![
"initializer_list".into(),
"type_traits".into(),
"utility".into(),
"iterator".into(),
"functional".into(),
"cstring".into(),
],
depended_by: vec!["vector".into(), "list".into(), "deque".into()],
provides: vec![
"std::for_each".into(),
"std::for_each_n".into(),
"std::count".into(),
"std::count_if".into(),
"std::mismatch".into(),
"std::find".into(),
"std::find_if".into(),
"std::find_if_not".into(),
"std::find_end".into(),
"std::find_first_of".into(),
"std::adjacent_find".into(),
"std::search".into(),
"std::search_n".into(),
"std::copy".into(),
"std::copy_if".into(),
"std::copy_n".into(),
"std::copy_backward".into(),
"std::move".into(),
"std::move_backward".into(),
"std::fill".into(),
"std::fill_n".into(),
"std::transform".into(),
"std::generate".into(),
"std::generate_n".into(),
"std::remove".into(),
"std::remove_if".into(),
"std::remove_copy".into(),
"std::remove_copy_if".into(),
"std::replace".into(),
"std::replace_if".into(),
"std::replace_copy".into(),
"std::replace_copy_if".into(),
"std::swap".into(),
"std::swap_ranges".into(),
"std::iter_swap".into(),
"std::reverse".into(),
"std::reverse_copy".into(),
"std::rotate".into(),
"std::rotate_copy".into(),
"std::shuffle".into(),
"std::sample".into(),
"std::unique".into(),
"std::unique_copy".into(),
"std::is_partitioned".into(),
"std::partition".into(),
"std::partition_copy".into(),
"std::stable_partition".into(),
"std::partition_point".into(),
"std::sort".into(),
"std::stable_sort".into(),
"std::partial_sort".into(),
"std::partial_sort_copy".into(),
"std::nth_element".into(),
"std::lower_bound".into(),
"std::upper_bound".into(),
"std::binary_search".into(),
"std::equal_range".into(),
"std::merge".into(),
"std::inplace_merge".into(),
"std::includes".into(),
"std::set_union".into(),
"std::set_intersection".into(),
"std::set_difference".into(),
"std::set_symmetric_difference".into(),
"std::push_heap".into(),
"std::pop_heap".into(),
"std::make_heap".into(),
"std::sort_heap".into(),
"std::is_heap".into(),
"std::is_heap_until".into(),
"std::min".into(),
"std::max".into(),
"std::minmax".into(),
"std::min_element".into(),
"std::max_element".into(),
"std::minmax_element".into(),
"std::clamp".into(),
"std::lexicographical_compare".into(),
"std::lexicographical_compare_three_way".into(),
"std::is_permutation".into(),
"std::next_permutation".into(),
"std::prev_permutation".into(),
"std::accumulate".into(),
"std::inner_product".into(),
"std::adjacent_difference".into(),
"std::partial_sum".into(),
"std::iota".into(),
"std::reduce".into(),
"std::exclusive_scan".into(),
"std::inclusive_scan".into(),
"std::transform_reduce".into(),
"std::transform_exclusive_scan".into(),
"std::transform_inclusive_scan".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::sort".into(), "algorithm".into()),
("std::find".into(), "algorithm".into()),
("std::copy".into(), "algorithm".into()),
("std::for_each".into(), "algorithm".into()),
("std::binary_search".into(), "algorithm".into()),
("std::swap".into(), "utility".into()), ],
});
self.add_header(CxxHeaderInfo {
name: "numeric".into(),
path: "numeric".into(),
depends_on: vec!["type_traits".into(), "iterator".into(), "utility".into()],
depended_by: vec![],
provides: vec![
"std::accumulate".into(),
"std::inner_product".into(),
"std::adjacent_difference".into(),
"std::partial_sum".into(),
"std::iota".into(),
"std::gcd".into(),
"std::lcm".into(),
"std::midpoint".into(),
"std::reduce".into(),
"std::exclusive_scan".into(),
"std::inclusive_scan".into(),
"std::transform_reduce".into(),
"std::transform_exclusive_scan".into(),
"std::transform_inclusive_scan".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::gcd".into(), "numeric".into()),
("std::lcm".into(), "numeric".into()),
("std::iota".into(), "numeric".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "functional".into(),
path: "functional".into(),
depends_on: vec!["type_traits".into(), "utility".into(), "memory".into()],
depended_by: vec![
"algorithm".into(),
"map".into(),
"set".into(),
"unordered_map".into(),
"unordered_set".into(),
"thread".into(),
"future".into(),
],
provides: vec![
"std::function".into(),
"std::move_only_function".into(),
"std::copyable_function".into(),
"std::bind".into(),
"std::placeholders".into(),
"std::mem_fn".into(),
"std::not_fn".into(),
"std::invoke".into(),
"std::reference_wrapper".into(),
"std::ref".into(),
"std::cref".into(),
"std::plus".into(),
"std::minus".into(),
"std::multiplies".into(),
"std::divides".into(),
"std::modulus".into(),
"std::negate".into(),
"std::equal_to".into(),
"std::not_equal_to".into(),
"std::greater".into(),
"std::less".into(),
"std::greater_equal".into(),
"std::less_equal".into(),
"std::logical_and".into(),
"std::logical_or".into(),
"std::logical_not".into(),
"std::bit_and".into(),
"std::bit_or".into(),
"std::bit_xor".into(),
"std::hash".into(),
"std::default_delete".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::function".into(), "functional".into()),
("std::bind".into(), "functional".into()),
("std::hash".into(), "functional".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "tuple".into(),
path: "tuple".into(),
depends_on: vec!["type_traits".into(), "utility".into(), "cstddef".into()],
depended_by: vec![],
provides: vec![
"std::tuple".into(),
"std::make_tuple".into(),
"std::tie".into(),
"std::forward_as_tuple".into(),
"std::tuple_cat".into(),
"std::get".into(),
"std::tuple_size".into(),
"std::tuple_element".into(),
"std::apply".into(),
"std::make_from_tuple".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::tuple".into(), "tuple".into()),
("std::make_tuple".into(), "tuple".into()),
("std::tie".into(), "tuple".into()),
("std::get".into(), "tuple".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "optional".into(),
path: "optional".into(),
depends_on: vec![
"type_traits".into(),
"utility".into(),
"memory".into(),
"stdexcept".into(),
"new".into(),
],
depended_by: vec![],
provides: vec![
"std::optional".into(),
"std::nullopt_t".into(),
"std::nullopt".into(),
"std::bad_optional_access".into(),
"std::make_optional".into(),
"std::hash<std::optional>".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![
("std::optional".into(), "optional".into()),
("std::nullopt".into(), "optional".into()),
("std::make_optional".into(), "optional".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "variant".into(),
path: "variant".into(),
depends_on: vec![
"type_traits".into(),
"utility".into(),
"memory".into(),
"stdexcept".into(),
"new".into(),
"limits".into(),
],
depended_by: vec![],
provides: vec![
"std::variant".into(),
"std::monostate".into(),
"std::bad_variant_access".into(),
"std::variant_size".into(),
"std::variant_alternative".into(),
"std::visit".into(),
"std::holds_alternative".into(),
"std::get".into(),
"std::get_if".into(),
"std::hash<std::variant>".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![
("std::variant".into(), "variant".into()),
("std::visit".into(), "variant".into()),
("std::holds_alternative".into(), "variant".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "any".into(),
path: "any".into(),
depends_on: vec![
"type_traits".into(),
"utility".into(),
"memory".into(),
"stdexcept".into(),
"new".into(),
"typeinfo".into(),
],
depended_by: vec![],
provides: vec![
"std::any".into(),
"std::any_cast".into(),
"std::make_any".into(),
"std::bad_any_cast".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![
("std::any".into(), "any".into()),
("std::any_cast".into(), "any".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "span".into(),
path: "span".into(),
depends_on: vec![
"cstddef".into(),
"type_traits".into(),
"iterator".into(),
"array".into(),
],
depended_by: vec![],
provides: vec![
"std::span".into(),
"std::as_bytes".into(),
"std::as_writable_bytes".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::span".into(), "span".into())],
});
self.add_header(CxxHeaderInfo {
name: "initializer_list".into(),
path: "initializer_list".into(),
depends_on: vec!["cstddef".into()],
depended_by: vec![
"vector".into(),
"map".into(),
"set".into(),
"deque".into(),
"list".into(),
"unordered_map".into(),
"unordered_set".into(),
],
provides: vec!["std::initializer_list".into()],
is_core: true,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![("std::initializer_list".into(), "initializer_list".into())],
});
self.add_header(CxxHeaderInfo {
name: "exception".into(),
path: "exception".into(),
depends_on: vec![],
depended_by: vec!["stdexcept".into(), "new".into(), "typeinfo".into()],
provides: vec![
"std::exception".into(),
"std::bad_exception".into(),
"std::nested_exception".into(),
"std::throw_with_nested".into(),
"std::rethrow_if_nested".into(),
"std::terminate".into(),
"std::unexpected".into(),
"std::set_terminate".into(),
"std::set_unexpected".into(),
"std::get_terminate".into(),
"std::get_unexpected".into(),
"std::uncaught_exception".into(),
"std::uncaught_exceptions".into(),
"std::exception_ptr".into(),
"std::current_exception".into(),
"std::rethrow_exception".into(),
"std::make_exception_ptr".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::exception".into(), "exception".into()),
("std::terminate".into(), "exception".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "stdexcept".into(),
path: "stdexcept".into(),
depends_on: vec!["exception".into(), "string".into()],
depended_by: vec![
"vector".into(),
"string".into(),
"optional".into(),
"variant".into(),
"any".into(),
],
provides: vec![
"std::logic_error".into(),
"std::domain_error".into(),
"std::invalid_argument".into(),
"std::length_error".into(),
"std::out_of_range".into(),
"std::runtime_error".into(),
"std::range_error".into(),
"std::overflow_error".into(),
"std::underflow_error".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::logic_error".into(), "stdexcept".into()),
("std::runtime_error".into(), "stdexcept".into()),
("std::out_of_range".into(), "stdexcept".into()),
("std::invalid_argument".into(), "stdexcept".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "system_error".into(),
path: "system_error".into(),
depends_on: vec!["stdexcept".into(), "string".into(), "cerrno".into()],
depended_by: vec!["filesystem".into(), "thread".into(), "future".into()],
provides: vec![
"std::error_code".into(),
"std::error_condition".into(),
"std::error_category".into(),
"std::system_error".into(),
"std::errc".into(),
"std::is_error_code_enum".into(),
"std::is_error_condition_enum".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::error_code".into(), "system_error".into()),
("std::system_error".into(), "system_error".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "iosfwd".into(),
path: "iosfwd".into(),
depends_on: vec![],
depended_by: vec![
"string".into(),
"iostream".into(),
"fstream".into(),
"sstream".into(),
],
provides: vec![
"std::ios".into(),
"std::wios".into(),
"std::istream".into(),
"std::wistream".into(),
"std::ostream".into(),
"std::wostream".into(),
"std::iostream".into(),
"std::wiostream".into(),
"std::fstream".into(),
"std::wfstream".into(),
"std::ifstream".into(),
"std::wifstream".into(),
"std::ofstream".into(),
"std::wofstream".into(),
"std::stringstream".into(),
"std::wstringstream".into(),
"std::istringstream".into(),
"std::wistringstream".into(),
"std::ostringstream".into(),
"std::wostringstream".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "iostream".into(),
path: "iostream".into(),
depends_on: vec![
"ios".into(),
"istream".into(),
"ostream".into(),
"streambuf".into(),
],
depended_by: vec![],
provides: vec![
"std::cin".into(),
"std::cout".into(),
"std::cerr".into(),
"std::clog".into(),
"std::wcin".into(),
"std::wcout".into(),
"std::wcerr".into(),
"std::wclog".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::cin".into(), "iostream".into()),
("std::cout".into(), "iostream".into()),
("std::cerr".into(), "iostream".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "ios".into(),
path: "ios".into(),
depends_on: vec!["iosfwd".into(), "exception".into(), "limits".into()],
depended_by: vec!["iostream".into(), "fstream".into(), "sstream".into()],
provides: vec![
"std::ios_base".into(),
"std::basic_ios".into(),
"std::ios_base::failure".into(),
"std::io_errc".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "istream".into(),
path: "istream".into(),
depends_on: vec!["ios".into()],
depended_by: vec!["iostream".into()],
provides: vec![
"std::basic_istream".into(),
"std::basic_iostream".into(),
"std::ws".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "ostream".into(),
path: "ostream".into(),
depends_on: vec!["ios".into()],
depended_by: vec!["iostream".into()],
provides: vec![
"std::basic_ostream".into(),
"std::endl".into(),
"std::ends".into(),
"std::flush".into(),
"std::emit_on_flush".into(),
"std::noemit_on_flush".into(),
"std::flush_emit".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::endl".into(), "ostream".into())],
});
self.add_header(CxxHeaderInfo {
name: "streambuf".into(),
path: "streambuf".into(),
depends_on: vec!["ios".into()],
depended_by: vec!["iostream".into(), "fstream".into(), "sstream".into()],
provides: vec!["std::basic_streambuf".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
self.add_header(CxxHeaderInfo {
name: "fstream".into(),
path: "fstream".into(),
depends_on: vec![
"istream".into(),
"ostream".into(),
"string".into(),
"filesystem".into(),
],
depended_by: vec![],
provides: vec![
"std::basic_fstream".into(),
"std::basic_ifstream".into(),
"std::basic_ofstream".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::ifstream".into(), "fstream".into()),
("std::ofstream".into(), "fstream".into()),
("std::fstream".into(), "fstream".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "sstream".into(),
path: "sstream".into(),
depends_on: vec!["istream".into(), "ostream".into(), "string".into()],
depended_by: vec![],
provides: vec![
"std::basic_stringstream".into(),
"std::basic_istringstream".into(),
"std::basic_ostringstream".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::istringstream".into(), "sstream".into()),
("std::ostringstream".into(), "sstream".into()),
("std::stringstream".into(), "sstream".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "iomanip".into(),
path: "iomanip".into(),
depends_on: vec!["ios".into(), "string".into()],
depended_by: vec![],
provides: vec![
"std::resetiosflags".into(),
"std::setiosflags".into(),
"std::setbase".into(),
"std::setfill".into(),
"std::setprecision".into(),
"std::setw".into(),
"std::get_money".into(),
"std::put_money".into(),
"std::get_time".into(),
"std::put_time".into(),
"std::quoted".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::setw".into(), "iomanip".into())],
});
self.add_header(CxxHeaderInfo {
name: "thread".into(),
path: "thread".into(),
depends_on: vec![
"functional".into(),
"memory".into(),
"string".into(),
"system_error".into(),
"chrono".into(),
"tuple".into(),
],
depended_by: vec![],
provides: vec![
"std::thread".into(),
"std::jthread".into(),
"std::thread::id".into(),
"std::this_thread::get_id".into(),
"std::this_thread::yield".into(),
"std::this_thread::sleep_for".into(),
"std::this_thread::sleep_until".into(),
"std::hash<std::thread::id>".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::thread".into(), "thread".into()),
("std::jthread".into(), "thread".into()),
("std::this_thread::sleep_for".into(), "thread".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "mutex".into(),
path: "mutex".into(),
depends_on: vec!["system_error".into(), "chrono".into(), "limits".into()],
depended_by: vec![
"shared_mutex".into(),
"condition_variable".into(),
"future".into(),
],
provides: vec![
"std::mutex".into(),
"std::timed_mutex".into(),
"std::recursive_mutex".into(),
"std::recursive_timed_mutex".into(),
"std::lock_guard".into(),
"std::scoped_lock".into(),
"std::unique_lock".into(),
"std::lock".into(),
"std::try_lock".into(),
"std::defer_lock_t".into(),
"std::try_to_lock_t".into(),
"std::adopt_lock_t".into(),
"std::defer_lock".into(),
"std::try_to_lock".into(),
"std::adopt_lock".into(),
"std::once_flag".into(),
"std::call_once".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::mutex".into(), "mutex".into()),
("std::lock_guard".into(), "mutex".into()),
("std::unique_lock".into(), "mutex".into()),
("std::scoped_lock".into(), "mutex".into()),
("std::recursive_mutex".into(), "mutex".into()),
("std::call_once".into(), "mutex".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "shared_mutex".into(),
path: "shared_mutex".into(),
depends_on: vec!["mutex".into(), "condition_variable".into()],
depended_by: vec![],
provides: vec![
"std::shared_mutex".into(),
"std::shared_timed_mutex".into(),
"std::shared_lock".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp14),
iwyu_mappings: vec![
("std::shared_mutex".into(), "shared_mutex".into()),
("std::shared_lock".into(), "shared_mutex".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "condition_variable".into(),
path: "condition_variable".into(),
depends_on: vec!["mutex".into(), "chrono".into()],
depended_by: vec!["shared_mutex".into(), "future".into()],
provides: vec![
"std::condition_variable".into(),
"std::condition_variable_any".into(),
"std::cv_status".into(),
"std::notify_all_at_thread_exit".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![(
"std::condition_variable".into(),
"condition_variable".into(),
)],
});
self.add_header(CxxHeaderInfo {
name: "future".into(),
path: "future".into(),
depends_on: vec![
"mutex".into(),
"condition_variable".into(),
"memory".into(),
"system_error".into(),
"chrono".into(),
"functional".into(),
],
depended_by: vec![],
provides: vec![
"std::future".into(),
"std::shared_future".into(),
"std::promise".into(),
"std::packaged_task".into(),
"std::async".into(),
"std::launch".into(),
"std::future_status".into(),
"std::future_error".into(),
"std::future_errc".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::future".into(), "future".into()),
("std::promise".into(), "future".into()),
("std::packaged_task".into(), "future".into()),
("std::async".into(), "future".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "atomic".into(),
path: "atomic".into(),
depends_on: vec!["cstddef".into(), "type_traits".into()],
depended_by: vec!["thread".into(), "future".into()],
provides: vec![
"std::atomic".into(),
"std::atomic_ref".into(),
"std::atomic_flag".into(),
"std::memory_order".into(),
"std::atomic_thread_fence".into(),
"std::atomic_signal_fence".into(),
"std::kill_dependency".into(),
"std::atomic_is_lock_free".into(),
"std::atomic_init".into(),
"std::atomic_flag_test_and_set".into(),
"std::atomic_flag_clear".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::atomic".into(), "atomic".into()),
("std::memory_order".into(), "atomic".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "chrono".into(),
path: "chrono".into(),
depends_on: vec![
"cstddef".into(),
"type_traits".into(),
"ratio".into(),
"limits".into(),
"string".into(),
],
depended_by: vec![
"thread".into(),
"mutex".into(),
"condition_variable".into(),
"future".into(),
],
provides: vec![
"std::chrono::duration".into(),
"std::chrono::time_point".into(),
"std::chrono::system_clock".into(),
"std::chrono::steady_clock".into(),
"std::chrono::high_resolution_clock".into(),
"std::chrono::hours".into(),
"std::chrono::minutes".into(),
"std::chrono::seconds".into(),
"std::chrono::milliseconds".into(),
"std::chrono::microseconds".into(),
"std::chrono::nanoseconds".into(),
"std::chrono::duration_cast".into(),
"std::chrono::time_point_cast".into(),
"std::chrono::floor".into(),
"std::chrono::ceil".into(),
"std::chrono::round".into(),
"std::chrono::abs".into(),
"std::chrono::parse".into(),
"std::chrono::month".into(),
"std::chrono::year".into(),
"std::chrono::weekday".into(),
"std::chrono::day".into(),
"std::chrono::year_month_day".into(),
"std::chrono::zoned_time".into(),
"std::chrono::current_zone".into(),
"std::chrono::locate_zone".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::chrono::duration".into(), "chrono".into()),
("std::chrono::system_clock".into(), "chrono".into()),
("std::chrono::steady_clock".into(), "chrono".into()),
("std::chrono::milliseconds".into(), "chrono".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "ratio".into(),
path: "ratio".into(),
depends_on: vec!["cstdint".into(), "type_traits".into()],
depended_by: vec!["chrono".into()],
provides: vec![
"std::ratio".into(),
"std::ratio_add".into(),
"std::ratio_subtract".into(),
"std::ratio_multiply".into(),
"std::ratio_divide".into(),
"std::ratio_equal".into(),
"std::ratio_not_equal".into(),
"std::ratio_less".into(),
"std::ratio_less_equal".into(),
"std::ratio_greater".into(),
"std::ratio_greater_equal".into(),
"std::atto".into(),
"std::femto".into(),
"std::pico".into(),
"std::nano".into(),
"std::micro".into(),
"std::milli".into(),
"std::centi".into(),
"std::deci".into(),
"std::deca".into(),
"std::hecto".into(),
"std::kilo".into(),
"std::mega".into(),
"std::giga".into(),
"std::tera".into(),
"std::peta".into(),
"std::exa".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![("std::ratio".into(), "ratio".into())],
});
self.add_header(CxxHeaderInfo {
name: "filesystem".into(),
path: "filesystem".into(),
depends_on: vec![
"string".into(),
"system_error".into(),
"chrono".into(),
"memory".into(),
"iterator".into(),
],
depended_by: vec!["fstream".into()],
provides: vec![
"std::filesystem::path".into(),
"std::filesystem::directory_entry".into(),
"std::filesystem::directory_iterator".into(),
"std::filesystem::recursive_directory_iterator".into(),
"std::filesystem::file_status".into(),
"std::filesystem::space_info".into(),
"std::filesystem::file_type".into(),
"std::filesystem::perms".into(),
"std::filesystem::copy_options".into(),
"std::filesystem::directory_options".into(),
"std::filesystem::exists".into(),
"std::filesystem::is_directory".into(),
"std::filesystem::is_regular_file".into(),
"std::filesystem::file_size".into(),
"std::filesystem::create_directory".into(),
"std::filesystem::create_directories".into(),
"std::filesystem::remove".into(),
"std::filesystem::remove_all".into(),
"std::filesystem::rename".into(),
"std::filesystem::copy".into(),
"std::filesystem::copy_file".into(),
"std::filesystem::copy_symlink".into(),
"std::filesystem::canonical".into(),
"std::filesystem::weakly_canonical".into(),
"std::filesystem::relative".into(),
"std::filesystem::proximate".into(),
"std::filesystem::absolute".into(),
"std::filesystem::temp_directory_path".into(),
"std::filesystem::current_path".into(),
"std::filesystem::hash_value".into(),
"std::filesystem::status".into(),
"std::filesystem::symlink_status".into(),
"std::filesystem::is_empty".into(),
"std::filesystem::is_symlink".into(),
"std::filesystem::is_block_file".into(),
"std::filesystem::is_character_file".into(),
"std::filesystem::is_fifo".into(),
"std::filesystem::is_socket".into(),
"std::filesystem::create_symlink".into(),
"std::filesystem::create_hard_link".into(),
"std::filesystem::last_write_time".into(),
"std::filesystem::permissions".into(),
"std::filesystem::read_symlink".into(),
"std::filesystem::resize_file".into(),
"std::filesystem::equivalent".into(),
"std::filesystem::u8path".into(),
"std::filesystem::path::preferred_separator".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp17),
iwyu_mappings: vec![
("std::filesystem::path".into(), "filesystem".into()),
("std::filesystem::exists".into(), "filesystem".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "random".into(),
path: "random".into(),
depends_on: vec![
"cstdint".into(),
"type_traits".into(),
"limits".into(),
"vector".into(),
"numeric".into(),
],
depended_by: vec!["algorithm".into()],
provides: vec![
"std::random_device".into(),
"std::default_random_engine".into(),
"std::linear_congruential_engine".into(),
"std::mersenne_twister_engine".into(),
"std::subtract_with_carry_engine".into(),
"std::discard_block_engine".into(),
"std::independent_bits_engine".into(),
"std::shuffle_order_engine".into(),
"std::uniform_int_distribution".into(),
"std::uniform_real_distribution".into(),
"std::bernoulli_distribution".into(),
"std::binomial_distribution".into(),
"std::negative_binomial_distribution".into(),
"std::geometric_distribution".into(),
"std::poisson_distribution".into(),
"std::exponential_distribution".into(),
"std::gamma_distribution".into(),
"std::weibull_distribution".into(),
"std::extreme_value_distribution".into(),
"std::normal_distribution".into(),
"std::lognormal_distribution".into(),
"std::chi_squared_distribution".into(),
"std::cauchy_distribution".into(),
"std::fisher_f_distribution".into(),
"std::student_t_distribution".into(),
"std::discrete_distribution".into(),
"std::piecewise_constant_distribution".into(),
"std::piecewise_linear_distribution".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::random_device".into(), "random".into()),
("std::mt19937".into(), "random".into()),
("std::uniform_int_distribution".into(), "random".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "regex".into(),
path: "regex".into(),
depends_on: vec![
"string".into(),
"iterator".into(),
"memory".into(),
"algorithm".into(),
"stdexcept".into(),
],
depended_by: vec![],
provides: vec![
"std::basic_regex".into(),
"std::regex".into(),
"std::wregex".into(),
"std::sub_match".into(),
"std::csub_match".into(),
"std::wcsub_match".into(),
"std::ssub_match".into(),
"std::wssub_match".into(),
"std::match_results".into(),
"std::cmatch".into(),
"std::wcmatch".into(),
"std::smatch".into(),
"std::wsmatch".into(),
"std::regex_match".into(),
"std::regex_search".into(),
"std::regex_replace".into(),
"std::regex_iterator".into(),
"std::regex_token_iterator".into(),
"std::regex_error".into(),
"std::regex_constants".into(),
"std::regex_traits".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp11),
iwyu_mappings: vec![
("std::regex".into(), "regex".into()),
("std::regex_match".into(), "regex".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "format".into(),
path: "format".into(),
depends_on: vec![
"string_view".into(),
"string".into(),
"type_traits".into(),
"iterator".into(),
"memory".into(),
"stdexcept".into(),
],
depended_by: vec!["print".into()],
provides: vec![
"std::format".into(),
"std::format_to".into(),
"std::format_to_n".into(),
"std::formatted_size".into(),
"std::vformat".into(),
"std::vformat_to".into(),
"std::formatter".into(),
"std::basic_format_string".into(),
"std::format_string".into(),
"std::wformat_string".into(),
"std::basic_format_args".into(),
"std::format_args".into(),
"std::basic_format_context".into(),
"std::format_context".into(),
"std::basic_format_parse_context".into(),
"std::format_parse_context".into(),
"std::format_error".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![
("std::format".into(), "format".into()),
("std::format_error".into(), "format".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "ranges".into(),
path: "ranges".into(),
depends_on: vec![
"iterator".into(),
"algorithm".into(),
"type_traits".into(),
"concepts".into(),
"functional".into(),
"span".into(),
],
depended_by: vec![],
provides: vec![
"std::ranges::range".into(),
"std::ranges::sized_range".into(),
"std::ranges::view".into(),
"std::ranges::input_range".into(),
"std::ranges::output_range".into(),
"std::ranges::forward_range".into(),
"std::ranges::bidirectional_range".into(),
"std::ranges::random_access_range".into(),
"std::ranges::contiguous_range".into(),
"std::ranges::begin".into(),
"std::ranges::end".into(),
"std::ranges::size".into(),
"std::ranges::empty".into(),
"std::ranges::data".into(),
"std::ranges::iterator_t".into(),
"std::ranges::range_value_t".into(),
"std::ranges::range_reference_t".into(),
"std::ranges::range_difference_t".into(),
"std::ranges::filter_view".into(),
"std::ranges::transform_view".into(),
"std::ranges::take_view".into(),
"std::ranges::take_while_view".into(),
"std::ranges::drop_view".into(),
"std::ranges::drop_while_view".into(),
"std::ranges::join_view".into(),
"std::ranges::split_view".into(),
"std::ranges::reverse_view".into(),
"std::ranges::elements_view".into(),
"std::ranges::keys_view".into(),
"std::ranges::values_view".into(),
"std::ranges::zip_view".into(),
"std::ranges::zip_transform_view".into(),
"std::ranges::adjacent_view".into(),
"std::ranges::adjacent_transform_view".into(),
"std::ranges::chunk_view".into(),
"std::ranges::slide_view".into(),
"std::ranges::chunk_by_view".into(),
"std::ranges::stride_view".into(),
"std::ranges::cartesian_product_view".into(),
"std::ranges::to".into(),
"std::views::all".into(),
"std::views::filter".into(),
"std::views::transform".into(),
"std::views::take".into(),
"std::views::drop".into(),
"std::views::reverse".into(),
"std::views::split".into(),
"std::views::join".into(),
"std::views::enumerate".into(),
"std::views::zip".into(),
"std::views::adjacent".into(),
"std::views::chunk".into(),
"std::views::slide".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![
("std::ranges::range".into(), "ranges".into()),
("std::ranges::begin".into(), "ranges".into()),
("std::views::filter".into(), "ranges".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "concepts".into(),
path: "concepts".into(),
depends_on: vec!["type_traits".into()],
depended_by: vec!["iterator".into(), "ranges".into()],
provides: vec![
"std::same_as".into(),
"std::derived_from".into(),
"std::convertible_to".into(),
"std::common_reference_with".into(),
"std::common_with".into(),
"std::integral".into(),
"std::signed_integral".into(),
"std::unsigned_integral".into(),
"std::floating_point".into(),
"std::assignable_from".into(),
"std::swappable".into(),
"std::swappable_with".into(),
"std::destructible".into(),
"std::constructible_from".into(),
"std::default_initializable".into(),
"std::move_constructible".into(),
"std::copy_constructible".into(),
"std::equality_comparable".into(),
"std::equality_comparable_with".into(),
"std::totally_ordered".into(),
"std::totally_ordered_with".into(),
"std::movable".into(),
"std::copyable".into(),
"std::semiregular".into(),
"std::regular".into(),
"std::invocable".into(),
"std::regular_invocable".into(),
"std::predicate".into(),
"std::relation".into(),
"std::equivalence_relation".into(),
"std::strict_weak_order".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::same_as".into(), "concepts".into())],
});
self.add_header(CxxHeaderInfo {
name: "coroutine".into(),
path: "coroutine".into(),
depends_on: vec![
"cstddef".into(),
"type_traits".into(),
"memory".into(),
"exception".into(),
],
depended_by: vec![],
provides: vec![
"std::coroutine_handle".into(),
"std::coroutine_traits".into(),
"std::noop_coroutine".into(),
"std::noop_coroutine_handle".into(),
"std::noop_coroutine_promise".into(),
"std::suspend_never".into(),
"std::suspend_always".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::coroutine_handle".into(), "coroutine".into())],
});
self.add_header(CxxHeaderInfo {
name: "source_location".into(),
path: "source_location".into(),
depends_on: vec!["cstdint".into()],
depended_by: vec![],
provides: vec!["std::source_location".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::source_location".into(), "source_location".into())],
});
self.add_header(CxxHeaderInfo {
name: "compare".into(),
path: "compare".into(),
depends_on: vec!["type_traits".into()],
depended_by: vec![],
provides: vec![
"std::strong_ordering".into(),
"std::weak_ordering".into(),
"std::partial_ordering".into(),
"std::strong_equality".into(),
"std::weak_equality".into(),
"std::partial_ordering".into(),
"std::is_eq".into(),
"std::is_neq".into(),
"std::is_lt".into(),
"std::is_lteq".into(),
"std::is_gt".into(),
"std::is_gteq".into(),
"std::compare_three_way".into(),
"std::compare_three_way_result".into(),
"std::common_comparison_category".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::strong_ordering".into(), "compare".into())],
});
self.add_header(CxxHeaderInfo {
name: "bit".into(),
path: "bit".into(),
depends_on: vec!["type_traits".into(), "cstdint".into(), "limits".into()],
depended_by: vec![],
provides: vec![
"std::bit_cast".into(),
"std::bit_ceil".into(),
"std::bit_floor".into(),
"std::bit_width".into(),
"std::rotl".into(),
"std::rotr".into(),
"std::countl_zero".into(),
"std::countl_one".into(),
"std::countr_zero".into(),
"std::countr_one".into(),
"std::popcount".into(),
"std::has_single_bit".into(),
"std::byteswap".into(),
"std::endian".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::bit_cast".into(), "bit".into())],
});
self.add_header(CxxHeaderInfo {
name: "numbers".into(),
path: "numbers".into(),
depends_on: vec![],
depended_by: vec![],
provides: vec![
"std::numbers::e".into(),
"std::numbers::pi".into(),
"std::numbers::sqrt2".into(),
"std::numbers::phi".into(),
"std::numbers::ln2".into(),
"std::numbers::ln10".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp20),
iwyu_mappings: vec![("std::numbers::pi".into(), "numbers".into())],
});
let c_compat_headers = [
("cassert", vec!["std::assert".into()]),
(
"cctype",
vec![
"std::isalnum".into(),
"std::isalpha".into(),
"std::isdigit".into(),
"std::islower".into(),
"std::isupper".into(),
"std::isspace".into(),
"std::tolower".into(),
"std::toupper".into(),
],
),
("cerrno", vec!["std::errno".into()]),
("cfloat", vec!["std::FLT_MIN".into(), "std::DBL_MAX".into()]),
(
"climits",
vec![
"std::INT_MIN".into(),
"std::INT_MAX".into(),
"std::ULLONG_MAX".into(),
],
),
(
"clocale",
vec!["std::setlocale".into(), "std::localeconv".into()],
),
(
"cmath",
vec![
"std::sin".into(),
"std::cos".into(),
"std::tan".into(),
"std::log".into(),
"std::exp".into(),
"std::pow".into(),
"std::sqrt".into(),
"std::abs".into(),
"std::floor".into(),
"std::ceil".into(),
"std::fmod".into(),
"std::fma".into(),
],
),
("csetjmp", vec!["std::setjmp".into(), "std::longjmp".into()]),
(
"csignal",
vec![
"std::signal".into(),
"std::raise".into(),
"std::sig_atomic_t".into(),
],
),
(
"cstdarg",
vec![
"std::va_list".into(),
"std::va_start".into(),
"std::va_end".into(),
"std::va_arg".into(),
],
),
(
"cstdio",
vec![
"std::FILE".into(),
"std::fopen".into(),
"std::fclose".into(),
"std::printf".into(),
"std::scanf".into(),
"std::fprintf".into(),
],
),
(
"cstring",
vec![
"std::strlen".into(),
"std::strcmp".into(),
"std::strcpy".into(),
"std::strcat".into(),
"std::memcpy".into(),
"std::memmove".into(),
"std::memset".into(),
"std::memcmp".into(),
],
),
(
"ctime",
vec![
"std::time".into(),
"std::clock".into(),
"std::difftime".into(),
"std::time_t".into(),
"std::clock_t".into(),
"std::tm".into(),
],
),
(
"cwchar",
vec![
"std::mbstate_t".into(),
"std::wint_t".into(),
"std::btowc".into(),
"std::wctob".into(),
],
),
(
"cwctype",
vec![
"std::iswalnum".into(),
"std::towlower".into(),
"std::towupper".into(),
],
),
];
for (name, provides) in c_compat_headers {
self.add_header(CxxHeaderInfo {
name: name.into(),
path: name.into(),
depends_on: vec![],
depended_by: vec!["string".into()],
provides,
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![],
});
}
self.add_header(CxxHeaderInfo {
name: "new".into(),
path: "new".into(),
depends_on: vec!["cstddef".into(), "exception".into()],
depended_by: vec![
"memory".into(),
"optional".into(),
"variant".into(),
"any".into(),
],
provides: vec![
"std::nothrow_t".into(),
"std::nothrow".into(),
"std::new_handler".into(),
"std::set_new_handler".into(),
"std::bad_alloc".into(),
"std::bad_array_new_length".into(),
"std::align_val_t".into(),
"std::destroying_delete_t".into(),
"std::operator new".into(),
"std::operator delete".into(),
"std::operator new[]".into(),
"std::operator delete[]".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::bad_alloc".into(), "new".into()),
("std::nothrow".into(), "new".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "typeinfo".into(),
path: "typeinfo".into(),
depends_on: vec!["exception".into()],
depended_by: vec!["any".into()],
provides: vec![
"std::type_info".into(),
"std::bad_cast".into(),
"std::bad_typeid".into(),
],
is_core: true,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![
("std::type_info".into(), "typeinfo".into()),
("std::bad_cast".into(), "typeinfo".into()),
],
});
self.add_header(CxxHeaderInfo {
name: "limits".into(),
path: "limits".into(),
depends_on: vec![],
depended_by: vec![
"ios".into(),
"random".into(),
"chrono".into(),
"variant".into(),
],
provides: vec![
"std::numeric_limits".into(),
"std::float_round_style".into(),
"std::float_denorm_style".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::numeric_limits".into(), "limits".into())],
});
self.add_header(CxxHeaderInfo {
name: "valarray".into(),
path: "valarray".into(),
depends_on: vec!["cstddef".into(), "initializer_list".into(), "cmath".into()],
depended_by: vec![],
provides: vec![
"std::valarray".into(),
"std::slice".into(),
"std::gslice".into(),
"std::mask_array".into(),
"std::indirect_array".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::valarray".into(), "valarray".into())],
});
self.add_header(CxxHeaderInfo {
name: "bitset".into(),
path: "bitset".into(),
depends_on: vec!["string".into(), "iosfwd".into()],
depended_by: vec![],
provides: vec!["std::bitset".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::bitset".into(), "bitset".into())],
});
self.add_header(CxxHeaderInfo {
name: "complex".into(),
path: "complex".into(),
depends_on: vec!["cmath".into(), "sstream".into()],
depended_by: vec!["valarray".into()],
provides: vec!["std::complex".into(), "std::complex_literals".into()],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::complex".into(), "complex".into())],
});
self.add_header(CxxHeaderInfo {
name: "locale".into(),
path: "locale".into(),
depends_on: vec![
"string".into(),
"memory".into(),
"iterator".into(),
"stdexcept".into(),
],
depended_by: vec!["ios".into()],
provides: vec![
"std::locale".into(),
"std::codecvt".into(),
"std::collate".into(),
"std::ctype".into(),
"std::messages".into(),
"std::money_get".into(),
"std::money_put".into(),
"std::moneypunct".into(),
"std::num_get".into(),
"std::num_put".into(),
"std::numpunct".into(),
"std::time_get".into(),
"std::time_put".into(),
"std::has_facet".into(),
"std::use_facet".into(),
"std::isalnum".into(),
],
is_core: false,
is_standard: true,
introduced_in: None,
iwyu_mappings: vec![("std::locale".into(), "locale".into())],
});
self.add_header(CxxHeaderInfo {
name: "expected".into(),
path: "expected".into(),
depends_on: vec![
"type_traits".into(),
"utility".into(),
"memory".into(),
"stdexcept".into(),
"new".into(),
],
depended_by: vec![],
provides: vec![
"std::expected".into(),
"std::unexpected".into(),
"std::bad_expected_access".into(),
"std::unexpect_t".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::expected".into(), "expected".into())],
});
self.add_header(CxxHeaderInfo {
name: "flat_map".into(),
path: "flat_map".into(),
depends_on: vec!["vector".into(), "algorithm".into(), "functional".into()],
depended_by: vec![],
provides: vec!["std::flat_map".into(), "std::flat_multimap".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::flat_map".into(), "flat_map".into())],
});
self.add_header(CxxHeaderInfo {
name: "flat_set".into(),
path: "flat_set".into(),
depends_on: vec!["vector".into(), "algorithm".into(), "functional".into()],
depended_by: vec![],
provides: vec!["std::flat_set".into(), "std::flat_multiset".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::flat_set".into(), "flat_set".into())],
});
self.add_header(CxxHeaderInfo {
name: "mdspan".into(),
path: "mdspan".into(),
depends_on: vec!["span".into(), "array".into(), "cstddef".into()],
depended_by: vec![],
provides: vec![
"std::mdspan".into(),
"std::layout_left".into(),
"std::layout_right".into(),
"std::layout_stride".into(),
"std::extents".into(),
"std::dextents".into(),
"std::default_accessor".into(),
],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::mdspan".into(), "mdspan".into())],
});
self.add_header(CxxHeaderInfo {
name: "print".into(),
path: "print".into(),
depends_on: vec!["format".into(), "string_view".into(), "cstdio".into()],
depended_by: vec![],
provides: vec!["std::print".into(), "std::println".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::print".into(), "print".into())],
});
self.add_header(CxxHeaderInfo {
name: "generator".into(),
path: "generator".into(),
depends_on: vec!["coroutine".into(), "memory".into(), "iterator".into()],
depended_by: vec![],
provides: vec!["std::generator".into()],
is_core: false,
is_standard: true,
introduced_in: Some(CppStandard::Cpp23),
iwyu_mappings: vec![("std::generator".into(), "generator".into())],
});
self.build_reverse_deps();
self.build_symbol_index();
}
fn add_header(&mut self, header: CxxHeaderInfo) {
self.headers.insert(header.name.clone(), header);
}
fn build_reverse_deps(&mut self) {
let mut reverse: HashMap<String, Vec<String>> = HashMap::new();
for (name, hdr) in &self.headers {
for dep in &hdr.depends_on {
reverse.entry(dep.clone()).or_default().push(name.clone());
}
}
for (name, deps) in reverse {
if let Some(hdr) = self.headers.get_mut(&name) {
hdr.depended_by = deps;
}
}
}
fn build_symbol_index(&mut self) {
for (name, hdr) in &self.headers {
for sym in &hdr.provides {
self.symbol_index.insert(sym.clone(), name.clone());
}
for (sym, _h) in &hdr.iwyu_mappings {
self.symbol_index
.entry(sym.clone())
.or_insert_with(|| name.clone());
}
}
}
pub fn resolve(&self, header: &str) -> Option<&str> {
self.headers.get(header).map(|h| h.path.as_str())
}
pub fn iwyu_for_symbol(&self, symbol: &str) -> Option<&str> {
self.symbol_index.get(symbol).map(String::as_str)
}
pub fn transitive_dependents(&self, header: &str) -> Vec<String> {
let mut result = Vec::new();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(header.to_string());
while let Some(h) = queue.pop_front() {
if !visited.insert(h.clone()) {
continue;
}
if let Some(info) = self.headers.get(&h) {
for dep in &info.depended_by {
if visited.insert(dep.clone()) {
result.push(dep.clone());
queue.push_back(dep.clone());
}
}
}
}
result
}
pub fn include_order(&self, headers: &[String]) -> Vec<String> {
let mut order = Vec::new();
let mut visited = HashSet::new();
for h in headers {
self.dfs_include_order(h, &mut visited, &mut order);
}
order.reverse();
order
}
fn dfs_include_order(
&self,
header: &str,
visited: &mut HashSet<String>,
order: &mut Vec<String>,
) {
if !visited.insert(header.to_string()) {
return;
}
if let Some(info) = self.headers.get(header) {
for dep in &info.depends_on {
self.dfs_include_order(dep, visited, order);
}
}
order.push(header.to_string());
}
pub fn all_headers(&self) -> Vec<&str> {
self.headers.keys().map(String::as_str).collect()
}
pub fn headers_for_standard(&self, standard: CppStandard) -> Vec<&str> {
self.headers
.iter()
.filter(|(_, h)| h.introduced_in == Some(standard))
.map(|(n, _)| n.as_str())
.collect()
}
pub fn core_headers(&self) -> Vec<&str> {
self.headers
.iter()
.filter(|(_, h)| h.is_core)
.map(|(n, _)| n.as_str())
.collect()
}
pub fn include_path_prefix(&self) -> &str {
&self.include_prefix
}
pub fn include_flags(&self) -> Vec<String> {
vec![
format!("-I{}", self.include_prefix),
format!("-stdlib={}", self.stdlib_kind),
]
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum X86LibCxxType {
StdString {
sso_capacity: usize,
total_size: usize,
llvm_type: String,
},
StdVector {
llvm_type: String,
size_bytes: usize,
},
StdArray {
element_type: String,
element_count: usize,
llvm_type: String,
size_bytes: usize,
},
UniquePtr {
pointee_type: String,
llvm_type: String,
size_bytes: usize,
},
SharedPtr {
pointee_type: String,
llvm_type: String,
size_bytes: usize,
},
WeakPtr {
pointee_type: String,
llvm_type: String,
size_bytes: usize,
},
Optional {
inner_type: String,
has_value_offset: usize,
llvm_type: String,
size_bytes: usize,
},
Variant {
alternatives: Vec<String>,
discriminant_type: String,
llvm_type: String,
size_bytes: usize,
},
Any {
llvm_type: String,
size_bytes: usize,
},
Pair {
first_type: String,
second_type: String,
llvm_type: String,
size_bytes: usize,
},
Tuple {
element_types: Vec<String>,
llvm_type: String,
size_bytes: usize,
},
Function {
return_type: String,
param_types: Vec<String>,
llvm_type: String,
size_bytes: usize,
},
StringView {
llvm_type: String,
size_bytes: usize,
},
Span {
element_type: String,
extent: Option<usize>,
llvm_type: String,
size_bytes: usize,
},
Atomic {
inner_type: String,
is_lock_free: bool,
llvm_type: String,
size_bytes: usize,
},
Mutex {
llvm_type: String,
size_bytes: usize,
},
SharedMutex {
llvm_type: String,
size_bytes: usize,
},
RecursiveMutex {
llvm_type: String,
size_bytes: usize,
},
ConditionVariable {
llvm_type: String,
size_bytes: usize,
},
Thread {
llvm_type: String,
size_bytes: usize,
},
Promise {
value_type: String,
llvm_type: String,
size_bytes: usize,
},
Future {
value_type: String,
llvm_type: String,
size_bytes: usize,
},
PackagedTask {
return_type: String,
param_types: Vec<String>,
llvm_type: String,
size_bytes: usize,
},
Iterator {
category: String,
value_type: String,
pointer_size: usize,
},
RawType(String),
}
impl fmt::Display for X86LibCxxType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86LibCxxType::StdString { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::StdVector { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::StdArray { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::UniquePtr { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::SharedPtr { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::WeakPtr { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Optional { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Variant { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Any { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Pair { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Tuple { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Function { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::StringView { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Span { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Atomic { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Mutex { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::SharedMutex { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::RecursiveMutex { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::ConditionVariable { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Thread { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Promise { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Future { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::PackagedTask { llvm_type, .. } => write!(f, "{}", llvm_type),
X86LibCxxType::Iterator { .. } => write!(f, "iterator"),
X86LibCxxType::RawType(t) => write!(f, "{}", t),
}
}
}
pub struct X86CxxTypeLowering {
platform: X86CxxPlatform,
is_64bit: bool,
ptr_size: usize,
type_cache: HashMap<String, X86LibCxxType>,
struct_type_names: HashMap<String, String>,
}
impl X86CxxTypeLowering {
pub fn new(platform: X86CxxPlatform, is_64bit: bool) -> Self {
let ptr_size = if is_64bit { 8 } else { 4 };
X86CxxTypeLowering {
platform,
is_64bit,
ptr_size,
type_cache: HashMap::new(),
struct_type_names: HashMap::new(),
}
}
pub fn initialize(&mut self) {
self.cache_std_string();
self.cache_std_vector();
self.cache_std_array();
self.cache_unique_ptr();
self.cache_shared_ptr();
self.cache_weak_ptr();
self.cache_optional();
self.cache_variant();
self.cache_any();
self.cache_pair();
self.cache_tuple();
self.cache_function();
self.cache_string_view();
self.cache_span();
self.cache_atomic();
self.cache_mutex();
self.cache_condition_variable();
self.cache_thread();
self.cache_promise_future();
self.cache_iterators();
}
pub fn get_type(&self, type_name: &str) -> Option<X86LibCxxType> {
self.type_cache.get(type_name).cloned()
}
pub fn register_struct_type(&mut self, name: &str, llvm_type: String) {
self.struct_type_names.insert(name.to_string(), llvm_type);
}
pub fn get_struct_type(&self, name: &str) -> Option<&str> {
self.struct_type_names.get(name).map(String::as_str)
}
fn cache_std_string(&mut self) {
let sso_cap = self.platform.sso_capacity();
let total_size = if self.is_64bit {
self.platform.string_size_64()
} else {
12 };
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let llvm_type = if sso_cap == 22 {
format!(
"%\"struct.std::string\" = type {{ [23 x i8], i8 }} ; libc++ SSO, total {} bytes",
total_size
)
} else {
format!(
"%\"struct.std::string\" = type {{ {}, i64, [16 x i8] }} ; libstdc++ SSO, total {} bytes",
ptr_ty, total_size
)
};
self.type_cache.insert(
"std::string".into(),
X86LibCxxType::StdString {
sso_capacity: sso_cap,
total_size,
llvm_type: llvm_type.clone(),
},
);
self.struct_type_names
.insert("std::string".into(), llvm_type);
}
fn cache_std_vector(&mut self) {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let size_bytes = self.ptr_size * 3;
let llvm_type = format!(
"%\"struct.std::vector\" = type {{ {}, {}, {} }} ; begin, end, end_of_storage",
ptr_ty, ptr_ty, ptr_ty
);
self.type_cache.insert(
"std::vector".into(),
X86LibCxxType::StdVector {
llvm_type: llvm_type.clone(),
size_bytes,
},
);
self.struct_type_names
.insert("std::vector".into(), llvm_type);
}
fn cache_std_array(&mut self) {
let llvm_type = "%\"struct.std::array\" = type { [N x element_type] }".into();
self.type_cache.insert(
"std::array".into(),
X86LibCxxType::StdArray {
element_type: "T".into(),
element_count: 0, llvm_type,
size_bytes: 0,
},
);
}
fn cache_unique_ptr(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::unique_ptr\" = type { i64 } ; single pointer (default deleter EBO)"
.into();
self.type_cache.insert(
"std::unique_ptr".into(),
X86LibCxxType::UniquePtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 8,
},
);
} else {
let llvm_type =
"%\"struct.std::unique_ptr\" = type { i32 } ; single pointer (default deleter EBO)"
.into();
self.type_cache.insert(
"std::unique_ptr".into(),
X86LibCxxType::UniquePtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 4,
},
);
}
}
fn cache_shared_ptr(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::shared_ptr\" = type { i64, i64 } ; ptr + control_block ptr".into();
self.type_cache.insert(
"std::shared_ptr".into(),
X86LibCxxType::SharedPtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 16,
},
);
} else {
let llvm_type =
"%\"struct.std::shared_ptr\" = type { i32, i32 } ; ptr + control_block ptr".into();
self.type_cache.insert(
"std::shared_ptr".into(),
X86LibCxxType::SharedPtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 8,
},
);
}
}
fn cache_weak_ptr(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::weak_ptr\" = type { i64, i64 } ; ptr + control_block ptr".into();
self.type_cache.insert(
"std::weak_ptr".into(),
X86LibCxxType::WeakPtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 16,
},
);
} else {
let llvm_type =
"%\"struct.std::weak_ptr\" = type { i32, i32 } ; ptr + control_block ptr".into();
self.type_cache.insert(
"std::weak_ptr".into(),
X86LibCxxType::WeakPtr {
pointee_type: "T".into(),
llvm_type,
size_bytes: 8,
},
);
}
}
fn cache_optional(&mut self) {
if self.is_64bit {
let llvm_type = "%\"struct.std::optional\" = type { i8, [7 x i8], T } ; bool has_value + padding + storage".into();
self.type_cache.insert(
"std::optional".into(),
X86LibCxxType::Optional {
inner_type: "T".into(),
has_value_offset: 0,
llvm_type,
size_bytes: 0, },
);
} else {
let llvm_type = "%\"struct.std::optional\" = type { i8, [3 x i8], T } ; bool has_value + padding + storage".into();
self.type_cache.insert(
"std::optional".into(),
X86LibCxxType::Optional {
inner_type: "T".into(),
has_value_offset: 0,
llvm_type,
size_bytes: 0,
},
);
}
}
fn cache_variant(&mut self) {
if self.is_64bit {
let llvm_type = "%\"struct.std::variant\" = type { i32, [4 x i8], union_storage } ; index + padding + union".into();
self.type_cache.insert(
"std::variant".into(),
X86LibCxxType::Variant {
alternatives: vec![],
discriminant_type: "i32".into(),
llvm_type,
size_bytes: 0,
},
);
} else {
let llvm_type = "%\"struct.std::variant\" = type { i32, union_storage } ; index + union (no padding)".into();
self.type_cache.insert(
"std::variant".into(),
X86LibCxxType::Variant {
alternatives: vec![],
discriminant_type: "i32".into(),
llvm_type,
size_bytes: 0,
},
);
}
}
fn cache_any(&mut self) {
if self.is_64bit {
let llvm_type = "%\"struct.std::any\" = type { i64, i64, i64 } ; 3 pointers".into();
self.type_cache.insert(
"std::any".into(),
X86LibCxxType::Any {
llvm_type,
size_bytes: 24,
},
);
} else {
let llvm_type = "%\"struct.std::any\" = type { i32, i32, i32 } ; 3 pointers".into();
self.type_cache.insert(
"std::any".into(),
X86LibCxxType::Any {
llvm_type,
size_bytes: 12,
},
);
}
}
fn cache_pair(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::pair\" = type { T_first, T_second } ; first then second".into();
self.type_cache.insert(
"std::pair".into(),
X86LibCxxType::Pair {
first_type: "T".into(),
second_type: "U".into(),
llvm_type,
size_bytes: 0, },
);
} else {
let llvm_type = "%\"struct.std::pair\" = type { T_first, T_second }".into();
self.type_cache.insert(
"std::pair".into(),
X86LibCxxType::Pair {
first_type: "T".into(),
second_type: "U".into(),
llvm_type,
size_bytes: 0,
},
);
}
}
fn cache_tuple(&mut self) {
let llvm_type =
"%\"struct.std::tuple\" = type { tuple_elements } ; recursive layout with EBO".into();
self.type_cache.insert(
"std::tuple".into(),
X86LibCxxType::Tuple {
element_types: vec![],
llvm_type,
size_bytes: 0,
},
);
}
fn cache_function(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::function\" = type { i64, i64, i64 } ; fn ptr + manager + storage"
.into();
self.type_cache.insert(
"std::function".into(),
X86LibCxxType::Function {
return_type: "R".into(),
param_types: vec![],
llvm_type,
size_bytes: 24,
},
);
} else {
let llvm_type =
"%\"struct.std::function\" = type { i32, i32, i32 } ; fn ptr + manager + storage"
.into();
self.type_cache.insert(
"std::function".into(),
X86LibCxxType::Function {
return_type: "R".into(),
param_types: vec![],
llvm_type,
size_bytes: 12,
},
);
}
}
fn cache_string_view(&mut self) {
if self.is_64bit {
let llvm_type = "%\"struct.std::string_view\" = type { i64, i64 } ; ptr + size".into();
self.type_cache.insert(
"std::string_view".into(),
X86LibCxxType::StringView {
llvm_type,
size_bytes: 16,
},
);
} else {
let llvm_type = "%\"struct.std::string_view\" = type { i32, i32 } ; ptr + size".into();
self.type_cache.insert(
"std::string_view".into(),
X86LibCxxType::StringView {
llvm_type,
size_bytes: 8,
},
);
}
}
fn cache_span(&mut self) {
if self.is_64bit {
let llvm_type = "%\"struct.std::span\" = type { i64, i64 } ; ptr + size (or i64 + i64 for static extent)".into();
self.type_cache.insert(
"std::span".into(),
X86LibCxxType::Span {
element_type: "T".into(),
extent: None,
llvm_type,
size_bytes: 16,
},
);
} else {
let llvm_type = "%\"struct.std::span\" = type { i32, i32 } ; ptr + size".into();
self.type_cache.insert(
"std::span".into(),
X86LibCxxType::Span {
element_type: "T".into(),
extent: None,
llvm_type,
size_bytes: 8,
},
);
}
}
fn cache_atomic(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::atomic\" = type { i64 } ; lock-free on x86 for int/long/ptr".into();
self.type_cache.insert(
"std::atomic".into(),
X86LibCxxType::Atomic {
inner_type: "T".into(),
is_lock_free: true, llvm_type,
size_bytes: 8,
},
);
} else {
let llvm_type =
"%\"struct.std::atomic\" = type { i32 } ; lock-free on x86 for int/ptr".into();
self.type_cache.insert(
"std::atomic".into(),
X86LibCxxType::Atomic {
inner_type: "T".into(),
is_lock_free: true,
llvm_type,
size_bytes: 4,
},
);
}
}
fn cache_mutex(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::mutex\" = type { [40 x i8] } ; pthread_mutex_t wrapper".into();
self.type_cache.insert(
"std::mutex".into(),
X86LibCxxType::Mutex {
llvm_type,
size_bytes: 40,
},
);
let llvm_shared =
"%\"struct.std::shared_mutex\" = type { [56 x i8] } ; pthread_rwlock_t wrapper"
.into();
self.type_cache.insert(
"std::shared_mutex".into(),
X86LibCxxType::SharedMutex {
llvm_type: llvm_shared,
size_bytes: 56,
},
);
let llvm_recursive = "%\"struct.std::recursive_mutex\" = type { [40 x i8] } ; pthread_mutex_t (recursive)".into();
self.type_cache.insert(
"std::recursive_mutex".into(),
X86LibCxxType::RecursiveMutex {
llvm_type: llvm_recursive,
size_bytes: 40,
},
);
} else {
let llvm_type: String =
"%\"struct.std::mutex\" = type { [24 x i8] } ; pthread_mutex_t wrapper (32-bit)"
.into();
self.type_cache.insert(
"std::mutex".into(),
X86LibCxxType::Mutex {
llvm_type: llvm_type.clone(),
size_bytes: 24,
},
);
let llvm_shared = "%\"struct.std::shared_mutex\" = type { [36 x i8] } ; pthread_rwlock_t wrapper (32-bit)".into();
self.type_cache.insert(
"std::shared_mutex".into(),
X86LibCxxType::SharedMutex {
llvm_type: llvm_shared,
size_bytes: 36,
},
);
let llvm_recursive = "%\"struct.std::recursive_mutex\" = type { [24 x i8] }".into();
self.type_cache.insert(
"std::recursive_mutex".into(),
X86LibCxxType::RecursiveMutex {
llvm_type: llvm_recursive,
size_bytes: 24,
},
);
}
}
fn cache_condition_variable(&mut self) {
if self.is_64bit {
let llvm_type =
"%\"struct.std::condition_variable\" = type { [48 x i8] } ; pthread_cond_t wrapper"
.into();
self.type_cache.insert(
"std::condition_variable".into(),
X86LibCxxType::ConditionVariable {
llvm_type,
size_bytes: 48,
},
);
} else {
let llvm_type =
"%\"struct.std::condition_variable\" = type { [48 x i8] } ; pthread_cond_t wrapper"
.into();
self.type_cache.insert(
"std::condition_variable".into(),
X86LibCxxType::ConditionVariable {
llvm_type,
size_bytes: 48,
},
);
}
}
fn cache_thread(&mut self) {
if self.is_64bit {
let llvm_type: String =
"%\"struct.std::thread\" = type { i64 } ; pthread_t (opaque handle)".into();
self.type_cache.insert(
"std::thread".into(),
X86LibCxxType::Thread {
llvm_type: llvm_type.clone(),
size_bytes: 8,
},
);
self.type_cache.insert(
"std::jthread".into(),
X86LibCxxType::Thread {
llvm_type,
size_bytes: 8,
},
);
} else {
let llvm_type: String =
"%\"struct.std::thread\" = type { i32 } ; pthread_t (opaque handle)".into();
self.type_cache.insert(
"std::thread".into(),
X86LibCxxType::Thread {
llvm_type: llvm_type.clone(),
size_bytes: 4,
},
);
self.type_cache.insert(
"std::jthread".into(),
X86LibCxxType::Thread {
llvm_type,
size_bytes: 4,
},
);
}
}
fn cache_promise_future(&mut self) {
if self.is_64bit {
let promise_ty =
"%\"struct.std::promise\" = type { i64 } ; shared state pointer".into();
let future_ty = "%\"struct.std::future\" = type { i64 } ; shared state pointer".into();
let task_ty =
"%\"struct.std::packaged_task\" = type { i64 } ; shared state + callable storage"
.into();
self.type_cache.insert(
"std::promise".into(),
X86LibCxxType::Promise {
value_type: "T".into(),
llvm_type: promise_ty,
size_bytes: 8,
},
);
self.type_cache.insert(
"std::future".into(),
X86LibCxxType::Future {
value_type: "T".into(),
llvm_type: future_ty,
size_bytes: 8,
},
);
self.type_cache.insert(
"std::packaged_task".into(),
X86LibCxxType::PackagedTask {
return_type: "R".into(),
param_types: vec![],
llvm_type: task_ty,
size_bytes: 8,
},
);
} else {
let promise_ty =
"%\"struct.std::promise\" = type { i32 } ; shared state pointer".into();
let future_ty = "%\"struct.std::future\" = type { i32 } ; shared state pointer".into();
let task_ty = "%\"struct.std::packaged_task\" = type { i32 }".into();
self.type_cache.insert(
"std::promise".into(),
X86LibCxxType::Promise {
value_type: "T".into(),
llvm_type: promise_ty,
size_bytes: 4,
},
);
self.type_cache.insert(
"std::future".into(),
X86LibCxxType::Future {
value_type: "T".into(),
llvm_type: future_ty,
size_bytes: 4,
},
);
self.type_cache.insert(
"std::packaged_task".into(),
X86LibCxxType::PackagedTask {
return_type: "R".into(),
param_types: vec![],
llvm_type: task_ty,
size_bytes: 4,
},
);
}
}
fn cache_iterators(&mut self) {
let categories = [
"std::input_iterator_tag",
"std::output_iterator_tag",
"std::forward_iterator_tag",
"std::bidirectional_iterator_tag",
"std::random_access_iterator_tag",
"std::contiguous_iterator_tag",
];
for cat in categories {
let llvm_type = format!("%\"struct.{}\" = type {{ i8 }} ; empty tag type", cat);
self.type_cache.insert(
cat.into(),
X86LibCxxType::Iterator {
category: cat.into(),
value_type: "T".into(),
pointer_size: self.ptr_size,
},
);
self.struct_type_names.insert(cat.into(), llvm_type);
}
}
pub fn pointer_size(&self) -> usize {
self.ptr_size
}
pub fn ptr_type_str(&self) -> &str {
if self.is_64bit {
"i64"
} else {
"i32"
}
}
pub fn size_t_type_str(&self) -> &str {
if self.is_64bit {
"i64"
} else {
"i32"
}
}
pub fn is_lock_free(&self, size_bytes: usize) -> bool {
size_bytes <= 8 || (self.is_64bit && size_bytes <= 16)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CxxABIKind {
Itanium,
Microsoft,
}
#[derive(Debug, Clone)]
pub enum X86CxxABIFeature {
VtablePointer { class_name: String, offset: usize },
BaseClassLayout {
class_name: String,
base_name: String,
is_virtual: bool,
offset: usize,
},
VirtualBaseLayout {
class_name: String,
vbase_name: String,
vbase_offset: usize,
},
ThisCall {
class_name: String,
method_name: String,
is_virtual: bool,
},
Constructor {
class_name: String,
ctor_kind: CtorKind,
params: Vec<String>,
},
Destructor {
class_name: String,
dtor_kind: DtorKind,
},
CopyConstructor {
class_name: String,
is_trivial: bool,
},
MoveConstructor {
class_name: String,
is_trivial: bool,
},
CopyAssignment {
class_name: String,
is_trivial: bool,
},
MoveAssignment {
class_name: String,
is_trivial: bool,
},
EmitVtable {
class_name: String,
is_primary: bool,
},
EmitVTT { class_name: String },
GuardVariable {
var_name: String,
is_thread_safe: bool,
},
ReturnSlot { class_name: String, is_named: bool },
MemberPointer {
class_name: String,
member_type: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CtorKind {
Complete,
Base,
CompleteAllocating,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DtorKind {
Complete,
Base,
Deleting,
}
#[derive(Debug, Clone)]
pub struct X86CxxABIResult {
pub ir: String,
pub metadata: HashMap<String, String>,
pub success: bool,
pub error: Option<String>,
}
pub struct X86CxxABILowering {
abi_kind: X86CxxABIKind,
platform: X86CxxPlatform,
is_64bit: bool,
triple: String,
ptr_size: usize,
class_layouts: HashMap<String, X86ClassLayout>,
}
#[derive(Debug, Clone)]
pub struct X86ClassLayout {
pub name: String,
pub size: usize,
pub alignment: usize,
pub vtable_offset: Option<usize>,
pub vtable_ptr_size: usize,
pub base_offsets: Vec<(String, usize, bool)>,
pub member_offsets: Vec<(String, usize, usize)>,
pub vbase_offsets: Vec<(String, usize)>,
pub is_pod: bool,
pub is_trivially_copyable: bool,
pub has_non_trivial_dtor: bool,
pub has_virtual_bases: bool,
}
impl X86CxxABILowering {
pub fn new(platform: X86CxxPlatform, is_64bit: bool, triple: &str) -> Self {
let abi_kind = if platform.uses_msvc_abi() {
X86CxxABIKind::Microsoft
} else {
X86CxxABIKind::Itanium
};
let ptr_size = if is_64bit { 8 } else { 4 };
X86CxxABILowering {
abi_kind,
platform,
is_64bit,
triple: triple.to_string(),
ptr_size,
class_layouts: HashMap::new(),
}
}
pub fn initialize(&mut self) {
self.class_layouts.clear();
}
pub fn lower(&self, feature: &X86CxxABIFeature) -> X86CxxABIResult {
match self.abi_kind {
X86CxxABIKind::Itanium => self.lower_itanium(feature),
X86CxxABIKind::Microsoft => self.lower_msvc(feature),
}
}
fn lower_itanium(&self, feature: &X86CxxABIFeature) -> X86CxxABIResult {
match feature {
X86CxxABIFeature::VtablePointer { class_name, offset } => {
self.lower_itanium_vtable_ptr(class_name, *offset)
}
X86CxxABIFeature::BaseClassLayout {
class_name,
base_name,
is_virtual,
offset,
} => self.lower_itanium_base_layout(class_name, base_name, *is_virtual, *offset),
X86CxxABIFeature::VirtualBaseLayout {
class_name,
vbase_name,
vbase_offset,
} => self.lower_itanium_vbase_layout(class_name, vbase_name, *vbase_offset),
X86CxxABIFeature::ThisCall {
class_name,
method_name,
is_virtual,
} => self.lower_itanium_this_call(class_name, method_name, *is_virtual),
X86CxxABIFeature::Constructor {
class_name,
ctor_kind,
params,
} => self.lower_itanium_ctor(class_name, *ctor_kind, params),
X86CxxABIFeature::Destructor {
class_name,
dtor_kind,
} => self.lower_itanium_dtor(class_name, *dtor_kind),
X86CxxABIFeature::CopyConstructor {
class_name,
is_trivial,
} => self.lower_itanium_copy_ctor(class_name, *is_trivial),
X86CxxABIFeature::MoveConstructor {
class_name,
is_trivial,
} => self.lower_itanium_move_ctor(class_name, *is_trivial),
X86CxxABIFeature::CopyAssignment {
class_name,
is_trivial,
} => self.lower_itanium_copy_assign(class_name, *is_trivial),
X86CxxABIFeature::MoveAssignment {
class_name,
is_trivial,
} => self.lower_itanium_move_assign(class_name, *is_trivial),
X86CxxABIFeature::EmitVtable {
class_name,
is_primary,
} => self.lower_itanium_emit_vtable(class_name, *is_primary),
X86CxxABIFeature::EmitVTT { class_name } => self.lower_itanium_emit_vtt(class_name),
X86CxxABIFeature::GuardVariable {
var_name,
is_thread_safe,
} => self.lower_itanium_guard_var(var_name, *is_thread_safe),
X86CxxABIFeature::ReturnSlot {
class_name,
is_named,
} => self.lower_itanium_return_slot(class_name, *is_named),
X86CxxABIFeature::MemberPointer {
class_name,
member_type,
} => self.lower_itanium_member_ptr(class_name, member_type),
}
}
fn lower_itanium_vtable_ptr(&self, class_name: &str, offset: usize) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"%\"struct.{}\" = type {{ {}, ... }} ; vtable ptr at offset {}",
class_name, ptr_ty, offset
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("vtable_offset".into(), offset.to_string()),
("vtable_ptr_type".into(), ptr_ty.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_base_layout(
&self,
class_name: &str,
base_name: &str,
is_virtual: bool,
offset: usize,
) -> X86CxxABIResult {
let ir = if is_virtual {
format!(
"; class {} has virtual base {} with vbase offset {} (indirect via vtable)",
class_name, base_name, offset
)
} else {
format!(
"; class {} has non-virtual base {} at direct offset {}",
class_name, base_name, offset
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("base_name".into(), base_name.to_string()),
("is_virtual".into(), is_virtual.to_string()),
("offset".into(), offset.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_vbase_layout(
&self,
class_name: &str,
vbase_name: &str,
vbase_offset: usize,
) -> X86CxxABIResult {
let ir = format!(
"; class {} virtual base {} accessible via vbase offset {} in vtable",
class_name, vbase_name, vbase_offset
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("vbase_name".into(), vbase_name.to_string()),
("vbase_offset".into(), vbase_offset.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_this_call(
&self,
class_name: &str,
method_name: &str,
is_virtual: bool,
) -> X86CxxABIResult {
let this_reg = if self.is_64bit { "%rdi" } else { "(%esp+4)" };
let call_style = if is_virtual {
format!(
"; virtual call: load vtable ptr from {}, then load fn at vtable[idx], adjust this if thunk\n",
this_reg
)
} else {
format!("; direct member call: this in {}\n", this_reg)
};
let ir = format!(
"define void @{}_{}({} %this, ...) {{\n{}\n ret void\n}}",
class_name,
method_name,
if self.is_64bit { "i64" } else { "i32" },
call_style
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("method".into(), method_name.to_string()),
("is_virtual".into(), is_virtual.to_string()),
("this_register".into(), this_reg.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_ctor(
&self,
class_name: &str,
ctor_kind: CtorKind,
params: &[String],
) -> X86CxxABIResult {
let mangled_name = match ctor_kind {
CtorKind::Complete => format!("_ZN{}{}{}", class_name.len(), class_name, "C1E"),
CtorKind::Base => format!("_ZN{}{}{}", class_name.len(), class_name, "C2E"),
CtorKind::CompleteAllocating => {
format!("_ZN{}{}{}", class_name.len(), class_name, "C3E")
}
};
let param_list = if params.is_empty() {
String::new()
} else {
params.join(", ")
};
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"define void @{}({} %this{}) {{\n ; {} constructor for class {}\n ; 1. Call base class constructors\n ; 2. Initialize vtable pointer\n ; 3. Initialize member variables\n ret void\n}}",
mangled_name,
ptr_ty,
if params.is_empty() { String::new() } else { format!(", {}", param_list) },
match ctor_kind {
CtorKind::Complete => "Complete object",
CtorKind::Base => "Base object",
CtorKind::CompleteAllocating => "Complete allocating",
},
class_name
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("ctor_kind".into(), format!("{:?}", ctor_kind)),
("mangled_name".into(), mangled_name),
]),
success: true,
error: None,
}
}
fn lower_itanium_dtor(&self, class_name: &str, dtor_kind: DtorKind) -> X86CxxABIResult {
let mangled_name = match dtor_kind {
DtorKind::Complete => format!("_ZN{}{}{}", class_name.len(), class_name, "D1E"),
DtorKind::Base => format!("_ZN{}{}{}", class_name.len(), class_name, "D2E"),
DtorKind::Deleting => format!("_ZN{}{}{}", class_name.len(), class_name, "D0E"),
};
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"define void @{}({} %this) {{\n ; {} destructor for class {}\n ; 1. Destroy member variables\n ; 2. Call base class destructors\n ; 3. If deleting: call operator delete(this)\n ret void\n}}",
mangled_name,
ptr_ty,
match dtor_kind {
DtorKind::Complete => "Complete object",
DtorKind::Base => "Base object",
DtorKind::Deleting => "Deleting",
},
class_name
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("dtor_kind".into(), format!("{:?}", dtor_kind)),
("mangled_name".into(), mangled_name),
]),
success: true,
error: None,
}
}
fn lower_itanium_copy_ctor(&self, class_name: &str, is_trivial: bool) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = if is_trivial {
format!(
"; trivial copy constructor for {}: memcpy(this, other, sizeof({}))",
class_name, class_name
)
} else {
format!(
"define void @_ZN{}C1ERKS_(i{} %this, {} %other) {{\n ; non-trivial copy constructor for {}\n ; copy base classes, then member-wise copy\n ret void\n}}",
class_name, ptr_ty, ptr_ty, class_name
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("is_trivial".into(), is_trivial.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_move_ctor(&self, class_name: &str, is_trivial: bool) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = if is_trivial {
format!(
"; trivial move constructor for {}: equivalent to copy (memcpy)",
class_name
)
} else {
format!(
"define void @_ZN{}C1EOS_(i{} %this, {} %other) {{\n ; non-trivial move constructor for {}\n ; move base classes, then member-wise move, then leave 'other' in valid-but-unspecified state\n ret void\n}}",
class_name, ptr_ty, ptr_ty, class_name
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("is_trivial".into(), is_trivial.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_copy_assign(&self, class_name: &str, is_trivial: bool) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = if is_trivial {
format!(
"; trivial copy assignment for {}: memcpy(this, other, sizeof({}))",
class_name, class_name
)
} else {
format!(
"define {} @_ZN{}aSERKS_(i{} %this, {} %other) {{\n ; non-trivial copy assignment for {}\n ; check self-assignment, copy bases, copy members, return *this\n ret {} %this\n}}",
ptr_ty, class_name, ptr_ty, ptr_ty, class_name, ptr_ty
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("is_trivial".into(), is_trivial.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_move_assign(&self, class_name: &str, is_trivial: bool) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = if is_trivial {
format!(
"; trivial move assignment for {}: equivalent to copy assignment",
class_name
)
} else {
format!(
"define {} @_ZN{}aSEOS_(i{} %this, {} %other) {{\n ; non-trivial move assignment for {}\n ; check self-assignment, move bases, move members, return *this\n ret {} %this\n}}",
ptr_ty, class_name, ptr_ty, ptr_ty, class_name, ptr_ty
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("is_trivial".into(), is_trivial.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_emit_vtable(&self, class_name: &str, is_primary: bool) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let linkage = if is_primary {
format!(
"@_ZTV{} = linkonce_odr dso_local unnamed_addr constant",
class_name
)
} else {
format!(
"@_ZTC{} = linkonce_odr dso_local unnamed_addr constant",
class_name
)
};
let ir = format!(
"{} [{} offset-to-top, {} typeinfo, ...] {{\n {} 0, ; offset-to-top\n {} @_ZTI{}, ; RTTI pointer\n ... ; virtual function pointers\n}}",
linkage,
ptr_ty,
ptr_ty,
ptr_ty,
ptr_ty,
class_name
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("is_primary".into(), is_primary.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_emit_vtt(&self, class_name: &str) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"@_ZTT{} = linkonce_odr dso_local unnamed_addr constant [{} ...]",
class_name, ptr_ty
);
X86CxxABIResult {
ir,
metadata: HashMap::from([("class".into(), class_name.to_string())]),
success: true,
error: None,
}
}
fn lower_itanium_guard_var(&self, var_name: &str, is_thread_safe: bool) -> X86CxxABIResult {
let guard_size = if self.is_64bit { 8 } else { 4 };
let ir = if is_thread_safe {
format!(
"@_ZGV{} = internal global i{} 0, align {} ; thread-safe guard variable for static local '{}'",
var_name, guard_size * 8, guard_size, var_name
)
} else {
format!(
"@_ZGV{} = internal global i8 0 ; non-thread-safe guard for '{}'",
var_name, var_name
)
};
X86CxxABIResult {
ir,
metadata: HashMap::from([
("var_name".into(), var_name.to_string()),
("is_thread_safe".into(), is_thread_safe.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_return_slot(&self, class_name: &str, is_named: bool) -> X86CxxABIResult {
let technique = if is_named { "NRVO" } else { "RVO" };
let ir = format!(
"; {} for class {}: caller provides hidden sret parameter pointing to return slot\n; This avoids copy/move of the return value.",
technique, class_name
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("technique".into(), technique.to_string()),
]),
success: true,
error: None,
}
}
fn lower_itanium_member_ptr(&self, class_name: &str, member_type: &str) -> X86CxxABIResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"%\"struct.member_ptr.{}:{}\" = type {{ {}, {} }} ; ptr + this-adjustment",
class_name, member_type, ptr_ty, ptr_ty
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("class".into(), class_name.to_string()),
("member_type".into(), member_type.to_string()),
]),
success: true,
error: None,
}
}
fn lower_msvc(&self, feature: &X86CxxABIFeature) -> X86CxxABIResult {
match feature {
X86CxxABIFeature::VtablePointer { class_name, offset } => {
self.lower_msvc_vtable_ptr(class_name, *offset)
}
X86CxxABIFeature::ThisCall {
class_name,
method_name,
is_virtual,
} => self.lower_msvc_this_call(class_name, method_name, *is_virtual),
_ => {
let ir = format!(
"; MSVC ABI lowering for {:?} — targeted for {} ({}-bit)\n; Using Microsoft x64 Software Conventions",
feature,
self.triple,
if self.is_64bit { 64 } else { 32 }
);
X86CxxABIResult {
ir,
metadata: HashMap::new(),
success: true,
error: None,
}
}
}
}
fn lower_msvc_vtable_ptr(&self, class_name: &str, offset: usize) -> X86CxxABIResult {
let ir = format!(
"%\"struct.{}\" = type {{ i8*, ... }} ; MSVC vfptr at offset {}\n; vbptr at offset {} if virtual bases present",
class_name, offset,
self.ptr_size
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("abi".into(), "msvc".into()),
("vtable_offset".into(), offset.to_string()),
]),
success: true,
error: None,
}
}
fn lower_msvc_this_call(
&self,
class_name: &str,
method_name: &str,
is_virtual: bool,
) -> X86CxxABIResult {
let this_reg = if self.is_64bit { "%rcx" } else { "%ecx" };
let ir = format!(
"; MSVC thiscall: this in {}\n; Method {}.{}\n",
this_reg, class_name, method_name
);
X86CxxABIResult {
ir,
metadata: HashMap::from([
("abi".into(), "msvc".into()),
("this_register".into(), this_reg.to_string()),
("is_virtual".into(), is_virtual.to_string()),
]),
success: true,
error: None,
}
}
pub fn compute_class_layout(
&mut self,
name: &str,
bases: &[(String, bool)], members: &[(String, usize, usize)], is_polymorphic: bool,
) -> X86ClassLayout {
let mut offset = 0usize;
let mut max_align = self.ptr_size;
let mut vtable_offset: Option<usize> = None;
let mut base_offsets = Vec::new();
let mut vbase_offsets = Vec::new();
let has_virtual_bases = bases.iter().any(|(_, v)| *v);
if is_polymorphic && !self.platform.uses_msvc_abi() {
vtable_offset = Some(offset);
offset = self.ptr_size;
}
for (base_name, is_virtual) in bases {
if !*is_virtual {
let align_mask = max_align - 1;
offset = (offset + align_mask) & !align_mask;
base_offsets.push((base_name.clone(), offset, false));
offset += self.ptr_size; }
}
for (member_name, size, align) in members {
max_align = std::cmp::max(max_align, *align);
let align_mask = *align - 1;
offset = (offset + align_mask) & !align_mask;
offset += size;
}
for (vbase_name, is_virtual) in bases {
if *is_virtual {
vbase_offsets.push((vbase_name.clone(), offset));
}
}
let align_mask = max_align - 1;
let total_size = (offset + align_mask) & !align_mask;
X86ClassLayout {
name: name.to_string(),
size: total_size,
alignment: max_align,
vtable_offset,
vtable_ptr_size: self.ptr_size,
base_offsets,
member_offsets: Vec::new(),
vbase_offsets,
is_pod: false,
is_trivially_copyable: false,
has_non_trivial_dtor: false,
has_virtual_bases,
}
}
pub fn abi_kind(&self) -> X86CxxABIKind {
self.abi_kind
}
pub fn ptr_size(&self) -> usize {
self.ptr_size
}
}
pub struct X86CxxExceptionHandling {
platform: X86CxxPlatform,
is_64bit: bool,
triple: String,
personality_fn: String,
eh_abi: X86EHABIKind,
lsda_format: X86LSDAFormat,
noexcept_cache: HashMap<String, bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86EHABIKind {
ItaniumDWARF,
MicrosoftSEH,
ARM,
SetjmpLongjmp,
Wasm,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86LSDAFormat {
GCC,
MSVCXData,
Compact,
}
#[derive(Debug, Clone)]
pub struct LSDADescriptor {
pub landing_pad_start: u32,
pub type_table_encoding: u8,
pub type_table_offset: u32,
pub call_site_encoding: u8,
pub call_site_table_length: u32,
}
#[derive(Debug, Clone)]
pub struct LandingPad {
pub id: u32,
pub label: String,
pub catch_types: Vec<String>,
pub catch_all: bool,
pub cleanups: Vec<String>,
pub filter_types: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct CallSiteEntry {
pub cs_start: u32,
pub cs_len: u32,
pub cs_lp: u32,
pub cs_action: u32,
}
#[derive(Debug, Clone)]
pub struct ActionEntry {
pub type_filter: i32,
pub next_action_offset: u32,
}
impl X86CxxExceptionHandling {
pub fn new(platform: X86CxxPlatform, is_64bit: bool, triple: &str) -> Self {
let (personality_fn, eh_abi, lsda_format) = if platform.uses_msvc_abi() {
(
"__CxxFrameHandler3".to_string(),
X86EHABIKind::MicrosoftSEH,
X86LSDAFormat::MSVCXData,
)
} else {
(
"__gxx_personality_v0".to_string(),
X86EHABIKind::ItaniumDWARF,
X86LSDAFormat::GCC,
)
};
X86CxxExceptionHandling {
platform,
is_64bit,
triple: triple.to_string(),
personality_fn,
eh_abi,
lsda_format,
noexcept_cache: HashMap::new(),
}
}
pub fn initialize(&mut self) {
self.noexcept_cache.clear();
}
pub fn personality_function(&self) -> &str {
&self.personality_fn
}
pub fn eh_abi_kind(&self) -> X86EHABIKind {
self.eh_abi
}
pub fn personality_decl(&self) -> String {
format!("declare i32 @{}()", self.personality_fn)
}
pub fn emit_landingpad(&self, pad: &LandingPad) -> String {
let mut ir = format!("{}:\n", pad.label);
ir.push_str(&format!(" %lp{} = landingpad {{ i8*, i32 }}\n", pad.id));
if !pad.cleanups.is_empty() {
ir.push_str(" cleanup\n");
}
for ty in &pad.catch_types {
ir.push_str(&format!(" catch i8* @_ZTI{}\n", ty));
}
if pad.catch_all {
ir.push_str(" catch i8* null\n");
}
for ft in &pad.filter_types {
ir.push_str(&format!(" filter [{} x i8*] [", ft));
ir.push_str("]\n");
}
ir.push_str(&format!(
" %exn{} = extractvalue {{ i8*, i32 }} %lp{}, 0\n",
pad.id, pad.id
));
ir.push_str(&format!(
" %sel{} = extractvalue {{ i8*, i32 }} %lp{}, 1\n",
pad.id, pad.id
));
ir
}
pub fn emit_lsda(
&self,
func_name: &str,
call_sites: &[CallSiteEntry],
actions: &[ActionEntry],
type_table: &[String],
has_cleanup: bool,
) -> String {
match self.lsda_format {
X86LSDAFormat::GCC => {
self.emit_lsda_gcc(func_name, call_sites, actions, type_table, has_cleanup)
}
X86LSDAFormat::MSVCXData => {
self.emit_lsda_msvc(func_name, call_sites, actions, type_table)
}
X86LSDAFormat::Compact => {
format!("; Compact LSDA for {} — not fully implemented\n", func_name)
}
}
}
fn emit_lsda_gcc(
&self,
func_name: &str,
call_sites: &[CallSiteEntry],
actions: &[ActionEntry],
type_table: &[String],
has_cleanup: bool,
) -> String {
let mut ir = format!(
" ; GCC LSDA for function {}\n ; @LPStart encoding: DW_EH_PE_omit\n ; @TType encoding: DW_EH_PE_absptr\n ; Call site encoding: DW_EH_PE_udata4\n",
func_name
);
ir.push_str(" ; Call site table:\n");
for cs in call_sites {
ir.push_str(&format!(
" ; cs_start: {}, cs_len: {}, landing_pad: {}, action: {}\n",
cs.cs_start, cs.cs_len, cs.cs_lp, cs.cs_action
));
}
if !actions.is_empty() {
ir.push_str(" ; Action table:\n");
for act in actions {
ir.push_str(&format!(
" ; type_filter: {}, next: {}\n",
act.type_filter, act.next_action_offset
));
}
}
if !type_table.is_empty() {
ir.push_str(" ; Type table:\n");
for (i, ty) in type_table.iter().enumerate() {
ir.push_str(&format!(" ; [{}]: @_ZTI{}\n", i, ty));
}
}
ir
}
fn emit_lsda_msvc(
&self,
func_name: &str,
call_sites: &[CallSiteEntry],
_actions: &[ActionEntry],
_type_table: &[String],
) -> String {
let mut ir = format!(
"; MSVC xdata LSDA for function {}\n; Structured Exception Handling (SEH) frame\n",
func_name
);
ir.push_str("; RUNTIME_FUNCTION entry:\n");
for cs in call_sites {
ir.push_str(&format!(
" ; BeginAddress: {}, EndAddress: {}, UnwindData: @unwind_{}_lp{}\n",
cs.cs_start,
cs.cs_start + cs.cs_len,
func_name,
cs.cs_lp
));
}
ir
}
pub fn is_noexcept(&self, func_name: &str) -> bool {
self.noexcept_cache.get(func_name).copied().unwrap_or(false)
}
pub fn mark_noexcept(&mut self, func_name: &str) {
self.noexcept_cache.insert(func_name.to_string(), true);
}
pub fn noexcept_metadata(&self, func_name: &str) -> String {
format!(
"; noexcept specification for {}\n!noexcept = !{{i32 1}}\n",
func_name
)
}
pub fn emit_throw(&self, exception_type: &str, exception_value: &str) -> String {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
format!(
" ; throw exception of type {}\n %exception = call {} @__cxa_allocate_exception(i64 {})\n call void @__cxa_throw({} %exception, {} @_ZTI{}, i8* null)\n unreachable\n",
exception_type,
ptr_ty,
if self.is_64bit { 8 } else { 4 },
ptr_ty,
ptr_ty,
exception_type
)
}
pub fn emit_try_catch(
&self,
try_body: &str,
catch_type: &str,
catch_body: &str,
landing_pad: &LandingPad,
) -> String {
let mut ir = String::new();
ir.push_str(" ; try block\n");
ir.push_str(try_body);
ir.push_str(&format!(" br label %try.cont.{}\n\n", landing_pad.id));
ir.push_str(&self.emit_landingpad(landing_pad));
ir.push_str(&format!(
" %matches{} = call i1 @__cxa_begin_catch(i8* %exn{})\n",
landing_pad.id, landing_pad.id
));
ir.push_str(catch_body);
ir.push_str(&format!(
" call void @__cxa_end_catch()\n br label %try.cont.{}\n\n",
landing_pad.id
));
ir.push_str(&format!("try.cont.{}:\n", landing_pad.id));
ir
}
pub fn emit_rethrow(&self) -> String {
" call void @__cxa_rethrow()\n unreachable\n".to_string()
}
pub fn emit_cxa_begin_catch(&self, landing_pad_id: u32) -> String {
format!(
" %catch_obj{} = call i8* @__cxa_begin_catch(i8* %exn{})\n",
landing_pad_id, landing_pad_id
)
}
pub fn emit_cxa_end_catch(&self) -> String {
" call void @__cxa_end_catch()\n".to_string()
}
pub fn emit_eh_runtime_decls(&self) -> String {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
format!(
"; Exception handling runtime declarations\n\
declare {} @__cxa_allocate_exception(i64)\n\
declare void @__cxa_throw({}, {}, i8*)\n\
declare i8* @__cxa_begin_catch(i8*)\n\
declare void @__cxa_end_catch()\n\
declare void @__cxa_rethrow()\n\
declare i8* @__cxa_get_exception_ptr(i8*)\n\
declare void @__cxa_call_unexpected(i8*)\n\
declare void @__cxa_bad_cast()\n\
declare void @__cxa_bad_typeid()\n\
declare void @__cxa_pure_virtual()\n\
declare void @__cxa_deleted_virtual()\n",
ptr_ty, ptr_ty, ptr_ty
)
}
}
pub struct X86CxxRTTI {
platform: X86CxxPlatform,
is_64bit: bool,
triple: String,
type_info_table: HashMap<String, X86TypeInfoEntry>,
rtti_builder: Option<RttiBuilder>,
}
#[derive(Debug, Clone)]
pub struct X86TypeInfoEntry {
pub type_name: String,
pub mangled_name: String,
pub ir: String,
pub is_class: bool,
pub is_pointer: bool,
pub vtable_offset: usize,
}
#[derive(Debug, Clone)]
pub struct TypeIDResult {
pub ir: String,
pub result_value: String,
}
#[derive(Debug, Clone)]
pub struct DynamicCastResult {
pub ir: String,
pub result_value: String,
pub is_cross_cast: bool,
}
impl X86CxxRTTI {
pub fn new(platform: X86CxxPlatform, is_64bit: bool, triple: &str) -> Self {
X86CxxRTTI {
platform,
is_64bit,
triple: triple.to_string(),
type_info_table: HashMap::new(),
rtti_builder: Some(RttiBuilder::new()),
}
}
pub fn initialize(&mut self) {
self.type_info_table.clear();
}
pub fn register_class_type(
&mut self,
type_name: &str,
mangled_name: &str,
base_classes: &[(String, bool, bool)], ) -> String {
if let Some(builder) = &mut self.rtti_builder {
let base_list: Vec<(String, bool, bool)> = base_classes.to_vec();
builder.build_class_rtti(type_name, &base_list);
}
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let ir = format!(
"@_ZTI{} = linkonce_odr dso_local constant {{\n i8* getelementptr inbounds (i8, i8* null, i64 0), ; vtable pointer to type_info vtable\n i8* getelementptr inbounds ([{} x i8], [{} x i8]* @_ZTS{}, i64 0, i64 0), ; mangled name string\n}}",
type_name,
type_name.len() + 1,
type_name.len() + 1,
type_name
);
let name_str = format!(
"@_ZTS{} = linkonce_odr constant [{} x i8] c\"{}\\00\"",
type_name,
type_name.len() + 1,
type_name
);
let entry = X86TypeInfoEntry {
type_name: type_name.to_string(),
mangled_name: mangled_name.to_string(),
ir: format!("{}\n{}", name_str, ir),
is_class: true,
is_pointer: false,
vtable_offset: 0,
};
self.type_info_table
.insert(type_name.to_string(), entry.clone());
entry.ir
}
pub fn has_type_info(&self, type_name: &str) -> bool {
self.type_info_table.contains_key(type_name)
}
pub fn get_type_info(&self, type_name: &str) -> Option<&X86TypeInfoEntry> {
self.type_info_table.get(type_name)
}
pub fn lower_typeid(&self, type_name: &str) -> TypeIDResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let entry = self.type_info_table.get(type_name);
let (ir, result) = if let Some(entry) = entry {
if entry.is_class {
let ir = format!(
" ; typeid({})\n %vtable_{} = load {}, {}* %obj_{}\n %ti_ptr_{} = getelementptr {}, {}* %vtable_{}, i64 -1\n %typeinfo_{} = load {}, {}* %ti_ptr_{}\n",
type_name,
type_name, ptr_ty, ptr_ty, type_name,
type_name, ptr_ty, ptr_ty, type_name,
type_name, ptr_ty, ptr_ty, type_name
);
(ir, format!("%typeinfo_{}", type_name))
} else {
let ir = format!(
" ; typeid({})\n %typeinfo_{} = bitcast {}* @_ZTI{} to {}*\n",
type_name, type_name, ptr_ty, type_name, ptr_ty
);
(ir, format!("%typeinfo_{}", type_name))
}
} else {
let ir = format!(
" ; typeid({}) — type not registered, would call __cxa_bad_typeid()\n",
type_name
);
(ir, "null".to_string())
};
TypeIDResult {
ir,
result_value: result,
}
}
pub fn lower_dynamic_cast(
&self,
source_type: &str,
target_type: &str,
source_ptr: &str,
) -> DynamicCastResult {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let src_entry = self.type_info_table.get(source_type);
let tgt_entry = self.type_info_table.get(target_type);
let is_cross_cast = false;
let ir = if src_entry.is_some() && tgt_entry.is_some() {
format!(
" ; dynamic_cast<{}*>({} {})\n %cast_result = call {} @__dynamic_cast(\n {} {},\n {}* @_ZTI{},\n {}* @_ZTI{},\n i64 -1)\n %cast_is_null = icmp eq {} %cast_result, null\n",
target_type, source_type, source_ptr,
ptr_ty,
ptr_ty, source_ptr,
ptr_ty, source_type,
ptr_ty, target_type,
ptr_ty
)
} else {
format!(
" ; dynamic_cast<{}*>({} {}) — using generic __dynamic_cast\n",
target_type, source_type, source_ptr
)
};
DynamicCastResult {
ir,
result_value: "%cast_result".to_string(),
is_cross_cast,
}
}
pub fn emit_dynamic_cast_decl(&self) -> String {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
format!(
"declare {} @__dynamic_cast({}, {}*, {}*, i64)\n",
ptr_ty, ptr_ty, ptr_ty, ptr_ty
)
}
pub fn emit_rtti_runtime_decls(&self) -> String {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
format!(
"declare {} @__dynamic_cast({}, {}*, {}*, i64)\n\
declare i1 @__cxa_guard_acquire({}*)\n\
declare void @__cxa_guard_release({}*)\n\
declare void @__cxa_guard_abort({}*)\n\
; type_info comparison uses pointer equality (Itanium ABI)\n\
; void* __cxa_bad_cast(); // called on failed dynamic_cast of references\n",
ptr_ty, ptr_ty, ptr_ty, ptr_ty, ptr_ty, ptr_ty, ptr_ty
)
}
pub fn emit_type_info_compare(&self, type1: &str, type2: &str) -> String {
format!(
" %ti_eq = icmp eq i8* @_ZTI{}, i8* @_ZTI{}\n ; type_info comparison is pointer equality in Itanium ABI\n",
type1, type2
)
}
}
pub struct X86CxxTemplates {
platform: X86CxxPlatform,
is_64bit: bool,
template_defs: HashMap<String, (Vec<String>, String)>,
instantiations: HashMap<String, String>,
pending_implicit: Vec<(String, Vec<X86CxxTemplateArg>)>,
odr_checked: HashSet<String>,
ptr_size: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86CxxTemplateArg {
Type(String),
Integral(i64),
Bool(bool),
Template(String),
NullPtr,
}
impl fmt::Display for X86CxxTemplateArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CxxTemplateArg::Type(t) => write!(f, "{}", t),
X86CxxTemplateArg::Integral(v) => write!(f, "{}", v),
X86CxxTemplateArg::Bool(b) => write!(f, "{}", b),
X86CxxTemplateArg::Template(t) => write!(f, "{}", t),
X86CxxTemplateArg::NullPtr => write!(f, "nullptr"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86DeductionResult {
pub success: bool,
pub deduced_args: HashMap<String, X86CxxTemplateArg>,
pub failure_reason: Option<String>,
}
#[derive(Debug, Clone)]
pub struct X86InstantiationContext {
pub template_name: String,
pub args: Vec<X86CxxTemplateArg>,
pub source_location: Option<String>,
pub depth: usize,
pub parent: Option<Box<X86InstantiationContext>>,
}
impl X86CxxTemplates {
pub fn new(platform: X86CxxPlatform, is_64bit: bool) -> Self {
X86CxxTemplates {
platform,
is_64bit,
template_defs: HashMap::new(),
instantiations: HashMap::new(),
pending_implicit: Vec::new(),
odr_checked: HashSet::new(),
ptr_size: if is_64bit { 8 } else { 4 },
}
}
pub fn initialize(&mut self) {
self.template_defs.clear();
self.instantiations.clear();
self.pending_implicit.clear();
self.odr_checked.clear();
}
pub fn register_template(&mut self, name: &str, param_names: Vec<String>, body: &str) {
self.template_defs
.insert(name.to_string(), (param_names, body.to_string()));
}
pub fn deduce_args(
&self,
template_name: &str,
call_args: &[X86CxxTemplateArg],
) -> X86DeductionResult {
let (params, _body) = match self.template_defs.get(template_name) {
Some(def) => def.clone(),
None => {
return X86DeductionResult {
success: false,
deduced_args: HashMap::new(),
failure_reason: Some(format!("Template '{}' not found", template_name)),
};
}
};
if params.len() != call_args.len() {
return X86DeductionResult {
success: false,
deduced_args: HashMap::new(),
failure_reason: Some(format!(
"Template '{}' expects {} parameter(s), got {} argument(s)",
template_name,
params.len(),
call_args.len()
)),
};
}
let mut deduced = HashMap::new();
for (i, param) in params.iter().enumerate() {
deduced.insert(param.clone(), call_args[i].clone());
}
X86DeductionResult {
success: true,
deduced_args: deduced,
failure_reason: None,
}
}
pub fn implicit_instantiate(
&mut self,
template_name: &str,
args: &[X86CxxTemplateArg],
) -> Result<String, String> {
let mangled = self.mangle_template_name(template_name, args);
if let Some(ir) = self.instantiations.get(&mangled) {
return Ok(ir.clone());
}
self.pending_implicit
.push((template_name.to_string(), args.to_vec()));
Ok(format!(
"; Pending implicit instantiation of {}<{}>\n",
template_name,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", ")
))
}
pub fn explicit_instantiate(
&mut self,
template_name: &str,
args: &[X86CxxTemplateArg],
) -> Result<String, String> {
self.instantiate(template_name, args)
}
pub fn instantiate(
&mut self,
template_name: &str,
args: &[X86CxxTemplateArg],
) -> Result<String, String> {
let (params, body) = self
.template_defs
.get(template_name)
.ok_or_else(|| format!("Template '{}' not defined", template_name))?
.clone();
if params.len() != args.len() {
return Err(format!(
"Template '{}' expects {} parameter(s), got {}",
template_name,
params.len(),
args.len()
));
}
let mangled = self.mangle_template_name(template_name, args);
if self.odr_checked.contains(&mangled) {
if let Some(existing) = self.instantiations.get(&mangled) {
return Ok(existing.clone());
}
}
let mut substituted = body.clone();
for (i, param) in params.iter().enumerate() {
let replacement = args[i].to_string();
substituted = substituted.replace(param, &replacement);
}
let ir = format!(
"; Template instantiation: {}<{}>\n\
; ODR COMDAT group: @{}\n\
{}\n",
template_name,
args.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", "),
mangled,
substituted
);
self.instantiations.insert(mangled.clone(), ir.clone());
self.odr_checked.insert(mangled);
Ok(ir)
}
pub fn mangle_template_name(&self, template_name: &str, args: &[X86CxxTemplateArg]) -> String {
let mut mangled = format!("_Z{}I", template_name.len());
mangled.push_str(template_name);
for arg in args {
match arg {
X86CxxTemplateArg::Type(t) => {
mangled.push_str(&format!("{}{}E", t.len(), t));
}
X86CxxTemplateArg::Integral(v) => {
mangled.push_str(&format!("Li{}E", v));
}
X86CxxTemplateArg::Bool(true) => {
mangled.push_str("Lb1E");
}
X86CxxTemplateArg::Bool(false) => {
mangled.push_str("Lb0E");
}
X86CxxTemplateArg::Template(t) => {
mangled.push_str(&format!("{}{}E", t.len(), t));
}
X86CxxTemplateArg::NullPtr => {
mangled.push_str("LDnE");
}
}
}
mangled.push('E'); mangled
}
pub fn check_odr(
&self,
template_name: &str,
args: &[X86CxxTemplateArg],
ir: &str,
) -> Result<(), String> {
let mangled = self.mangle_template_name(template_name, args);
if let Some(existing) = self.instantiations.get(&mangled) {
if existing != ir {
return Err(format!(
"ODR violation: template '{}' with args {:?} has conflicting definitions",
template_name, args
));
}
}
Ok(())
}
pub fn process_pending(&mut self) -> Result<Vec<String>, Vec<String>> {
let pending = std::mem::take(&mut self.pending_implicit);
let mut results = Vec::new();
let mut errors = Vec::new();
for (name, args) in pending {
match self.instantiate(&name, &args) {
Ok(ir) => results.push(ir),
Err(e) => errors.push(e),
}
}
if errors.is_empty() {
Ok(results)
} else {
Err(errors)
}
}
pub fn template_count(&self) -> usize {
self.template_defs.len()
}
pub fn instantiation_count(&self) -> usize {
self.instantiations.len()
}
pub fn instantiated_names(&self) -> Vec<&str> {
self.instantiations.keys().map(String::as_str).collect()
}
pub fn make_context(
template_name: &str,
args: Vec<X86CxxTemplateArg>,
source_location: Option<String>,
parent: Option<Box<X86InstantiationContext>>,
) -> X86InstantiationContext {
let depth = parent.as_ref().map(|p| p.depth + 1).unwrap_or(1);
X86InstantiationContext {
template_name: template_name.to_string(),
args,
source_location,
depth,
parent,
}
}
}
impl X86InstantiationContext {
pub fn backtrace(&self) -> String {
let mut bt = String::new();
let mut current: Option<&X86InstantiationContext> = Some(self);
while let Some(ctx) = current {
bt.push_str(&format!(
" in instantiation of '{}<{}>'",
ctx.template_name,
ctx.args
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(", ")
));
if let Some(ref loc) = ctx.source_location {
bt.push_str(&format!(" at {}", loc));
}
bt.push('\n');
current = ctx.parent.as_ref().map(|p| p.as_ref());
}
bt
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86CxxDiagLevel {
Note = 0,
Warning = 1,
Error = 2,
Fatal = 3,
}
impl fmt::Display for X86CxxDiagLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86CxxDiagLevel::Note => write!(f, "note"),
X86CxxDiagLevel::Warning => write!(f, "warning"),
X86CxxDiagLevel::Error => write!(f, "error"),
X86CxxDiagLevel::Fatal => write!(f, "fatal error"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CxxDiagnostic {
pub level: X86CxxDiagLevel,
pub message: String,
pub location: Option<String>,
pub code: Option<String>,
pub fixit: Option<String>,
}
impl fmt::Display for X86CxxDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref loc) = self.location {
write!(f, "{}:", loc)?;
}
write!(f, " {}: {}", self.level, self.message)?;
if let Some(ref code) = self.code {
write!(f, " [{}]", code)?;
}
if let Some(ref fixit) = self.fixit {
write!(f, "\n fix-it: {}", fixit)?;
}
Ok(())
}
}
pub struct X86CxxDiagnosticEngine {
diagnostics: Vec<X86CxxDiagnostic>,
error_limit: usize,
werror: bool,
error_count: usize,
warning_count: usize,
}
impl X86CxxDiagnosticEngine {
pub fn new() -> Self {
X86CxxDiagnosticEngine {
diagnostics: Vec::new(),
error_limit: 20,
werror: false,
error_count: 0,
warning_count: 0,
}
}
pub fn emit(&mut self, diag: X86CxxDiagnostic) {
let is_error = diag.level >= X86CxxDiagLevel::Error
|| (self.werror && diag.level == X86CxxDiagLevel::Warning);
if is_error {
self.error_count += 1;
}
if diag.level == X86CxxDiagLevel::Warning {
self.warning_count += 1;
}
self.diagnostics.push(diag);
}
pub fn error(&mut self, message: &str) {
self.emit(X86CxxDiagnostic {
level: X86CxxDiagLevel::Error,
message: message.to_string(),
location: None,
code: None,
fixit: None,
});
}
pub fn warning(&mut self, message: &str) {
self.emit(X86CxxDiagnostic {
level: X86CxxDiagLevel::Warning,
message: message.to_string(),
location: None,
code: None,
fixit: None,
});
}
pub fn has_errors(&self) -> bool {
self.error_count > 0
}
pub fn error_count(&self) -> usize {
self.error_count
}
pub fn warning_count(&self) -> usize {
self.warning_count
}
pub fn drain(&mut self) -> Vec<X86CxxDiagnostic> {
std::mem::take(&mut self.diagnostics)
}
pub fn render(&self) -> String {
self.diagnostics
.iter()
.map(|d| d.to_string())
.collect::<Vec<_>>()
.join("\n")
}
}
impl Default for X86CxxDiagnosticEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86VTableLayout {
pub class_name: String,
pub symbol: String,
pub entries: Vec<X86VTableEntry>,
pub is_primary: bool,
pub construction_vtables: Vec<X86ConstructionVTable>,
pub vtt_entries: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86VTableEntry {
pub index: usize,
pub kind: X86VTableEntryKind,
pub mangled: Option<String>,
pub this_adjustment: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum X86VTableEntryKind {
OffsetToTop,
RttiPointer,
VirtualFunction,
VirtualBaseOffset,
CompleteDtor,
DeletingDtor,
BaseObjectDtor,
ConstructionVBaseOffset,
}
#[derive(Debug, Clone)]
pub struct X86ConstructionVTable {
pub base_class: String,
pub complete_class: String,
pub entries: Vec<X86VTableEntry>,
pub in_object_delta: i64,
}
impl X86VTableLayout {
pub fn new(class_name: &str, is_primary: bool) -> Self {
let symbol = format!("_ZTV{}", class_name);
X86VTableLayout {
class_name: class_name.to_string(),
symbol,
entries: Vec::new(),
is_primary,
construction_vtables: Vec::new(),
vtt_entries: Vec::new(),
}
}
pub fn add_entry(&mut self, entry: X86VTableEntry) {
self.entries.push(entry);
}
pub fn add_construction_vtable(&mut self, cvt: X86ConstructionVTable) {
self.construction_vtables.push(cvt);
}
pub fn size_bytes(&self, ptr_size: usize) -> usize {
let main_size = self.entries.len() * ptr_size;
let cvt_size: usize = self
.construction_vtables
.iter()
.map(|cvt| cvt.entries.len() * ptr_size)
.sum();
main_size + cvt_size
}
pub fn emit_ir(&self, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
let mut ir = format!(
"@{} = linkonce_odr dso_local unnamed_addr constant [{} x {}] [\n",
self.symbol,
self.entries.len(),
ptr_ty
);
for entry in &self.entries {
match &entry.kind {
X86VTableEntryKind::OffsetToTop => {
ir.push_str(&format!(" {} 0, ; offset-to-top\n", ptr_ty));
}
X86VTableEntryKind::RttiPointer => {
let rtti_sym = format!("@_ZTI{}", self.class_name);
ir.push_str(&format!(
" {} ptrtoint ({}* {} to {}), ; RTTI pointer\n",
ptr_ty, ptr_ty, rtti_sym, ptr_ty
));
}
X86VTableEntryKind::VirtualFunction => {
if let Some(ref mangled) = entry.mangled {
ir.push_str(&format!(
" {} ptrtoint ({} ()* @{} to {}), ; virtual fn\n",
ptr_ty, ptr_ty, mangled, ptr_ty
));
}
}
_ => {
ir.push_str(&format!(" {} 0, ; {:?}\n", ptr_ty, entry.kind));
}
}
}
ir.push_str("]\n");
ir
}
}
#[derive(Debug, Clone)]
pub struct X86ThunkInfo {
pub mangled_name: String,
pub target_mangled: String,
pub this_adjustment: i64,
pub vcall_offset: Option<i64>,
pub return_adjustment: Option<i64>,
}
impl X86ThunkInfo {
pub fn new(mangled_name: String, target_mangled: String, this_adjustment: i64) -> Self {
X86ThunkInfo {
mangled_name,
target_mangled,
this_adjustment,
vcall_offset: None,
return_adjustment: None,
}
}
pub fn with_vcall_offset(mut self, offset: i64) -> Self {
self.vcall_offset = Some(offset);
self
}
pub fn with_return_adjustment(mut self, adj: i64) -> Self {
self.return_adjustment = Some(adj);
self
}
pub fn emit_ir(&self, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
let mut ir = format!(
"define linkonce_odr void @{}({} %this, ...) {{\n",
self.mangled_name, ptr_ty
);
if self.this_adjustment != 0 {
ir.push_str(&format!(
" %adjusted_this = getelementptr i8, {} %this, i64 {}\n",
ptr_ty, self.this_adjustment
));
} else {
ir.push_str(" %adjusted_this = bitcast i8* %this to i8*\n");
}
if let Some(vcall) = self.vcall_offset {
ir.push_str(&format!(
" %vtable = load {}, {}* %adjusted_this\n",
ptr_ty, ptr_ty
));
ir.push_str(&format!(
" %vbase_offset_ptr = getelementptr {}, {}* %vtable, i64 {}\n",
ptr_ty, ptr_ty, vcall
));
ir.push_str(&format!(
" %vbase_offset = load {}, {}* %vbase_offset_ptr\n",
ptr_ty, ptr_ty
));
ir.push_str(&format!(
" %final_this = getelementptr i8, {} %adjusted_this, {} %vbase_offset\n",
ptr_ty, ptr_ty
));
}
ir.push_str(&format!(
" tail call void @{}(...)\n ret void\n}}\n",
self.target_mangled
));
ir
}
}
#[derive(Debug, Clone)]
pub struct X86StaticGuard {
pub name: String,
pub guard_name: String,
pub is_thread_safe: bool,
pub init_started_bit: u8,
pub init_complete_bit: u8,
pub address: Option<u64>,
}
impl X86StaticGuard {
pub fn new(var_name: &str, is_thread_safe: bool) -> Self {
let guard_name = format!("_ZGV{}", var_name);
X86StaticGuard {
name: var_name.to_string(),
guard_name,
is_thread_safe,
init_started_bit: if is_thread_safe { 7 } else { 0 },
init_complete_bit: 0,
address: None,
}
}
pub fn emit_ir(&self, ptr_size: usize) -> String {
if self.is_thread_safe {
let guard_size = if ptr_size == 8 { 64 } else { 32 };
format!(
"@{} = internal global i{} 0, align {}\n",
self.guard_name, guard_size, ptr_size
)
} else {
format!("@{} = internal global i8 0\n", self.guard_name)
}
}
pub fn emit_guard_pattern(&self, init_code: &str) -> String {
let ptr_ty = if self.is_thread_safe { "i64" } else { "i8" };
format!(
" ; Static local guard pattern for '{}'\n\
; Check if already initialized\n\
%guard_load = load {}, {}* @{}\n\
%guard_cmp = icmp eq {} %guard_load, 1\n\
br i1 %guard_cmp, label %init_done, label %do_init\n\
\n\
do_init:\n\
; Acquire guard (or call __cxa_guard_acquire)\n\
%guard_acquired = call i1 @__cxa_guard_acquire({}* @{})\n\
br i1 %guard_acquired, label %init_body, label %wait_for_init\n\
\n\
init_body:\n\
{}\n\
call void @__cxa_guard_release({}* @{})\n\
br label %init_done\n\
\n\
wait_for_init:\n\
; Spin-wait for init to complete\n\
call void @__cxa_guard_wait({}* @{})\n\
br label %init_done\n\
\n\
init_done:\n",
self.name,
ptr_ty,
ptr_ty,
self.guard_name,
ptr_ty,
ptr_ty,
self.guard_name,
init_code,
ptr_ty,
self.guard_name,
ptr_ty,
self.guard_name
)
}
}
#[derive(Debug, Clone)]
pub struct X86ArrayCtorDtorInfo {
pub element_type: String,
pub count: usize,
pub is_construction: bool,
pub is_trivial: bool,
pub loop_ir: Option<String>,
}
impl X86ArrayCtorDtorInfo {
pub fn new(element_type: &str, count: usize, is_construction: bool, is_trivial: bool) -> Self {
X86ArrayCtorDtorInfo {
element_type: element_type.to_string(),
count,
is_construction,
is_trivial,
loop_ir: None,
}
}
pub fn generate_loop(&mut self, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
if self.is_trivial {
if self.is_construction {
format!(
"; trivial array construction for {} * {} {}: no-op\n",
ptr_ty, self.element_type, self.count
)
} else {
format!(
"; trivial array destruction for {} * {}: no-op\n",
self.element_type, self.count
)
}
} else {
let op = if self.is_construction {
"construct"
} else {
"destroy"
};
let ir = format!(
" ; Array {} loop for {} elements of type {}\n\
%array_ptr = alloca {}, i64 {}\n\
%i = alloca i64\n\
store i64 0, {}* %i\n\
br label %loop_header\n\
\n\
loop_header:\n\
%idx = load i64, {}* %i\n\
%done = icmp eq i64 %idx, {}\n\
br i1 %done, label %loop_exit, label %loop_body\n\
\n\
loop_body:\n\
%elem_ptr = getelementptr {}, {}* %array_ptr, i64 %idx\n\
call void @{}({}* %elem_ptr)\n\
%next_idx = add i64 %idx, 1\n\
store i64 %next_idx, {}* %i\n\
br label %loop_header\n\
\n\
loop_exit:\n",
op,
self.count,
self.element_type,
ptr_ty,
self.count,
ptr_ty,
ptr_ty,
self.count,
ptr_ty,
ptr_ty,
op,
ptr_ty,
ptr_ty
);
self.loop_ir = Some(ir.clone());
ir
}
}
}
#[derive(Debug, Clone)]
pub struct X86NewDeleteLowering {
pub alloc_size: usize,
pub alignment: usize,
pub is_array: bool,
pub is_nothrow: bool,
pub is_aligned: bool,
pub placement_args: Vec<String>,
pub ir: Option<String>,
}
impl X86NewDeleteLowering {
pub fn new_scalar(size: usize, alignment: usize) -> Self {
X86NewDeleteLowering {
alloc_size: size,
alignment,
is_array: false,
is_nothrow: false,
is_aligned: false,
placement_args: Vec::new(),
ir: None,
}
}
pub fn as_array(mut self) -> Self {
self.is_array = true;
self
}
pub fn as_nothrow(mut self) -> Self {
self.is_nothrow = true;
self
}
pub fn as_aligned(mut self) -> Self {
self.is_aligned = true;
self
}
pub fn with_placement(mut self, arg: String) -> Self {
self.placement_args.push(arg);
self
}
pub fn generate_new_ir(&self, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
if !self.placement_args.is_empty() {
format!(
" ; placement new: return placement pointer\n %new_ptr = bitcast i8* {} to {}*\n",
self.placement_args[0], ptr_ty
)
} else if self.is_nothrow {
let op = if self.is_array {
"_ZnamRKSt9nothrow_t"
} else {
"_ZnwmRKSt9nothrow_t"
};
format!(
" ; nothrow operator new (size: {}, align: {})\n\
%new_ptr = call {} @{}(i64 {}, i8* @__nothrow)\n\
%new_ptr_null = icmp eq {} %new_ptr, null\n",
self.alloc_size, self.alignment, ptr_ty, op, self.alloc_size, ptr_ty
)
} else if self.is_aligned {
let op = if self.is_array {
"_ZnamSt11align_val_t"
} else {
"_ZnwmSt11align_val_t"
};
format!(
" ; aligned operator new (size: {}, align: {})\n\
%new_ptr = call {} @{}(i64 {}, i64 {})\n",
self.alloc_size, self.alignment, ptr_ty, op, self.alloc_size, self.alignment
)
} else {
let op = if self.is_array { "_Znam" } else { "_Znwm" };
format!(
" ; operator new (size: {}, align: {})\n\
%new_ptr = call {} @{}(i64 {})\n",
self.alloc_size, self.alignment, ptr_ty, op, self.alloc_size
)
}
}
pub fn generate_delete_ir(&self, ptr: &str, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
let op = if self.is_array {
if self.is_aligned {
"_ZdaPvSt11align_val_t"
} else if self.is_nothrow {
"_ZdaPvRKSt9nothrow_t"
} else {
"_ZdaPv"
}
} else {
if self.is_aligned {
"_ZdlPvSt11align_val_t"
} else if self.is_nothrow {
"_ZdlPvRKSt9nothrow_t"
} else {
"_ZdlPv"
}
};
format!(
" ; operator delete (ptr: {})\n call void @{}({} {})\n",
ptr, op, ptr_ty, ptr
)
}
}
#[derive(Debug, Clone)]
pub struct X86ConcurrencyLowering {
pub lock_free_sizes: Vec<usize>,
pub mutex_kind: X86MutexKind,
pub condvar_kind: X86CondVarKind,
pub tls_model: X86TLSModel,
pub has_tsx: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86MutexKind {
PthreadMutex,
WindowsSRWLock,
Futex,
Spinlock,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CondVarKind {
PthreadCond,
WindowsCondVar,
FutexCond,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TLSModel {
ELFTLS,
MachOTLS,
WindowsTLS,
EmulatedTLS,
}
impl X86ConcurrencyLowering {
pub fn new(platform: X86CxxPlatform, is_64bit: bool) -> Self {
let (mutex_kind, condvar_kind, tls_model) = match platform {
X86CxxPlatform::LinuxGnu | X86CxxPlatform::LinuxMusl | X86CxxPlatform::Android => (
X86MutexKind::Futex,
X86CondVarKind::FutexCond,
X86TLSModel::ELFTLS,
),
X86CxxPlatform::FreeBSD => (
X86MutexKind::PthreadMutex,
X86CondVarKind::PthreadCond,
X86TLSModel::ELFTLS,
),
X86CxxPlatform::Darwin => (
X86MutexKind::PthreadMutex,
X86CondVarKind::PthreadCond,
X86TLSModel::MachOTLS,
),
X86CxxPlatform::WindowsMSVC | X86CxxPlatform::WindowsMinGW => (
X86MutexKind::WindowsSRWLock,
X86CondVarKind::WindowsCondVar,
X86TLSModel::WindowsTLS,
),
_ => (
X86MutexKind::Spinlock,
X86CondVarKind::PthreadCond,
X86TLSModel::EmulatedTLS,
),
};
let lock_free_sizes = if is_64bit {
vec![1, 2, 4, 8, 16] } else {
vec![1, 2, 4] };
X86ConcurrencyLowering {
lock_free_sizes,
mutex_kind,
condvar_kind,
tls_model,
has_tsx: false, }
}
pub fn is_lock_free(&self, size: usize) -> bool {
self.lock_free_sizes.contains(&size)
}
pub fn emit_mutex_lock(&self, mutex_ptr: &str) -> String {
match self.mutex_kind {
X86MutexKind::PthreadMutex => {
format!(" call i32 @pthread_mutex_lock(i8* {})\n", mutex_ptr)
}
X86MutexKind::WindowsSRWLock => {
format!(" call void @AcquireSRWLockExclusive(i8* {})\n", mutex_ptr)
}
X86MutexKind::Futex => {
format!(
" ; futex-based mutex lock on {}\n call i32 @__llvm_futex_lock(i32* {})\n",
mutex_ptr, mutex_ptr
)
}
X86MutexKind::Spinlock => {
format!(
" ; spinlock acquire on {}\n call void @spin_lock(i8* {})\n",
mutex_ptr, mutex_ptr
)
}
}
}
pub fn emit_mutex_unlock(&self, mutex_ptr: &str) -> String {
match self.mutex_kind {
X86MutexKind::PthreadMutex => {
format!(" call i32 @pthread_mutex_unlock(i8* {})\n", mutex_ptr)
}
X86MutexKind::WindowsSRWLock => {
format!(" call void @ReleaseSRWLockExclusive(i8* {})\n", mutex_ptr)
}
X86MutexKind::Futex => {
format!(" call i32 @__llvm_futex_unlock(i32* {})\n", mutex_ptr)
}
X86MutexKind::Spinlock => {
format!(" call void @spin_unlock(i8* {})\n", mutex_ptr)
}
}
}
pub fn emit_cond_wait(&self, cv_ptr: &str, mutex_ptr: &str) -> String {
match self.condvar_kind {
X86CondVarKind::PthreadCond => {
format!(
" call i32 @pthread_cond_wait(i8* {}, i8* {})\n",
cv_ptr, mutex_ptr
)
}
X86CondVarKind::WindowsCondVar => {
format!(
" call i32 @SleepConditionVariableSRW(i8* {}, i8* {}, i32 -1, i32 0)\n",
cv_ptr, mutex_ptr
)
}
X86CondVarKind::FutexCond => {
format!(
" call i32 @__llvm_futex_cond_wait(i32* {}, i32* {})\n",
cv_ptr, mutex_ptr
)
}
}
}
pub fn emit_cond_notify_one(&self, cv_ptr: &str) -> String {
match self.condvar_kind {
X86CondVarKind::PthreadCond => {
format!(" call i32 @pthread_cond_signal(i8* {})\n", cv_ptr)
}
X86CondVarKind::WindowsCondVar => {
format!(" call void @WakeConditionVariable(i8* {})\n", cv_ptr)
}
X86CondVarKind::FutexCond => {
format!(" call i32 @__llvm_futex_cond_signal(i32* {})\n", cv_ptr)
}
}
}
pub fn emit_tls_access(&self, var_name: &str, ptr_size: usize) -> String {
let ptr_ty = if ptr_size == 8 { "i64" } else { "i32" };
match self.tls_model {
X86TLSModel::ELFTLS => {
format!(
" %tls_addr = call {}* @__tls_get_addr({}* @{})\n",
ptr_ty, ptr_ty, var_name
)
}
X86TLSModel::MachOTLS => {
format!(
" %tls_addr = call {}* @tlv_get_address({}* @{})\n",
ptr_ty, ptr_ty, var_name
)
}
X86TLSModel::WindowsTLS => {
format!(
" %tls_addr = call {}* @__tls_get_addr_internal({}* @{})\n",
ptr_ty, ptr_ty, var_name
)
}
X86TLSModel::EmulatedTLS => {
format!(
" ; emulated TLS: load from global TLS map\n %tls_key = load {}, {}* @tls_key_for_{}\n %tls_addr = call {}* @pthread_getspecific(i32 %tls_key)\n",
ptr_ty, ptr_ty, var_name, ptr_ty
)
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86MemoryOrder {
Relaxed = 0,
Consume = 1,
Acquire = 2,
Release = 3,
AcqRel = 4,
SeqCst = 5,
}
impl X86MemoryOrder {
pub fn to_llvm_ordering(&self) -> &'static str {
match self {
X86MemoryOrder::Relaxed => "monotonic",
X86MemoryOrder::Consume => "acquire", X86MemoryOrder::Acquire => "acquire",
X86MemoryOrder::Release => "release",
X86MemoryOrder::AcqRel => "acq_rel",
X86MemoryOrder::SeqCst => "seq_cst",
}
}
pub fn requires_mfence(&self) -> bool {
matches!(self, X86MemoryOrder::SeqCst)
}
pub fn is_compiler_barrier_only(&self) -> bool {
matches!(self, X86MemoryOrder::Relaxed)
}
}
impl fmt::Display for X86MemoryOrder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
X86MemoryOrder::Relaxed => write!(f, "memory_order_relaxed"),
X86MemoryOrder::Consume => write!(f, "memory_order_consume"),
X86MemoryOrder::Acquire => write!(f, "memory_order_acquire"),
X86MemoryOrder::Release => write!(f, "memory_order_release"),
X86MemoryOrder::AcqRel => write!(f, "memory_order_acq_rel"),
X86MemoryOrder::SeqCst => write!(f, "memory_order_seq_cst"),
}
}
}
pub struct X86AtomicLowering {
platform: X86CxxPlatform,
is_64bit: bool,
}
impl X86AtomicLowering {
pub fn new(platform: X86CxxPlatform, is_64bit: bool) -> Self {
X86AtomicLowering { platform, is_64bit }
}
pub fn emit_fence(&self, order: X86MemoryOrder) -> String {
if order.requires_mfence() {
" ; seq_cst fence on x86\n fence seq_cst\n ; or: mfence (full memory barrier)\n"
.to_string()
} else if order == X86MemoryOrder::Release {
" ; release fence on x86: compiler barrier only (x86 TSO ensures no StoreLoad reorder)\n fence release\n".to_string()
} else {
format!(
" ; acquire fence: x86 load has implicit acquire semantics\n fence {}\n",
order.to_llvm_ordering()
)
}
}
pub fn emit_atomic_load(
&self,
dest: &str,
ptr: &str,
ty: &str,
order: X86MemoryOrder,
) -> String {
format!(
" {} = load atomic {}, {}* {}, align {} {}\n",
dest,
ty,
ty,
ptr,
if ty == "i64" { 8 } else { 4 },
order.to_llvm_ordering()
)
}
pub fn emit_atomic_store(
&self,
ptr: &str,
value: &str,
ty: &str,
order: X86MemoryOrder,
) -> String {
format!(
" store atomic {} {}, {}* {}, align {} {}\n",
ty,
value,
ty,
ptr,
if ty == "i64" { 8 } else { 4 },
order.to_llvm_ordering()
)
}
pub fn emit_atomic_cmpxchg(
&self,
result: &str,
ptr: &str,
expected: &str,
desired: &str,
ty: &str,
success_order: X86MemoryOrder,
failure_order: X86MemoryOrder,
) -> String {
format!(
" {} = cmpxchg {}* {}, {} {}, {} {} {} {}\n",
result,
ty,
ptr,
ty,
expected,
ty,
desired,
success_order.to_llvm_ordering(),
failure_order.to_llvm_ordering()
)
}
pub fn emit_atomic_rmw(
&self,
result: &str,
op: &str, ptr: &str,
value: &str,
ty: &str,
order: X86MemoryOrder,
) -> String {
format!(
" {} = atomicrmw {} {}* {}, {} {} {}\n",
result,
op,
ty,
ptr,
ty,
value,
order.to_llvm_ordering()
)
}
}
#[derive(Debug, Clone)]
pub struct X86StdLibCompat {
pub primary: CXXStdLib,
pub libcxx_libstdcxx_interop: bool,
pub incompatibilities: Vec<X86ABIIncompatibility>,
pub required_libs: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86ABIIncompatibility {
pub feature: String,
pub description: String,
pub affects: Vec<CXXStdLib>,
pub workaround: Option<String>,
}
impl X86StdLibCompat {
pub fn new(platform: X86CxxPlatform) -> Self {
let primary = match platform.default_stdlib() {
"libstdc++" => CXXStdLib::LibStdCxx,
_ => CXXStdLib::LibCxx,
};
let mut incompatibilities = Vec::new();
incompatibilities.push(X86ABIIncompatibility {
feature: "std::string SSO layout".into(),
description: "libc++ uses 24-byte string (22-char SSO), libstdc++ uses 32-byte string (15-char SSO). Passing std::string across library boundaries requires matching implementations.".into(),
affects: vec![CXXStdLib::LibCxx, CXXStdLib::LibStdCxx],
workaround: Some("Use std::string_view or const char* at ABI boundaries".into()),
});
incompatibilities.push(X86ABIIncompatibility {
feature: "std::list node layout".into(),
description:
"Node size and alignment of std::list may differ due to allocator differences."
.into(),
affects: vec![CXXStdLib::LibCxx, CXXStdLib::LibStdCxx],
workaround: Some("Avoid passing std::list across shared library boundaries".into()),
});
incompatibilities.push(X86ABIIncompatibility {
feature: "Exception type matching".into(),
description: "RTTI-based catch matching may fail across different standard library implementations.".into(),
affects: vec![CXXStdLib::LibCxx, CXXStdLib::LibStdCxx],
workaround: Some("Catch by std::exception& only at boundaries".into()),
});
incompatibilities.push(X86ABIIncompatibility {
feature: "iostream initialization order".into(),
description: "The order of std::cin/cout/cerr initialization may differ.".into(),
affects: vec![CXXStdLib::LibCxx, CXXStdLib::LibStdCxx],
workaround: Some("Use ios_base::Init or avoid static-init-time iostream usage".into()),
});
let required_libs = match platform {
X86CxxPlatform::LinuxGnu => vec!["-lstdc++".into(), "-lm".into(), "-lpthread".into()],
X86CxxPlatform::LinuxMusl => vec!["-lc++".into(), "-lc++abi".into(), "-lunwind".into()],
X86CxxPlatform::Darwin => vec!["-lc++".into()],
X86CxxPlatform::FreeBSD => vec!["-lc++".into(), "-lpthread".into()],
X86CxxPlatform::WindowsMSVC => vec!["msvcp140.lib".into(), "vcruntime.lib".into()],
X86CxxPlatform::WindowsMinGW => vec!["-lstdc++".into(), "-lpthread".into()],
_ => vec!["-lc++".into()],
};
X86StdLibCompat {
primary,
libcxx_libstdcxx_interop: false,
incompatibilities,
required_libs,
}
}
pub fn linker_flags(&self) -> Vec<String> {
self.required_libs.clone()
}
pub fn check_feature(&self, feature: &str) -> Option<&X86ABIIncompatibility> {
self.incompatibilities.iter().find(|i| i.feature == feature)
}
}
pub struct X86MSVCABILowering {
pub is_64bit: bool,
pub uses_complete_object_locator: bool,
pub uses_class_hierarchy_descriptor: bool,
}
#[derive(Debug, Clone)]
pub struct X86MSVCCompleteObjectLocator {
pub signature: u32,
pub offset: u32,
pub cd_offset: u32,
pub type_descriptor_ptr: u64,
pub class_hierarchy_descriptor_ptr: u64,
pub self_relative_ptr: u64,
}
#[derive(Debug, Clone)]
pub struct X86MSVCClassHierarchyDescriptor {
pub signature: u32,
pub attributes: u32,
pub num_base_classes: u32,
pub base_class_array_ptr: u64,
}
#[derive(Debug, Clone)]
pub struct X86MSVCBaseClassDescriptor {
pub type_descriptor_ptr: u64,
pub num_contained_bases: u32,
pub mdisp: i32,
pub pdisp: i32,
pub vdisp: i32,
pub attributes: u32,
pub class_hierarchy_descriptor_ptr: u64,
}
impl X86MSVCABILowering {
pub fn new(is_64bit: bool) -> Self {
X86MSVCABILowering {
is_64bit,
uses_complete_object_locator: true,
uses_class_hierarchy_descriptor: true,
}
}
pub fn emit_complete_object_locator(&self, class_name: &str, vftable_offset: u32) -> String {
format!(
"@_RTC{} = private constant {{ i32, i32, i32, i8*, i8*, i8* }} {{\n\
i32 0, ; signature\n\
i32 {}, ; vftable offset\n\
i32 0, ; cd_offset\n\
i8* bitcast ({}* @_RTI{} to i8*), ; type descriptor\n\
i8* bitcast ({}* @_RTC1{} to i8*), ; class hierarchy\n\
i8* null ; self-relative pointer (patched)\n\
}}",
class_name,
vftable_offset,
if self.is_64bit { "i64" } else { "i32" },
class_name,
if self.is_64bit { "i64" } else { "i32" },
class_name
)
}
pub fn emit_vftable(&self, class_name: &str, virtual_fns: &[String]) -> String {
let ptr_ty = if self.is_64bit { "i64" } else { "i32" };
let mut ir = format!(
"@_VFT{} = private constant [{} x {}] [\n",
class_name,
virtual_fns.len() + 1,
ptr_ty
);
ir.push_str(&format!(
" {} ptrtoint ({}* @_RTC{} to {}), ; meta pointer (RTTI COL)\n",
ptr_ty, ptr_ty, class_name, ptr_ty
));
for (i, fn_name) in virtual_fns.iter().enumerate() {
ir.push_str(&format!(
" {} ptrtoint ({} ()* @{} to {}), ; vfn[{}]\n",
ptr_ty, ptr_ty, fn_name, ptr_ty, i
));
}
ir.push_str("]\n");
ir
}
pub fn emit_member_pointer_type(&self, class_name: &str, member_type: &str) -> String {
if self.is_64bit {
format!(
"%\"member.ptr.{}.{}\" = type {{ i32 }} ; single inheritance (MSVC x64)\n\
%\"member.ptr.multi.{}.{}\" = type {{ i64, i32, i32 }} ; multiple inheritance (MSVC x64)",
class_name, member_type,
class_name, member_type
)
} else {
format!(
"%\"member.ptr.{}.{}\" = type {{ i32 }} ; single inheritance (MSVC x86)\n\
%\"member.ptr.multi.{}.{}\" = type {{ i32, i32, i32 }} ; multiple inheritance (MSVC x86)",
class_name, member_type,
class_name, member_type
)
}
}
pub fn emit_seh_catch_handler(
&self,
func_name: &str,
catch_type: &str,
handler_body: &str,
) -> String {
format!(
"define internal i32 @{}catch_{}(i8* %exception, i8* %establisher_frame) personality i32 (...)* @__CxxFrameHandler3 {{\n\
entry:\n\
%is_type = call i1 @__RTtypeid(i8* %exception, i8* bitcast ({}* @_RTI{} to i8*))\n\
br i1 %is_type, label %handle, label %continue_search\n\
handle:\n\
{}\n\
ret i32 1 ; EXCEPTION_EXECUTE_HANDLER\n\
continue_search:\n\
ret i32 0 ; EXCEPTION_CONTINUE_SEARCH\n\
}}",
func_name,
catch_type,
if self.is_64bit { "i64" } else { "i32" },
catch_type,
handler_body
)
}
}
#[derive(Debug, Clone)]
pub struct X86CxxTypeTraitsLowering {
pub data_layout: String,
pub char_is_signed: bool,
pub wchar_size: usize,
pub long_double_size: usize,
pub pointer_size_bits: usize,
pub size_t_size: usize,
pub max_align: usize,
}
impl X86CxxTypeTraitsLowering {
pub fn new(is_64bit: bool, platform: X86CxxPlatform) -> Self {
let (pointer_size_bits, size_t_size, wchar_size, long_double_size) = if is_64bit {
(
64,
8,
4,
if platform == X86CxxPlatform::LinuxGnu {
16
} else {
16
},
)
} else {
(32, 4, 4, 12)
};
let max_align = if is_64bit { 16 } else { 8 };
let char_is_signed = matches!(
platform,
X86CxxPlatform::LinuxGnu | X86CxxPlatform::Darwin | X86CxxPlatform::FreeBSD
);
let data_layout = if is_64bit {
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into()
} else {
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32-S128".into()
};
X86CxxTypeTraitsLowering {
data_layout,
char_is_signed,
wchar_size,
long_double_size,
pointer_size_bits,
size_t_size,
max_align,
}
}
pub fn evaluate_trait(&self, trait_name: &str, type_name: &str) -> Option<bool> {
match trait_name {
"is_void" => Some(type_name == "void"),
"is_integral" => Some(matches!(
type_name,
"bool"
| "char"
| "signed char"
| "unsigned char"
| "short"
| "unsigned short"
| "int"
| "unsigned int"
| "long"
| "unsigned long"
| "long long"
| "unsigned long long"
)),
"is_floating_point" => Some(matches!(type_name, "float" | "double" | "long double")),
"is_pointer" => Some(type_name.ends_with('*')),
"is_lvalue_reference" => Some(type_name.ends_with('&')),
"is_rvalue_reference" => Some(type_name.ends_with("&&")),
"is_array" => Some(type_name.contains('[')),
"is_const" => Some(type_name.starts_with("const ")),
"is_signed" => Some(matches!(
type_name,
"signed char"
| "short"
| "int"
| "long"
| "long long"
| "float"
| "double"
| "long double"
)),
"is_unsigned" => Some(matches!(
type_name,
"bool"
| "unsigned char"
| "unsigned short"
| "unsigned int"
| "unsigned long"
| "unsigned long long"
)),
"is_empty" => Some(false),
"is_polymorphic" => Some(false),
"is_abstract" => Some(false),
"is_final" => Some(false),
"is_trivial" => {
Some(type_name == "int" || type_name == "char" || type_name == "double")
}
"is_standard_layout" => Some(true),
"is_trivially_copyable" => Some(true),
"is_pod" => Some(true),
"is_literal_type" => Some(true),
_ => None,
}
}
pub fn size_of(&self, type_name: &str) -> Option<usize> {
match type_name {
"void" => Some(0),
"bool" => Some(1),
"char" | "signed char" | "unsigned char" | "char8_t" => Some(1),
"short" | "unsigned short" => Some(2),
"wchar_t" | "char16_t" => Some(self.wchar_size),
"char32_t" => Some(4),
"int" | "unsigned int" => Some(4),
"long" | "unsigned long" => {
if self.pointer_size_bits == 64 {
Some(8)
} else {
Some(4)
}
}
"long long" | "unsigned long long" => Some(8),
"__int128" | "unsigned __int128" => Some(16),
"float" => Some(4),
"double" => Some(8),
"long double" => Some(self.long_double_size),
"size_t" => Some(self.size_t_size),
"ptrdiff_t" => Some(self.size_t_size),
"intptr_t" | "uintptr_t" => Some(self.size_t_size),
"nullptr_t" => Some(self.size_t_size),
_ => None,
}
}
pub fn align_of(&self, type_name: &str) -> Option<usize> {
let size = self.size_of(type_name)?;
if size > self.max_align {
Some(self.max_align)
} else {
Some(size)
}
}
pub fn emit_sizeof(&self, type_name: &str) -> String {
let size = self.size_of(type_name).unwrap_or(0);
format!(
" ; sizeof({}) = {} on x86-{}\n",
type_name, size, self.pointer_size_bits
)
}
pub fn emit_alignof(&self, type_name: &str) -> String {
let align = self.align_of(type_name).unwrap_or(1);
format!(
" ; alignof({}) = {} on x86-{}\n",
type_name, align, self.pointer_size_bits
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86IteratorCategory {
Input = 0,
Output = 1,
Forward = 2,
Bidirectional = 3,
RandomAccess = 4,
Contiguous = 5,
}
impl X86IteratorCategory {
pub fn tag_type_name(&self) -> &str {
match self {
X86IteratorCategory::Input => "std::input_iterator_tag",
X86IteratorCategory::Output => "std::output_iterator_tag",
X86IteratorCategory::Forward => "std::forward_iterator_tag",
X86IteratorCategory::Bidirectional => "std::bidirectional_iterator_tag",
X86IteratorCategory::RandomAccess => "std::random_access_iterator_tag",
X86IteratorCategory::Contiguous => "std::contiguous_iterator_tag",
}
}
pub fn satisfies(&self, required: X86IteratorCategory) -> bool {
*self >= required && !matches!(*self, X86IteratorCategory::Output)
}
pub fn llvm_tag_type(&self) -> String {
format!("%struct.{}\" = type {{ i8 }}", self.tag_type_name())
}
}
impl fmt::Display for X86IteratorCategory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.tag_type_name())
}
}
pub struct X86IteratorConceptCheck {
pub enforce_concepts: bool,
pub require_contiguous_tag: bool,
}
impl X86IteratorConceptCheck {
pub fn new(cpp_standard: CppStandard) -> Self {
X86IteratorConceptCheck {
enforce_concepts: matches!(cpp_standard, CppStandard::Cpp20 | CppStandard::Cpp23),
require_contiguous_tag: matches!(cpp_standard, CppStandard::Cpp20 | CppStandard::Cpp23),
}
}
pub fn validate(&self, iterator_type: &str, category: X86IteratorCategory) -> Vec<String> {
let mut diags = Vec::new();
match category {
X86IteratorCategory::Input => diags.push(format!(
"// {} must be readable and incrementable",
iterator_type
)),
X86IteratorCategory::Forward => {
diags.push(format!("// {} must be multipass", iterator_type))
}
X86IteratorCategory::Bidirectional => {
diags.push(format!("// {} must support --", iterator_type))
}
X86IteratorCategory::RandomAccess => {
diags.push(format!("// {} must support [] and +=", iterator_type))
}
X86IteratorCategory::Contiguous => {
if self.require_contiguous_tag {
diags.push(format!("// {} must have contiguous storage", iterator_type))
}
}
X86IteratorCategory::Output => {
diags.push(format!("// {} must be writable", iterator_type))
}
}
diags
}
pub fn check_tag_dispatch(
&self,
iter: &str,
declared: X86IteratorCategory,
actual: X86IteratorCategory,
) -> Option<String> {
if declared > actual {
Some(format!(
"warning: {} declares {} but only satisfies {}",
iter,
declared.tag_type_name(),
actual.tag_type_name()
))
} else {
None
}
}
}
pub struct X86CxxLLVMIntrinsicMap {
pub intrinsics: HashMap<String, String>,
pub use_intrinsics: bool,
}
impl X86CxxLLVMIntrinsicMap {
pub fn new(is_64bit: bool) -> Self {
let mut intrinsics = HashMap::new();
let pb = if is_64bit { "i64" } else { "i32" };
intrinsics.insert("memcpy".into(), format!("@llvm.memcpy.p0i8.p0i8.{}", pb));
intrinsics.insert("memmove".into(), format!("@llvm.memmove.p0i8.p0i8.{}", pb));
intrinsics.insert("memset".into(), format!("@llvm.memset.p0i8.{}", pb));
intrinsics.insert(
"sadd_overflow".into(),
"@llvm.sadd.with.overflow.i32".into(),
);
intrinsics.insert(
"uadd_overflow".into(),
"@llvm.uadd.with.overflow.i32".into(),
);
intrinsics.insert(
"ssub_overflow".into(),
"@llvm.ssub.with.overflow.i32".into(),
);
intrinsics.insert(
"usub_overflow".into(),
"@llvm.usub.with.overflow.i32".into(),
);
intrinsics.insert(
"smul_overflow".into(),
"@llvm.smul.with.overflow.i32".into(),
);
intrinsics.insert(
"umul_overflow".into(),
"@llvm.umul.with.overflow.i32".into(),
);
intrinsics.insert("ctpop".into(), "@llvm.ctpop.i32".into());
intrinsics.insert("ctlz".into(), "@llvm.ctlz.i32".into());
intrinsics.insert("cttz".into(), "@llvm.cttz.i32".into());
intrinsics.insert("bswap".into(), "@llvm.bswap.i32".into());
intrinsics.insert("bitreverse".into(), "@llvm.bitreverse.i32".into());
intrinsics.insert("fshl".into(), "@llvm.fshl.i32".into());
intrinsics.insert("fshr".into(), "@llvm.fshr.i32".into());
intrinsics.insert("expect".into(), "@llvm.expect.i32".into());
intrinsics.insert("assume".into(), "@llvm.assume".into());
intrinsics.insert("trap".into(), "@llvm.trap".into());
intrinsics.insert("stackprotector".into(), "@llvm.stackprotector".into());
intrinsics.insert("stackguard".into(), "@llvm.stackguard".into());
intrinsics.insert("sadd_sat".into(), "@llvm.sadd.sat.i32".into());
intrinsics.insert("uadd_sat".into(), "@llvm.uadd.sat.i32".into());
intrinsics.insert("ssub_sat".into(), "@llvm.ssub.sat.i32".into());
intrinsics.insert("usub_sat".into(), "@llvm.usub.sat.i32".into());
intrinsics.insert("abs".into(), "@llvm.abs.i32".into());
intrinsics.insert("smax".into(), "@llvm.smax.i32".into());
intrinsics.insert("smin".into(), "@llvm.smin.i32".into());
intrinsics.insert("umax".into(), "@llvm.umax.i32".into());
intrinsics.insert("umin".into(), "@llvm.umin.i32".into());
X86CxxLLVMIntrinsicMap {
intrinsics,
use_intrinsics: true,
}
}
pub fn resolve(&self, operation: &str) -> Option<&str> {
self.intrinsics.get(operation).map(String::as_str)
}
pub fn emit_decls(&self) -> String {
let mut ir = String::from("; LLVM intrinsic declarations for C++ stdlib\n");
for (op, intr) in &self.intrinsics {
ir.push_str(&format!("; {} -> {}\n", op, intr));
}
ir
}
pub fn emit_call(&self, operation: &str, args: &[&str]) -> String {
let intrinsic = self.resolve(operation).unwrap_or("@unknown");
format!(
" call void @{}({})\n",
intrinsic.trim_start_matches('@'),
args.join(", ")
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_libcxx_creation_linux_64() {
let lib = X86LibCxx::new(
"x86_64-unknown-linux-gnu",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
assert_eq!(lib.platform, X86CxxPlatform::LinuxGnu);
assert!(lib.is_64bit);
assert_eq!(lib.stdlib_kind, CXXStdLib::LibCxx);
}
#[test]
fn test_libcxx_creation_darwin_64() {
let lib = X86LibCxx::new(
"x86_64-apple-darwin22.0",
true,
CppStandard::Cpp20,
CXXStdLib::LibCxx,
);
assert_eq!(lib.platform, X86CxxPlatform::Darwin);
assert!(lib.is_64bit);
}
#[test]
fn test_libcxx_creation_windows_msvc() {
let lib = X86LibCxx::new(
"x86_64-pc-windows-msvc",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
assert_eq!(lib.platform, X86CxxPlatform::WindowsMSVC);
}
#[test]
fn test_libcxx_creation_linux_32() {
let lib = X86LibCxx::new(
"i386-unknown-linux-gnu",
false,
CppStandard::Cpp14,
CXXStdLib::LibStdCxx,
);
assert_eq!(lib.platform, X86CxxPlatform::LinuxGnu);
assert!(!lib.is_64bit);
assert_eq!(lib.stdlib_kind, CXXStdLib::LibStdCxx);
}
#[test]
fn test_libcxx_initialize() {
let mut lib = X86LibCxx::new(
"x86_64-unknown-linux-gnu",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
lib.initialize();
assert!(lib.resolve_header("vector").is_some());
assert!(lib.resolve_header("string").is_some());
assert!(lib.resolve_header("memory").is_some());
assert!(lib.get_stdlib_type("std::string").is_some());
assert!(lib.get_stdlib_type("std::vector").is_some());
assert!(lib.get_stdlib_type("std::unique_ptr").is_some());
}
#[test]
fn test_platform_detection_linux_gnu() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-unknown-linux-gnu"),
X86CxxPlatform::LinuxGnu
);
assert_eq!(
X86CxxPlatform::from_triple("i686-pc-linux-gnu"),
X86CxxPlatform::LinuxGnu
);
}
#[test]
fn test_platform_detection_darwin() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-apple-darwin21.6.0"),
X86CxxPlatform::Darwin
);
assert_eq!(
X86CxxPlatform::from_triple("x86_64-apple-macos12.0"),
X86CxxPlatform::Darwin
);
}
#[test]
fn test_platform_detection_windows() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-pc-windows-msvc"),
X86CxxPlatform::WindowsMSVC
);
assert_eq!(
X86CxxPlatform::from_triple("x86_64-w64-mingw32"),
X86CxxPlatform::WindowsMinGW
);
}
#[test]
fn test_platform_detection_freebsd() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-unknown-freebsd13.1"),
X86CxxPlatform::FreeBSD
);
}
#[test]
fn test_platform_detection_android() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-linux-android"),
X86CxxPlatform::Android
);
}
#[test]
fn test_platform_detection_musl() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-unknown-linux-musl"),
X86CxxPlatform::LinuxMusl
);
}
#[test]
fn test_platform_detection_bare_metal() {
assert_eq!(
X86CxxPlatform::from_triple("x86_64-unknown-none"),
X86CxxPlatform::BareMetal
);
}
#[test]
fn test_platform_itanium_abi_detection() {
assert!(X86CxxPlatform::LinuxGnu.uses_itanium_abi());
assert!(X86CxxPlatform::Darwin.uses_itanium_abi());
assert!(X86CxxPlatform::FreeBSD.uses_itanium_abi());
assert!(!X86CxxPlatform::WindowsMSVC.uses_itanium_abi());
}
#[test]
fn test_platform_default_stdlib() {
assert_eq!(X86CxxPlatform::LinuxGnu.default_stdlib(), "libstdc++");
assert_eq!(X86CxxPlatform::Darwin.default_stdlib(), "libc++");
assert_eq!(X86CxxPlatform::WindowsMSVC.default_stdlib(), "msvcp140");
assert_eq!(X86CxxPlatform::WindowsMinGW.default_stdlib(), "libstdc++");
}
#[test]
fn test_platform_sso_capacity() {
assert_eq!(X86CxxPlatform::Darwin.sso_capacity(), 22);
assert_eq!(X86CxxPlatform::FreeBSD.sso_capacity(), 22);
assert_eq!(X86CxxPlatform::Android.sso_capacity(), 22);
assert_eq!(X86CxxPlatform::LinuxGnu.sso_capacity(), 15);
assert_eq!(X86CxxPlatform::WindowsMSVC.sso_capacity(), 15);
}
#[test]
fn test_platform_string_size_64() {
assert_eq!(X86CxxPlatform::Darwin.string_size_64(), 24);
assert_eq!(X86CxxPlatform::LinuxGnu.string_size_64(), 32);
}
#[test]
fn test_header_mapping_creation() {
let hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
assert!(!hm.include_prefix.is_empty());
assert!(hm.include_prefix.contains("include"));
}
#[test]
fn test_header_mapping_build_index() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let all = hm.all_headers();
assert!(all.len() >= 50, "Expected 50+ headers, got {}", all.len());
}
#[test]
fn test_header_mapping_known_headers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert!(hm.resolve("vector").is_some());
assert!(hm.resolve("string").is_some());
assert!(hm.resolve("memory").is_some());
assert!(hm.resolve("algorithm").is_some());
assert!(hm.resolve("utility").is_some());
assert!(hm.resolve("iterator").is_some());
assert!(hm.resolve("type_traits").is_some());
assert!(hm.resolve("tuple").is_some());
assert!(hm.resolve("optional").is_some());
assert!(hm.resolve("variant").is_some());
assert!(hm.resolve("any").is_some());
assert!(hm.resolve("span").is_some());
assert!(hm.resolve("string_view").is_some());
assert!(hm.resolve("functional").is_some());
assert!(hm.resolve("thread").is_some());
assert!(hm.resolve("mutex").is_some());
assert!(hm.resolve("shared_mutex").is_some());
assert!(hm.resolve("condition_variable").is_some());
assert!(hm.resolve("future").is_some());
assert!(hm.resolve("atomic").is_some());
assert!(hm.resolve("chrono").is_some());
assert!(hm.resolve("filesystem").is_some());
assert!(hm.resolve("exception").is_some());
assert!(hm.resolve("stdexcept").is_some());
assert!(hm.resolve("initializer_list").is_some());
assert!(hm.resolve("typeinfo").is_some());
assert!(hm.resolve("new").is_some());
}
#[test]
fn test_header_mapping_containers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert!(hm.resolve("vector").is_some());
assert!(hm.resolve("array").is_some());
assert!(hm.resolve("deque").is_some());
assert!(hm.resolve("list").is_some());
assert!(hm.resolve("forward_list").is_some());
assert!(hm.resolve("map").is_some());
assert!(hm.resolve("set").is_some());
assert!(hm.resolve("unordered_map").is_some());
assert!(hm.resolve("unordered_set").is_some());
assert!(hm.resolve("stack").is_some());
assert!(hm.resolve("queue").is_some());
}
#[test]
fn test_header_mapping_iwyu() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::vector"), Some("vector"));
assert_eq!(hm.iwyu_for_symbol("std::string"), Some("string"));
assert_eq!(hm.iwyu_for_symbol("std::unique_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::shared_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::weak_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::sort"), Some("algorithm"));
assert_eq!(hm.iwyu_for_symbol("std::find"), Some("algorithm"));
assert_eq!(hm.iwyu_for_symbol("std::optional"), Some("optional"));
assert_eq!(hm.iwyu_for_symbol("std::variant"), Some("variant"));
assert_eq!(hm.iwyu_for_symbol("std::any"), Some("any"));
assert_eq!(hm.iwyu_for_symbol("std::span"), Some("span"));
assert_eq!(hm.iwyu_for_symbol("std::to_string"), Some("string"));
assert_eq!(hm.iwyu_for_symbol("std::error_code"), Some("system_error"));
}
#[test]
fn test_header_mapping_dependencies() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let headers = hm.all_headers();
let cstddef = hm.transitive_dependents("cstddef");
assert!(!cstddef.is_empty());
}
#[test]
fn test_header_mapping_include_order() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let order = hm.include_order(&["vector".into(), "string".into()]);
let cstddef_pos = order.iter().position(|h| h == "cstddef");
let vector_pos = order.iter().position(|h| h == "vector");
assert!(cstddef_pos.is_some());
assert!(vector_pos.is_some());
assert!(cstddef_pos.unwrap() < vector_pos.unwrap());
}
#[test]
fn test_header_mapping_cpp20_headers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert!(hm.resolve("format").is_some());
assert!(hm.resolve("ranges").is_some());
assert!(hm.resolve("concepts").is_some());
assert!(hm.resolve("coroutine").is_some());
assert!(hm.resolve("span").is_some());
assert!(hm.resolve("compare").is_some());
assert!(hm.resolve("bit").is_some());
assert!(hm.resolve("numbers").is_some());
assert!(hm.resolve("source_location").is_some());
}
#[test]
fn test_header_mapping_cpp23_headers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert!(hm.resolve("expected").is_some());
assert!(hm.resolve("flat_map").is_some());
assert!(hm.resolve("flat_set").is_some());
assert!(hm.resolve("mdspan").is_some());
assert!(hm.resolve("print").is_some());
assert!(hm.resolve("generator").is_some());
}
#[test]
fn test_header_mapping_core_headers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let core = hm.core_headers();
assert!(core.contains(&"cstddef"));
assert!(core.contains(&"cstdlib"));
assert!(core.contains(&"utility"));
assert!(core.contains(&"type_traits"));
assert!(core.contains(&"initializer_list"));
assert!(core.contains(&"exception"));
assert!(core.contains(&"new"));
assert!(core.contains(&"typeinfo"));
}
#[test]
fn test_header_mapping_include_flags() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let flags = hm.include_flags();
assert!(flags.iter().any(|f| f.starts_with("-I")));
assert!(flags.iter().any(|f| f.starts_with("-stdlib=")));
}
#[test]
fn test_type_lowering_creation_64() {
let tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
assert_eq!(tl.pointer_size(), 8);
assert_eq!(tl.ptr_type_str(), "i64");
assert_eq!(tl.size_t_type_str(), "i64");
}
#[test]
fn test_type_lowering_creation_32() {
let tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, false);
assert_eq!(tl.pointer_size(), 4);
assert_eq!(tl.ptr_type_str(), "i32");
assert_eq!(tl.size_t_type_str(), "i32");
}
#[test]
fn test_type_lowering_initialize() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
assert!(tl.get_type("std::string").is_some());
assert!(tl.get_type("std::vector").is_some());
assert!(tl.get_type("std::unique_ptr").is_some());
assert!(tl.get_type("std::shared_ptr").is_some());
assert!(tl.get_type("std::weak_ptr").is_some());
assert!(tl.get_type("std::optional").is_some());
assert!(tl.get_type("std::variant").is_some());
assert!(tl.get_type("std::any").is_some());
assert!(tl.get_type("std::pair").is_some());
assert!(tl.get_type("std::tuple").is_some());
assert!(tl.get_type("std::function").is_some());
assert!(tl.get_type("std::string_view").is_some());
assert!(tl.get_type("std::span").is_some());
assert!(tl.get_type("std::atomic").is_some());
assert!(tl.get_type("std::mutex").is_some());
assert!(tl.get_type("std::shared_mutex").is_some());
assert!(tl.get_type("std::recursive_mutex").is_some());
assert!(tl.get_type("std::condition_variable").is_some());
assert!(tl.get_type("std::thread").is_some());
assert!(tl.get_type("std::jthread").is_some());
assert!(tl.get_type("std::promise").is_some());
assert!(tl.get_type("std::future").is_some());
assert!(tl.get_type("std::packaged_task").is_some());
}
#[test]
fn test_type_lowering_std_string_linux_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::StdString {
sso_capacity,
total_size,
..
}) = tl.get_type("std::string")
{
assert_eq!(sso_capacity, 15); assert_eq!(total_size, 32);
} else {
panic!("Expected StdString type");
}
}
#[test]
fn test_type_lowering_std_string_darwin_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::Darwin, true);
tl.initialize();
if let Some(X86LibCxxType::StdString {
sso_capacity,
total_size,
..
}) = tl.get_type("std::string")
{
assert_eq!(sso_capacity, 22); assert_eq!(total_size, 24);
} else {
panic!("Expected StdString type");
}
}
#[test]
fn test_type_lowering_vector_sizes() {
let mut tl64 = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl64.initialize();
if let Some(X86LibCxxType::StdVector { size_bytes, .. }) = tl64.get_type("std::vector") {
assert_eq!(size_bytes, 24); }
let mut tl32 = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, false);
tl32.initialize();
if let Some(X86LibCxxType::StdVector { size_bytes, .. }) = tl32.get_type("std::vector") {
assert_eq!(size_bytes, 12); }
}
#[test]
fn test_type_lowering_smart_pointers_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::UniquePtr { size_bytes, .. }) = tl.get_type("std::unique_ptr") {
assert_eq!(size_bytes, 8);
}
if let Some(X86LibCxxType::SharedPtr { size_bytes, .. }) = tl.get_type("std::shared_ptr") {
assert_eq!(size_bytes, 16);
}
if let Some(X86LibCxxType::WeakPtr { size_bytes, .. }) = tl.get_type("std::weak_ptr") {
assert_eq!(size_bytes, 16);
}
}
#[test]
fn test_type_lowering_smart_pointers_32() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, false);
tl.initialize();
if let Some(X86LibCxxType::UniquePtr { size_bytes, .. }) = tl.get_type("std::unique_ptr") {
assert_eq!(size_bytes, 4);
}
if let Some(X86LibCxxType::SharedPtr { size_bytes, .. }) = tl.get_type("std::shared_ptr") {
assert_eq!(size_bytes, 8);
}
if let Some(X86LibCxxType::WeakPtr { size_bytes, .. }) = tl.get_type("std::weak_ptr") {
assert_eq!(size_bytes, 8);
}
}
#[test]
fn test_type_lowering_mutex_sizes_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::Mutex { size_bytes, .. }) = tl.get_type("std::mutex") {
assert_eq!(size_bytes, 40); }
if let Some(X86LibCxxType::SharedMutex { size_bytes, .. }) =
tl.get_type("std::shared_mutex")
{
assert_eq!(size_bytes, 56); }
}
#[test]
fn test_type_lowering_string_view_and_span_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::StringView { size_bytes, .. }) = tl.get_type("std::string_view")
{
assert_eq!(size_bytes, 16); }
if let Some(X86LibCxxType::Span { size_bytes, .. }) = tl.get_type("std::span") {
assert_eq!(size_bytes, 16); }
}
#[test]
fn test_type_lowering_atomic_lock_free() {
let tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
assert!(tl.is_lock_free(1));
assert!(tl.is_lock_free(2));
assert!(tl.is_lock_free(4));
assert!(tl.is_lock_free(8));
let tl32 = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, false);
assert!(!tl32.is_lock_free(16)); }
#[test]
fn test_type_lowering_iterator_tags() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
let tags = [
"std::input_iterator_tag",
"std::output_iterator_tag",
"std::forward_iterator_tag",
"std::bidirectional_iterator_tag",
"std::random_access_iterator_tag",
"std::contiguous_iterator_tag",
];
for tag in &tags {
assert!(tl.get_type(tag).is_some(), "Expected type for {}", tag);
}
}
#[test]
fn test_type_lowering_struct_registration() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.register_struct_type("MyClass", "%\"struct.MyClass\" = type { i64, i32 }".into());
assert_eq!(
tl.get_struct_type("MyClass"),
Some("%\"struct.MyClass\" = type { i64, i32 }")
);
}
#[test]
fn test_abi_lowering_creation_itanium() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
assert_eq!(abi.abi_kind(), X86CxxABIKind::Itanium);
assert_eq!(abi.ptr_size(), 8);
}
#[test]
fn test_abi_lowering_creation_msvc() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::WindowsMSVC, true, "x86_64-pc-windows-msvc");
assert_eq!(abi.abi_kind(), X86CxxABIKind::Microsoft);
}
#[test]
fn test_abi_lowering_vtable_pointer() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::VtablePointer {
class_name: "MyClass".into(),
offset: 0,
});
assert!(result.success);
assert!(result.ir.contains("vtable"));
assert_eq!(
result.metadata.get("vtable_offset").map(|s| s.as_str()),
Some("0")
);
}
#[test]
fn test_abi_lowering_this_call() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::ThisCall {
class_name: "MyClass".into(),
method_name: "foo".into(),
is_virtual: false,
});
assert!(result.success);
assert!(result.ir.contains("%rdi")); }
#[test]
fn test_abi_lowering_this_call_msvc() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::WindowsMSVC, true, "x86_64-pc-windows-msvc");
let result = abi.lower(&X86CxxABIFeature::ThisCall {
class_name: "MyClass".into(),
method_name: "foo".into(),
is_virtual: true,
});
assert!(result.success);
assert!(result.ir.contains("%rcx")); }
#[test]
fn test_abi_lowering_constructor() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::Constructor {
class_name: "MyClass".into(),
ctor_kind: CtorKind::Complete,
params: vec![],
});
assert!(result.success);
assert!(result.ir.contains("C1E")); }
#[test]
fn test_abi_lowering_destructor() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::Destructor {
class_name: "MyClass".into(),
dtor_kind: DtorKind::Deleting,
});
assert!(result.success);
assert!(result.ir.contains("D0E")); }
#[test]
fn test_abi_lowering_copy_move() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let copy_result = abi.lower(&X86CxxABIFeature::CopyConstructor {
class_name: "MyClass".into(),
is_trivial: true,
});
assert!(copy_result.success);
assert!(copy_result.ir.contains("trivial"));
let move_result = abi.lower(&X86CxxABIFeature::MoveConstructor {
class_name: "MyClass".into(),
is_trivial: false,
});
assert!(move_result.success);
assert!(move_result.ir.contains("non-trivial"));
}
#[test]
fn test_abi_lowering_vtable_emission() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::EmitVtable {
class_name: "MyClass".into(),
is_primary: true,
});
assert!(result.success);
assert!(result.ir.contains("_ZTV")); }
#[test]
fn test_abi_lowering_guard_variable() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::GuardVariable {
var_name: "my_static".into(),
is_thread_safe: true,
});
assert!(result.success);
assert!(result.ir.contains("thread-safe"));
assert!(result.ir.contains("guard"));
}
#[test]
fn test_abi_lowering_return_slot() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::ReturnSlot {
class_name: "BigClass".into(),
is_named: true,
});
assert!(result.success);
assert!(result.ir.contains("NRVO"));
}
#[test]
fn test_abi_lowering_member_pointer() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::MemberPointer {
class_name: "MyClass".into(),
member_type: "int".into(),
});
assert!(result.success);
assert!(result.ir.contains("this-adjustment"));
}
#[test]
fn test_abi_lowering_class_layout() {
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let layout = abi.compute_class_layout(
"TestClass",
&[("Base".into(), false)],
&[("x".into(), 4, 4), ("y".into(), 8, 8)],
true,
);
assert_eq!(layout.name, "TestClass");
assert!(layout.vtable_offset.is_some());
assert_eq!(layout.vtable_offset.unwrap(), 0);
assert!(!layout.base_offsets.is_empty());
}
#[test]
fn test_eh_creation_itanium() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
assert_eq!(eh.eh_abi_kind(), X86EHABIKind::ItaniumDWARF);
assert_eq!(eh.personality_function(), "__gxx_personality_v0");
}
#[test]
fn test_eh_creation_msvc() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::WindowsMSVC,
true,
"x86_64-pc-windows-msvc",
);
assert_eq!(eh.eh_abi_kind(), X86EHABIKind::MicrosoftSEH);
assert_eq!(eh.personality_function(), "__CxxFrameHandler3");
}
#[test]
fn test_eh_personality_decl() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let decl = eh.personality_decl();
assert!(decl.contains("__gxx_personality_v0"));
assert!(decl.contains("declare"));
}
#[test]
fn test_eh_landing_pad() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let pad = LandingPad {
id: 1,
label: "lpad1".into(),
catch_types: vec!["std::exception".into()],
catch_all: false,
cleanups: vec![],
filter_types: vec![],
};
let ir = eh.emit_landingpad(&pad);
assert!(ir.contains("lpad1:"));
assert!(ir.contains("landingpad"));
assert!(ir.contains("catch"));
}
#[test]
fn test_eh_landing_pad_catch_all() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let pad = LandingPad {
id: 2,
label: "lpad2".into(),
catch_types: vec![],
catch_all: true,
cleanups: vec!["cleanup1".into()],
filter_types: vec![],
};
let ir = eh.emit_landingpad(&pad);
assert!(ir.contains("cleanup"));
assert!(ir.contains("catch i8* null"));
}
#[test]
fn test_eh_lsda_generation() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let call_sites = vec![CallSiteEntry {
cs_start: 0,
cs_len: 10,
cs_lp: 1,
cs_action: 0,
}];
let actions = vec![ActionEntry {
type_filter: 1,
next_action_offset: 0,
}];
let ir = eh.emit_lsda(
"my_func",
&call_sites,
&actions,
&["std::exception".into()],
true,
);
assert!(ir.contains("LSDA"));
assert!(ir.contains("my_func"));
}
#[test]
fn test_eh_noexcept() {
let mut eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
assert!(!eh.is_noexcept("my_func"));
eh.mark_noexcept("my_func");
assert!(eh.is_noexcept("my_func"));
}
#[test]
fn test_eh_throw_generation() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let ir = eh.emit_throw("MyException", "%exn_obj");
assert!(ir.contains("__cxa_allocate_exception"));
assert!(ir.contains("__cxa_throw"));
assert!(ir.contains("unreachable"));
}
#[test]
fn test_eh_try_catch() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let pad = LandingPad {
id: 1,
label: "lpad".into(),
catch_types: vec!["std::exception".into()],
catch_all: false,
cleanups: vec![],
filter_types: vec![],
};
let ir = eh.emit_try_catch(
" call void @may_throw()\n",
"std::exception",
" call void @handle()\n",
&pad,
);
assert!(ir.contains("try block"));
assert!(ir.contains("__cxa_begin_catch"));
assert!(ir.contains("__cxa_end_catch"));
}
#[test]
fn test_eh_rethrow() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let ir = eh.emit_rethrow();
assert!(ir.contains("__cxa_rethrow"));
assert!(ir.contains("unreachable"));
}
#[test]
fn test_eh_runtime_decls() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let decls = eh.emit_eh_runtime_decls();
assert!(decls.contains("__cxa_allocate_exception"));
assert!(decls.contains("__cxa_throw"));
assert!(decls.contains("__cxa_begin_catch"));
assert!(decls.contains("__cxa_end_catch"));
assert!(decls.contains("__cxa_rethrow"));
assert!(decls.contains("__cxa_pure_virtual"));
assert!(decls.contains("__cxa_deleted_virtual"));
}
#[test]
fn test_rtti_creation() {
let rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
assert!(!rtti.has_type_info("TestClass"));
}
#[test]
fn test_rtti_register_type() {
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let ir =
rtti.register_class_type("MyClass", "_ZN7MyClassE", &[("Base".into(), false, true)]);
assert!(ir.contains("_ZTI"));
assert!(ir.contains("_ZTS"));
assert!(ir.contains("MyClass"));
assert!(rtti.has_type_info("MyClass"));
}
#[test]
fn test_rtti_typeid() {
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
rtti.register_class_type("MyClass", "_ZN7MyClassE", &[]);
let result = rtti.lower_typeid("MyClass");
assert!(!result.ir.is_empty());
assert!(!result.result_value.is_empty());
}
#[test]
fn test_rtti_dynamic_cast() {
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
rtti.register_class_type("Derived", "_ZN7DerivedE", &[("Base".into(), false, true)]);
rtti.register_class_type("Base", "_ZN4BaseE", &[]);
let result = rtti.lower_dynamic_cast("Derived", "Base", "%obj");
assert!(result.ir.contains("__dynamic_cast"));
assert_eq!(result.result_value, "%cast_result");
}
#[test]
fn test_rtti_dynamic_cast_decl() {
let rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let decl = rtti.emit_dynamic_cast_decl();
assert!(decl.contains("__dynamic_cast"));
}
#[test]
fn test_rtti_type_info_compare() {
let rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let ir = rtti.emit_type_info_compare("MyClass", "YourClass");
assert!(ir.contains("icmp eq"));
assert!(ir.contains("type_info"));
}
#[test]
fn test_rtti_runtime_decls() {
let rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let decls = rtti.emit_rtti_runtime_decls();
assert!(decls.contains("__dynamic_cast"));
assert!(decls.contains("__cxa_guard_acquire"));
assert!(decls.contains("__cxa_guard_release"));
assert!(decls.contains("__cxa_guard_abort"));
}
#[test]
fn test_templates_creation() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
assert_eq!(tmpl.template_count(), 0);
assert_eq!(tmpl.instantiation_count(), 0);
}
#[test]
fn test_templates_register() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"my_abs",
vec!["T".into()],
"T my_abs(T x) { return x < 0 ? -x : x; }",
);
assert_eq!(tmpl.template_count(), 1);
}
#[test]
fn test_templates_deduce_args() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"my_max",
vec!["T".into()],
"T my_max(T a, T b) { return a > b ? a : b; }",
);
let result = tmpl.deduce_args("my_max", &[X86CxxTemplateArg::Type("int".into())]);
assert!(result.success);
assert_eq!(
result.deduced_args.get("T"),
Some(&X86CxxTemplateArg::Type("int".into()))
);
}
#[test]
fn test_templates_deduce_args_mismatch() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("my_func", vec!["T".into(), "U".into()], "void f();");
let result = tmpl.deduce_args("my_func", &[X86CxxTemplateArg::Type("int".into())]);
assert!(!result.success);
}
#[test]
fn test_templates_instantiate() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"my_min",
vec!["T".into()],
"T my_min(T a, T b) { return a < b ? a : b; }",
);
let result = tmpl.instantiate("my_min", &[X86CxxTemplateArg::Type("int".into())]);
assert!(result.is_ok());
let ir = result.unwrap();
assert!(ir.contains("int"));
assert!(ir.contains("my_min"));
}
#[test]
fn test_templates_instantiate_cached() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"square",
vec!["T".into()],
"T square(T x) { return x * x; }",
);
let r1 = tmpl.instantiate("square", &[X86CxxTemplateArg::Type("double".into())]);
assert!(r1.is_ok());
let r2 = tmpl.instantiate("square", &[X86CxxTemplateArg::Type("double".into())]);
assert!(r2.is_ok());
assert_eq!(r1.unwrap(), r2.unwrap());
}
#[test]
fn test_templates_mangle_name() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled = tmpl.mangle_template_name("vector", &[X86CxxTemplateArg::Type("int".into())]);
assert!(mangled.starts_with("_Z"));
assert!(mangled.contains("vector"));
assert!(mangled.contains("int"));
}
#[test]
fn test_templates_mangle_integral() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled = tmpl.mangle_template_name(
"array",
&[
X86CxxTemplateArg::Type("int".into()),
X86CxxTemplateArg::Integral(5),
],
);
assert!(mangled.contains("5"));
}
#[test]
fn test_templates_implicit_instantiation() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("foo", vec!["T".into()], "void foo(T x);");
let result = tmpl.implicit_instantiate("foo", &[X86CxxTemplateArg::Type("char".into())]);
assert!(result.is_ok());
assert!(result.unwrap().contains("Pending implicit"));
}
#[test]
fn test_templates_explicit_instantiation() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("bar", vec!["T".into()], "void bar(T x);");
let result = tmpl.explicit_instantiate("bar", &[X86CxxTemplateArg::Type("long".into())]);
assert!(result.is_ok());
assert_eq!(tmpl.instantiation_count(), 1);
}
#[test]
fn test_templates_odr_check() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let result = tmpl.check_odr(
"nonexistent",
&[X86CxxTemplateArg::Type("int".into())],
"ir",
);
assert!(result.is_ok());
}
#[test]
fn test_templates_process_pending() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("proc", vec!["T".into()], "void proc(T x);");
let _ = tmpl.implicit_instantiate("proc", &[X86CxxTemplateArg::Type("float".into())]);
assert_eq!(tmpl.instantiation_count(), 0);
let results = tmpl.process_pending();
assert!(results.is_ok());
assert_eq!(tmpl.instantiation_count(), 1);
}
#[test]
fn test_templates_instantiation_context() {
let ctx = X86CxxTemplates::make_context(
"vector",
vec![X86CxxTemplateArg::Type("int".into())],
Some("file.cpp:42".into()),
None,
);
assert_eq!(ctx.depth, 1);
assert_eq!(ctx.template_name, "vector");
let bt = ctx.backtrace();
assert!(bt.contains("vector"));
assert!(bt.contains("int"));
assert!(bt.contains("file.cpp:42"));
}
#[test]
fn test_templates_nested_context() {
let parent = Box::new(X86CxxTemplates::make_context(
"vector",
vec![X86CxxTemplateArg::Type("int".into())],
Some("file.cpp:10".into()),
None,
));
let child = X86CxxTemplates::make_context(
"allocator",
vec![X86CxxTemplateArg::Type("int".into())],
Some("file.cpp:42".into()),
Some(parent),
);
assert_eq!(child.depth, 2);
assert!(child.parent.is_some());
let bt = child.backtrace();
assert!(bt.contains("vector"));
assert!(bt.contains("allocator"));
}
#[test]
fn test_template_arg_display() {
assert_eq!(X86CxxTemplateArg::Type("int".into()).to_string(), "int");
assert_eq!(X86CxxTemplateArg::Integral(42).to_string(), "42");
assert_eq!(X86CxxTemplateArg::Bool(true).to_string(), "true");
assert_eq!(X86CxxTemplateArg::Bool(false).to_string(), "false");
assert_eq!(X86CxxTemplateArg::NullPtr.to_string(), "nullptr");
}
#[test]
fn test_full_pipeline_linux_64() {
let mut lib = X86LibCxx::new(
"x86_64-unknown-linux-gnu",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
lib.initialize();
let path = lib.resolve_header("vector");
assert!(path.is_some());
let vec_ty = lib.get_stdlib_type("std::vector");
assert!(vec_ty.is_some());
let result = lib.lower_abi(&X86CxxABIFeature::ThisCall {
class_name: "MyClass".into(),
method_name: "foo".into(),
is_virtual: false,
});
assert!(result.success);
let personality = lib.get_personality_fn();
assert_eq!(personality, "__gxx_personality_v0");
lib.rtti
.register_class_type("TestClass", "_ZN9TestClassE", &[]);
assert!(lib.has_rtti("TestClass"));
lib.templates
.register_template("test_tmpl", vec!["T".into()], "void test_tmpl(T x) { }");
let inst = lib.instantiate_template("test_tmpl", &[X86CxxTemplateArg::Type("int".into())]);
assert!(inst.is_ok());
}
#[test]
fn test_full_pipeline_darwin_64() {
let mut lib = X86LibCxx::new(
"x86_64-apple-darwin22.0",
true,
CppStandard::Cpp20,
CXXStdLib::LibCxx,
);
lib.initialize();
assert_eq!(lib.platform, X86CxxPlatform::Darwin);
if let Some(ty) = lib.get_stdlib_type("std::string") {
if let X86LibCxxType::StdString {
sso_capacity,
total_size,
..
} = ty
{
assert_eq!(sso_capacity, 22);
assert_eq!(total_size, 24);
}
}
}
#[test]
fn test_full_pipeline_windows_msvc() {
let mut lib = X86LibCxx::new(
"x86_64-pc-windows-msvc",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
lib.initialize();
assert_eq!(lib.platform, X86CxxPlatform::WindowsMSVC);
assert_eq!(lib.get_personality_fn(), "__CxxFrameHandler3");
let result = lib.lower_abi(&X86CxxABIFeature::VtablePointer {
class_name: "Test".into(),
offset: 0,
});
assert!(result.success);
assert!(result.metadata.get("abi").map(|s| s.as_str()) == Some("msvc"));
}
#[test]
fn test_full_pipeline_32bit() {
let mut lib = X86LibCxx::new(
"i386-unknown-linux-gnu",
false,
CppStandard::Cpp14,
CXXStdLib::LibStdCxx,
);
lib.initialize();
assert!(!lib.is_64bit);
if let Some(ty) = lib.get_stdlib_type("std::vector") {
if let X86LibCxxType::StdVector { size_bytes, .. } = ty {
assert_eq!(size_bytes, 12);
}
}
if let Some(ty) = lib.get_stdlib_type("std::unique_ptr") {
if let X86LibCxxType::UniquePtr { size_bytes, .. } = ty {
assert_eq!(size_bytes, 4);
}
}
}
#[test]
fn test_itanium_ctor_variants() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let c1 = abi.lower(&X86CxxABIFeature::Constructor {
class_name: "Foo".into(),
ctor_kind: CtorKind::Complete,
params: vec!["int".into()],
});
assert!(c1.ir.contains("C1E"));
assert!(!c1.ir.contains("C2E"));
let c2 = abi.lower(&X86CxxABIFeature::Constructor {
class_name: "Foo".into(),
ctor_kind: CtorKind::Base,
params: vec![],
});
assert!(c2.ir.contains("C2E"));
}
#[test]
fn test_itanium_dtor_variants() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let d0 = abi.lower(&X86CxxABIFeature::Destructor {
class_name: "Foo".into(),
dtor_kind: DtorKind::Deleting,
});
assert!(d0.ir.contains("D0E"));
let d1 = abi.lower(&X86CxxABIFeature::Destructor {
class_name: "Foo".into(),
dtor_kind: DtorKind::Complete,
});
assert!(d1.ir.contains("D1E"));
let d2 = abi.lower(&X86CxxABIFeature::Destructor {
class_name: "Foo".into(),
dtor_kind: DtorKind::Base,
});
assert!(d2.ir.contains("D2E"));
}
#[test]
fn test_abi_virtual_base_layout() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::VirtualBaseLayout {
class_name: "DiamondBottom".into(),
vbase_name: "VirtualBase".into(),
vbase_offset: 32,
});
assert!(result.success);
assert!(result.ir.contains("virtual"));
assert_eq!(
result.metadata.get("vbase_name").map(|s| s.as_str()),
Some("VirtualBase")
);
}
#[test]
fn test_abi_emit_vtt() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::EmitVTT {
class_name: "DiamondDerived".into(),
});
assert!(result.success);
assert!(result.ir.contains("_ZTT"));
}
#[test]
fn test_abi_copy_assignment_trivial() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::CopyAssignment {
class_name: "PODClass".into(),
is_trivial: true,
});
assert!(result.success);
assert!(result.ir.contains("trivial"));
}
#[test]
fn test_abi_move_assignment_non_trivial() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::MoveAssignment {
class_name: "NonTrivialClass".into(),
is_trivial: false,
});
assert!(result.success);
assert!(result.ir.contains("self-assignment"));
}
#[test]
fn test_abi_msvc_vtable_ptr() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::WindowsMSVC, true, "x86_64-pc-windows-msvc");
let result = abi.lower(&X86CxxABIFeature::VtablePointer {
class_name: "WinClass".into(),
offset: 0,
});
assert!(result.success);
assert!(result.ir.contains("vfptr"));
assert!(result.ir.contains("vbptr"));
}
#[test]
fn test_abi_msvc_this_call_x86() {
let abi = X86CxxABILowering::new(
X86CxxPlatform::WindowsMSVC,
false, "i386-pc-windows-msvc",
);
let result = abi.lower(&X86CxxABIFeature::ThisCall {
class_name: "Win32Class".into(),
method_name: "method".into(),
is_virtual: true,
});
assert!(result.success);
assert!(result.ir.contains("%ecx"));
}
#[test]
fn test_class_layout_polymorphic() {
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let layout =
abi.compute_class_layout("Poly", &[], &[("x".into(), 4, 4), ("y".into(), 8, 8)], true);
assert_eq!(layout.vtable_offset, Some(0));
assert!(layout.size >= 16); }
#[test]
fn test_class_layout_non_polymorphic() {
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let layout = abi.compute_class_layout(
"PlainStruct",
&[],
&[("a".into(), 4, 4), ("b".into(), 4, 4)],
false,
);
assert_eq!(layout.vtable_offset, None);
assert_eq!(layout.size, 8); }
#[test]
fn test_class_layout_with_bases() {
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let layout = abi.compute_class_layout(
"Derived",
&[("Base".into(), false)],
&[("extra".into(), 4, 4)],
true,
);
assert!(layout.vtable_offset.is_some());
assert!(!layout.base_offsets.is_empty());
assert_eq!(layout.base_offsets[0].0, "Base");
}
#[test]
fn test_class_layout_with_virtual_bases() {
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let layout = abi.compute_class_layout(
"DiamondDerived",
&[
("Left".into(), false),
("Right".into(), false),
("VBase".into(), true),
],
&[],
true,
);
assert!(layout.has_virtual_bases);
assert!(!layout.vbase_offsets.is_empty());
}
#[test]
fn test_lsda_descriptor_fields() {
let lsda = LSDADescriptor {
landing_pad_start: 0,
type_table_encoding: 0xff,
type_table_offset: 32,
call_site_encoding: 0x01,
call_site_table_length: 48,
};
assert_eq!(lsda.type_table_encoding, 0xff);
assert_eq!(lsda.call_site_table_length, 48);
}
#[test]
fn test_call_site_entry() {
let cs = CallSiteEntry {
cs_start: 10,
cs_len: 20,
cs_lp: 1,
cs_action: 0,
};
assert_eq!(cs.cs_start, 10);
assert_eq!(cs.cs_len, 20);
assert_eq!(cs.cs_lp, 1);
}
#[test]
fn test_action_entry_filter() {
let action = ActionEntry {
type_filter: 2,
next_action_offset: 8,
};
assert_eq!(action.type_filter, 2);
assert_eq!(action.next_action_offset, 8);
}
#[test]
fn test_eh_msvc_lsda() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::WindowsMSVC,
true,
"x86_64-pc-windows-msvc",
);
let call_sites = vec![CallSiteEntry {
cs_start: 0,
cs_len: 64,
cs_lp: 1,
cs_action: 1,
}];
let ir = eh.emit_lsda("win_func", &call_sites, &[], &[], false);
assert!(ir.contains("xdata"));
assert!(ir.contains("SEH"));
assert!(ir.contains("RUNTIME_FUNCTION"));
}
#[test]
fn test_eh_cxa_begin_end_catch() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let begin = eh.emit_cxa_begin_catch(5);
assert!(begin.contains("__cxa_begin_catch"));
assert!(begin.contains("%exn5"));
let end = eh.emit_cxa_end_catch();
assert!(end.contains("__cxa_end_catch"));
}
#[test]
fn test_eh_noexcept_metadata() {
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let meta = eh.noexcept_metadata("noexcept_fn");
assert!(meta.contains("noexcept"));
}
#[test]
fn test_rtti_type_info_entry_structure() {
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
rtti.register_class_type(
"FullClass",
"_ZN9FullClassE",
&[("Base1".into(), false, true), ("Base2".into(), false, true)],
);
assert!(rtti.has_type_info("FullClass"));
let entry = rtti.get_type_info("FullClass");
assert!(entry.is_some());
let entry = entry.unwrap();
assert!(entry.is_class);
assert!(!entry.is_pointer);
assert!(entry.ir.contains("_ZTI"));
assert!(entry.ir.contains("_ZTS"));
}
#[test]
fn test_rtti_typeid_for_non_class() {
let rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = rtti.lower_typeid("UnknownType");
assert!(result.ir.contains("not registered"));
}
#[test]
fn test_rtti_dynamic_cast_cross_cast() {
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
rtti.register_class_type("A", "_ZN1AE", &[]);
rtti.register_class_type("B", "_ZN1BE", &[]);
let result = rtti.lower_dynamic_cast("A", "B", "%a_ptr");
assert!(result.ir.contains("__dynamic_cast"));
assert!(!result.ir.is_empty());
}
#[test]
fn test_templates_multiple_params() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"pair_tmpl",
vec!["T1".into(), "T2".into()],
"struct pair { T1 first; T2 second; };",
);
let result = tmpl.instantiate(
"pair_tmpl",
&[
X86CxxTemplateArg::Type("int".into()),
X86CxxTemplateArg::Type("double".into()),
],
);
assert!(result.is_ok());
let ir = result.unwrap();
assert!(ir.contains("int"));
assert!(ir.contains("double"));
}
#[test]
fn test_templates_non_type_integral() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("array_size", vec!["T".into(), "N".into()], "T arr[N];");
let result = tmpl.instantiate(
"array_size",
&[
X86CxxTemplateArg::Type("char".into()),
X86CxxTemplateArg::Integral(256),
],
);
assert!(result.is_ok());
assert!(result.unwrap().contains("256"));
}
#[test]
fn test_templates_bool_argument() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template(
"cond_wrapper",
vec!["B".into()],
"enable_if<B, T>::type value;",
);
let result = tmpl.instantiate("cond_wrapper", &[X86CxxTemplateArg::Bool(true)]);
assert!(result.is_ok());
}
#[test]
fn test_templates_nullptr_argument() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("null_term", vec!["P".into()], "P ptr;");
let result = tmpl.instantiate("null_term", &[X86CxxTemplateArg::NullPtr]);
assert!(result.is_ok());
}
#[test]
fn test_templates_deduction_failure_unknown() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let result = tmpl.deduce_args("nonexistent", &[]);
assert!(!result.success);
assert!(result.failure_reason.is_some());
}
#[test]
fn test_templates_instantiation_error_unknown() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let result = tmpl.instantiate("not_defined", &[]);
assert!(result.is_err());
}
#[test]
fn test_templates_odr_violation_detection() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("odr_test", vec!["T".into()], "void f(T x) {}");
let r1 = tmpl.instantiate("odr_test", &[X86CxxTemplateArg::Type("int".into())]);
assert!(r1.is_ok());
let result = tmpl.check_odr(
"odr_test",
&[X86CxxTemplateArg::Type("int".into())],
"different IR body",
);
assert!(result.is_err());
}
#[test]
fn test_templates_mangle_bool_true() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled = tmpl.mangle_template_name("enable_if", &[X86CxxTemplateArg::Bool(true)]);
assert!(mangled.contains("Lb1E"));
}
#[test]
fn test_templates_mangle_bool_false() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled = tmpl.mangle_template_name("enable_if", &[X86CxxTemplateArg::Bool(false)]);
assert!(mangled.contains("Lb0E"));
}
#[test]
fn test_templates_mangle_nullptr() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled = tmpl.mangle_template_name("is_null", &[X86CxxTemplateArg::NullPtr]);
assert!(mangled.contains("LDnE"));
}
#[test]
fn test_templates_mangle_template_arg() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let mangled =
tmpl.mangle_template_name("rebind", &[X86CxxTemplateArg::Template("allocator".into())]);
assert!(mangled.contains("allocator"));
}
#[test]
fn test_templates_instantiation_count() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("cnt", vec!["T".into()], "T val;");
assert_eq!(tmpl.instantiation_count(), 0);
tmpl.instantiate("cnt", &[X86CxxTemplateArg::Type("int".into())])
.unwrap();
assert_eq!(tmpl.instantiation_count(), 1);
tmpl.instantiate("cnt", &[X86CxxTemplateArg::Type("float".into())])
.unwrap();
assert_eq!(tmpl.instantiation_count(), 2);
}
#[test]
fn test_templates_instantiated_names() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("name_test", vec!["T".into()], "T val;");
tmpl.instantiate("name_test", &[X86CxxTemplateArg::Type("int".into())])
.unwrap();
let names = tmpl.instantiated_names();
assert_eq!(names.len(), 1);
}
#[test]
fn test_type_lowering_struct_types_registered() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
assert!(tl.get_struct_type("std::string").is_some());
assert!(tl.get_struct_type("std::vector").is_some());
assert!(tl.get_struct_type("std::input_iterator_tag").is_some());
}
#[test]
fn test_type_lowering_future_promise_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::Promise { size_bytes, .. }) = tl.get_type("std::promise") {
assert_eq!(size_bytes, 8);
}
if let Some(X86LibCxxType::Future { size_bytes, .. }) = tl.get_type("std::future") {
assert_eq!(size_bytes, 8);
}
if let Some(X86LibCxxType::PackagedTask { size_bytes, .. }) =
tl.get_type("std::packaged_task")
{
assert_eq!(size_bytes, 8);
}
}
#[test]
fn test_type_lowering_function_64() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::Function { size_bytes, .. }) = tl.get_type("std::function") {
assert_eq!(size_bytes, 24);
}
}
#[test]
fn test_type_lowering_condition_variable() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::ConditionVariable { size_bytes, .. }) =
tl.get_type("std::condition_variable")
{
assert_eq!(size_bytes, 48);
}
}
#[test]
fn test_type_lowering_thread_types() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(X86LibCxxType::Thread { size_bytes, .. }) = tl.get_type("std::thread") {
assert_eq!(size_bytes, 8);
}
if let Some(X86LibCxxType::Thread { size_bytes, .. }) = tl.get_type("std::jthread") {
assert_eq!(size_bytes, 8);
}
}
#[test]
fn test_type_lowering_lock_free_boundaries() {
let tl64 = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
assert!(tl64.is_lock_free(8));
assert!(tl64.is_lock_free(16)); assert!(!tl64.is_lock_free(32));
let tl32 = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, false);
assert!(tl32.is_lock_free(4));
assert!(!tl32.is_lock_free(8));
}
#[test]
fn test_type_lowering_display() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
if let Some(ty) = tl.get_type("std::vector") {
let display = format!("{}", ty);
assert!(!display.is_empty());
}
if let Some(ty) = tl.get_type("std::string") {
let display = format!("{}", ty);
assert!(!display.is_empty());
}
if let Some(ty) = tl.get_type("std::input_iterator_tag") {
let display = format!("{}", ty);
assert_eq!(display, "iterator");
}
}
#[test]
fn test_header_mapping_c_compat_headers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let c_compat = [
"cassert", "cctype", "cerrno", "cfloat", "climits", "clocale", "cmath", "csetjmp",
"csignal", "cstdarg", "cstdio", "cstring", "ctime", "cwchar", "cwctype",
];
for h in &c_compat {
assert!(hm.resolve(h).is_some(), "Missing C compat header: {}", h);
}
}
#[test]
fn test_header_mapping_iwyu_for_all_containers() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::vector"), Some("vector"));
assert_eq!(hm.iwyu_for_symbol("std::vector<bool>"), Some("vector"));
assert_eq!(hm.iwyu_for_symbol("std::deque"), Some("deque"));
assert_eq!(hm.iwyu_for_symbol("std::list"), Some("list"));
assert_eq!(
hm.iwyu_for_symbol("std::forward_list"),
Some("forward_list")
);
assert_eq!(hm.iwyu_for_symbol("std::map"), Some("map"));
assert_eq!(hm.iwyu_for_symbol("std::multimap"), Some("map"));
assert_eq!(hm.iwyu_for_symbol("std::set"), Some("set"));
assert_eq!(
hm.iwyu_for_symbol("std::unordered_map"),
Some("unordered_map")
);
assert_eq!(
hm.iwyu_for_symbol("std::unordered_set"),
Some("unordered_set")
);
}
#[test]
fn test_header_mapping_iwyu_for_string_types() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::string"), Some("string"));
assert_eq!(hm.iwyu_for_symbol("std::wstring"), Some("string"));
assert_eq!(hm.iwyu_for_symbol("std::basic_string"), Some("string"));
assert_eq!(hm.iwyu_for_symbol("std::string_view"), Some("string_view"));
assert_eq!(
hm.iwyu_for_symbol("std::basic_string_view"),
Some("string_view")
);
}
#[test]
fn test_header_mapping_iwyu_for_smart_ptrs() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::unique_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::shared_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::weak_ptr"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::make_unique"), Some("memory"));
assert_eq!(hm.iwyu_for_symbol("std::make_shared"), Some("memory"));
}
#[test]
fn test_header_mapping_iwyu_mutex_types() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::mutex"), Some("mutex"));
assert_eq!(hm.iwyu_for_symbol("std::lock_guard"), Some("mutex"));
assert_eq!(hm.iwyu_for_symbol("std::unique_lock"), Some("mutex"));
assert_eq!(hm.iwyu_for_symbol("std::scoped_lock"), Some("mutex"));
assert_eq!(hm.iwyu_for_symbol("std::recursive_mutex"), Some("mutex"));
assert_eq!(
hm.iwyu_for_symbol("std::shared_mutex"),
Some("shared_mutex")
);
assert_eq!(hm.iwyu_for_symbol("std::shared_lock"), Some("shared_mutex"));
}
#[test]
fn test_header_mapping_iwyu_exception_types() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
assert_eq!(hm.iwyu_for_symbol("std::exception"), Some("exception"));
assert_eq!(hm.iwyu_for_symbol("std::logic_error"), Some("stdexcept"));
assert_eq!(hm.iwyu_for_symbol("std::runtime_error"), Some("stdexcept"));
assert_eq!(hm.iwyu_for_symbol("std::out_of_range"), Some("stdexcept"));
assert_eq!(hm.iwyu_for_symbol("std::bad_alloc"), Some("new"));
assert_eq!(hm.iwyu_for_symbol("std::type_info"), Some("typeinfo"));
}
#[test]
fn test_header_mapping_standard_version_filter() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let cpp11 = hm.headers_for_standard(CppStandard::Cpp11);
assert!(cpp11.contains(&"array"));
assert!(cpp11.contains(&"forward_list"));
assert!(cpp11.contains(&"unordered_map"));
let cpp17 = hm.headers_for_standard(CppStandard::Cpp17);
assert!(cpp17.contains(&"optional"));
assert!(cpp17.contains(&"variant"));
assert!(cpp17.contains(&"any"));
assert!(cpp17.contains(&"string_view"));
assert!(cpp17.contains(&"filesystem"));
let cpp20 = hm.headers_for_standard(CppStandard::Cpp20);
assert!(cpp20.contains(&"span"));
assert!(cpp20.contains(&"format"));
assert!(cpp20.contains(&"ranges"));
assert!(cpp20.contains(&"concepts"));
assert!(cpp20.contains(&"bit"));
let cpp23 = hm.headers_for_standard(CppStandard::Cpp23);
assert!(cpp23.contains(&"expected"));
assert!(cpp23.contains(&"flat_map"));
assert!(cpp23.contains(&"mdspan"));
}
#[test]
fn test_header_mapping_transitive_deps() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let deps = hm.transitive_dependents("cstddef");
assert!(!deps.is_empty());
}
#[test]
fn test_header_mapping_include_order_respects_deps() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let order = hm.include_order(&["utility".into()]);
let tt_pos = order.iter().position(|h| h == "type_traits");
let util_pos = order.iter().position(|h| h == "utility");
assert!(tt_pos.is_some());
assert!(util_pos.is_some());
assert!(tt_pos.unwrap() < util_pos.unwrap());
}
#[test]
fn test_platform_sso_all_variants() {
let platforms = [
(X86CxxPlatform::Darwin, 22),
(X86CxxPlatform::FreeBSD, 22),
(X86CxxPlatform::Android, 22),
(X86CxxPlatform::Fuchsia, 22),
(X86CxxPlatform::LinuxGnu, 15),
(X86CxxPlatform::LinuxMusl, 15),
(X86CxxPlatform::WindowsMSVC, 15),
(X86CxxPlatform::WindowsMinGW, 15),
(X86CxxPlatform::Cygwin, 15),
(X86CxxPlatform::GenericElf, 15),
(X86CxxPlatform::BareMetal, 15),
];
for (platform, expected_sso) in platforms {
assert_eq!(
platform.sso_capacity(),
expected_sso,
"Wrong SSO capacity for {:?}",
platform
);
}
}
#[test]
fn test_platform_string_size_all_variants() {
let small_string_platforms = [
X86CxxPlatform::Darwin,
X86CxxPlatform::FreeBSD,
X86CxxPlatform::Android,
X86CxxPlatform::Fuchsia,
];
for p in &small_string_platforms {
assert_eq!(p.string_size_64(), 24, "Wrong string size for {:?}", p);
}
let large_string_platforms = [
X86CxxPlatform::LinuxGnu,
X86CxxPlatform::LinuxMusl,
X86CxxPlatform::WindowsMSVC,
X86CxxPlatform::WindowsMinGW,
];
for p in &large_string_platforms {
assert_eq!(p.string_size_64(), 32, "Wrong string size for {:?}", p);
}
}
#[test]
fn test_platform_display() {
assert_eq!(format!("{}", X86CxxPlatform::LinuxGnu), "linux-gnu");
assert_eq!(format!("{}", X86CxxPlatform::Darwin), "darwin");
assert_eq!(format!("{}", X86CxxPlatform::WindowsMSVC), "windows-msvc");
assert_eq!(format!("{}", X86CxxPlatform::BareMetal), "bare-metal");
}
#[test]
fn test_header_mapping_all_headers_iteration() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
hm.build_index();
let all = hm.all_headers();
for h in &all {
assert!(hm.resolve(h).is_some(), "Cannot resolve header: {}", h);
}
}
#[test]
fn test_type_lowering_all_stdlib_types() {
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
tl.initialize();
let expected_types = [
"std::string",
"std::vector",
"std::array",
"std::unique_ptr",
"std::shared_ptr",
"std::weak_ptr",
"std::optional",
"std::variant",
"std::any",
"std::pair",
"std::tuple",
"std::function",
"std::string_view",
"std::span",
"std::atomic",
"std::mutex",
"std::shared_mutex",
"std::recursive_mutex",
"std::condition_variable",
"std::thread",
"std::jthread",
"std::promise",
"std::future",
"std::packaged_task",
];
for ty_name in &expected_types {
assert!(
tl.get_type(ty_name).is_some(),
"Missing type lowering for {}",
ty_name
);
}
}
#[test]
fn test_abi_lowering_all_features() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let features = [
X86CxxABIFeature::VtablePointer {
class_name: "X".into(),
offset: 0,
},
X86CxxABIFeature::BaseClassLayout {
class_name: "X".into(),
base_name: "B".into(),
is_virtual: false,
offset: 8,
},
X86CxxABIFeature::VirtualBaseLayout {
class_name: "X".into(),
vbase_name: "VB".into(),
vbase_offset: 16,
},
X86CxxABIFeature::ThisCall {
class_name: "X".into(),
method_name: "f".into(),
is_virtual: false,
},
X86CxxABIFeature::EmitVtable {
class_name: "X".into(),
is_primary: true,
},
X86CxxABIFeature::EmitVTT {
class_name: "X".into(),
},
X86CxxABIFeature::GuardVariable {
var_name: "guard".into(),
is_thread_safe: true,
},
X86CxxABIFeature::ReturnSlot {
class_name: "X".into(),
is_named: false,
},
X86CxxABIFeature::MemberPointer {
class_name: "X".into(),
member_type: "int".into(),
},
];
for feat in &features {
let result = abi.lower(feat);
assert!(result.success, "Feature {:?} failed", feat);
}
}
#[test]
fn test_many_template_instantiations() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("many", vec!["T".into()], "T val;");
let types = [
"int",
"long",
"short",
"char",
"float",
"double",
"unsigned",
"bool",
"void*",
"char*",
"const char*",
"long long",
"unsigned long",
"size_t",
"ptrdiff_t",
];
for t in &types {
let result = tmpl.instantiate("many", &[X86CxxTemplateArg::Type(t.to_string())]);
assert!(result.is_ok(), "Failed to instantiate many<{}>", t);
}
assert_eq!(tmpl.instantiation_count(), types.len());
}
#[test]
fn test_ctor_kind_equality() {
assert_eq!(CtorKind::Complete, CtorKind::Complete);
assert_ne!(CtorKind::Complete, CtorKind::Base);
assert_ne!(CtorKind::Base, CtorKind::CompleteAllocating);
}
#[test]
fn test_dtor_kind_equality() {
assert_eq!(DtorKind::Complete, DtorKind::Complete);
assert_ne!(DtorKind::Complete, DtorKind::Base);
assert_ne!(DtorKind::Base, DtorKind::Deleting);
}
#[test]
fn test_x86_eh_abi_kind_equality() {
assert_eq!(X86EHABIKind::ItaniumDWARF, X86EHABIKind::ItaniumDWARF);
assert_ne!(X86EHABIKind::ItaniumDWARF, X86EHABIKind::MicrosoftSEH);
}
#[test]
fn test_x86_lsda_format_equality() {
assert_eq!(X86LSDAFormat::GCC, X86LSDAFormat::GCC);
assert_ne!(X86LSDAFormat::GCC, X86LSDAFormat::MSVCXData);
}
#[test]
fn test_x86_cxx_abi_kind_equality() {
assert_eq!(X86CxxABIKind::Itanium, X86CxxABIKind::Itanium);
assert_ne!(X86CxxABIKind::Itanium, X86CxxABIKind::Microsoft);
}
#[test]
fn test_x86_cxx_platform_equality() {
assert_eq!(X86CxxPlatform::LinuxGnu, X86CxxPlatform::LinuxGnu);
assert_ne!(X86CxxPlatform::LinuxGnu, X86CxxPlatform::Darwin);
assert_ne!(X86CxxPlatform::Darwin, X86CxxPlatform::WindowsMSVC);
}
#[test]
fn test_x86_libcxx_type_partial_eq() {
let t1 = X86LibCxxType::RawType("i32".into());
let t2 = X86LibCxxType::RawType("i32".into());
let t3 = X86LibCxxType::RawType("i64".into());
assert_eq!(t1, t2);
assert_ne!(t1, t3);
}
#[test]
fn test_x86_cxx_template_arg_partial_eq() {
assert_eq!(
X86CxxTemplateArg::Type("int".into()),
X86CxxTemplateArg::Type("int".into())
);
assert_ne!(
X86CxxTemplateArg::Type("int".into()),
X86CxxTemplateArg::Type("float".into())
);
assert_eq!(
X86CxxTemplateArg::Integral(0),
X86CxxTemplateArg::Integral(0)
);
assert_ne!(
X86CxxTemplateArg::Integral(0),
X86CxxTemplateArg::Integral(1)
);
assert_eq!(X86CxxTemplateArg::Bool(true), X86CxxTemplateArg::Bool(true));
assert_ne!(
X86CxxTemplateArg::Bool(true),
X86CxxTemplateArg::Bool(false)
);
}
#[test]
fn test_mangle_simple_type() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let m = tmpl.mangle_template_name("vector", &[X86CxxTemplateArg::Type("int".into())]);
assert!(m.starts_with("_Z"));
assert!(m.contains("vector"));
assert!(m.ends_with('E'));
}
#[test]
fn test_mangle_multiple_types() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let m = tmpl.mangle_template_name(
"map",
&[
X86CxxTemplateArg::Type("int".into()),
X86CxxTemplateArg::Type("double".into()),
],
);
assert!(m.contains("int"));
assert!(m.contains("double"));
}
#[test]
fn test_mangle_integral_negative() {
let tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
let m = tmpl.mangle_template_name("ratio", &[X86CxxTemplateArg::Integral(-1)]);
assert!(m.contains("-1"));
}
#[test]
fn test_full_stdlib_lowering_roundtrip() {
let mut lib = X86LibCxx::new(
"x86_64-unknown-linux-gnu",
true,
CppStandard::Cpp17,
CXXStdLib::LibCxx,
);
lib.initialize();
assert!(lib.header_mapping.resolve("string").is_some());
assert_eq!(
lib.header_mapping.iwyu_for_symbol("std::vector"),
Some("vector")
);
let string_ty = lib.type_lowering.get_type("std::string");
assert!(string_ty.is_some());
let abi_result = lib.abi_lowering.lower(&X86CxxABIFeature::Constructor {
class_name: "MyClass".into(),
ctor_kind: CtorKind::Complete,
params: vec![],
});
assert!(abi_result.success);
let personality = lib.exception_handling.personality_function();
assert!(!personality.is_empty());
lib.rtti
.register_class_type("TestRTTI", "_ZN8TestRTTIE", &[]);
assert!(lib.rtti.has_type_info("TestRTTI"));
lib.templates
.register_template("full_test", vec!["T".into()], "T val;");
let inst = lib
.templates
.instantiate("full_test", &[X86CxxTemplateArg::Type("int".into())]);
assert!(inst.is_ok());
}
#[test]
fn test_abi_result_metadata_classes() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::Constructor {
class_name: "MetaTest".into(),
ctor_kind: CtorKind::Complete,
params: vec!["int".into(), "float".into()],
});
assert!(result.success);
assert!(result.metadata.contains_key("class"));
assert!(result.metadata.contains_key("ctor_kind"));
assert!(result.metadata.contains_key("mangled_name"));
}
#[test]
fn test_abi_result_metadata_vtable() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let result = abi.lower(&X86CxxABIFeature::EmitVtable {
class_name: "VTableMeta".into(),
is_primary: false,
});
assert!(result.success);
assert_eq!(
result.metadata.get("is_primary").map(|s| s.as_str()),
Some("false")
);
}
#[test]
fn test_diag_engine_creation() {
let engine = X86CxxDiagnosticEngine::new();
assert!(!engine.has_errors());
assert_eq!(engine.error_count(), 0);
assert_eq!(engine.warning_count(), 0);
}
#[test]
fn test_diag_engine_emit_error() {
let mut engine = X86CxxDiagnosticEngine::new();
engine.error("Something went wrong");
assert!(engine.has_errors());
assert_eq!(engine.error_count(), 1);
assert_eq!(engine.warning_count(), 0);
}
#[test]
fn test_diag_engine_emit_warning() {
let mut engine = X86CxxDiagnosticEngine::new();
engine.warning("Deprecated feature");
assert!(!engine.has_errors());
assert_eq!(engine.error_count(), 0);
assert_eq!(engine.warning_count(), 1);
}
#[test]
fn test_diag_engine_multiple() {
let mut engine = X86CxxDiagnosticEngine::new();
engine.error("e1");
engine.error("e2");
engine.warning("w1");
engine.warning("w2");
assert_eq!(engine.error_count(), 2);
assert_eq!(engine.warning_count(), 2);
}
#[test]
fn test_diag_engine_drain() {
let mut engine = X86CxxDiagnosticEngine::new();
engine.error("test error");
engine.warning("test warning");
let diags = engine.drain();
assert_eq!(diags.len(), 2);
}
#[test]
fn test_diag_formatting() {
let diag = X86CxxDiagnostic {
level: X86CxxDiagLevel::Error,
message: "type mismatch".into(),
location: Some("file.cpp:42:10".into()),
code: Some("E0001".into()),
fixit: Some("use int instead of float".into()),
};
let formatted = format!("{}", diag);
assert!(formatted.contains("error"));
assert!(formatted.contains("type mismatch"));
}
#[test]
fn test_diag_level_display() {
assert_eq!(format!("{}", X86CxxDiagLevel::Note), "note");
assert_eq!(format!("{}", X86CxxDiagLevel::Warning), "warning");
assert_eq!(format!("{}", X86CxxDiagLevel::Error), "error");
}
#[test]
fn test_diag_level_ordering() {
assert!(X86CxxDiagLevel::Note < X86CxxDiagLevel::Warning);
assert!(X86CxxDiagLevel::Warning < X86CxxDiagLevel::Error);
assert!(X86CxxDiagLevel::Error < X86CxxDiagLevel::Fatal);
}
#[test]
fn test_vtable_layout_creation() {
let layout = X86VTableLayout::new("MyClass", true);
assert_eq!(layout.class_name, "MyClass");
assert!(layout.is_primary);
assert!(layout.entries.is_empty());
}
#[test]
fn test_vtable_layout_add_entries() {
let mut layout = X86VTableLayout::new("MyClass", true);
layout.add_entry(X86VTableEntry {
index: 0,
kind: X86VTableEntryKind::OffsetToTop,
mangled: None,
this_adjustment: None,
});
layout.add_entry(X86VTableEntry {
index: 1,
kind: X86VTableEntryKind::RttiPointer,
mangled: None,
this_adjustment: None,
});
assert_eq!(layout.entries.len(), 2);
assert_eq!(layout.size_bytes(8), 16);
assert_eq!(layout.size_bytes(4), 8);
}
#[test]
fn test_vtable_emit_ir() {
let mut layout = X86VTableLayout::new("Test", true);
layout.add_entry(X86VTableEntry {
index: 0,
kind: X86VTableEntryKind::OffsetToTop,
mangled: None,
this_adjustment: None,
});
let ir = layout.emit_ir(8);
assert!(ir.contains("linkonce_odr"));
assert!(ir.contains("offset-to-top"));
}
#[test]
fn test_vtable_entry_kind_equality() {
assert_eq!(
X86VTableEntryKind::OffsetToTop,
X86VTableEntryKind::OffsetToTop
);
assert_ne!(
X86VTableEntryKind::OffsetToTop,
X86VTableEntryKind::RttiPointer
);
}
#[test]
fn test_thunk_creation() {
let thunk = X86ThunkInfo::new(
"_ZTv0_n12_N7Derived3fooEv".into(),
"_ZN7Derived3fooEv".into(),
8,
);
assert_eq!(thunk.this_adjustment, 8);
assert!(thunk.vcall_offset.is_none());
}
#[test]
fn test_thunk_with_vcall() {
let thunk =
X86ThunkInfo::new("thunk_vcall".into(), "target".into(), 0).with_vcall_offset(24);
assert_eq!(thunk.vcall_offset, Some(24));
}
#[test]
fn test_thunk_emit_ir() {
let thunk = X86ThunkInfo::new("thunk_name".into(), "target_name".into(), 16);
let ir = thunk.emit_ir(8);
assert!(ir.contains("linkonce_odr"));
assert!(ir.contains("adjusted_this"));
assert!(ir.contains("tail call"));
}
#[test]
fn test_thunk_zero_adjustment() {
let thunk = X86ThunkInfo::new("t".into(), "target".into(), 0);
let ir = thunk.emit_ir(8);
assert!(ir.contains("bitcast"));
}
#[test]
fn test_static_guard_creation() {
let guard = X86StaticGuard::new("my_local", true);
assert_eq!(guard.name, "my_local");
assert!(guard.is_thread_safe);
assert!(guard.guard_name.starts_with("_ZGV"));
}
#[test]
fn test_static_guard_non_thread_safe() {
let guard = X86StaticGuard::new("local", false);
assert!(!guard.is_thread_safe);
}
#[test]
fn test_static_guard_emit_ir_64() {
let guard = X86StaticGuard::new("loc64", true);
let ir = guard.emit_ir(8);
assert!(ir.contains("internal global"));
}
#[test]
fn test_static_guard_emit_ir_32() {
let guard = X86StaticGuard::new("loc32", true);
let ir = guard.emit_ir(4);
assert!(ir.contains("i32"));
}
#[test]
fn test_static_guard_emit_pattern() {
let guard = X86StaticGuard::new("safe", true);
let pat = guard.emit_guard_pattern(" call void @init()\n");
assert!(pat.contains("__cxa_guard_acquire"));
assert!(pat.contains("__cxa_guard_release"));
}
#[test]
fn test_array_ctor_dtor_trivial() {
let mut info = X86ArrayCtorDtorInfo::new("POD", 100, true, true);
let ir = info.generate_loop(8);
assert!(ir.contains("no-op"));
}
#[test]
fn test_array_ctor_dtor_non_trivial() {
let mut info = X86ArrayCtorDtorInfo::new("NonTrivial", 5, true, false);
let ir = info.generate_loop(8);
assert!(ir.contains("loop_header"));
assert!(ir.contains("loop_exit"));
}
#[test]
fn test_new_delete_creation() {
let l = X86NewDeleteLowering::new_scalar(16, 8);
assert_eq!(l.alloc_size, 16);
assert_eq!(l.alignment, 8);
assert!(!l.is_array);
}
#[test]
fn test_new_delete_array_flag() {
let l = X86NewDeleteLowering::new_scalar(24, 8).as_array();
assert!(l.is_array);
}
#[test]
fn test_new_delete_nothrow_flag() {
let l = X86NewDeleteLowering::new_scalar(8, 4).as_nothrow();
assert!(l.is_nothrow);
}
#[test]
fn test_new_delete_aligned_flag() {
let l = X86NewDeleteLowering::new_scalar(64, 64).as_aligned();
assert!(l.is_aligned);
}
#[test]
fn test_new_generate_ir_scalar() {
let l = X86NewDeleteLowering::new_scalar(32, 8);
let ir = l.generate_new_ir(8);
assert!(ir.contains("_Znwm"));
}
#[test]
fn test_new_generate_ir_array() {
let l = X86NewDeleteLowering::new_scalar(64, 8).as_array();
let ir = l.generate_new_ir(8);
assert!(ir.contains("_Znam"));
}
#[test]
fn test_new_generate_ir_nothrow() {
let l = X86NewDeleteLowering::new_scalar(16, 8).as_nothrow();
let ir = l.generate_new_ir(8);
assert!(ir.contains("nothrow"));
}
#[test]
fn test_new_generate_ir_aligned() {
let l = X86NewDeleteLowering::new_scalar(128, 64).as_aligned();
let ir = l.generate_new_ir(8);
assert!(ir.contains("aligned"));
}
#[test]
fn test_delete_generate_ir_scalar() {
let l = X86NewDeleteLowering::new_scalar(16, 8);
let ir = l.generate_delete_ir("%ptr", 8);
assert!(ir.contains("_ZdlPv"));
}
#[test]
fn test_delete_generate_ir_array() {
let l = X86NewDeleteLowering::new_scalar(16, 8).as_array();
let ir = l.generate_delete_ir("%arr", 8);
assert!(ir.contains("_ZdaPv"));
}
#[test]
fn test_construction_vtable() {
let cvt = X86ConstructionVTable {
base_class: "Base".into(),
complete_class: "Derived".into(),
entries: vec![],
in_object_delta: 16,
};
assert_eq!(cvt.in_object_delta, 16);
}
#[test]
fn test_vtable_with_construction_vtable() {
let mut layout = X86VTableLayout::new("Derived", true);
layout.add_entry(X86VTableEntry {
index: 0,
kind: X86VTableEntryKind::OffsetToTop,
mangled: None,
this_adjustment: None,
});
layout.add_construction_vtable(X86ConstructionVTable {
base_class: "VBase".into(),
complete_class: "Derived".into(),
entries: vec![X86VTableEntry {
index: 0,
kind: X86VTableEntryKind::ConstructionVBaseOffset,
mangled: None,
this_adjustment: None,
}],
in_object_delta: 32,
});
assert_eq!(layout.construction_vtables.len(), 1);
assert_eq!(layout.size_bytes(8), 16);
}
#[test]
fn test_concurrency_linux_creation() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::LinuxGnu, true);
assert_eq!(cl.mutex_kind, X86MutexKind::Futex);
assert_eq!(cl.condvar_kind, X86CondVarKind::FutexCond);
assert_eq!(cl.tls_model, X86TLSModel::ELFTLS);
}
#[test]
fn test_concurrency_darwin_creation() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::Darwin, true);
assert_eq!(cl.mutex_kind, X86MutexKind::PthreadMutex);
assert_eq!(cl.tls_model, X86TLSModel::MachOTLS);
}
#[test]
fn test_concurrency_windows_creation() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::WindowsMSVC, true);
assert_eq!(cl.mutex_kind, X86MutexKind::WindowsSRWLock);
assert_eq!(cl.condvar_kind, X86CondVarKind::WindowsCondVar);
assert_eq!(cl.tls_model, X86TLSModel::WindowsTLS);
}
#[test]
fn test_concurrency_lock_free_sizes_64() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::LinuxGnu, true);
assert!(cl.is_lock_free(4));
assert!(cl.is_lock_free(8));
assert!(cl.is_lock_free(16));
assert!(!cl.is_lock_free(32));
}
#[test]
fn test_concurrency_lock_free_sizes_32() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::LinuxGnu, false);
assert!(cl.is_lock_free(4));
assert!(!cl.is_lock_free(8));
assert!(!cl.is_lock_free(16));
}
#[test]
fn test_concurrency_emit_mutex_lock() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = cl.emit_mutex_lock("%mtx");
assert!(ir.contains("futex"));
}
#[test]
fn test_concurrency_emit_mutex_unlock_win() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::WindowsMSVC, true);
let ir = cl.emit_mutex_unlock("%mtx");
assert!(ir.contains("SRWLock"));
}
#[test]
fn test_concurrency_emit_cond_wait() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::Darwin, true);
let ir = cl.emit_cond_wait("%cv", "%mtx");
assert!(ir.contains("pthread_cond_wait"));
}
#[test]
fn test_concurrency_emit_cond_notify() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::WindowsMSVC, true);
let ir = cl.emit_cond_notify_one("%cv");
assert!(ir.contains("WakeConditionVariable"));
}
#[test]
fn test_concurrency_tls_access_elf() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = cl.emit_tls_access("my_tls_var", 8);
assert!(ir.contains("__tls_get_addr"));
}
#[test]
fn test_concurrency_tls_access_windows() {
let cl = X86ConcurrencyLowering::new(X86CxxPlatform::WindowsMSVC, true);
let ir = cl.emit_tls_access("my_tls_var", 8);
assert!(ir.contains("__tls_get_addr_internal"));
}
#[test]
fn test_memory_order_to_llvm() {
assert_eq!(X86MemoryOrder::Relaxed.to_llvm_ordering(), "monotonic");
assert_eq!(X86MemoryOrder::Acquire.to_llvm_ordering(), "acquire");
assert_eq!(X86MemoryOrder::Release.to_llvm_ordering(), "release");
assert_eq!(X86MemoryOrder::AcqRel.to_llvm_ordering(), "acq_rel");
assert_eq!(X86MemoryOrder::SeqCst.to_llvm_ordering(), "seq_cst");
}
#[test]
fn test_memory_order_requires_mfence() {
assert!(X86MemoryOrder::SeqCst.requires_mfence());
assert!(!X86MemoryOrder::Relaxed.requires_mfence());
assert!(!X86MemoryOrder::Acquire.requires_mfence());
assert!(!X86MemoryOrder::Release.requires_mfence());
}
#[test]
fn test_memory_order_compiler_barrier() {
assert!(X86MemoryOrder::Relaxed.is_compiler_barrier_only());
assert!(!X86MemoryOrder::Acquire.is_compiler_barrier_only());
assert!(!X86MemoryOrder::SeqCst.is_compiler_barrier_only());
}
#[test]
fn test_memory_order_display() {
assert_eq!(
format!("{}", X86MemoryOrder::Relaxed),
"memory_order_relaxed"
);
assert_eq!(
format!("{}", X86MemoryOrder::SeqCst),
"memory_order_seq_cst"
);
}
#[test]
fn test_atomic_lowering_fence() {
let al = X86AtomicLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = al.emit_fence(X86MemoryOrder::SeqCst);
assert!(ir.contains("mfence") || ir.contains("seq_cst"));
}
#[test]
fn test_atomic_lowering_load() {
let al = X86AtomicLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = al.emit_atomic_load("%val", "%ptr", "i64", X86MemoryOrder::Acquire);
assert!(ir.contains("load atomic"));
}
#[test]
fn test_atomic_lowering_store() {
let al = X86AtomicLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = al.emit_atomic_store("%ptr", "42", "i32", X86MemoryOrder::Release);
assert!(ir.contains("store atomic"));
}
#[test]
fn test_atomic_lowering_cmpxchg() {
let al = X86AtomicLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = al.emit_atomic_cmpxchg(
"%r",
"%ptr",
"%exp",
"%des",
"i64",
X86MemoryOrder::AcqRel,
X86MemoryOrder::Acquire,
);
assert!(ir.contains("cmpxchg"));
}
#[test]
fn test_atomic_lowering_rmw() {
let al = X86AtomicLowering::new(X86CxxPlatform::LinuxGnu, true);
let ir = al.emit_atomic_rmw("%r", "add", "%ptr", "1", "i64", X86MemoryOrder::SeqCst);
assert!(ir.contains("atomicrmw"));
}
#[test]
fn test_stdlib_compat_linux() {
let compat = X86StdLibCompat::new(X86CxxPlatform::LinuxGnu);
assert_eq!(compat.primary, CXXStdLib::LibStdCxx);
assert!(!compat.libcxx_libstdcxx_interop);
let flags = compat.linker_flags();
assert!(flags.iter().any(|f| f.contains("stdc++")));
}
#[test]
fn test_stdlib_compat_darwin() {
let compat = X86StdLibCompat::new(X86CxxPlatform::Darwin);
assert_eq!(compat.primary, CXXStdLib::LibCxx);
let flags = compat.linker_flags();
assert!(flags.iter().any(|f| f.contains("c++")));
}
#[test]
fn test_stdlib_compat_incompatibilities() {
let compat = X86StdLibCompat::new(X86CxxPlatform::LinuxGnu);
assert!(compat.incompatibilities.len() >= 3);
let sso = compat.check_feature("std::string SSO layout");
assert!(sso.is_some());
assert!(sso.unwrap().description.contains("SSO"));
}
#[test]
fn test_stdlib_compat_windows() {
let compat = X86StdLibCompat::new(X86CxxPlatform::WindowsMSVC);
let flags = compat.linker_flags();
assert!(flags.iter().any(|f| f.contains("msvcp")));
}
#[test]
fn test_stdlib_compat_workaround() {
let compat = X86StdLibCompat::new(X86CxxPlatform::LinuxGnu);
let sso = compat.check_feature("std::string SSO layout").unwrap();
assert!(sso.workaround.is_some());
assert!(sso.workaround.as_ref().unwrap().contains("string_view"));
}
#[test]
fn test_msvc_abi_creation() {
let abi = X86MSVCABILowering::new(true);
assert!(abi.is_64bit);
assert!(abi.uses_complete_object_locator);
}
#[test]
fn test_msvc_abi_col_emission() {
let abi = X86MSVCABILowering::new(true);
let ir = abi.emit_complete_object_locator("MyWinClass", 0);
assert!(ir.contains("_RTC"));
assert!(ir.contains("signature"));
}
#[test]
fn test_msvc_abi_vftable_emission() {
let abi = X86MSVCABILowering::new(true);
let ir = abi.emit_vftable("WinClass", &["_ZN8WinClass5vfuncEv".into()]);
assert!(ir.contains("_VFT"));
assert!(ir.contains("meta pointer"));
}
#[test]
fn test_msvc_abi_member_pointer_64() {
let abi = X86MSVCABILowering::new(true);
let ir = abi.emit_member_pointer_type("WinClass", "int");
assert!(ir.contains("i32"));
assert!(ir.contains("i64"));
}
#[test]
fn test_msvc_abi_member_pointer_32() {
let abi = X86MSVCABILowering::new(false);
let ir = abi.emit_member_pointer_type("Win32", "float");
assert!(ir.contains("single inheritance"));
}
#[test]
fn test_msvc_seh_catch_handler() {
let abi = X86MSVCABILowering::new(true);
let ir = abi.emit_seh_catch_handler("my_func", "std::exception", " call void @handle()\n");
assert!(ir.contains("__CxxFrameHandler3"));
assert!(ir.contains("EXCEPTION_EXECUTE_HANDLER"));
}
#[test]
fn test_msvc_col_struct() {
let col = X86MSVCCompleteObjectLocator {
signature: 0,
offset: 0,
cd_offset: 0,
type_descriptor_ptr: 0x1000,
class_hierarchy_descriptor_ptr: 0x2000,
self_relative_ptr: 0x3000,
};
assert_eq!(col.signature, 0);
assert_eq!(col.type_descriptor_ptr, 0x1000);
}
#[test]
fn test_msvc_hierarchy_descriptor() {
let chd = X86MSVCClassHierarchyDescriptor {
signature: 0,
attributes: 1,
num_base_classes: 2,
base_class_array_ptr: 0x4000,
};
assert_eq!(chd.num_base_classes, 2);
}
#[test]
fn test_msvc_base_class_descriptor() {
let bcd = X86MSVCBaseClassDescriptor {
type_descriptor_ptr: 0x1000,
num_contained_bases: 0,
mdisp: 0,
pdisp: -1,
vdisp: 0,
attributes: 0,
class_hierarchy_descriptor_ptr: 0x5000,
};
assert_eq!(bcd.pdisp, -1);
}
#[test]
fn test_mutex_kind_variants() {
assert_eq!(X86MutexKind::PthreadMutex, X86MutexKind::PthreadMutex);
assert_ne!(X86MutexKind::PthreadMutex, X86MutexKind::Futex);
assert_ne!(X86MutexKind::WindowsSRWLock, X86MutexKind::Spinlock);
}
#[test]
fn test_condvar_kind_variants() {
assert_eq!(X86CondVarKind::PthreadCond, X86CondVarKind::PthreadCond);
assert_ne!(X86CondVarKind::PthreadCond, X86CondVarKind::WindowsCondVar);
}
#[test]
fn test_tls_model_variants() {
assert_eq!(X86TLSModel::ELFTLS, X86TLSModel::ELFTLS);
assert_ne!(X86TLSModel::ELFTLS, X86TLSModel::WindowsTLS);
assert_ne!(X86TLSModel::MachOTLS, X86TLSModel::EmulatedTLS);
}
#[test]
fn test_tls_all_models() {
let models = [
(X86CxxPlatform::LinuxGnu, "__tls_get_addr"),
(X86CxxPlatform::Darwin, "tlv_get_address"),
(X86CxxPlatform::WindowsMSVC, "__tls_get_addr_internal"),
(X86CxxPlatform::BareMetal, "pthread_getspecific"),
];
for (platform, expected) in &models {
let cl = X86ConcurrencyLowering::new(*platform, true);
let ir = cl.emit_tls_access("x", 8);
assert!(
ir.contains(expected),
"TLS access for {:?} should contain '{}'",
platform,
expected
);
}
}
#[test]
fn test_type_traits_lowering_creation_64() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.pointer_size_bits, 64);
assert_eq!(tl.size_t_size, 8);
}
#[test]
fn test_type_traits_lowering_creation_32() {
let tl = X86CxxTypeTraitsLowering::new(false, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.pointer_size_bits, 32);
assert_eq!(tl.size_t_size, 4);
}
#[test]
fn test_type_traits_evaluate_is_void() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_void", "void"), Some(true));
assert_eq!(tl.evaluate_trait("is_void", "int"), Some(false));
}
#[test]
fn test_type_traits_evaluate_is_integral() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_integral", "int"), Some(true));
assert_eq!(
tl.evaluate_trait("is_integral", "unsigned long"),
Some(true)
);
assert_eq!(tl.evaluate_trait("is_integral", "float"), Some(false));
}
#[test]
fn test_type_traits_evaluate_is_floating_point() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_floating_point", "float"), Some(true));
assert_eq!(tl.evaluate_trait("is_floating_point", "double"), Some(true));
assert_eq!(tl.evaluate_trait("is_floating_point", "int"), Some(false));
}
#[test]
fn test_type_traits_evaluate_is_pointer() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_pointer", "int*"), Some(true));
assert_eq!(tl.evaluate_trait("is_pointer", "char*"), Some(true));
assert_eq!(tl.evaluate_trait("is_pointer", "int"), Some(false));
}
#[test]
fn test_type_traits_evaluate_is_reference() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_lvalue_reference", "int&"), Some(true));
assert_eq!(
tl.evaluate_trait("is_rvalue_reference", "int&&"),
Some(true)
);
}
#[test]
fn test_type_traits_size_of_builtins() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("bool"), Some(1));
assert_eq!(tl.size_of("char"), Some(1));
assert_eq!(tl.size_of("short"), Some(2));
assert_eq!(tl.size_of("int"), Some(4));
assert_eq!(tl.size_of("long"), Some(8));
assert_eq!(tl.size_of("long long"), Some(8));
assert_eq!(tl.size_of("float"), Some(4));
assert_eq!(tl.size_of("double"), Some(8));
assert_eq!(tl.size_of("size_t"), Some(8));
}
#[test]
fn test_type_traits_size_of_32bit() {
let tl = X86CxxTypeTraitsLowering::new(false, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("long"), Some(4));
assert_eq!(tl.size_of("size_t"), Some(4));
assert_eq!(tl.size_of("intptr_t"), Some(4));
}
#[test]
fn test_type_traits_align_of() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.align_of("int"), Some(4));
assert_eq!(tl.align_of("double"), Some(8));
}
#[test]
fn test_type_traits_emit_sizeof() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
let ir = tl.emit_sizeof("int");
assert!(ir.contains("sizeof")); }
#[test]
fn test_type_traits_darwin_char_signed() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::Darwin);
assert!(tl.char_is_signed);
}
#[test]
fn test_iterator_category_tag_names() {
assert_eq!(
X86IteratorCategory::Input.tag_type_name(),
"std::input_iterator_tag"
);
assert_eq!(
X86IteratorCategory::Forward.tag_type_name(),
"std::forward_iterator_tag"
);
assert_eq!(
X86IteratorCategory::RandomAccess.tag_type_name(),
"std::random_access_iterator_tag"
);
assert_eq!(
X86IteratorCategory::Contiguous.tag_type_name(),
"std::contiguous_iterator_tag"
);
}
#[test]
fn test_iterator_category_satisfies() {
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::Forward));
assert!(X86IteratorCategory::Bidirectional.satisfies(X86IteratorCategory::Input));
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::RandomAccess));
}
#[test]
fn test_iterator_category_display() {
assert_eq!(
format!("{}", X86IteratorCategory::Forward),
"std::forward_iterator_tag"
);
assert_eq!(
format!("{}", X86IteratorCategory::Contiguous),
"std::contiguous_iterator_tag"
);
}
#[test]
fn test_iterator_concept_check_creation() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
assert!(check.enforce_concepts);
assert!(check.require_contiguous_tag);
let check14 = X86IteratorConceptCheck::new(CppStandard::Cpp14);
assert!(!check14.enforce_concepts);
}
#[test]
fn test_iterator_concept_validate() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
let diags = check.validate("MyIter", X86IteratorCategory::RandomAccess);
assert!(!diags.is_empty());
}
#[test]
fn test_iterator_concept_tag_dispatch() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
let warn = check.check_tag_dispatch(
"BadIter",
X86IteratorCategory::RandomAccess,
X86IteratorCategory::Forward,
);
assert!(warn.is_some());
assert!(warn.unwrap().contains("warning"));
}
#[test]
fn test_iterator_concept_tag_dispatch_ok() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
let warn = check.check_tag_dispatch(
"GoodIter",
X86IteratorCategory::Forward,
X86IteratorCategory::RandomAccess,
);
assert!(warn.is_none());
}
#[test]
fn test_intrinsic_map_creation_64() {
let map = X86CxxLLVMIntrinsicMap::new(true);
assert!(map.use_intrinsics);
assert!(map.intrinsics.len() > 20);
}
#[test]
fn test_intrinsic_map_creation_32() {
let map = X86CxxLLVMIntrinsicMap::new(false);
assert!(map.intrinsics.len() > 20);
}
#[test]
fn test_intrinsic_map_resolve() {
let map = X86CxxLLVMIntrinsicMap::new(true);
let memcpy = map.resolve("memcpy");
assert!(memcpy.is_some());
assert!(memcpy.unwrap().contains("llvm.memcpy"));
}
#[test]
fn test_intrinsic_map_resolve_nonexistent() {
let map = X86CxxLLVMIntrinsicMap::new(true);
assert!(map.resolve("nonexistent_operation").is_none());
}
#[test]
fn test_intrinsic_map_emit_decls() {
let map = X86CxxLLVMIntrinsicMap::new(true);
let decls = map.emit_decls();
assert!(!decls.is_empty());
assert!(decls.contains("llvm.memcpy"));
}
#[test]
fn test_intrinsic_map_emit_call() {
let map = X86CxxLLVMIntrinsicMap::new(true);
let call = map.emit_call("memcpy", &["%dst", "%src", "i64 8"]); assert!(call.contains("call")); }
#[test]
fn test_intrinsic_map_known_operations() {
let map = X86CxxLLVMIntrinsicMap::new(true);
let ops = [
"memcpy", "memmove", "memset", "ctpop", "ctlz", "cttz", "bswap", "trap", "assume",
"abs", "smax", "smin",
];
for op in &ops {
assert!(map.resolve(op).is_some(), "Missing intrinsic for {}", op);
}
}
#[test]
fn test_type_traits_x86_data_model() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("short"), Some(2));
assert_eq!(tl.size_of("int"), Some(4));
assert_eq!(tl.size_of("long"), Some(8));
assert_eq!(tl.size_of("long long"), Some(8));
assert_eq!(tl.size_of("void*").or(Some(tl.size_t_size)), Some(8)); }
#[test]
fn test_type_traits_ilp32_data_model() {
let tl = X86CxxTypeTraitsLowering::new(false, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("short"), Some(2));
assert_eq!(tl.size_of("int"), Some(4));
assert_eq!(tl.size_of("long"), Some(4));
assert_eq!(tl.size_of("long long"), Some(8));
assert_eq!(tl.size_of("void*").or(Some(tl.size_t_size)), Some(4));
}
#[test]
fn test_iterator_category_hierarchy() {
assert!(X86IteratorCategory::Contiguous > X86IteratorCategory::RandomAccess);
assert!(X86IteratorCategory::RandomAccess > X86IteratorCategory::Bidirectional);
assert!(X86IteratorCategory::Bidirectional > X86IteratorCategory::Forward);
assert!(X86IteratorCategory::Forward > X86IteratorCategory::Input);
}
#[test]
fn test_iterator_input_satisfies_input() {
assert!(X86IteratorCategory::Input.satisfies(X86IteratorCategory::Input));
}
#[test]
fn test_iterator_forward_satisfies_forward_and_input() {
assert!(X86IteratorCategory::Forward.satisfies(X86IteratorCategory::Input));
assert!(X86IteratorCategory::Forward.satisfies(X86IteratorCategory::Forward));
}
#[test]
fn test_iterator_bidirectional_satisfies_chain() {
assert!(X86IteratorCategory::Bidirectional.satisfies(X86IteratorCategory::Input));
assert!(X86IteratorCategory::Bidirectional.satisfies(X86IteratorCategory::Forward));
assert!(X86IteratorCategory::Bidirectional.satisfies(X86IteratorCategory::Bidirectional));
}
#[test]
fn test_iterator_random_access_satisfies_chain() {
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::Input));
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::Forward));
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::Bidirectional));
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::RandomAccess));
}
#[test]
fn test_iterator_contiguous_satisfies_all() {
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::Input));
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::Forward));
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::Bidirectional));
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::RandomAccess));
assert!(X86IteratorCategory::Contiguous.satisfies(X86IteratorCategory::Contiguous));
}
#[test]
fn test_iterator_concept_validate_cpp17() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp17);
assert!(!check.enforce_concepts);
assert!(!check.require_contiguous_tag);
}
#[test]
fn test_iterator_concept_validate_cpp23() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp23);
assert!(check.enforce_concepts);
assert!(check.require_contiguous_tag);
}
#[test]
fn test_iterator_concept_validate_all_categories() {
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
let cats = [
X86IteratorCategory::Input,
X86IteratorCategory::Forward,
X86IteratorCategory::Bidirectional,
X86IteratorCategory::RandomAccess,
X86IteratorCategory::Contiguous,
X86IteratorCategory::Output,
];
for cat in &cats {
let diags = check.validate("TestIter", *cat);
assert!(!diags.is_empty(), "No diagnostic for {:?}", cat);
}
}
#[test]
fn test_iterator_category_ordering() {
let mut cats = vec![
X86IteratorCategory::Contiguous,
X86IteratorCategory::Input,
X86IteratorCategory::Forward,
X86IteratorCategory::RandomAccess,
X86IteratorCategory::Bidirectional,
];
cats.sort();
assert_eq!(cats[0], X86IteratorCategory::Input);
assert_eq!(cats[4], X86IteratorCategory::Contiguous);
}
#[test]
fn test_type_traits_all_primitive_types() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
let types = [
"void",
"bool",
"char",
"short",
"int",
"long",
"long long",
"float",
"double",
"long double",
];
for t in &types {
let sz = tl.size_of(t);
assert!(sz.is_some(), "No size for type {}", t);
assert!(
sz.unwrap() > 0 || *t == "void",
"Size of {} should be > 0 or void",
t
);
}
}
#[test]
fn test_type_traits_is_trivial() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_trivial", "int"), Some(true));
assert_eq!(tl.evaluate_trait("is_trivial", "double"), Some(true));
}
#[test]
fn test_type_traits_is_const() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("is_const", "const int"), Some(true));
assert_eq!(tl.evaluate_trait("is_const", "int"), Some(false));
}
#[test]
fn test_type_traits_unknown_trait() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.evaluate_trait("nonexistent_trait", "int"), None);
}
#[test]
fn test_type_traits_unknown_type_size() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("SomeUserDefinedClass"), None);
}
#[test]
fn test_type_traits_long_double_linux() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert_eq!(tl.size_of("long double"), Some(16));
}
#[test]
fn test_full_pipeline_type_traits_and_iterators() {
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
let check = X86IteratorConceptCheck::new(CppStandard::Cpp20);
let iter_size = tl.size_of("intptr_t").unwrap();
assert_eq!(iter_size, 8);
assert!(X86IteratorCategory::RandomAccess.satisfies(X86IteratorCategory::Forward));
let diags = check.validate(
"std::vector<int>::iterator",
X86IteratorCategory::RandomAccess,
);
assert!(!diags.is_empty());
}
#[test]
fn test_full_pipeline_intrinsics_and_traits() {
let map = X86CxxLLVMIntrinsicMap::new(true);
let tl = X86CxxTypeTraitsLowering::new(true, X86CxxPlatform::LinuxGnu);
assert!(map.resolve("memcpy").is_some());
assert_eq!(tl.size_of("size_t"), tl.size_of("intptr_t"));
let decls = map.emit_decls();
assert!(!decls.is_empty());
}
#[test]
fn test_stress_all_components_instantiated() {
let lib = X86LibCxx::new(
"x86_64-unknown-linux-gnu",
true,
CppStandard::Cpp20,
CXXStdLib::LibCxx,
);
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
hm.build_index();
tl.initialize();
abi.initialize();
rtti.register_class_type("StressTest", "_ZN10StressTestE", &[]);
tmpl.register_template("stress_tmpl", vec!["T".into()], "T val;");
assert!(hm.all_headers().len() >= 50);
assert!(tl.get_type("std::string").is_some());
assert!(tmpl
.instantiate("stress_tmpl", &[X86CxxTemplateArg::Type("int".into())])
.is_ok());
assert!(rtti.has_type_info("StressTest"));
assert!(!eh.personality_function().is_empty());
}
#[test]
fn test_stress_many_abi_features() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
for i in 0..20 {
let result = abi.lower(&X86CxxABIFeature::Constructor {
class_name: format!("Class{}", i),
ctor_kind: CtorKind::Complete,
params: vec![],
});
assert!(result.success, "Constructor lowering failed for Class{}", i);
}
}
#[test]
fn test_stress_many_template_instantiations() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("stressN", vec!["T".into()], "T data;");
let types = [
"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64",
];
for t in &types {
assert!(tmpl
.instantiate("stressN", &[X86CxxTemplateArg::Type(t.to_string())])
.is_ok());
}
assert_eq!(tmpl.instantiation_count(), types.len());
}
#[test]
fn test_stress_all_platforms_detected() {
let triples = [
("x86_64-unknown-linux-gnu", X86CxxPlatform::LinuxGnu),
("x86_64-apple-darwin21.0", X86CxxPlatform::Darwin),
("x86_64-pc-windows-msvc", X86CxxPlatform::WindowsMSVC),
("x86_64-w64-mingw32", X86CxxPlatform::WindowsMinGW),
("x86_64-unknown-freebsd13.0", X86CxxPlatform::FreeBSD),
("x86_64-linux-android", X86CxxPlatform::Android),
("x86_64-unknown-linux-musl", X86CxxPlatform::LinuxMusl),
("x86_64-unknown-none", X86CxxPlatform::BareMetal),
("i386-unknown-linux-gnu", X86CxxPlatform::LinuxGnu),
];
for (triple, expected) in &triples {
let detected = X86CxxPlatform::from_triple(triple);
assert_eq!(detected, *expected, "Wrong platform for triple: {}", triple);
}
}
#[test]
fn test_stress_new_delete_all_variants() {
let scalar = X86NewDeleteLowering::new_scalar(16, 8);
assert!(!scalar.is_array);
let array = X86NewDeleteLowering::new_scalar(32, 8).as_array();
assert!(array.is_array);
let nothrow = X86NewDeleteLowering::new_scalar(8, 4).as_nothrow();
assert!(nothrow.is_nothrow);
let aligned = X86NewDeleteLowering::new_scalar(128, 64).as_aligned();
assert!(aligned.is_aligned);
let placement = X86NewDeleteLowering::new_scalar(4, 4).with_placement("%buf".into());
assert!(!placement.placement_args.is_empty());
}
#[test]
fn test_stress_vtable_roundtrip() {
let mut layout = X86VTableLayout::new("StressVTable", true);
for i in 0..10 {
layout.add_entry(X86VTableEntry {
index: i,
kind: if i == 0 {
X86VTableEntryKind::OffsetToTop
} else {
X86VTableEntryKind::VirtualFunction
},
mangled: Some(format!("_ZN10StressVTable{}vfunc{}Ev", i, i)),
this_adjustment: None,
});
}
assert_eq!(layout.entries.len(), 10);
assert_eq!(layout.size_bytes(8), 80);
}
#[test]
fn test_stress_all_components_instantiated_v2() {
let mut hm = X86CxxHeaderMapping::new(X86CxxPlatform::LinuxGnu, CXXStdLib::LibCxx);
let mut tl = X86CxxTypeLowering::new(X86CxxPlatform::LinuxGnu, true);
let mut abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let eh = X86CxxExceptionHandling::new(
X86CxxPlatform::LinuxGnu,
true,
"x86_64-unknown-linux-gnu",
);
let mut rtti = X86CxxRTTI::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
hm.build_index();
tl.initialize();
abi.initialize();
rtti.register_class_type("StressTest", "_ZN10StressTestE", &[]);
tmpl.register_template("stress_tmpl", vec!["T".into()], "T val;");
assert!(hm.all_headers().len() >= 50);
assert!(tl.get_type("std::string").is_some());
assert!(tmpl
.instantiate("stress_tmpl", &[X86CxxTemplateArg::Type("int".into())])
.is_ok());
assert!(rtti.has_type_info("StressTest"));
assert!(!eh.personality_function().is_empty());
}
#[test]
fn test_stress_many_abi_features_v2() {
let abi =
X86CxxABILowering::new(X86CxxPlatform::LinuxGnu, true, "x86_64-unknown-linux-gnu");
for i in 0..20 {
let result = abi.lower(&X86CxxABIFeature::Constructor {
class_name: format!("Class{}", i),
ctor_kind: CtorKind::Complete,
params: vec![],
});
assert!(result.success, "Constructor lowering failed for Class{}", i);
}
}
#[test]
fn test_stress_many_template_instantiations_v2() {
let mut tmpl = X86CxxTemplates::new(X86CxxPlatform::LinuxGnu, true);
tmpl.register_template("stressN", vec!["T".into()], "T data;");
let types = [
"i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64",
];
for t in &types {
assert!(tmpl
.instantiate("stressN", &[X86CxxTemplateArg::Type(t.to_string())])
.is_ok());
}
assert_eq!(tmpl.instantiation_count(), types.len());
}
#[test]
fn test_stress_all_platforms_detected_v2() {
let triples = [
("x86_64-unknown-linux-gnu", X86CxxPlatform::LinuxGnu),
("x86_64-apple-darwin21.0", X86CxxPlatform::Darwin),
("x86_64-pc-windows-msvc", X86CxxPlatform::WindowsMSVC),
("x86_64-w64-mingw32", X86CxxPlatform::WindowsMinGW),
("x86_64-unknown-freebsd13.0", X86CxxPlatform::FreeBSD),
("x86_64-linux-android", X86CxxPlatform::Android),
("x86_64-unknown-linux-musl", X86CxxPlatform::LinuxMusl),
("x86_64-unknown-none", X86CxxPlatform::BareMetal),
("i386-unknown-linux-gnu", X86CxxPlatform::LinuxGnu),
];
for (triple, expected) in &triples {
let detected = X86CxxPlatform::from_triple(triple);
assert_eq!(detected, *expected, "Wrong platform for triple: {}", triple);
}
}
#[test]
fn test_stress_new_delete_all_variants_v2() {
let scalar = X86NewDeleteLowering::new_scalar(16, 8);
assert!(!scalar.is_array);
let array = X86NewDeleteLowering::new_scalar(32, 8).as_array();
assert!(array.is_array);
let nothrow = X86NewDeleteLowering::new_scalar(8, 4).as_nothrow();
assert!(nothrow.is_nothrow);
let aligned = X86NewDeleteLowering::new_scalar(128, 64).as_aligned();
assert!(aligned.is_aligned);
let placement = X86NewDeleteLowering::new_scalar(4, 4).with_placement("%buf".into());
assert!(!placement.placement_args.is_empty());
}
#[test]
fn test_stress_vtable_roundtrip_v2() {
let mut layout = X86VTableLayout::new("StressVTable", true);
for i in 0..10 {
layout.add_entry(X86VTableEntry {
index: i,
kind: if i == 0 {
X86VTableEntryKind::OffsetToTop
} else {
X86VTableEntryKind::VirtualFunction
},
mangled: Some(format!("_ZN10StressVTable{}vfunc{}Ev", i, i)),
this_adjustment: None,
});
}
assert_eq!(layout.entries.len(), 10);
assert_eq!(layout.size_bytes(8), 80);
}
#[test]
fn test_stress_diagnostic_engine_many_diags() {
let mut engine = X86CxxDiagnosticEngine::new();
for i in 0..50 {
if i % 2 == 0 {
engine.error(&format!("error {}", i));
} else {
engine.warning(&format!("warning {}", i));
}
}
assert_eq!(engine.error_count(), 25);
assert_eq!(engine.warning_count(), 25);
}
#[test]
fn test_stress_type_lowering_all_platforms() {
let platforms = [
X86CxxPlatform::LinuxGnu,
X86CxxPlatform::Darwin,
X86CxxPlatform::WindowsMSVC,
X86CxxPlatform::FreeBSD,
X86CxxPlatform::Android,
];
for p in &platforms {
let mut tl = X86CxxTypeLowering::new(*p, true);
tl.initialize();
assert!(
tl.get_type("std::string").is_some(),
"Missing type for {:?}",
p
);
}
}
}