use llvm_native_core::attributes::{
AttrGroup, AttrIndex, AttributeKind, AttributeList, AttributeSet, FramePointerKind,
ParamAttrKind,
};
use llvm_native_core::types::TypeKind;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::sync::Arc;
impl fmt::Display for FramePointerKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FramePointerKind::All => write!(f, "all"),
FramePointerKind::NonLeaf => write!(f, "non-leaf"),
FramePointerKind::None => write!(f, "none"),
}
}
}
impl PartialOrd for FramePointerKind {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for FramePointerKind {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let a = match self {
FramePointerKind::All => 0,
FramePointerKind::NonLeaf => 1,
FramePointerKind::None => 2,
};
let b = match other {
FramePointerKind::All => 0,
FramePointerKind::NonLeaf => 1,
FramePointerKind::None => 2,
};
a.cmp(&b)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ExtendedAttrKind {
Alignment(u64),
Dereferenceable(u64),
DereferenceableOrNull(u64),
StackAlignment(u64),
ByVal,
ByRef,
StructRet,
InReg,
Returned,
SExt,
ZExt,
NoAlias,
NoCapture,
NoFree,
NonNull,
NoUndef,
ImmArg,
Preallocated,
InAlloca,
Nest,
ElementType,
AllocatedPointer,
AllocAlign,
AllocSize(u32, u32),
VScaleRange(u32, u32),
SwiftAsync,
SwiftError,
SwiftSelf,
Range(u64, u64),
InitialExec,
NoFPClass(u64),
Writable,
DeadOnUnwind,
Captures,
NoInline,
AlwaysInline,
OptimizeNone,
OptimizeForSize,
MinSize,
NoDuplicate,
Convergent,
NoUnwind,
NoReturn,
WillReturn,
Cold,
Hot,
InlineHint,
NoMerge,
MustProgress,
NoRecurse,
NoSync,
NullPointerIsValid,
NoCallback,
NoProfile,
SkipProfile,
OptDebug,
OptForFuzzing,
UseSampleProfile,
NoBuiltin,
ReturnsTwice,
JumpTable,
NoJumpTables,
NonLazyBind,
NoCfCheck,
SpeculativeLoadHardening,
StackProtect,
StackProtectReq,
StackProtectStrong,
NoStackProtect,
ShadowCallStack,
SafeStack,
SanitizeAddress,
SanitizeThread,
SanitizeMemory,
SanitizeHWAddress,
SanitizeMemTag,
NoSanitize,
SanitizeCoverage,
DisableSanitizerInstrumentation,
SanitizeMetadata,
SanitizeCFI,
UWTable,
NoUWTable,
FramePointer(FramePointerKind),
Naked,
NoImplicitFloat,
NoRedZone,
IndirectBranchTracking,
BranchTargetEnforcement,
FnRetThunkExtern,
Hotpatch,
PatchableFunction(u32, u32),
ReadNone,
ReadOnly,
WriteOnly,
ArgMemOnly,
InaccessibleMemOnly,
InaccessibleMemOrArgMemOnly,
Memory(MemoryEffects),
StrictFP,
NoFPClassFn(u64),
Speculatable,
NoSpeculativeExecution,
StringKV(String, String),
}
impl fmt::Display for ExtendedAttrKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExtendedAttrKind::Alignment(n) => write!(f, "align {}", n),
ExtendedAttrKind::Dereferenceable(n) => write!(f, "dereferenceable({})", n),
ExtendedAttrKind::DereferenceableOrNull(n) => {
write!(f, "dereferenceable_or_null({})", n)
}
ExtendedAttrKind::StackAlignment(n) => write!(f, "alignstack({})", n),
ExtendedAttrKind::ByVal => write!(f, "byval"),
ExtendedAttrKind::ByRef => write!(f, "byref"),
ExtendedAttrKind::StructRet => write!(f, "sret"),
ExtendedAttrKind::InReg => write!(f, "inreg"),
ExtendedAttrKind::Returned => write!(f, "returned"),
ExtendedAttrKind::SExt => write!(f, "signext"),
ExtendedAttrKind::ZExt => write!(f, "zeroext"),
ExtendedAttrKind::NoAlias => write!(f, "noalias"),
ExtendedAttrKind::NoCapture => write!(f, "nocapture"),
ExtendedAttrKind::NoFree => write!(f, "nofree"),
ExtendedAttrKind::NonNull => write!(f, "nonnull"),
ExtendedAttrKind::NoUndef => write!(f, "noundef"),
ExtendedAttrKind::ImmArg => write!(f, "immarg"),
ExtendedAttrKind::Preallocated => write!(f, "preallocated"),
ExtendedAttrKind::InAlloca => write!(f, "inalloca"),
ExtendedAttrKind::Nest => write!(f, "nest"),
ExtendedAttrKind::ElementType => write!(f, "elementtype"),
ExtendedAttrKind::AllocatedPointer => write!(f, "allocptr"),
ExtendedAttrKind::AllocAlign => write!(f, "allocalign"),
ExtendedAttrKind::AllocSize(e, n) => write!(f, "allocsize({}, {})", e, n),
ExtendedAttrKind::VScaleRange(min, max) => write!(f, "vscale_range({}, {})", min, max),
ExtendedAttrKind::SwiftAsync => write!(f, "swiftasync"),
ExtendedAttrKind::SwiftError => write!(f, "swifterror"),
ExtendedAttrKind::SwiftSelf => write!(f, "swiftself"),
ExtendedAttrKind::Range(s, e) => write!(f, "range({}, {})", s, e),
ExtendedAttrKind::InitialExec => write!(f, "initialexec"),
ExtendedAttrKind::NoFPClass(mask) => write!(f, "nofpclass({})", mask),
ExtendedAttrKind::Writable => write!(f, "writable"),
ExtendedAttrKind::DeadOnUnwind => write!(f, "dead_on_unwind"),
ExtendedAttrKind::Captures => write!(f, "captures"),
ExtendedAttrKind::NoInline => write!(f, "noinline"),
ExtendedAttrKind::AlwaysInline => write!(f, "alwaysinline"),
ExtendedAttrKind::OptimizeNone => write!(f, "optnone"),
ExtendedAttrKind::OptimizeForSize => write!(f, "optsize"),
ExtendedAttrKind::MinSize => write!(f, "minsize"),
ExtendedAttrKind::NoDuplicate => write!(f, "noduplicate"),
ExtendedAttrKind::Convergent => write!(f, "convergent"),
ExtendedAttrKind::NoUnwind => write!(f, "nounwind"),
ExtendedAttrKind::NoReturn => write!(f, "noreturn"),
ExtendedAttrKind::WillReturn => write!(f, "willreturn"),
ExtendedAttrKind::Cold => write!(f, "cold"),
ExtendedAttrKind::Hot => write!(f, "hot"),
ExtendedAttrKind::InlineHint => write!(f, "inlinehint"),
ExtendedAttrKind::NoMerge => write!(f, "nomerge"),
ExtendedAttrKind::MustProgress => write!(f, "mustprogress"),
ExtendedAttrKind::NoRecurse => write!(f, "norecurse"),
ExtendedAttrKind::NoSync => write!(f, "nosync"),
ExtendedAttrKind::NullPointerIsValid => write!(f, "null_pointer_is_valid"),
ExtendedAttrKind::NoCallback => write!(f, "nocallback"),
ExtendedAttrKind::NoProfile => write!(f, "noprofile"),
ExtendedAttrKind::SkipProfile => write!(f, "skipprofile"),
ExtendedAttrKind::OptDebug => write!(f, "optdebug"),
ExtendedAttrKind::OptForFuzzing => write!(f, "optforfuzzing"),
ExtendedAttrKind::UseSampleProfile => write!(f, "use-sample-profile"),
ExtendedAttrKind::NoBuiltin => write!(f, "nobuiltin"),
ExtendedAttrKind::ReturnsTwice => write!(f, "returns_twice"),
ExtendedAttrKind::JumpTable => write!(f, "jumptable"),
ExtendedAttrKind::NoJumpTables => write!(f, "no-jump-tables"),
ExtendedAttrKind::NonLazyBind => write!(f, "nonlazybind"),
ExtendedAttrKind::NoCfCheck => write!(f, "nocf_check"),
ExtendedAttrKind::SpeculativeLoadHardening => write!(f, "speculative_load_hardening"),
ExtendedAttrKind::StackProtect => write!(f, "ssp"),
ExtendedAttrKind::StackProtectReq => write!(f, "sspreq"),
ExtendedAttrKind::StackProtectStrong => write!(f, "sspstrong"),
ExtendedAttrKind::NoStackProtect => write!(f, "nosspprotect"),
ExtendedAttrKind::ShadowCallStack => write!(f, "shadowcallstack"),
ExtendedAttrKind::SafeStack => write!(f, "safestack"),
ExtendedAttrKind::SanitizeAddress => write!(f, "sanitize_address"),
ExtendedAttrKind::SanitizeThread => write!(f, "sanitize_thread"),
ExtendedAttrKind::SanitizeMemory => write!(f, "sanitize_memory"),
ExtendedAttrKind::SanitizeHWAddress => write!(f, "sanitize_hwaddress"),
ExtendedAttrKind::SanitizeMemTag => write!(f, "sanitize_memtag"),
ExtendedAttrKind::NoSanitize => write!(f, "nosanitize"),
ExtendedAttrKind::SanitizeCoverage => write!(f, "sanitize_coverage"),
ExtendedAttrKind::DisableSanitizerInstrumentation => {
write!(f, "disable_sanitizer_instrumentation")
}
ExtendedAttrKind::SanitizeMetadata => write!(f, "sanitize_metadata"),
ExtendedAttrKind::SanitizeCFI => write!(f, "sanitize_cfi"),
ExtendedAttrKind::UWTable => write!(f, "uwtable"),
ExtendedAttrKind::NoUWTable => write!(f, "nouwtable"),
ExtendedAttrKind::FramePointer(k) => write!(f, "frame-pointer({})", k),
ExtendedAttrKind::Naked => write!(f, "naked"),
ExtendedAttrKind::NoImplicitFloat => write!(f, "no-implicit-float"),
ExtendedAttrKind::NoRedZone => write!(f, "noredzone"),
ExtendedAttrKind::IndirectBranchTracking => write!(f, "indirect-branch-tracking"),
ExtendedAttrKind::BranchTargetEnforcement => write!(f, "branch-target-enforcement"),
ExtendedAttrKind::FnRetThunkExtern => write!(f, "fn_ret_thunk_extern"),
ExtendedAttrKind::Hotpatch => write!(f, "hotpatch"),
ExtendedAttrKind::PatchableFunction(pre, suf) => {
write!(f, "patchable-function({}, {})", pre, suf)
}
ExtendedAttrKind::ReadNone => write!(f, "readnone"),
ExtendedAttrKind::ReadOnly => write!(f, "readonly"),
ExtendedAttrKind::WriteOnly => write!(f, "writeonly"),
ExtendedAttrKind::ArgMemOnly => write!(f, "argmemonly"),
ExtendedAttrKind::InaccessibleMemOnly => write!(f, "inaccessiblememonly"),
ExtendedAttrKind::InaccessibleMemOrArgMemOnly => {
write!(f, "inaccessiblemem_or_argmemonly")
}
ExtendedAttrKind::Memory(me) => write!(f, "memory({})", me),
ExtendedAttrKind::StrictFP => write!(f, "strictfp"),
ExtendedAttrKind::NoFPClassFn(mask) => write!(f, "nofpclass({})", mask),
ExtendedAttrKind::Speculatable => write!(f, "speculatable"),
ExtendedAttrKind::NoSpeculativeExecution => write!(f, "nospeculativeexecution"),
ExtendedAttrKind::StringKV(k, v) => write!(f, "\"{}\"=\"{}\"", k, v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemoryLocation {
ArgMem,
InaccessibleMem,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ModRefInfo {
NoModRef,
Ref,
Mod,
ModRef,
}
impl ModRefInfo {
pub fn does_not_access(&self) -> bool {
matches!(self, ModRefInfo::NoModRef)
}
pub fn only_reads(&self) -> bool {
matches!(self, ModRefInfo::Ref)
}
pub fn only_writes(&self) -> bool {
matches!(self, ModRefInfo::Mod)
}
pub fn does_read(&self) -> bool {
matches!(self, ModRefInfo::Ref | ModRefInfo::ModRef)
}
pub fn does_write(&self) -> bool {
matches!(self, ModRefInfo::Mod | ModRefInfo::ModRef)
}
pub fn union_with(self, other: ModRefInfo) -> ModRefInfo {
use ModRefInfo::*;
match (self, other) {
(NoModRef, x) | (x, NoModRef) => x,
(Ref, Mod) | (Mod, Ref) | (ModRef, _) | (_, ModRef) => ModRef,
(Ref, Ref) => Ref,
(Mod, Mod) => Mod,
}
}
pub fn intersect_with(self, other: ModRefInfo) -> ModRefInfo {
use ModRefInfo::*;
match (self, other) {
(x, y) if x == y => x,
(ModRef, x) | (x, ModRef) => x,
_ => NoModRef,
}
}
}
impl fmt::Display for ModRefInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ModRefInfo::NoModRef => write!(f, "none"),
ModRefInfo::Ref => write!(f, "read"),
ModRefInfo::Mod => write!(f, "write"),
ModRefInfo::ModRef => write!(f, "readwrite"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MemoryEffects {
pub argmem: ModRefInfo,
pub inaccessiblemem: ModRefInfo,
pub other: ModRefInfo,
}
impl MemoryEffects {
pub fn unknown() -> Self {
MemoryEffects {
argmem: ModRefInfo::ModRef,
inaccessiblemem: ModRefInfo::ModRef,
other: ModRefInfo::ModRef,
}
}
pub fn none() -> Self {
MemoryEffects {
argmem: ModRefInfo::NoModRef,
inaccessiblemem: ModRefInfo::NoModRef,
other: ModRefInfo::NoModRef,
}
}
pub fn read_only() -> Self {
MemoryEffects {
argmem: ModRefInfo::Ref,
inaccessiblemem: ModRefInfo::Ref,
other: ModRefInfo::Ref,
}
}
pub fn write_only() -> Self {
MemoryEffects {
argmem: ModRefInfo::Mod,
inaccessiblemem: ModRefInfo::Mod,
other: ModRefInfo::Mod,
}
}
pub fn argmem_only() -> Self {
MemoryEffects {
argmem: ModRefInfo::ModRef,
inaccessiblemem: ModRefInfo::NoModRef,
other: ModRefInfo::NoModRef,
}
}
pub fn inaccessible_mem_only() -> Self {
MemoryEffects {
argmem: ModRefInfo::NoModRef,
inaccessiblemem: ModRefInfo::ModRef,
other: ModRefInfo::NoModRef,
}
}
pub fn inaccessible_or_argmem_only() -> Self {
MemoryEffects {
argmem: ModRefInfo::ModRef,
inaccessiblemem: ModRefInfo::ModRef,
other: ModRefInfo::NoModRef,
}
}
pub fn get_mod_ref(&self, loc: MemoryLocation) -> ModRefInfo {
match loc {
MemoryLocation::ArgMem => self.argmem,
MemoryLocation::InaccessibleMem => self.inaccessiblemem,
MemoryLocation::Other => self.other,
}
}
pub fn set_mod_ref(&mut self, loc: MemoryLocation, mri: ModRefInfo) {
match loc {
MemoryLocation::ArgMem => self.argmem = mri,
MemoryLocation::InaccessibleMem => self.inaccessiblemem = mri,
MemoryLocation::Other => self.other = mri,
}
}
pub fn union_with(&self, other: &MemoryEffects) -> MemoryEffects {
MemoryEffects {
argmem: self.argmem.union_with(other.argmem),
inaccessiblemem: self.inaccessiblemem.union_with(other.inaccessiblemem),
other: self.other.union_with(other.other),
}
}
pub fn intersect_with(&self, other: &MemoryEffects) -> MemoryEffects {
MemoryEffects {
argmem: self.argmem.intersect_with(other.argmem),
inaccessiblemem: self.inaccessiblemem.intersect_with(other.inaccessiblemem),
other: self.other.intersect_with(other.other),
}
}
pub fn does_not_access_memory(&self) -> bool {
self.argmem == ModRefInfo::NoModRef
&& self.inaccessiblemem == ModRefInfo::NoModRef
&& self.other == ModRefInfo::NoModRef
}
pub fn only_reads_memory(&self) -> bool {
!self.argmem.does_write()
&& !self.inaccessiblemem.does_write()
&& !self.other.does_write()
&& (self.argmem.does_read()
|| self.inaccessiblemem.does_read()
|| self.other.does_read())
}
pub fn only_writes_memory(&self) -> bool {
!self.argmem.does_read()
&& !self.inaccessiblemem.does_read()
&& !self.other.does_read()
&& (self.argmem.does_write()
|| self.inaccessiblemem.does_write()
|| self.other.does_write())
}
pub fn only_accesses_arg_memory(&self) -> bool {
self.inaccessiblemem == ModRefInfo::NoModRef && self.other == ModRefInfo::NoModRef
}
pub fn only_accesses_inaccessible_memory(&self) -> bool {
self.argmem == ModRefInfo::NoModRef && self.other == ModRefInfo::NoModRef
}
}
impl fmt::Display for MemoryEffects {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"argmem: {}, inaccessiblemem: {}, other: {}",
self.argmem, self.inaccessiblemem, self.other
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TargetAttr {
TargetCpu(String),
TargetFeatures(String),
TuneCpu(String),
StackProbeSize(u32),
StackRealign,
ForceAlignStack,
MinLegalAVX(u32),
Prefer128Bit,
Prefer256Bit,
Prefer512Bit,
UseSoftFloat,
NoSSE,
BranchFusion,
TargetArch(String),
SignReturnAddress(String),
BranchTargetEnforcement,
MemTag,
AapcsFrameChain,
PacG,
StreamingSVE,
LocallyStreaming,
NewZA,
InZA,
OutZA,
InOutZA,
PreservesZA,
ZAgnostic,
TargetABI(String),
CodeModel(String),
Interrupt(String),
VLen(u32),
UWTableKind(u32),
CFICanonicalJumpTables,
FnRetThunkExtern,
NoReturnThunkExtern,
PreserveAll,
CmseNonsecureCall,
CmseNonsecureEntry,
AmdgpuKernel,
AmdgpuFlatWorkGroupSize(u32, u32),
AmdgpuWavesPerEU(u32, u32),
AmdgpuNumSGPR(u32),
AmdgpuNumVGPR(u32),
NvptxKernel,
NvptxMaxThreadsPerBlock(u32),
}
impl fmt::Display for TargetAttr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TargetAttr::TargetCpu(s) => write!(f, "target-cpu=\"{}\"", s),
TargetAttr::TargetFeatures(s) => write!(f, "target-features=\"{}\"", s),
TargetAttr::TuneCpu(s) => write!(f, "tune-cpu=\"{}\"", s),
TargetAttr::StackProbeSize(n) => write!(f, "stack-probe-size=\"{}\"", n),
TargetAttr::StackRealign => write!(f, "stackrealign"),
TargetAttr::ForceAlignStack => write!(f, "force-align-stack"),
TargetAttr::MinLegalAVX(v) => write!(f, "min-legal-avx-version=\"{}\"", v),
TargetAttr::Prefer128Bit => write!(f, "prefer-128-bit"),
TargetAttr::Prefer256Bit => write!(f, "prefer-256-bit"),
TargetAttr::Prefer512Bit => write!(f, "prefer-512-bit"),
TargetAttr::UseSoftFloat => write!(f, "use-soft-float"),
TargetAttr::NoSSE => write!(f, "no-sse"),
TargetAttr::BranchFusion => write!(f, "branch-fusion"),
TargetAttr::TargetArch(s) => write!(f, "target-arch=\"{}\"", s),
TargetAttr::SignReturnAddress(s) => write!(f, "sign-return-address=\"{}\"", s),
TargetAttr::BranchTargetEnforcement => write!(f, "branch-target-enforcement"),
TargetAttr::MemTag => write!(f, "memtag"),
TargetAttr::AapcsFrameChain => write!(f, "aapcs-frame-chain"),
TargetAttr::PacG => write!(f, "pacg"),
TargetAttr::StreamingSVE => write!(f, "streaming-sve"),
TargetAttr::LocallyStreaming => write!(f, "locally-streaming"),
TargetAttr::NewZA => write!(f, "new-za"),
TargetAttr::InZA => write!(f, "in-za"),
TargetAttr::OutZA => write!(f, "out-za"),
TargetAttr::InOutZA => write!(f, "inout-za"),
TargetAttr::PreservesZA => write!(f, "preserves-za"),
TargetAttr::ZAgnostic => write!(f, "z-agnostic"),
TargetAttr::TargetABI(s) => write!(f, "target-abi=\"{}\"", s),
TargetAttr::CodeModel(s) => write!(f, "code-model=\"{}\"", s),
TargetAttr::Interrupt(s) => write!(f, "interrupt=\"{}\"", s),
TargetAttr::VLen(n) => write!(f, "vlen=\"{}\"", n),
TargetAttr::UWTableKind(k) => write!(f, "uwtable({})", k),
TargetAttr::CFICanonicalJumpTables => write!(f, "cfi-canonical-jump-tables"),
TargetAttr::FnRetThunkExtern => write!(f, "fn_ret_thunk_extern"),
TargetAttr::NoReturnThunkExtern => write!(f, "no-return-thunk-extern"),
TargetAttr::PreserveAll => write!(f, "preserve_all"),
TargetAttr::CmseNonsecureCall => write!(f, "cmse-nonsecure-call"),
TargetAttr::CmseNonsecureEntry => write!(f, "cmse-nonsecure-entry"),
TargetAttr::AmdgpuKernel => write!(f, "amdgpu-kernel"),
TargetAttr::AmdgpuFlatWorkGroupSize(min, max) => {
write!(f, "amdgpu-flat-work-group-size=\"{},{}\"", min, max)
}
TargetAttr::AmdgpuWavesPerEU(min, max) => {
write!(f, "amdgpu-waves-per-eu=\"{},{}\"", min, max)
}
TargetAttr::AmdgpuNumSGPR(n) => write!(f, "amdgpu-num-sgpr=\"{}\"", n),
TargetAttr::AmdgpuNumVGPR(n) => write!(f, "amdgpu-num-vgpr=\"{}\"", n),
TargetAttr::NvptxKernel => write!(f, "nvptx-kernel"),
TargetAttr::NvptxMaxThreadsPerBlock(n) => {
write!(f, "nvptx-max-threads-per-block=\"{}\"", n)
}
}
}
}
#[derive(Debug, Clone)]
pub struct ExtendedAttrList {
pub sets: BTreeMap<u32, ExtendedAttrSet>,
pub target_attrs: Vec<TargetAttr>,
pub string_attrs: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct ExtendedAttrSet {
pub index: u32,
pub attrs: Vec<ExtendedAttrKind>,
pub alignment: Option<u64>,
pub deref_bytes: Option<u64>,
pub deref_or_null_bytes: Option<u64>,
pub stack_align: Option<u64>,
}
impl ExtendedAttrSet {
pub fn new(index: u32) -> Self {
ExtendedAttrSet {
index,
attrs: Vec::new(),
alignment: None,
deref_bytes: None,
deref_or_null_bytes: None,
stack_align: None,
}
}
pub fn add_attr(&mut self, attr: ExtendedAttrKind) -> &mut Self {
match &attr {
ExtendedAttrKind::Alignment(n) => self.alignment = Some(*n),
ExtendedAttrKind::Dereferenceable(n) => self.deref_bytes = Some(*n),
ExtendedAttrKind::DereferenceableOrNull(n) => self.deref_or_null_bytes = Some(*n),
ExtendedAttrKind::StackAlignment(n) => self.stack_align = Some(*n),
_ => {}
}
if !self.attrs.contains(&attr) {
self.attrs.push(attr);
}
self
}
pub fn remove_attr(&mut self, attr: &ExtendedAttrKind) -> &mut Self {
self.attrs.retain(|a| a != attr);
match attr {
ExtendedAttrKind::Alignment(_) => self.alignment = None,
ExtendedAttrKind::Dereferenceable(_) => self.deref_bytes = None,
ExtendedAttrKind::DereferenceableOrNull(_) => self.deref_or_null_bytes = None,
ExtendedAttrKind::StackAlignment(_) => self.stack_align = None,
_ => {}
}
self
}
pub fn has_attr(&self, attr: &ExtendedAttrKind) -> bool {
self.attrs.contains(attr)
}
pub fn has_attr_kind(&self, kind: fn(u64) -> ExtendedAttrKind) -> bool {
self.attrs
.iter()
.any(|a| std::mem::discriminant(a) == std::mem::discriminant(&kind(0)))
}
pub fn get_align(&self) -> Option<u64> {
self.alignment
}
pub fn get_dereferenceable_bytes(&self) -> Option<u64> {
self.deref_bytes
}
pub fn is_non_null(&self) -> bool {
self.has_attr(&ExtendedAttrKind::NonNull)
}
pub fn is_no_capture(&self) -> bool {
self.has_attr(&ExtendedAttrKind::NoCapture)
}
pub fn is_no_alias(&self) -> bool {
self.has_attr(&ExtendedAttrKind::NoAlias)
}
pub fn is_empty(&self) -> bool {
self.attrs.is_empty()
&& self.alignment.is_none()
&& self.deref_bytes.is_none()
&& self.deref_or_null_bytes.is_none()
&& self.stack_align.is_none()
}
pub fn len(&self) -> usize {
self.attrs.len()
}
pub fn merge(&self, other: &ExtendedAttrSet) -> ExtendedAttrSet {
let mut result = self.clone();
for attr in &other.attrs {
result.add_attr(attr.clone());
}
if other.alignment.is_some() {
result.alignment = other.alignment;
}
if other.deref_bytes.is_some() {
result.deref_bytes = other.deref_bytes;
}
if other.deref_or_null_bytes.is_some() {
result.deref_or_null_bytes = other.deref_or_null_bytes;
}
if other.stack_align.is_some() {
result.stack_align = other.stack_align;
}
result
}
pub fn to_llvm_string(&self) -> String {
let mut parts: Vec<String> = self.attrs.iter().map(|a| a.to_string()).collect();
if let Some(n) = self.alignment {
parts.push(format!("align {}", n));
}
if let Some(n) = self.stack_align {
parts.push(format!("alignstack({})", n));
}
parts.join(" ")
}
}
impl ExtendedAttrList {
pub fn new() -> Self {
ExtendedAttrList {
sets: BTreeMap::new(),
target_attrs: Vec::new(),
string_attrs: HashMap::new(),
}
}
pub fn get_or_create_set(&mut self, index: u32) -> &mut ExtendedAttrSet {
self.sets
.entry(index)
.or_insert_with(|| ExtendedAttrSet::new(index))
}
pub fn get_set(&self, index: u32) -> Option<&ExtendedAttrSet> {
self.sets.get(&index)
}
pub fn add_fn_attr(&mut self, attr: ExtendedAttrKind) -> &mut Self {
self.get_or_create_set(0).add_attr(attr);
self
}
pub fn add_ret_attr(&mut self, attr: ExtendedAttrKind) -> &mut Self {
self.get_or_create_set(1).add_attr(attr);
self
}
pub fn add_param_attr(&mut self, param_index: u32, attr: ExtendedAttrKind) -> &mut Self {
self.get_or_create_set(param_index + 2).add_attr(attr);
self
}
pub fn remove_fn_attr(&mut self, attr: &ExtendedAttrKind) -> &mut Self {
if let Some(s) = self.sets.get_mut(&0) {
s.remove_attr(attr);
}
self
}
pub fn has_fn_attr(&self, attr: &ExtendedAttrKind) -> bool {
self.sets.get(&0).map_or(false, |s| s.has_attr(attr))
}
pub fn has_param_attr(&self, param_index: u32, attr: &ExtendedAttrKind) -> bool {
self.sets
.get(&(param_index + 2))
.map_or(false, |s| s.has_attr(attr))
}
pub fn get_memory_effects(&self) -> MemoryEffects {
if self.has_fn_attr(&ExtendedAttrKind::ReadNone) {
return MemoryEffects::none();
}
if let Some(s) = self.sets.get(&0) {
for attr in &s.attrs {
if let ExtendedAttrKind::Memory(me) = attr {
return me.clone();
}
}
if s.has_attr(&ExtendedAttrKind::ReadOnly) {
return MemoryEffects::read_only();
}
if s.has_attr(&ExtendedAttrKind::WriteOnly) {
return MemoryEffects::write_only();
}
if s.has_attr(&ExtendedAttrKind::InaccessibleMemOrArgMemOnly) {
return MemoryEffects::inaccessible_or_argmem_only();
}
if s.has_attr(&ExtendedAttrKind::InaccessibleMemOnly) {
return MemoryEffects::inaccessible_mem_only();
}
if s.has_attr(&ExtendedAttrKind::ArgMemOnly) {
return MemoryEffects::argmem_only();
}
}
MemoryEffects::unknown()
}
pub fn add_target_attr(&mut self, attr: TargetAttr) -> &mut Self {
if !self.target_attrs.contains(&attr) {
self.target_attrs.push(attr);
}
self
}
pub fn add_string_attr(&mut self, key: &str, value: &str) -> &mut Self {
self.string_attrs.insert(key.to_string(), value.to_string());
self
}
pub fn get_string_attr(&self, key: &str) -> Option<&str> {
self.string_attrs.get(key).map(|s| s.as_str())
}
pub fn has_string_attr(&self, key: &str) -> bool {
self.string_attrs.contains_key(key)
}
pub fn is_empty(&self) -> bool {
self.sets.values().all(|s| s.is_empty())
&& self.target_attrs.is_empty()
&& self.string_attrs.is_empty()
}
pub fn num_attr_sets(&self) -> usize {
self.sets.len()
}
pub fn merge(&self, other: &ExtendedAttrList) -> ExtendedAttrList {
let mut result = self.clone();
for (idx, set) in &other.sets {
let entry = result.get_or_create_set(*idx);
*entry = entry.merge(set);
}
for attr in &other.target_attrs {
result.add_target_attr(attr.clone());
}
for (k, v) in &other.string_attrs {
result.add_string_attr(k, v);
}
result
}
pub fn to_function_attr_string(&self) -> String {
let mut parts = Vec::new();
if let Some(s) = self.sets.get(&0) {
parts.push(s.to_llvm_string());
}
for attr in &self.target_attrs {
parts.push(attr.to_string());
}
for (k, v) in &self.string_attrs {
parts.push(format!("\"{}\"=\"{}\"", k, v));
}
parts.join(" ")
}
}
impl Default for ExtendedAttrList {
fn default() -> Self {
ExtendedAttrList::new()
}
}
#[derive(Debug, Clone)]
pub struct InferredAttributes {
pub fn_attrs: Vec<ExtendedAttrKind>,
pub ret_attrs: Vec<ExtendedAttrKind>,
pub param_attrs: HashMap<u32, Vec<ExtendedAttrKind>>,
pub is_readnone: bool,
pub is_readonly: bool,
pub is_nounwind: bool,
pub is_willreturn: bool,
pub is_norecurse: bool,
pub is_argmemonly: bool,
pub memory_effects: MemoryEffects,
}
impl Default for InferredAttributes {
fn default() -> Self {
InferredAttributes {
fn_attrs: Vec::new(),
ret_attrs: Vec::new(),
param_attrs: HashMap::new(),
is_readnone: false,
is_readonly: false,
is_nounwind: false,
is_willreturn: false,
is_norecurse: false,
is_argmemonly: false,
memory_effects: MemoryEffects::unknown(),
}
}
}
pub struct AttributionInference {
pub known: HashMap<String, InferredAttributes>,
pub call_graph: HashMap<String, Vec<String>>,
}
impl AttributionInference {
pub fn new() -> Self {
AttributionInference {
known: HashMap::new(),
call_graph: HashMap::new(),
}
}
pub fn observe_function(&mut self, name: &str, inferred: InferredAttributes) {
self.known.insert(name.to_string(), inferred);
}
pub fn add_call_edge(&mut self, caller: &str, callee: &str) {
self.call_graph
.entry(caller.to_string())
.or_insert_with(Vec::new)
.push(callee.to_string());
}
pub fn deduce_nounwind(&self, name: &str, has_no_unwind_inst: bool) -> bool {
if !has_no_unwind_inst {
return false;
}
self.all_callees_satisfy(name, |callee| {
self.known
.get(callee)
.map(|inf| inf.is_nounwind)
.unwrap_or(false)
})
}
pub fn deduce_willreturn(&self, name: &str, has_infinite_loop: bool) -> bool {
if has_infinite_loop {
return false;
}
self.all_callees_satisfy(name, |callee| {
self.known
.get(callee)
.map(|inf| inf.is_willreturn)
.unwrap_or(false)
})
}
pub fn deduce_argmemonly(&self, name: &str, has_global_access: bool) -> bool {
if has_global_access {
return false;
}
self.all_callees_satisfy(name, |callee| {
self.known
.get(callee)
.map(|inf| inf.is_argmemonly)
.unwrap_or(false)
})
}
pub fn deduce_norecurse(&self, name: &str) -> bool {
!self.calls_itself_directly_or_indirectly(name)
}
pub fn deduce_memory_effects(
&self,
name: &str,
local_effects: &MemoryEffects,
) -> MemoryEffects {
let mut result = local_effects.clone();
if let Some(callees) = self.call_graph.get(name) {
for callee in callees {
if let Some(inf) = self.known.get(callee) {
result = result.union_with(&inf.memory_effects);
} else {
result = result.union_with(&MemoryEffects::unknown());
}
}
}
result
}
pub fn propagate_top_down(&self) -> HashMap<String, InferredAttributes> {
self.known.clone()
}
fn all_callees_satisfy<F: Fn(&str) -> bool>(&self, name: &str, pred: F) -> bool {
let mut visited = HashSet::new();
let mut stack: Vec<String> = vec![name.to_string()];
while let Some(current) = stack.pop() {
if !visited.insert(current.clone()) {
continue;
}
if let Some(callees) = self.call_graph.get(¤t) {
for callee in callees {
if !pred(callee) {
return false;
}
stack.push(callee.clone());
}
}
}
true
}
fn calls_itself_directly_or_indirectly(&self, name: &str) -> bool {
let mut visited = HashSet::new();
let mut stack: Vec<String> = vec![name.to_string()];
visited.insert(name.to_string());
while let Some(current) = stack.pop() {
if let Some(callees) = self.call_graph.get(¤t) {
for callee in callees {
if callee == name {
return true;
}
if visited.insert(callee.clone()) {
stack.push(callee.clone());
}
}
}
}
false
}
}
impl Default for AttributionInference {
fn default() -> Self {
AttributionInference::new()
}
}
pub struct AttrStringParser;
impl AttrStringParser {
pub fn parse_fn_attrs(input: &str) -> ExtendedAttrList {
let mut list = ExtendedAttrList::new();
let tokens = Self::tokenize(input);
let mut i = 0;
while i < tokens.len() {
match tokens[i].as_str() {
"align" => {
if i + 1 < tokens.len() {
if let Ok(n) = tokens[i + 1].parse::<u64>() {
list.add_fn_attr(ExtendedAttrKind::Alignment(n));
i += 1;
}
}
}
"alignstack" => {
if i + 1 < tokens.len() {
if let Some(n) = Self::parse_paren_value(&tokens[i + 1]) {
list.add_fn_attr(ExtendedAttrKind::StackAlignment(n));
}
}
}
"alwaysinline" => {
list.add_fn_attr(ExtendedAttrKind::AlwaysInline);
}
"argmemonly" => {
list.add_fn_attr(ExtendedAttrKind::ArgMemOnly);
}
"cold" => {
list.add_fn_attr(ExtendedAttrKind::Cold);
}
"convergent" => {
list.add_fn_attr(ExtendedAttrKind::Convergent);
}
"hot" => {
list.add_fn_attr(ExtendedAttrKind::Hot);
}
"inaccessiblememonly" => {
list.add_fn_attr(ExtendedAttrKind::InaccessibleMemOnly);
}
"inaccessiblemem_or_argmemonly" => {
list.add_fn_attr(ExtendedAttrKind::InaccessibleMemOrArgMemOnly);
}
"inlinehint" => {
list.add_fn_attr(ExtendedAttrKind::InlineHint);
}
"minsize" => {
list.add_fn_attr(ExtendedAttrKind::MinSize);
}
"mustprogress" => {
list.add_fn_attr(ExtendedAttrKind::MustProgress);
}
"naked" => {
list.add_fn_attr(ExtendedAttrKind::Naked);
}
"no-implicit-float" => {
list.add_fn_attr(ExtendedAttrKind::NoImplicitFloat);
}
"nobuiltin" => {
list.add_fn_attr(ExtendedAttrKind::NoBuiltin);
}
"nocallback" => {
list.add_fn_attr(ExtendedAttrKind::NoCallback);
}
"nocf_check" => {
list.add_fn_attr(ExtendedAttrKind::NoCfCheck);
}
"noduplicate" => {
list.add_fn_attr(ExtendedAttrKind::NoDuplicate);
}
"nofree" => {
list.add_fn_attr(ExtendedAttrKind::NoFree);
}
"noimplicitfloat" => {
list.add_fn_attr(ExtendedAttrKind::NoImplicitFloat);
}
"noinline" => {
list.add_fn_attr(ExtendedAttrKind::NoInline);
}
"nomerge" => {
list.add_fn_attr(ExtendedAttrKind::NoMerge);
}
"nonlazybind" => {
list.add_fn_attr(ExtendedAttrKind::NonLazyBind);
}
"noprofile" => {
list.add_fn_attr(ExtendedAttrKind::NoProfile);
}
"norecurse" => {
list.add_fn_attr(ExtendedAttrKind::NoRecurse);
}
"noredzone" => {
list.add_fn_attr(ExtendedAttrKind::NoRedZone);
}
"noreturn" => {
list.add_fn_attr(ExtendedAttrKind::NoReturn);
}
"nosync" => {
list.add_fn_attr(ExtendedAttrKind::NoSync);
}
"nounwind" => {
list.add_fn_attr(ExtendedAttrKind::NoUnwind);
}
"null_pointer_is_valid" => {
list.add_fn_attr(ExtendedAttrKind::NullPointerIsValid);
}
"optdebug" => {
list.add_fn_attr(ExtendedAttrKind::OptDebug);
}
"optforfuzzing" => {
list.add_fn_attr(ExtendedAttrKind::OptForFuzzing);
}
"optnone" => {
list.add_fn_attr(ExtendedAttrKind::OptimizeNone);
}
"optsize" => {
list.add_fn_attr(ExtendedAttrKind::OptimizeForSize);
}
"readnone" => {
list.add_fn_attr(ExtendedAttrKind::ReadNone);
}
"readonly" => {
list.add_fn_attr(ExtendedAttrKind::ReadOnly);
}
"returns_twice" => {
list.add_fn_attr(ExtendedAttrKind::ReturnsTwice);
}
"safestack" => {
list.add_fn_attr(ExtendedAttrKind::SafeStack);
}
"sanitize_address" => {
list.add_fn_attr(ExtendedAttrKind::SanitizeAddress);
}
"sanitize_thread" => {
list.add_fn_attr(ExtendedAttrKind::SanitizeThread);
}
"sanitize_memory" => {
list.add_fn_attr(ExtendedAttrKind::SanitizeMemory);
}
"sanitize_hwaddress" => {
list.add_fn_attr(ExtendedAttrKind::SanitizeHWAddress);
}
"sanitize_memtag" => {
list.add_fn_attr(ExtendedAttrKind::SanitizeMemTag);
}
"shadowcallstack" => {
list.add_fn_attr(ExtendedAttrKind::ShadowCallStack);
}
"skipprofile" => {
list.add_fn_attr(ExtendedAttrKind::SkipProfile);
}
"speculatable" => {
list.add_fn_attr(ExtendedAttrKind::Speculatable);
}
"speculative_load_hardening" => {
list.add_fn_attr(ExtendedAttrKind::SpeculativeLoadHardening);
}
"ssp" => {
list.add_fn_attr(ExtendedAttrKind::StackProtect);
}
"sspreq" => {
list.add_fn_attr(ExtendedAttrKind::StackProtectReq);
}
"sspstrong" => {
list.add_fn_attr(ExtendedAttrKind::StackProtectStrong);
}
"strictfp" => {
list.add_fn_attr(ExtendedAttrKind::StrictFP);
}
"uwtable" => {
list.add_fn_attr(ExtendedAttrKind::UWTable);
}
"willreturn" => {
list.add_fn_attr(ExtendedAttrKind::WillReturn);
}
"writeonly" => {
list.add_fn_attr(ExtendedAttrKind::WriteOnly);
}
"use-sample-profile" => {
list.add_fn_attr(ExtendedAttrKind::UseSampleProfile);
}
"frame-pointer" => {
if i + 1 < tokens.len() {
let fp = match tokens[i + 1].as_str() {
"all" => FramePointerKind::All,
"non-leaf" => FramePointerKind::NonLeaf,
"none" => FramePointerKind::None,
_ => FramePointerKind::None,
};
list.add_fn_attr(ExtendedAttrKind::FramePointer(fp));
i += 1;
}
}
"patchable-function" => {
if i + 1 < tokens.len() {
if let Some((pre, suf)) = Self::parse_two_paren_values(&tokens[i + 1]) {
list.add_fn_attr(ExtendedAttrKind::PatchableFunction(pre, suf));
}
}
}
"vscale_range" => {
if i + 1 < tokens.len() {
if let Some((min, max)) = Self::parse_two_paren_values(&tokens[i + 1]) {
list.add_fn_attr(ExtendedAttrKind::VScaleRange(min as u32, max as u32));
}
}
}
"nofpclass" => {
if i + 1 < tokens.len() {
if let Some(mask) = Self::parse_paren_value(&tokens[i + 1]) {
list.add_fn_attr(ExtendedAttrKind::NoFPClassFn(mask));
}
}
}
s if s.starts_with("\"target-cpu\"") => {
if let Some(val) = Self::parse_string_attr_value(&tokens[i]) {
list.add_target_attr(TargetAttr::TargetCpu(val));
}
}
s if s.starts_with("\"target-features\"") => {
if let Some(val) = Self::parse_string_attr_value(&tokens[i]) {
list.add_target_attr(TargetAttr::TargetFeatures(val));
}
}
s if s.contains('=') => {
let (key, value) = Self::parse_kv(s);
list.add_string_attr(&key, &value);
}
_ => {}
}
i += 1;
}
list
}
fn tokenize(input: &str) -> Vec<String> {
let mut tokens = Vec::new();
let mut current = String::new();
let mut in_quotes = false;
let mut in_parens = 0i32;
let chars: Vec<char> = input.chars().collect();
let mut i = 0;
while i < chars.len() {
let ch = chars[i];
match ch {
'"' => {
in_quotes = !in_quotes;
current.push(ch);
}
'(' => {
in_parens += 1;
current.push(ch);
}
')' => {
in_parens -= 1;
current.push(ch);
}
' ' | '\t' | ',' => {
if !in_quotes && in_parens <= 0 {
if !current.is_empty() {
tokens.push(current.clone());
current.clear();
}
} else {
current.push(ch);
}
}
_ => {
current.push(ch);
}
}
i += 1;
}
if !current.is_empty() {
tokens.push(current);
}
tokens
}
fn parse_paren_value(s: &str) -> Option<u64> {
if let Some(start) = s.find('(') {
if let Some(end) = s.rfind(')') {
let inner = &s[start + 1..end];
return inner.parse::<u64>().ok();
}
}
s.parse::<u64>().ok()
}
fn parse_two_paren_values(s: &str) -> Option<(u32, u32)> {
if let Some(start) = s.find('(') {
if let Some(end) = s.rfind(')') {
let inner = &s[start + 1..end];
let parts: Vec<&str> = inner.split(',').collect();
if parts.len() >= 2 {
let a = parts[0].trim().parse::<u32>().ok()?;
let b = parts[1].trim().parse::<u32>().ok()?;
return Some((a, b));
}
}
}
None
}
fn parse_string_attr_value(s: &str) -> Option<String> {
if let Some(eq) = s.find('=') {
let val = &s[eq + 1..];
let trimmed = val.trim_matches('"');
return Some(trimmed.to_string());
}
None
}
fn parse_kv(s: &str) -> (String, String) {
if let Some(eq) = s.find('=') {
let key = s[..eq].trim_matches('"').to_string();
let val = s[eq + 1..].trim_matches('"').to_string();
return (key, val);
}
(s.to_string(), String::new())
}
}
pub struct AttrCompat;
impl AttrCompat {
pub fn to_core_attrkind(ext: &ExtendedAttrKind) -> Option<AttributeKind> {
match ext {
ExtendedAttrKind::NoInline => Some(AttributeKind::NoInline),
ExtendedAttrKind::AlwaysInline => Some(AttributeKind::AlwaysInline),
ExtendedAttrKind::OptimizeNone => Some(AttributeKind::OptimizeNone),
ExtendedAttrKind::OptimizeForSize => Some(AttributeKind::OptimizeForSize),
ExtendedAttrKind::MinSize => Some(AttributeKind::Minsize),
ExtendedAttrKind::NoDuplicate => Some(AttributeKind::NoDuplicate),
ExtendedAttrKind::Convergent => Some(AttributeKind::Convergent),
ExtendedAttrKind::NoUnwind => Some(AttributeKind::NoUnwind),
ExtendedAttrKind::NoReturn => Some(AttributeKind::NoReturn),
ExtendedAttrKind::WillReturn => Some(AttributeKind::WillReturn),
ExtendedAttrKind::Cold => Some(AttributeKind::Cold),
ExtendedAttrKind::Hot => Some(AttributeKind::Hot),
ExtendedAttrKind::InlineHint => Some(AttributeKind::InlineHint),
ExtendedAttrKind::NoMerge => Some(AttributeKind::NoMerge),
ExtendedAttrKind::MustProgress => Some(AttributeKind::MustProgress),
ExtendedAttrKind::NoRecurse => Some(AttributeKind::NoRecurse),
ExtendedAttrKind::NoSync => Some(AttributeKind::NoSync),
ExtendedAttrKind::NullPointerIsValid => Some(AttributeKind::NullPointerIsValid),
ExtendedAttrKind::NoCallback => Some(AttributeKind::NoCallback),
ExtendedAttrKind::NoProfile => Some(AttributeKind::NoProfile),
ExtendedAttrKind::SkipProfile => Some(AttributeKind::SkipProfile),
ExtendedAttrKind::OptDebug => Some(AttributeKind::OptDebug),
ExtendedAttrKind::OptForFuzzing => Some(AttributeKind::OptForFuzzing),
ExtendedAttrKind::UseSampleProfile => Some(AttributeKind::UseSampleProfile),
ExtendedAttrKind::NoBuiltin => Some(AttributeKind::NoBuiltin),
ExtendedAttrKind::ReturnsTwice => Some(AttributeKind::ReturnsTwice),
ExtendedAttrKind::JumpTable => Some(AttributeKind::JumpTable),
ExtendedAttrKind::NonLazyBind => Some(AttributeKind::NonLazyBind),
ExtendedAttrKind::NoCfCheck => Some(AttributeKind::NoCfCheck),
ExtendedAttrKind::SpeculativeLoadHardening => {
Some(AttributeKind::SpeculativeLoadHardening)
}
ExtendedAttrKind::StackProtect => Some(AttributeKind::Ssp),
ExtendedAttrKind::StackProtectReq => Some(AttributeKind::SspReq),
ExtendedAttrKind::StackProtectStrong => Some(AttributeKind::SspStrong),
ExtendedAttrKind::ShadowCallStack => Some(AttributeKind::ShadowCallStack),
ExtendedAttrKind::SafeStack => Some(AttributeKind::SafeStack),
ExtendedAttrKind::SanitizeAddress => Some(AttributeKind::SanitizeAddress),
ExtendedAttrKind::SanitizeThread => Some(AttributeKind::SanitizeThread),
ExtendedAttrKind::SanitizeMemory => Some(AttributeKind::SanitizeMemory),
ExtendedAttrKind::SanitizeHWAddress => Some(AttributeKind::SanitizeHwAddress),
ExtendedAttrKind::SanitizeMemTag => Some(AttributeKind::SanitizeMemTag),
ExtendedAttrKind::NoSanitize => Some(AttributeKind::NoSanitize),
ExtendedAttrKind::SanitizeCoverage => Some(AttributeKind::SanitizeCoverage),
ExtendedAttrKind::DisableSanitizerInstrumentation => {
Some(AttributeKind::DisableSanitizerInstrumentation)
}
ExtendedAttrKind::UWTable => Some(AttributeKind::UWTable),
ExtendedAttrKind::FramePointer(k) => Some(AttributeKind::FramePointer(*k)),
ExtendedAttrKind::Naked => Some(AttributeKind::Naked),
ExtendedAttrKind::NoImplicitFloat => Some(AttributeKind::NoImplicitFloat),
ExtendedAttrKind::NoRedZone => Some(AttributeKind::NoRedZone),
ExtendedAttrKind::FnRetThunkExtern => Some(AttributeKind::FnRetThunkExtern),
ExtendedAttrKind::Hotpatch => Some(AttributeKind::Hotpatch),
ExtendedAttrKind::ReadNone => Some(AttributeKind::ReadNone),
ExtendedAttrKind::ReadOnly => Some(AttributeKind::ReadOnly),
ExtendedAttrKind::WriteOnly => Some(AttributeKind::WriteOnly),
ExtendedAttrKind::ArgMemOnly => Some(AttributeKind::ArgMemOnly),
ExtendedAttrKind::InaccessibleMemOnly => Some(AttributeKind::InaccessibleMemOnly),
ExtendedAttrKind::InaccessibleMemOrArgMemOnly => {
Some(AttributeKind::InaccessibleMemOrArgMemOnly)
}
ExtendedAttrKind::StrictFP => Some(AttributeKind::StrictFP),
ExtendedAttrKind::Speculatable => Some(AttributeKind::Speculatable),
_ => None, }
}
pub fn from_core_attrkind(kind: &AttributeKind) -> ExtendedAttrKind {
match kind {
AttributeKind::NoInline => ExtendedAttrKind::NoInline,
AttributeKind::AlwaysInline => ExtendedAttrKind::AlwaysInline,
AttributeKind::OptimizeNone => ExtendedAttrKind::OptimizeNone,
AttributeKind::OptimizeForSize => ExtendedAttrKind::OptimizeForSize,
AttributeKind::Minsize => ExtendedAttrKind::MinSize,
AttributeKind::NoDuplicate => ExtendedAttrKind::NoDuplicate,
AttributeKind::Convergent => ExtendedAttrKind::Convergent,
AttributeKind::NoUnwind => ExtendedAttrKind::NoUnwind,
AttributeKind::NoReturn => ExtendedAttrKind::NoReturn,
AttributeKind::WillReturn => ExtendedAttrKind::WillReturn,
AttributeKind::Cold => ExtendedAttrKind::Cold,
AttributeKind::Hot => ExtendedAttrKind::Hot,
AttributeKind::InlineHint => ExtendedAttrKind::InlineHint,
AttributeKind::NoMerge => ExtendedAttrKind::NoMerge,
AttributeKind::MustProgress => ExtendedAttrKind::MustProgress,
AttributeKind::NoRecurse => ExtendedAttrKind::NoRecurse,
AttributeKind::NoSync => ExtendedAttrKind::NoSync,
AttributeKind::NullPointerIsValid => ExtendedAttrKind::NullPointerIsValid,
AttributeKind::NoCallback => ExtendedAttrKind::NoCallback,
AttributeKind::NoProfile => ExtendedAttrKind::NoProfile,
AttributeKind::SkipProfile => ExtendedAttrKind::SkipProfile,
AttributeKind::OptDebug => ExtendedAttrKind::OptDebug,
AttributeKind::OptForFuzzing => ExtendedAttrKind::OptForFuzzing,
AttributeKind::UseSampleProfile => ExtendedAttrKind::UseSampleProfile,
AttributeKind::NoBuiltin => ExtendedAttrKind::NoBuiltin,
AttributeKind::ReturnsTwice => ExtendedAttrKind::ReturnsTwice,
AttributeKind::JumpTable => ExtendedAttrKind::JumpTable,
AttributeKind::NonLazyBind => ExtendedAttrKind::NonLazyBind,
AttributeKind::NoCfCheck => ExtendedAttrKind::NoCfCheck,
AttributeKind::SpeculativeLoadHardening => ExtendedAttrKind::SpeculativeLoadHardening,
AttributeKind::Ssp => ExtendedAttrKind::StackProtect,
AttributeKind::SspReq => ExtendedAttrKind::StackProtectReq,
AttributeKind::SspStrong => ExtendedAttrKind::StackProtectStrong,
AttributeKind::ShadowCallStack => ExtendedAttrKind::ShadowCallStack,
AttributeKind::SafeStack => ExtendedAttrKind::SafeStack,
AttributeKind::SanitizeAddress => ExtendedAttrKind::SanitizeAddress,
AttributeKind::SanitizeThread => ExtendedAttrKind::SanitizeThread,
AttributeKind::SanitizeMemory => ExtendedAttrKind::SanitizeMemory,
AttributeKind::SanitizeHwAddress => ExtendedAttrKind::SanitizeHWAddress,
AttributeKind::SanitizeMemTag => ExtendedAttrKind::SanitizeMemTag,
AttributeKind::NoSanitize => ExtendedAttrKind::NoSanitize,
AttributeKind::SanitizeCoverage => ExtendedAttrKind::SanitizeCoverage,
AttributeKind::DisableSanitizerInstrumentation => {
ExtendedAttrKind::DisableSanitizerInstrumentation
}
AttributeKind::UWTable => ExtendedAttrKind::UWTable,
AttributeKind::FramePointer(k) => ExtendedAttrKind::FramePointer(*k),
AttributeKind::Naked => ExtendedAttrKind::Naked,
AttributeKind::NoImplicitFloat => ExtendedAttrKind::NoImplicitFloat,
AttributeKind::NoRedZone => ExtendedAttrKind::NoRedZone,
AttributeKind::FnRetThunkExtern => ExtendedAttrKind::FnRetThunkExtern,
AttributeKind::Hotpatch => ExtendedAttrKind::Hotpatch,
AttributeKind::ReadNone => ExtendedAttrKind::ReadNone,
AttributeKind::ReadOnly => ExtendedAttrKind::ReadOnly,
AttributeKind::WriteOnly => ExtendedAttrKind::WriteOnly,
AttributeKind::ArgMemOnly => ExtendedAttrKind::ArgMemOnly,
AttributeKind::InaccessibleMemOnly => ExtendedAttrKind::InaccessibleMemOnly,
AttributeKind::InaccessibleMemOrArgMemOnly => {
ExtendedAttrKind::InaccessibleMemOrArgMemOnly
}
AttributeKind::StrictFP => ExtendedAttrKind::StrictFP,
AttributeKind::Speculatable => ExtendedAttrKind::Speculatable,
_ => ExtendedAttrKind::StringKV(kind.to_string(), String::new()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DenormalMode {
IEEE,
PreserveSign,
PositiveZero,
Dynamic,
}
impl fmt::Display for DenormalMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DenormalMode::IEEE => write!(f, "ieee"),
DenormalMode::PreserveSign => write!(f, "preserve-sign"),
DenormalMode::PositiveZero => write!(f, "positive-zero"),
DenormalMode::Dynamic => write!(f, "dynamic"),
}
}
}
#[derive(Debug, Clone)]
pub struct FPEnvironment {
pub denormal_fp_math: Option<DenormalMode>,
pub denormal_fp32_math: Option<DenormalMode>,
pub fast_math_flags: FastMathFlags,
pub fp_exception_behavior: FPExceptionBehavior,
}
impl Default for FPEnvironment {
fn default() -> Self {
FPEnvironment {
denormal_fp_math: None,
denormal_fp32_math: None,
fast_math_flags: FastMathFlags::default(),
fp_exception_behavior: FPExceptionBehavior::Ignore,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FastMathFlags {
pub no_nans: bool,
pub no_infs: bool,
pub no_signed_zeros: bool,
pub allow_reciprocal: bool,
pub allow_contract: bool,
pub allow_reassociation: bool,
pub approx_func: bool,
}
impl FastMathFlags {
pub fn all() -> Self {
FastMathFlags {
no_nans: true,
no_infs: true,
no_signed_zeros: true,
allow_reciprocal: true,
allow_contract: true,
allow_reassociation: true,
approx_func: true,
}
}
pub fn none() -> Self {
FastMathFlags::default()
}
pub fn is_any_set(&self) -> bool {
self.no_nans
|| self.no_infs
|| self.no_signed_zeros
|| self.allow_reciprocal
|| self.allow_contract
|| self.allow_reassociation
|| self.approx_func
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FPExceptionBehavior {
Ignore,
MayTrap,
Strict,
}
impl Default for FPExceptionBehavior {
fn default() -> Self {
FPExceptionBehavior::Ignore
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CallingConvention {
C = 0,
Fast = 8,
Cold = 9,
GHC = 10,
HiPE = 11,
WebKitJS = 12,
AnyReg = 13,
PreserveMost = 14,
PreserveAll = 15,
Swift = 16,
CXXFastTLS = 17,
Tail = 18,
CFGuardCheck = 19,
SwiftTail = 20,
X86StdCall = 64,
X86FastCall = 65,
X86ThisCall = 70,
X86VectorCall = 80,
X86RegCall = 91,
ArmAPCS = 66,
ArmAAPCS = 67,
ArmAAPCSVFP = 68,
MSP430Intr = 69,
AVRIntr = 71,
AVRSignal = 72,
PTXKernel = 73,
PTXDevice = 74,
SPIRFunction = 75,
SPIRKernel = 76,
IntelOCLBI = 77,
X8664SysV = 78,
Win64 = 79,
AMDGPUVS = 87,
AMDGPUGS = 88,
AMDGPUPS = 89,
AMDGPUCS = 90,
AMDGPUKernel = 92,
AArch64VectorCall = 97,
AArch64SVEPCS = 98,
WASMEmscriptenInvoke = 99,
WASMEmscriptenLandingPad = 100,
RISCVVectorCall = 101,
MaxID = 1023,
}
impl CallingConvention {
pub fn from_id(id: u32) -> CallingConvention {
match id {
0 => CallingConvention::C,
8 => CallingConvention::Fast,
9 => CallingConvention::Cold,
10 => CallingConvention::GHC,
11 => CallingConvention::HiPE,
12 => CallingConvention::WebKitJS,
13 => CallingConvention::AnyReg,
14 => CallingConvention::PreserveMost,
15 => CallingConvention::PreserveAll,
16 => CallingConvention::Swift,
17 => CallingConvention::CXXFastTLS,
18 => CallingConvention::Tail,
19 => CallingConvention::CFGuardCheck,
20 => CallingConvention::SwiftTail,
64 => CallingConvention::X86StdCall,
65 => CallingConvention::X86FastCall,
70 => CallingConvention::X86ThisCall,
80 => CallingConvention::X86VectorCall,
91 => CallingConvention::X86RegCall,
66 => CallingConvention::ArmAPCS,
67 => CallingConvention::ArmAAPCS,
68 => CallingConvention::ArmAAPCSVFP,
69 => CallingConvention::MSP430Intr,
71 => CallingConvention::AVRIntr,
72 => CallingConvention::AVRSignal,
73 => CallingConvention::PTXKernel,
74 => CallingConvention::PTXDevice,
75 => CallingConvention::SPIRFunction,
76 => CallingConvention::SPIRKernel,
77 => CallingConvention::IntelOCLBI,
78 => CallingConvention::X8664SysV,
79 => CallingConvention::Win64,
87 => CallingConvention::AMDGPUVS,
88 => CallingConvention::AMDGPUGS,
89 => CallingConvention::AMDGPUPS,
90 => CallingConvention::AMDGPUCS,
92 => CallingConvention::AMDGPUKernel,
97 => CallingConvention::AArch64VectorCall,
98 => CallingConvention::AArch64SVEPCS,
99 => CallingConvention::WASMEmscriptenInvoke,
100 => CallingConvention::WASMEmscriptenLandingPad,
101 => CallingConvention::RISCVVectorCall,
_ => CallingConvention::C,
}
}
pub fn to_id(self) -> u32 {
self as u32
}
pub fn name(self) -> &'static str {
match self {
CallingConvention::C => "ccc",
CallingConvention::Fast => "fastcc",
CallingConvention::Cold => "coldcc",
CallingConvention::GHC => "ghccc",
CallingConvention::HiPE => "hipecc",
CallingConvention::WebKitJS => "webkit_jscc",
CallingConvention::AnyReg => "anyregcc",
CallingConvention::PreserveMost => "preserve_mostcc",
CallingConvention::PreserveAll => "preserve_allcc",
CallingConvention::Swift => "swiftcc",
CallingConvention::CXXFastTLS => "cxx_fast_tlscc",
CallingConvention::Tail => "tailcc",
CallingConvention::CFGuardCheck => "cfguard_checkcc",
CallingConvention::SwiftTail => "swifttailcc",
CallingConvention::X86StdCall => "x86_stdcallcc",
CallingConvention::X86FastCall => "x86_fastcallcc",
CallingConvention::X86ThisCall => "x86_thiscallcc",
CallingConvention::X86VectorCall => "x86_vectorcallcc",
CallingConvention::X86RegCall => "x86_regcallcc",
CallingConvention::ArmAPCS => "arm_apcscc",
CallingConvention::ArmAAPCS => "arm_aapcscc",
CallingConvention::ArmAAPCSVFP => "arm_aapcs_vfpcc",
CallingConvention::MSP430Intr => "msp430_intrcc",
CallingConvention::AVRIntr => "avr_intrcc",
CallingConvention::AVRSignal => "avr_signalcc",
CallingConvention::PTXKernel => "ptx_kernel",
CallingConvention::PTXDevice => "ptx_device",
CallingConvention::SPIRFunction => "spir_function",
CallingConvention::SPIRKernel => "spir_kernel",
CallingConvention::IntelOCLBI => "intel_ocl_bicc",
CallingConvention::X8664SysV => "x86_64_sysvcc",
CallingConvention::Win64 => "win64cc",
CallingConvention::AMDGPUVS => "amdgpu_vs",
CallingConvention::AMDGPUGS => "amdgpu_gs",
CallingConvention::AMDGPUPS => "amdgpu_ps",
CallingConvention::AMDGPUCS => "amdgpu_cs",
CallingConvention::AMDGPUKernel => "amdgpu_kernel",
CallingConvention::AArch64VectorCall => "aarch64_vector_pcs",
CallingConvention::AArch64SVEPCS => "aarch64_sve_pcs",
CallingConvention::WASMEmscriptenInvoke => "wasm_emscripten_invoke",
CallingConvention::WASMEmscriptenLandingPad => "wasm_emscripten_landingpad",
CallingConvention::RISCVVectorCall => "riscv_vector_cc",
CallingConvention::MaxID => "max",
}
}
}
impl fmt::Display for CallingConvention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_effects_none() {
let me = MemoryEffects::none();
assert!(me.does_not_access_memory());
assert!(!me.only_reads_memory());
assert!(!me.only_writes_memory());
}
#[test]
fn test_memory_effects_readonly() {
let me = MemoryEffects::read_only();
assert!(!me.does_not_access_memory());
assert!(me.only_reads_memory());
assert!(!me.only_writes_memory());
}
#[test]
fn test_memory_effects_union() {
let a = MemoryEffects::read_only();
let b = MemoryEffects::write_only();
let u = a.union_with(&b);
assert_eq!(u.argmem, ModRefInfo::ModRef);
}
#[test]
fn test_memory_effects_intersect() {
let a = MemoryEffects::unknown();
let b = MemoryEffects::read_only();
let i = a.intersect_with(&b);
assert_eq!(i.argmem, ModRefInfo::Ref);
}
#[test]
fn test_extended_attr_list_basic() {
let mut list = ExtendedAttrList::new();
list.add_fn_attr(ExtendedAttrKind::NoInline);
list.add_fn_attr(ExtendedAttrKind::ReadNone);
list.add_fn_attr(ExtendedAttrKind::Alignment(16));
assert!(list.has_fn_attr(&ExtendedAttrKind::NoInline));
assert!(list.has_fn_attr(&ExtendedAttrKind::ReadNone));
assert!(!list.has_fn_attr(&ExtendedAttrKind::NoReturn));
}
#[test]
fn test_extended_attr_list_param() {
let mut list = ExtendedAttrList::new();
list.add_param_attr(0, ExtendedAttrKind::NonNull);
list.add_param_attr(0, ExtendedAttrKind::SExt);
list.add_param_attr(1, ExtendedAttrKind::NoCapture);
assert!(list.has_param_attr(0, &ExtendedAttrKind::NonNull));
assert!(!list.has_param_attr(1, &ExtendedAttrKind::NonNull));
assert!(list.has_param_attr(1, &ExtendedAttrKind::NoCapture));
}
#[test]
fn test_extended_attr_set_merge() {
let mut a = ExtendedAttrSet::new(0);
a.add_attr(ExtendedAttrKind::NoInline);
let mut b = ExtendedAttrSet::new(0);
b.add_attr(ExtendedAttrKind::NoUnwind);
let merged = a.merge(&b);
assert!(merged.has_attr(&ExtendedAttrKind::NoInline));
assert!(merged.has_attr(&ExtendedAttrKind::NoUnwind));
}
#[test]
fn test_memory_effects_from_attrs() {
let mut list = ExtendedAttrList::new();
list.add_fn_attr(ExtendedAttrKind::ReadNone);
let me = list.get_memory_effects();
assert!(me.does_not_access_memory());
let mut list2 = ExtendedAttrList::new();
list2.add_fn_attr(ExtendedAttrKind::ReadOnly);
let me2 = list2.get_memory_effects();
assert!(me2.only_reads_memory());
}
#[test]
fn test_attr_string_parser_simple() {
let list = AttrStringParser::parse_fn_attrs("nounwind readonly ssp align 16");
assert!(list.has_fn_attr(&ExtendedAttrKind::NoUnwind));
assert!(list.has_fn_attr(&ExtendedAttrKind::ReadOnly));
assert!(list.has_fn_attr(&ExtendedAttrKind::StackProtect));
}
#[test]
fn test_attr_string_parser_complex() {
let list = AttrStringParser::parse_fn_attrs("noinline optnone nofree nosync willreturn");
assert!(list.has_fn_attr(&ExtendedAttrKind::NoInline));
assert!(list.has_fn_attr(&ExtendedAttrKind::OptimizeNone));
assert!(list.has_fn_attr(&ExtendedAttrKind::NoFree));
assert!(list.has_fn_attr(&ExtendedAttrKind::NoSync));
assert!(list.has_fn_attr(&ExtendedAttrKind::WillReturn));
}
#[test]
fn test_target_attrs() {
let mut list = ExtendedAttrList::new();
list.add_target_attr(TargetAttr::TargetCpu("skylake".to_string()));
list.add_target_attr(TargetAttr::TargetFeatures("+avx2,-sse4.1".to_string()));
assert_eq!(list.target_attrs.len(), 2);
}
#[test]
fn test_attribution_inference_nounwind() {
let mut engine = AttributionInference::new();
let mut inf_a = InferredAttributes::default();
inf_a.is_nounwind = true;
engine.observe_function("callee", inf_a);
engine.add_call_edge("caller", "callee");
assert!(engine.deduce_nounwind("caller", true));
}
#[test]
fn test_attribution_inference_willreturn() {
let mut engine = AttributionInference::new();
let mut inf = InferredAttributes::default();
inf.is_willreturn = true;
engine.observe_function("leaf", inf);
engine.add_call_edge("parent", "leaf");
assert!(engine.deduce_willreturn("parent", false));
}
#[test]
fn test_attribution_inference_recursive() {
let engine = AttributionInference::new();
assert!(!engine.calls_itself_directly_or_indirectly("f"));
}
#[test]
fn test_calling_convention_ids() {
assert_eq!(CallingConvention::from_id(0), CallingConvention::C);
assert_eq!(CallingConvention::from_id(8), CallingConvention::Fast);
assert_eq!(CallingConvention::Fast.to_id(), 8);
assert_eq!(CallingConvention::AArch64SVEPCS.to_id(), 98);
}
#[test]
fn test_fp_environment_default() {
let env = FPEnvironment::default();
assert!(env.denormal_fp_math.is_none());
assert!(!env.fast_math_flags.is_any_set());
}
#[test]
fn test_fast_math_flags() {
let flags = FastMathFlags::all();
assert!(flags.is_any_set());
assert!(flags.no_nans);
assert!(flags.no_infs);
}
#[test]
fn test_extended_attr_kind_display() {
assert_eq!(ExtendedAttrKind::NoInline.to_string(), "noinline");
assert_eq!(ExtendedAttrKind::Alignment(16).to_string(), "align 16");
assert_eq!(ExtendedAttrKind::ReadNone.to_string(), "readnone");
}
#[test]
fn test_extended_attr_list_string_attrs() {
let mut list = ExtendedAttrList::new();
list.add_string_attr("mykey", "myvalue");
assert_eq!(list.get_string_attr("mykey"), Some("myvalue"));
assert!(list.has_string_attr("mykey"));
}
#[test]
fn test_attr_set_is_empty() {
let set = ExtendedAttrSet::new(0);
assert!(set.is_empty());
}
#[test]
fn test_attr_set_to_llvm_string() {
let mut set = ExtendedAttrSet::new(0);
set.add_attr(ExtendedAttrKind::NoInline);
set.add_attr(ExtendedAttrKind::Alignment(8));
let s = set.to_llvm_string();
assert!(s.contains("noinline"));
assert!(s.contains("align 8"));
}
#[test]
fn test_modrefinfo_display() {
assert_eq!(ModRefInfo::NoModRef.to_string(), "none");
assert_eq!(ModRefInfo::Ref.to_string(), "read");
assert_eq!(ModRefInfo::ModRef.to_string(), "readwrite");
}
#[test]
fn test_denormal_mode_display() {
assert_eq!(DenormalMode::IEEE.to_string(), "ieee");
assert_eq!(DenormalMode::PreserveSign.to_string(), "preserve-sign");
}
}