llvm_ir/
function.rs

1use crate::debugloc::{DebugLoc, HasDebugLoc};
2use crate::module::{Comdat, DLLStorageClass, Linkage, Visibility};
3use crate::types::{TypeRef, Typed, Types};
4use crate::{BasicBlock, ConstantRef, Name};
5
6/// See [LLVM 14 docs on Functions](https://releases.llvm.org/14.0.0/docs/LangRef.html#functions)
7#[derive(PartialEq, Clone, Debug, Hash)]
8pub struct Function {
9    pub name: String,
10    pub parameters: Vec<Parameter>,
11    pub is_var_arg: bool,
12    pub return_type: TypeRef,
13    pub basic_blocks: Vec<BasicBlock>,
14    pub function_attributes: Vec<FunctionAttribute>, // llvm-hs-pure has Vec<Either<GroupID, FunctionAttribute>>, but I'm not sure how the GroupID ones come about
15    pub return_attributes: Vec<ParameterAttribute>,
16    pub linkage: Linkage,
17    pub visibility: Visibility,
18    pub dll_storage_class: DLLStorageClass, // llvm-hs-pure has Option<DLLStorageClass>, but the llvm_sys api doesn't look like it can fail
19    pub calling_convention: CallingConvention,
20    pub section: Option<String>,
21    pub comdat: Option<Comdat>, // llvm-hs-pure has Option<String>, I'm not sure why
22    pub alignment: u32,
23    /// See [LLVM 14 docs on Garbage Collector Strategy Names](https://releases.llvm.org/14.0.0/docs/LangRef.html#gc)
24    pub garbage_collector_name: Option<String>,
25    // pub prefix: Option<ConstantRef>,  // appears to not be exposed in the LLVM C API, only the C++ API
26    /// Personalities are used for exception handling. See [LLVM 14 docs on Personality Function](https://releases.llvm.org/14.0.0/docs/LangRef.html#personalityfn)
27    pub personality_function: Option<ConstantRef>,
28    pub debugloc: Option<DebugLoc>,
29    // --TODO not yet implemented-- pub metadata: Vec<(String, MetadataRef<MetadataNode>)>,
30}
31
32impl Typed for Function {
33    fn get_type(&self, types: &Types) -> TypeRef {
34        types.func_type(
35            self.return_type.clone(),
36            self.parameters.iter().map(|p| types.type_of(p)).collect(),
37            self.is_var_arg,
38        )
39    }
40}
41
42impl HasDebugLoc for Function {
43    fn get_debug_loc(&self) -> &Option<DebugLoc> {
44        &self.debugloc
45    }
46}
47
48impl Function {
49    /// Get the `BasicBlock` having the given `Name` (if any).
50    pub fn get_bb_by_name(&self, name: &Name) -> Option<&BasicBlock> {
51        self.basic_blocks.iter().find(|bb| &bb.name == name)
52    }
53
54    /// A Function instance as empty as possible, using defaults
55    pub fn new(name: impl Into<String>) -> Self {
56        Self {
57            name: name.into(),
58            parameters: vec![],
59            is_var_arg: false,
60            return_type: Types::blank_for_testing().void(),
61            basic_blocks: vec![],
62            function_attributes: vec![],
63            return_attributes: vec![],
64            linkage: Linkage::Private,
65            visibility: Visibility::Default,
66            dll_storage_class: DLLStorageClass::Default,
67            calling_convention: CallingConvention::C,
68            section: None,
69            comdat: None,
70            alignment: 4,
71            garbage_collector_name: None,
72            personality_function: None,
73            debugloc: None,
74        }
75    }
76}
77
78/// See [LLVM 14 docs on Functions](https://releases.llvm.org/14.0.0/docs/LangRef.html#functions)
79#[derive(PartialEq, Clone, Debug, Hash)]
80pub struct FunctionDeclaration {
81    pub name: String,
82    pub parameters: Vec<Parameter>,
83    pub is_var_arg: bool,
84    pub return_type: TypeRef,
85    pub return_attributes: Vec<ParameterAttribute>,
86    pub linkage: Linkage,
87    pub visibility: Visibility,
88    pub dll_storage_class: DLLStorageClass,
89    pub calling_convention: CallingConvention,
90    pub alignment: u32,
91    /// See [LLVM 14 docs on Garbage Collector Strategy Names](https://releases.llvm.org/14.0.0/docs/LangRef.html#gc)
92    pub garbage_collector_name: Option<String>,
93    pub debugloc: Option<DebugLoc>,
94}
95
96#[derive(PartialEq, Clone, Debug, Hash)]
97pub struct Parameter {
98    pub name: Name,
99    pub ty: TypeRef,
100    pub attributes: Vec<ParameterAttribute>,
101}
102
103impl Typed for Parameter {
104    fn get_type(&self, _types: &Types) -> TypeRef {
105        self.ty.clone()
106    }
107}
108
109/// See [LLVM 14 docs on Calling Conventions](https://releases.llvm.org/14.0.0/docs/LangRef.html#callingconv)
110#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
111#[allow(non_camel_case_types)]
112pub enum CallingConvention {
113    C,
114    Fast,
115    Cold,
116    GHC,
117    HiPE,
118    WebKit_JS,
119    AnyReg,
120    PreserveMost,
121    PreserveAll,
122    Swift,
123    CXX_FastTLS,
124    X86_StdCall,
125    X86_FastCall,
126    X86_RegCall,
127    X86_ThisCall,
128    X86_VectorCall,
129    X86_Intr,
130    X86_64_SysV,
131    ARM_APCS,
132    ARM_AAPCS,
133    ARM_AAPCS_VFP,
134    MSP430_INTR,
135    MSP430_Builtin,
136    PTX_Kernel,
137    PTX_Device,
138    SPIR_FUNC,
139    SPIR_KERNEL,
140    Intel_OCL_BI,
141    Win64,
142    HHVM,
143    HHVM_C,
144    AVR_Intr,
145    AVR_Signal,
146    AVR_Builtin,
147    AMDGPU_CS,
148    AMDGPU_ES,
149    AMDGPU_GS,
150    AMDGPU_HS,
151    AMDGPU_LS,
152    AMDGPU_PS,
153    AMDGPU_VS,
154    AMDGPU_Kernel,
155    /// This is used if LLVM returns a calling convention not in `LLVMCallConv`.
156    /// E.g., perhaps a calling convention was added to LLVM and this enum hasn't been updated yet.
157    Numbered(u32),
158}
159
160/// Describes how a given location in memory can be accessed.
161/// See [LLVM 16 docs on FunctionAttributes](https://releases.llvm.org/16.0.0/docs/LangRef.html#fnattrs),
162/// the section on memory(...)
163#[derive(PartialEq, Eq, Clone, Debug, Hash)]
164pub enum MemoryEffect {
165    None,
166    Read,
167    Write,
168    ReadWrite
169}
170
171impl MemoryEffect {
172    // See https://github.com/llvm/llvm-project/blob/7cbf1a2591520c2491aa35339f227775f4d3adf6/llvm/include/llvm/Support/ModRef.h#L27
173    pub(crate) fn from_llvm_bits(val : u64) -> Self {
174        match val {
175            0b00 => Self::None,
176            0b01 => Self::Read,
177            0b10 => Self::Write,
178            0b11 => Self::ReadWrite,
179            _ => panic!("Memory effect given unexpected bits {}", val)
180        }
181    }
182}
183
184/// See [LLVM 14 docs on Function Attributes](https://releases.llvm.org/14.0.0/docs/LangRef.html#fnattrs)
185#[derive(PartialEq, Eq, Clone, Debug, Hash)]
186pub enum FunctionAttribute {
187    AlignStack(u64),
188    AllocSize {
189        elt_size: u32,
190        num_elts: Option<u32>,
191    },
192    AlwaysInline,
193    Builtin,
194    Cold,
195    Convergent,
196    InaccessibleMemOnly,
197    InaccessibleMemOrArgMemOnly,
198    InlineHint,
199    JumpTable,
200    MinimizeSize,
201    Naked,
202    NoBuiltin,
203    NoCFCheck,
204    NoDuplicate,
205    NoFree,
206    NoImplicitFloat,
207    NoInline,
208    #[cfg(feature = "llvm-11-or-greater")]
209    NoMerge,
210    NonLazyBind,
211    NoRedZone,
212    NoReturn,
213    NoRecurse,
214    WillReturn,
215    ReturnsTwice,
216    NoSync,
217    NoUnwind,
218    #[cfg(feature = "llvm-11-or-greater")]
219    NullPointerIsValid,
220    OptForFuzzing,
221    OptNone,
222    OptSize,
223    ReadNone,
224    ReadOnly,
225    WriteOnly,
226    ArgMemOnly,
227    SafeStack,
228    SanitizeAddress,
229    SanitizeMemory,
230    SanitizeThread,
231    SanitizeHWAddress,
232    SanitizeMemTag,
233    ShadowCallStack,
234    SpeculativeLoadHardening,
235    Speculatable,
236    StackProtect,
237    StackProtectReq,
238    StackProtectStrong,
239    StrictFP,
240    UWTable,
241    #[cfg(feature = "llvm-16-or-greater")]
242    Memory {
243        default: MemoryEffect,
244        argmem: MemoryEffect,
245        inaccessible_mem: MemoryEffect
246    },
247    StringAttribute {
248        kind: String,
249        value: String, // for no value, use ""
250    },
251    UnknownAttribute, // this is used if we get a value not in the above list
252}
253
254/// `ParameterAttribute`s can apply to function parameters as well as function return types.
255/// See [LLVM 14 docs on Parameter Attributes](https://releases.llvm.org/14.0.0/docs/LangRef.html#paramattrs)
256#[derive(PartialEq, Eq, Clone, Debug, Hash)]
257pub enum ParameterAttribute {
258    ZeroExt,
259    SignExt,
260    InReg,
261    #[cfg(feature = "llvm-11-or-lower")]
262    ByVal,
263    #[cfg(feature = "llvm-12-or-greater")]
264    ByVal(TypeRef),
265    #[cfg(feature = "llvm-11")]
266    Preallocated,
267    #[cfg(feature = "llvm-12-or-greater")]
268    Preallocated(TypeRef),
269    #[cfg(feature = "llvm-12-or-lower")]
270    InAlloca,
271    #[cfg(feature = "llvm-13-or-greater")]
272    InAlloca(TypeRef),
273    #[cfg(feature = "llvm-11-or-lower")]
274    SRet,
275    #[cfg(feature = "llvm-12-or-greater")]
276    SRet(TypeRef),
277    Alignment(u64),
278    NoAlias,
279    NoCapture,
280    NoFree,
281    Nest,
282    Returned,
283    NonNull,
284    Dereferenceable(u64),
285    DereferenceableOrNull(u64),
286    SwiftSelf,
287    SwiftError,
288    ImmArg,
289    #[cfg(feature = "llvm-11-or-greater")]
290    NoUndef,
291    StringAttribute {
292        kind: String,
293        value: String, // for no value, use ""
294    },
295    UnknownAttribute, // this is used if we get an EnumAttribute not in the above list; or, for LLVM 11 or lower, also for some TypeAttributes (due to C API limitations)
296    #[cfg(feature = "llvm-12-or-greater")]
297    UnknownTypeAttribute(TypeRef), // this is used if we get a TypeAttribute not in the above list
298}
299
300pub type GroupID = usize;
301
302// ********* //
303// from_llvm //
304// ********* //
305
306use crate::constant::Constant;
307use crate::from_llvm::*;
308use crate::llvm_sys::*;
309use crate::module::ModuleContext;
310#[cfg(feature = "llvm-12-or-greater")]
311use crate::types::TypesBuilder;
312use llvm_sys::comdat::*;
313use llvm_sys::{LLVMAttributeFunctionIndex, LLVMAttributeReturnIndex};
314use std::collections::HashMap;
315use std::ffi::CString;
316
317/// This struct contains data used when translating from llvm-sys into our data
318/// structures. The data here is local to a particular Function.
319pub(crate) struct FunctionContext<'a> {
320    /// Map from llvm-sys basic block to its `Name`
321    // We use LLVMBasicBlockRef as a *const, even though it's technically a *mut
322    #[allow(clippy::mutable_key_type)]
323    pub bb_names: &'a HashMap<LLVMBasicBlockRef, Name>,
324    /// Map from llvm-sys value to its `Name`
325    // We use LLVMValueRef as a *const, even though it's technically a *mut
326    #[allow(clippy::mutable_key_type)]
327    pub val_names: &'a HashMap<LLVMValueRef, Name>,
328    /// this counter is used to number parameters, variables, and basic blocks that aren't named
329    pub ctr: usize,
330}
331
332impl FunctionDeclaration {
333    pub(crate) fn from_llvm_ref(func: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
334        let func = unsafe { LLVMIsAFunction(func) };
335        assert!(!func.is_null());
336        debug!("Processing func {:?}", unsafe { get_value_name(func) });
337
338        let (decl, _) = FunctionDeclaration::from_llvm_ref_internal(func, ctx);
339        decl
340    }
341
342    /// this helper is shared by `FunctionDeclaration` and `Function`. It
343    /// provides the whole `FunctionDeclaration`, and also the value of the
344    /// `local_ctr` after parameters are processed (which is needed by
345    /// `Function`).
346    fn from_llvm_ref_internal(func: LLVMValueRef, ctx: &mut ModuleContext) -> (Self, usize) {
347        #[cfg(feature = "llvm-14-or-lower")]
348        let functy = unsafe { LLVMGetElementType(LLVMTypeOf(func)) }; // for some reason the TypeOf a function is <pointer to function> and not just <function> so we have to deref it like this
349        #[cfg(feature = "llvm-15-or-greater")]
350        let functy = unsafe { LLVMGlobalGetValueType(func) };
351        let mut local_ctr = 0; // this counter is used to number parameters, variables, and basic blocks that aren't named
352        let decl = Self {
353            name: unsafe { get_value_name(func) },
354            parameters: {
355                let parameters: Vec<Parameter> = get_parameters(func)
356                    .enumerate()
357                    .map(|(i, p)| Parameter {
358                        name: Name::name_or_num(unsafe { get_value_name(p) }, &mut local_ctr),
359                        ty: ctx.types.type_from_llvm_ref(unsafe { LLVMTypeOf(p) }),
360                        attributes: {
361                            let param_num = i + 1; // https://docs.rs/llvm-sys/100.0.1/llvm_sys/type.LLVMAttributeIndex.html indicates that parameter numbers are 1-indexed here; see issue #4
362                            let num_attrs =
363                                unsafe { LLVMGetAttributeCountAtIndex(func, param_num as u32) };
364                            let mut attrs: Vec<LLVMAttributeRef> =
365                                Vec::with_capacity(num_attrs as usize);
366                            unsafe {
367                                LLVMGetAttributesAtIndex(
368                                    func,
369                                    param_num as u32,
370                                    attrs.as_mut_ptr(),
371                                );
372                                attrs.set_len(num_attrs as usize);
373                            };
374                            attrs
375                                .into_iter()
376                                .map(|attr| {
377                                    ParameterAttribute::from_llvm_ref(
378                                        attr,
379                                        &ctx.attrsdata,
380                                        #[cfg(feature = "llvm-12-or-greater")]
381                                        &mut ctx.types,
382                                    )
383                                })
384                                .collect()
385                        },
386                    })
387                    .collect();
388                debug!("Collected info on {} parameters", parameters.len());
389                parameters
390            },
391            is_var_arg: unsafe { LLVMIsFunctionVarArg(functy) } != 0,
392            return_type: ctx
393                .types
394                .type_from_llvm_ref(unsafe { LLVMGetReturnType(functy) }),
395            return_attributes: {
396                let num_attrs =
397                    unsafe { LLVMGetAttributeCountAtIndex(func, LLVMAttributeReturnIndex) };
398                if num_attrs > 0 {
399                    let mut attrs: Vec<LLVMAttributeRef> = Vec::with_capacity(num_attrs as usize);
400                    unsafe {
401                        LLVMGetAttributesAtIndex(
402                            func,
403                            LLVMAttributeReturnIndex,
404                            attrs.as_mut_ptr(),
405                        );
406                        attrs.set_len(num_attrs as usize);
407                    };
408                    attrs
409                        .into_iter()
410                        .map(|attr| {
411                            ParameterAttribute::from_llvm_ref(
412                                attr,
413                                &ctx.attrsdata,
414                                #[cfg(feature = "llvm-12-or-greater")]
415                                &mut ctx.types,
416                            )
417                        })
418                        .collect()
419                } else {
420                    vec![]
421                }
422            },
423            linkage: Linkage::from_llvm(unsafe { LLVMGetLinkage(func) }),
424            visibility: Visibility::from_llvm(unsafe { LLVMGetVisibility(func) }),
425            dll_storage_class: DLLStorageClass::from_llvm(unsafe { LLVMGetDLLStorageClass(func) }),
426            calling_convention: CallingConvention::from_u32(unsafe {
427                LLVMGetFunctionCallConv(func)
428            }),
429            alignment: unsafe { LLVMGetAlignment(func) },
430            garbage_collector_name: unsafe { get_gc(func) },
431            debugloc: DebugLoc::from_llvm_no_col(func),
432        };
433        (decl, local_ctr)
434    }
435}
436
437impl Function {
438    pub(crate) fn from_llvm_ref(func: LLVMValueRef, ctx: &mut ModuleContext) -> Self {
439        let func = unsafe { LLVMIsAFunction(func) };
440        assert!(!func.is_null());
441        debug!("Processing func {:?}", unsafe { get_value_name(func) });
442
443        // `Function` is a strict superset of the information in
444        // `FunctionDeclaration`, so we start by collecting all of the
445        // information shared by `FunctionDeclaration`, reusing that code
446        let (decl, ctr_val_after_parameters) =
447            FunctionDeclaration::from_llvm_ref_internal(func, ctx);
448
449        // Functions require two passes over their bodies.
450        // First we make a pass just to map `LLVMBasicBlockRef`s to `Name`s and `LLVMValueRef`s to `Name`s.
451        // Then we do the actual detailed pass.
452        // This is necessary because some instructions (e.g., forward branches) will reference
453        //   `LLVMBasicBlockRef`s and/or `LLVMValueRef`s which we wouldn't have
454        //   seen before if we tried to do everything in one pass, and therefore
455        //   we wouldn't necessarily know what `Name` the block or value had yet.
456        let mut local_ctr = ctr_val_after_parameters; // this counter is used to number parameters, variables, and basic blocks that aren't named
457        let bbresults: Vec<_> = get_basic_blocks(func)
458            .map(|bb| (bb, BasicBlock::first_pass_names(bb, &mut local_ctr)))
459            .collect();
460        // We use LLVMBasicBlockRef as a *const, even though it's technically a *mut
461        #[allow(clippy::mutable_key_type)]
462        let bb_names: HashMap<LLVMBasicBlockRef, Name> = bbresults
463            .iter()
464            .map(|(bb, (bbname, _))| (*bb, bbname.clone()))
465            .collect();
466        debug!("Collected names of {} basic blocks", bb_names.len());
467        // We use LLVMValueRef as a *const, even though it's technically a *mut
468        #[allow(clippy::mutable_key_type)]
469        let val_names: HashMap<LLVMValueRef, Name> = bbresults
470            .into_iter()
471            .flat_map(|(_, (_, namepairs))| namepairs.into_iter())
472            .chain(get_parameters(func).zip(decl.parameters.iter().map(|p| p.name.clone())))
473            .collect();
474        debug!("Collected names of {} values", val_names.len());
475        let mut func_ctx = FunctionContext {
476            bb_names: &bb_names,
477            val_names: &val_names,
478            ctr: ctr_val_after_parameters, // restart the local_ctr; the second pass should number everything exactly the same though
479        };
480
481        Self {
482            name: decl.name,
483            parameters: decl.parameters,
484            is_var_arg: decl.is_var_arg,
485            return_type: decl.return_type,
486            basic_blocks: {
487                get_basic_blocks(func)
488                    .map(|bb| BasicBlock::from_llvm_ref(bb, ctx, &mut func_ctx))
489                    .collect()
490            },
491            function_attributes: {
492                let num_attrs =
493                    unsafe { LLVMGetAttributeCountAtIndex(func, LLVMAttributeFunctionIndex) };
494                if num_attrs > 0 {
495                    let mut attrs: Vec<LLVMAttributeRef> = Vec::with_capacity(num_attrs as usize);
496                    unsafe {
497                        LLVMGetAttributesAtIndex(
498                            func,
499                            LLVMAttributeFunctionIndex,
500                            attrs.as_mut_ptr(),
501                        );
502                        attrs.set_len(num_attrs as usize);
503                    };
504                    attrs
505                        .into_iter()
506                        .map(|attr| FunctionAttribute::from_llvm_ref(attr, &ctx.attrsdata))
507                        .collect()
508                } else {
509                    vec![]
510                }
511            },
512            return_attributes: decl.return_attributes,
513            linkage: decl.linkage,
514            visibility: decl.visibility,
515            dll_storage_class: decl.dll_storage_class,
516            calling_convention: decl.calling_convention,
517            section: unsafe { get_section(func) },
518            comdat: {
519                let comdat = unsafe { LLVMGetComdat(func) };
520                if comdat.is_null() {
521                    None
522                } else {
523                    Some(Comdat::from_llvm_ref(comdat))
524                }
525            },
526            alignment: decl.alignment,
527            garbage_collector_name: decl.garbage_collector_name,
528            personality_function: {
529                if unsafe { LLVMHasPersonalityFn(func) } != 0 {
530                    Some(Constant::from_llvm_ref(
531                        unsafe { LLVMGetPersonalityFn(func) },
532                        ctx,
533                    ))
534                } else {
535                    None
536                }
537            },
538            debugloc: decl.debugloc,
539            // metadata: unimplemented!("Function.metadata"),
540        }
541    }
542}
543
544impl CallingConvention {
545    #[allow(clippy::cognitive_complexity)]
546    #[rustfmt::skip] // each calling convention on one line, even if lines get a little long
547    pub(crate) fn from_u32(u: u32) -> Self {
548        use llvm_sys::LLVMCallConv;
549        match u {
550            _ if u == LLVMCallConv::LLVMCCallConv as u32 => CallingConvention::C,
551            _ if u == LLVMCallConv::LLVMFastCallConv as u32 => CallingConvention::Fast,
552            _ if u == LLVMCallConv::LLVMColdCallConv as u32 => CallingConvention::Cold,
553            _ if u == LLVMCallConv::LLVMGHCCallConv as u32 => CallingConvention::GHC,
554            _ if u == LLVMCallConv::LLVMHiPECallConv as u32 => CallingConvention::HiPE,
555            #[cfg(feature = "llvm-17-or-lower")]
556            _ if u == LLVMCallConv::LLVMWebKitJSCallConv as u32 => CallingConvention::WebKit_JS,
557            _ if u == LLVMCallConv::LLVMAnyRegCallConv as u32 => CallingConvention::AnyReg,
558            _ if u == LLVMCallConv::LLVMPreserveMostCallConv as u32 => CallingConvention::PreserveMost,
559            _ if u == LLVMCallConv::LLVMPreserveAllCallConv as u32 => CallingConvention::PreserveAll,
560            _ if u == LLVMCallConv::LLVMSwiftCallConv as u32 => CallingConvention::Swift,
561            _ if u == LLVMCallConv::LLVMCXXFASTTLSCallConv as u32 => CallingConvention::CXX_FastTLS,
562            _ if u == LLVMCallConv::LLVMX86StdcallCallConv as u32 => CallingConvention::X86_StdCall,
563            _ if u == LLVMCallConv::LLVMX86FastcallCallConv as u32 => CallingConvention::X86_FastCall,
564            _ if u == LLVMCallConv::LLVMX86RegCallCallConv as u32 => CallingConvention::X86_RegCall,
565            _ if u == LLVMCallConv::LLVMX86ThisCallCallConv as u32 => CallingConvention::X86_ThisCall,
566            _ if u == LLVMCallConv::LLVMX86VectorCallCallConv as u32 => CallingConvention::X86_VectorCall,
567            _ if u == LLVMCallConv::LLVMX86INTRCallConv as u32 => CallingConvention::X86_Intr,
568            _ if u == LLVMCallConv::LLVMX8664SysVCallConv as u32 => CallingConvention::X86_64_SysV,
569            _ if u == LLVMCallConv::LLVMARMAPCSCallConv as u32 => CallingConvention::ARM_APCS,
570            _ if u == LLVMCallConv::LLVMARMAAPCSCallConv as u32 => CallingConvention::ARM_AAPCS,
571            _ if u == LLVMCallConv::LLVMARMAAPCSVFPCallConv as u32 => CallingConvention::ARM_AAPCS_VFP,
572            _ if u == LLVMCallConv::LLVMMSP430INTRCallConv as u32 => CallingConvention::MSP430_INTR,
573            _ if u == LLVMCallConv::LLVMMSP430BUILTINCallConv as u32 => CallingConvention::MSP430_Builtin,
574            _ if u == LLVMCallConv::LLVMPTXKernelCallConv as u32 => CallingConvention::PTX_Kernel,
575            _ if u == LLVMCallConv::LLVMPTXDeviceCallConv as u32 => CallingConvention::PTX_Device,
576            _ if u == LLVMCallConv::LLVMSPIRFUNCCallConv as u32 => CallingConvention::SPIR_FUNC,
577            _ if u == LLVMCallConv::LLVMSPIRKERNELCallConv as u32 => CallingConvention::SPIR_KERNEL,
578            _ if u == LLVMCallConv::LLVMIntelOCLBICallConv as u32 => CallingConvention::Intel_OCL_BI,
579            _ if u == LLVMCallConv::LLVMWin64CallConv as u32 => CallingConvention::Win64,
580            _ if u == LLVMCallConv::LLVMHHVMCallConv as u32 => CallingConvention::HHVM,
581            _ if u == LLVMCallConv::LLVMHHVMCCallConv as u32 => CallingConvention::HHVM_C,
582            _ if u == LLVMCallConv::LLVMAVRINTRCallConv as u32 => CallingConvention::AVR_Intr,
583            _ if u == LLVMCallConv::LLVMAVRSIGNALCallConv as u32 => CallingConvention::AVR_Signal,
584            _ if u == LLVMCallConv::LLVMAVRBUILTINCallConv as u32 => CallingConvention::AVR_Builtin,
585            _ if u == LLVMCallConv::LLVMAMDGPUCSCallConv as u32 => CallingConvention::AMDGPU_CS,
586            _ if u == LLVMCallConv::LLVMAMDGPUESCallConv as u32 => CallingConvention::AMDGPU_ES,
587            _ if u == LLVMCallConv::LLVMAMDGPUGSCallConv as u32 => CallingConvention::AMDGPU_GS,
588            _ if u == LLVMCallConv::LLVMAMDGPUHSCallConv as u32 => CallingConvention::AMDGPU_HS,
589            _ if u == LLVMCallConv::LLVMAMDGPULSCallConv as u32 => CallingConvention::AMDGPU_LS,
590            _ if u == LLVMCallConv::LLVMAMDGPUPSCallConv as u32 => CallingConvention::AMDGPU_PS,
591            _ if u == LLVMCallConv::LLVMAMDGPUVSCallConv as u32 => CallingConvention::AMDGPU_VS,
592            _ if u == LLVMCallConv::LLVMAMDGPUKERNELCallConv as u32 => CallingConvention::AMDGPU_Kernel,
593            _ => CallingConvention::Numbered(u),
594        }
595    }
596}
597
598pub(crate) struct AttributesData {
599    function_attribute_names: HashMap<u32, String>,
600    param_attribute_names: HashMap<u32, String>,
601}
602
603impl AttributesData {
604    pub fn create() -> Self {
605        let function_attribute_names = [
606            "alignstack",
607            "allocsize",
608            "alwaysinline",
609            "builtin",
610            "cold",
611            "convergent",
612            #[cfg(feature = "llvm-15-or-lower")]
613            "inaccessiblememonly",
614            #[cfg(feature = "llvm-15-or-lower")]
615            "inaccessiblemem_or_argmemonly",
616            "inlinehint",
617            "jumptable",
618            "minsize",
619            "naked",
620            "nobuiltin",
621            "nocf_check",
622            "noduplicate",
623            "nofree",
624            "noimplicitfloat",
625            "noinline",
626            #[cfg(feature = "llvm-11-or-greater")]
627            "nomerge",
628            "nonlazybind",
629            "noredzone",
630            "noreturn",
631            "norecurse",
632            "willreturn",
633            "returns_twice",
634            "nosync",
635            "nounwind",
636            #[cfg(feature = "llvm-11-or-greater")]
637            "null_pointer_is_valid",
638            "optforfuzzing",
639            "optnone",
640            "optsize",
641            "readnone",
642            "readonly",
643            "writeonly",
644            #[cfg(feature = "llvm-15-or-lower")]
645            "argmemonly",
646            "safestack",
647            "sanitize_address",
648            "sanitize_memory",
649            "sanitize_thread",
650            "sanitize_hwaddress",
651            "sanitize_memtag",
652            "shadowcallstack",
653            "speculative_load_hardening",
654            "speculatable",
655            "ssp",
656            "sspreq",
657            "sspstrong",
658            "strictfp",
659            "uwtable",
660            #[cfg(feature = "llvm-16-or-greater")]
661            "memory"
662        ]
663        .iter()
664        .map(|&attrname| {
665            let cstr = CString::new(attrname).unwrap();
666            let kind = unsafe { LLVMGetEnumAttributeKindForName(cstr.as_ptr(), attrname.len()) };
667            assert_ne!(kind, 0, "Function attribute {:?} not found", attrname);
668            (kind, attrname.into())
669        })
670        .collect();
671        let param_attribute_names = [
672            "zeroext",
673            "signext",
674            "inreg",
675            "byval",
676            #[cfg(feature = "llvm-11-or-greater")]
677            "preallocated",
678            "inalloca",
679            "sret",
680            "align",
681            "noalias",
682            "nocapture",
683            "nofree",
684            "nest",
685            "returned",
686            "nonnull",
687            "dereferenceable",
688            "dereferenceable_or_null",
689            "swiftself",
690            "swifterror",
691            "immarg",
692            #[cfg(feature = "llvm-11-or-greater")]
693            "noundef",
694        ]
695        .iter()
696        .map(|&attrname| {
697            let cstr = CString::new(attrname).unwrap();
698            let kind = unsafe { LLVMGetEnumAttributeKindForName(cstr.as_ptr(), attrname.len()) };
699            assert_ne!(kind, 0, "Parameter attribute {:?} not found", attrname);
700            (kind, attrname.into())
701        })
702        .collect();
703        Self {
704            function_attribute_names,
705            param_attribute_names,
706        }
707    }
708
709    /// Get the string name of an enum-style function attribute, or `None` if
710    /// it's not one that we know about
711    pub fn lookup_function_attr(&self, kind: u32) -> Option<&str> {
712        self.function_attribute_names.get(&kind).map(|s| s.as_str())
713    }
714
715    /// Get the string name of an enum-style parameter attribute, or `None` if
716    /// it's not one that we know about
717    pub fn lookup_param_attr(&self, kind: u32) -> Option<&str> {
718        self.param_attribute_names.get(&kind).map(|s| s.as_str())
719    }
720}
721
722impl FunctionAttribute {
723    pub(crate) fn from_llvm_ref(a: LLVMAttributeRef, attrsdata: &AttributesData) -> Self {
724        if unsafe { LLVMIsEnumAttribute(a) } != 0 {
725            let kind = unsafe { LLVMGetEnumAttributeKind(a) };
726            match attrsdata.lookup_function_attr(kind) {
727                Some("alignstack") => Self::AlignStack(unsafe { LLVMGetEnumAttributeValue(a) }),
728                Some("allocsize") => {
729                    let value = unsafe { LLVMGetEnumAttributeValue(a) };
730                    // looking at the LLVM implementation as of this writing
731                    // (near the top of Attributes.cpp),
732                    // the elt_size value is the upper 32 bits, and the num_elts value
733                    // is the lower 32 bits, or the sentinel -1 for None
734                    let elt_size = (value >> 32) as u32;
735                    let num_elts = match (value & 0xFFFF_FFFF) as u32 {
736                        0xFFFF_FFFF => None,
737                        val => Some(val),
738                    };
739                    Self::AllocSize { elt_size, num_elts }
740                },
741                Some("alwaysinline") => Self::AlwaysInline,
742                Some("builtin") => Self::Builtin,
743                Some("cold") => Self::Cold,
744                Some("convergent") => Self::Convergent,
745                Some("inaccessiblememonly") => Self::InaccessibleMemOnly,
746                Some("inaccessiblemem_or_argmemonly") => Self::InaccessibleMemOrArgMemOnly,
747                Some("inlinehint") => Self::InlineHint,
748                Some("jumptable") => Self::JumpTable,
749                Some("minsize") => Self::MinimizeSize,
750                Some("naked") => Self::Naked,
751                Some("nobuiltin") => Self::NoBuiltin,
752                Some("nocf_check") => Self::NoCFCheck,
753                Some("noduplicate") => Self::NoDuplicate,
754                Some("nofree") => Self::NoFree,
755                Some("noimplicitfloat") => Self::NoImplicitFloat,
756                Some("noinline") => Self::NoInline,
757                #[cfg(feature = "llvm-11-or-greater")]
758                Some("nomerge") => Self::NoMerge,
759                Some("nonlazybind") => Self::NonLazyBind,
760                Some("noredzone") => Self::NoRedZone,
761                Some("noreturn") => Self::NoReturn,
762                Some("norecurse") => Self::NoRecurse,
763                Some("willreturn") => Self::WillReturn,
764                Some("returns_twice") => Self::ReturnsTwice,
765                Some("nosync") => Self::NoSync,
766                Some("nounwind") => Self::NoUnwind,
767                #[cfg(feature = "llvm-11-or-greater")]
768                Some("null_pointer_is_valid") => Self::NullPointerIsValid,
769                Some("optforfuzzing") => Self::OptForFuzzing,
770                Some("optnone") => Self::OptNone,
771                Some("optsize") => Self::OptSize,
772                Some("readnone") => Self::ReadNone,
773                Some("readonly") => Self::ReadOnly,
774                Some("writeonly") => Self::WriteOnly,
775                Some("argmemonly") => Self::ArgMemOnly,
776                Some("safestack") => Self::SafeStack,
777                Some("sanitize_address") => Self::SanitizeAddress,
778                Some("sanitize_memory") => Self::SanitizeMemory,
779                Some("sanitize_thread") => Self::SanitizeThread,
780                Some("sanitize_hwaddress") => Self::SanitizeHWAddress,
781                Some("sanitize_memtag") => Self::SanitizeMemTag,
782                Some("shadowcallstack") => Self::ShadowCallStack,
783                Some("speculative_load_hardening") => Self::SpeculativeLoadHardening,
784                Some("speculatable") => Self::Speculatable,
785                Some("ssp") => Self::StackProtect,
786                Some("sspreq") => Self::StackProtectReq,
787                Some("sspstrong") => Self::StackProtectStrong,
788                Some("strictfp") => Self::StrictFP,
789                Some("uwtable") => Self::UWTable,
790                #[cfg(feature = "llvm-16-or-greater")]
791                Some("memory") => {
792                    let value = unsafe { LLVMGetEnumAttributeValue(a) };
793
794                    // The value is encoded as a bitmask for the possible effects, shifted for each location kind,
795                    // and merged together
796                    // See https://github.com/llvm/llvm-project/blob/7cbf1a2591520c2491aa35339f227775f4d3adf6/llvm/include/llvm/Support/ModRef.h#L63
797                    // for the breakdown of the encoding logic
798
799                    let encoded_argmem           = (value >> 0) & 0b11;
800                    let encoded_inaccessible_mem = (value >> 2) & 0b11;
801                    let encoded_default_mem      = (value >> 4) & 0b11;
802
803                    Self::Memory {
804                        default: MemoryEffect::from_llvm_bits(encoded_default_mem),
805                        argmem: MemoryEffect::from_llvm_bits(encoded_argmem),
806                        inaccessible_mem: MemoryEffect::from_llvm_bits(encoded_inaccessible_mem)
807                    }
808                },
809                Some(s) => panic!("Unhandled value from lookup_function_attr: {:?}", s),
810                None => {
811                    debug!("unknown enum function attr {}", kind);
812                    Self::UnknownAttribute
813                },
814            }
815        } else if unsafe { LLVMIsStringAttribute(a) } != 0 {
816            Self::StringAttribute {
817                kind: unsafe { get_string_attribute_kind(a) },
818                value: unsafe { get_string_attribute_value(a) },
819            }
820        } else {
821            debug!("Encountered an unknown function attribute: neither enum nor string");
822            Self::UnknownAttribute
823        }
824    }
825}
826
827impl ParameterAttribute {
828    pub(crate) fn from_llvm_ref(
829        a: LLVMAttributeRef,
830        attrsdata: &AttributesData,
831        #[cfg(feature = "llvm-12-or-greater")] types: &mut TypesBuilder,
832    ) -> Self {
833        if unsafe { LLVMIsEnumAttribute(a) } != 0 {
834            let kind = unsafe { LLVMGetEnumAttributeKind(a) };
835            match attrsdata.lookup_param_attr(kind) {
836                Some("zeroext") => Self::ZeroExt,
837                Some("signext") => Self::SignExt,
838                Some("inreg") => Self::InReg,
839                #[cfg(feature = "llvm-11-or-lower")]
840                Some("byval") => Self::ByVal,
841                #[cfg(feature = "llvm-11")]
842                Some("preallocated") => Self::Preallocated,
843                #[cfg(feature = "llvm-12-or-lower")]
844                Some("inalloca") => Self::InAlloca,
845                #[cfg(feature = "llvm-11-or-lower")]
846                Some("sret") => Self::SRet,
847                Some("align") => Self::Alignment(unsafe { LLVMGetEnumAttributeValue(a) }),
848                Some("noalias") => Self::NoAlias,
849                Some("nocapture") => Self::NoCapture,
850                Some("nofree") => Self::NoFree,
851                Some("nest") => Self::Nest,
852                Some("returned") => Self::Returned,
853                Some("nonnull") => Self::NonNull,
854                Some("dereferenceable") => {
855                    Self::Dereferenceable(unsafe { LLVMGetEnumAttributeValue(a) })
856                },
857                Some("dereferenceable_or_null") => {
858                    Self::DereferenceableOrNull(unsafe { LLVMGetEnumAttributeValue(a) })
859                },
860                Some("swiftself") => Self::SwiftSelf,
861                Some("swifterror") => Self::SwiftError,
862                Some("immarg") => Self::ImmArg,
863                #[cfg(feature = "llvm-11-or-greater")]
864                Some("noundef") => Self::NoUndef,
865                Some(s) => panic!("Unhandled value from lookup_param_attr: {:?}", s),
866                None => {
867                    debug!("unknown enum param attr {}", kind);
868                    Self::UnknownAttribute
869                },
870            }
871        } else if unsafe { LLVMIsStringAttribute(a) } != 0 {
872            Self::StringAttribute {
873                kind: unsafe { get_string_attribute_kind(a) },
874                value: unsafe { get_string_attribute_value(a) },
875            }
876        } else if Self::is_type_attr(a) {
877            #[cfg(feature = "llvm-11-or-lower")]
878            {
879                debug!("Encountered a type attr, which shouldn't happen on LLVM 11 or lower");
880                Self::UnknownAttribute
881            }
882            #[cfg(feature = "llvm-12-or-greater")]
883            {
884                let kind = unsafe { LLVMGetEnumAttributeKind(a) };
885                let ty = types.type_from_llvm_ref(unsafe { LLVMGetTypeAttributeValue(a) });
886                match attrsdata.lookup_param_attr(kind) {
887                    Some("byval") => Self::ByVal(ty),
888                    Some("preallocated") => Self::Preallocated(ty),
889                    #[cfg(feature = "llvm-13-or-greater")]
890                    Some("inalloca") => Self::InAlloca(ty),
891                    Some("sret") => Self::SRet(ty),
892                    Some(s) => panic!("Unhandled value from lookup_param_attr: {:?}", s),
893                    None => {
894                        debug!("unknown type param attr {}", kind);
895                        Self::UnknownTypeAttribute(ty)
896                    },
897                }
898            }
899        } else {
900            debug!("Encountered an unknown parameter attribute: neither enum, string, nor type");
901            Self::UnknownAttribute
902        }
903    }
904
905    #[cfg(feature = "llvm-11-or-lower")]
906    fn is_type_attr(_a: LLVMAttributeRef) -> bool {
907        false
908    }
909    #[cfg(feature = "llvm-12-or-greater")]
910    fn is_type_attr(a: LLVMAttributeRef) -> bool {
911        unsafe { LLVMIsTypeAttribute(a) != 0 }
912    }
913}