1use std::convert::{TryFrom, TryInto};
4
5use hashbrown::HashMap;
6use llvm_support::bitcodes::{AttributeCode, IrBlockId};
7use llvm_support::{AttributeId, AttributeKind, MaybeAlign};
8use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
9use thiserror::Error;
10
11use crate::block::IrBlock;
12use crate::map::{MapError, PartialMapCtx};
13use crate::unroll::{UnrolledBlock, UnrolledRecord};
14
15#[derive(Debug, Error)]
17pub enum AttributeError {
18 #[error("unknown attribute code")]
20 UnknownAttributeCode(#[from] TryFromPrimitiveError<AttributeCode>),
21
22 #[error("unknown attribute kind")]
24 UnknownAttributeKind(#[from] TryFromPrimitiveError<AttributeKind>),
25
26 #[error("wrong block for code: {0:?}")]
28 WrongBlock(AttributeCode),
29
30 #[error("attribute structure too short")]
32 TooShort,
33
34 #[error("bad attribute string")]
36 BadString,
37
38 #[error("unknown attribute ID")]
40 UnknownAttributeId(#[from] TryFromPrimitiveError<AttributeId>),
41
42 #[error("malformed attribute (format doesn't match ID): {0}: {1:?}")]
44 AttributeMalformed(&'static str, AttributeId),
45
46 #[error("FIXME: unsupported integer attribute: {0:?}")]
48 IntAttributeUnsupported(AttributeId),
49
50 #[error("nonexistent attribute group: {0}")]
52 BadAttributeGroup(u32),
53
54 #[error("attribute group record for {0:?} too short ({1} < 3)")]
56 GroupTooShort(AttributeCode, usize),
57
58 #[error("under/overconsumed fields in attribute group record ({0} fields, {1} consumed)")]
60 GroupSizeMismatch(usize, usize),
61
62 #[error("mapping error in string table")]
64 Map(#[from] MapError),
65}
66
67#[non_exhaustive]
69#[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive)]
70#[repr(u64)]
71pub enum EnumAttribute {
72 AlwaysInline = AttributeId::AlwaysInline as u64,
74 ByVal = AttributeId::ByVal as u64,
76 InlineHint = AttributeId::InlineHint as u64,
78 InReg = AttributeId::InReg as u64,
80 MinSize = AttributeId::MinSize as u64,
82 Naked = AttributeId::Naked as u64,
84 Nest = AttributeId::Nest as u64,
86 NoAlias = AttributeId::NoAlias as u64,
88 NoBuiltin = AttributeId::NoBuiltin as u64,
90 NoCapture = AttributeId::NoCapture as u64,
92 NoDuplicate = AttributeId::NoDuplicate as u64,
94 NoImplicitFloat = AttributeId::NoImplicitFloat as u64,
96 NoInline = AttributeId::NoInline as u64,
98 NonLazyBind = AttributeId::NonLazyBind as u64,
100 NoRedZone = AttributeId::NoRedZone as u64,
102 NoReturn = AttributeId::NoReturn as u64,
104 NoUnwind = AttributeId::NoUnwind as u64,
106 OptimizeForSize = AttributeId::OptimizeForSize as u64,
108 ReadNone = AttributeId::ReadNone as u64,
110 ReadOnly = AttributeId::ReadOnly as u64,
112 Returned = AttributeId::Returned as u64,
114 ReturnsTwice = AttributeId::ReturnsTwice as u64,
116 SExt = AttributeId::SExt as u64,
118 StackProtect = AttributeId::StackProtect as u64,
120 StackProtectReq = AttributeId::StackProtectReq as u64,
122 StackProtectStrong = AttributeId::StackProtectStrong as u64,
124 StructRet = AttributeId::StructRet as u64,
126 SanitizeAddress = AttributeId::SanitizeAddress as u64,
128 SanitizeThread = AttributeId::SanitizeThread as u64,
130 SanitizeMemory = AttributeId::SanitizeMemory as u64,
132 UwTable = AttributeId::UwTable as u64,
134 ZExt = AttributeId::ZExt as u64,
136 Builtin = AttributeId::Builtin as u64,
138 Cold = AttributeId::Cold as u64,
140 OptimizeNone = AttributeId::OptimizeNone as u64,
142 InAlloca = AttributeId::InAlloca as u64,
144 NonNull = AttributeId::NonNull as u64,
146 JumpTable = AttributeId::JumpTable as u64,
148 Convergent = AttributeId::Convergent as u64,
150 SafeStack = AttributeId::SafeStack as u64,
152 ArgMemOnly = AttributeId::ArgMemOnly as u64,
154 SwiftSelf = AttributeId::SwiftSelf as u64,
156 SwiftError = AttributeId::SwiftError as u64,
158 NoRecurse = AttributeId::NoRecurse as u64,
160 InaccessiblememOnly = AttributeId::InaccessiblememOnly as u64,
162 InaccessiblememOrArgmemonly = AttributeId::InaccessiblememOrArgmemonly as u64,
164 WriteOnly = AttributeId::WriteOnly as u64,
166 Speculatable = AttributeId::Speculatable as u64,
168 StrictFp = AttributeId::StrictFp as u64,
170 SanitizeHwAddress = AttributeId::SanitizeHwAddress as u64,
172 NoCfCheck = AttributeId::NoCfCheck as u64,
174 OptForFuzzing = AttributeId::OptForFuzzing as u64,
176 Shadowcallstack = AttributeId::Shadowcallstack as u64,
178 SpeculativeLoadHardening = AttributeId::SpeculativeLoadHardening as u64,
180 ImmArg = AttributeId::ImmArg as u64,
182 WillReturn = AttributeId::WillReturn as u64,
184 NoFree = AttributeId::NoFree as u64,
186 NoSync = AttributeId::NoSync as u64,
188 SanitizeMemtag = AttributeId::SanitizeMemtag as u64,
190 Preallocated = AttributeId::Preallocated as u64,
192 NoMerge = AttributeId::NoMerge as u64,
194 NullPointerIsValid = AttributeId::NullPointerIsValid as u64,
196 NoUndef = AttributeId::NoUndef as u64,
198 ByRef = AttributeId::ByRef as u64,
200 MustProgress = AttributeId::MustProgress as u64,
202 NoCallback = AttributeId::NoCallback as u64,
204 Hot = AttributeId::Hot as u64,
206 NoProfile = AttributeId::NoProfile as u64,
208 SwiftAsync = AttributeId::SwiftAsync as u64,
210 NoSanitizeCoverage = AttributeId::NoSanitizeCoverage as u64,
212 ElementType = AttributeId::ElementType as u64,
214 DisableSanitizerInstrumentation = AttributeId::DisableSanitizerInstrumentation as u64,
216}
217
218impl TryFrom<AttributeId> for EnumAttribute {
219 type Error = AttributeError;
220
221 fn try_from(value: AttributeId) -> Result<Self, Self::Error> {
222 (value as u64)
223 .try_into()
224 .map_err(|_| AttributeError::AttributeMalformed("non-enum attribute ID given", value))
225 }
226}
227
228#[non_exhaustive]
230#[derive(Copy, Clone, Debug, PartialEq)]
231pub enum IntAttribute {
232 Alignment(MaybeAlign),
234 StackAlignment(MaybeAlign),
236 Dereferenceable(u64),
238 DereferenceableOrNull(u64),
240 AllocSize(u32, Option<u32>),
242 VScaleRange(u32, u32),
244}
245
246impl TryFrom<(AttributeId, u64)> for IntAttribute {
247 type Error = AttributeError;
248
249 fn try_from((key, value): (AttributeId, u64)) -> Result<Self, Self::Error> {
250 if EnumAttribute::try_from(key).is_err() {
253 return Err(AttributeError::AttributeMalformed(
254 "expected int attribute, but given enum ID",
255 key,
256 ));
257 }
258
259 Ok(match key {
260 AttributeId::Alignment => {
261 let value = u8::try_from(value).map_err(|_| {
262 AttributeError::AttributeMalformed(
263 "attribute value too large (invalid alignment)",
264 key,
265 )
266 })?;
267
268 IntAttribute::Alignment(
269 MaybeAlign::try_from(value).map_err(|_| {
270 AttributeError::AttributeMalformed("invalid alignment", key)
271 })?,
272 )
273 }
274 AttributeId::StackAlignment => {
275 let value = u8::try_from(value).map_err(|_| {
276 AttributeError::AttributeMalformed(
277 "attribute value too large (invalid alignment)",
278 key,
279 )
280 })?;
281
282 IntAttribute::StackAlignment(
283 MaybeAlign::try_from(value).map_err(|_| {
284 AttributeError::AttributeMalformed("invalid alignment", key)
285 })?,
286 )
287 }
288 AttributeId::Dereferenceable => IntAttribute::Dereferenceable(value),
289 AttributeId::DereferenceableOrNull => IntAttribute::DereferenceableOrNull(value),
290 AttributeId::AllocSize => {
291 if value == 0 {
292 return Err(AttributeError::AttributeMalformed(
293 "allocasize argument invalid: cannot be (0, 0)",
294 key,
295 ));
296 }
297
298 let elt_size = (value >> 32) as u32;
302 let num_elts = match value as u32 {
303 u32::MAX => None,
304 num_elts => Some(num_elts),
305 };
306
307 IntAttribute::AllocSize(elt_size, num_elts)
308 }
309 AttributeId::VScaleRange => {
310 let min = (value >> 32) as u32;
311 let max = match value as u32 {
312 0 => min,
313 max => max,
314 };
315
316 IntAttribute::VScaleRange(max, min)
317 }
318 o => return Err(AttributeError::IntAttributeUnsupported(o)),
319 })
320 }
321}
322
323#[non_exhaustive]
325#[derive(Clone, Debug, PartialEq)]
326pub enum Attribute {
327 Enum(EnumAttribute),
329 Int(IntAttribute),
331 Str(String),
333 StrKeyValue(String, String),
335}
336
337impl Attribute {
338 fn from_record(start: usize, record: &UnrolledRecord) -> Result<(usize, Self), AttributeError> {
341 let mut fieldcount = 0;
342
343 macro_rules! next {
348 () => {
349 if let Some(field) = record.fields().get(start + fieldcount) {
350 fieldcount += 1;
351 Ok(*field)
352 } else {
353 Err(AttributeError::TooShort)
354 }
355 };
356 }
357
358 macro_rules! take_string {
359 () => {{
361 let str_bytes = record.fields()[start + fieldcount..]
362 .iter()
363 .take_while(|f| **f != 0)
364 .map(|f| u8::try_from(*f))
365 .collect::<Result<Vec<_>, _>>()
366 .map_err(|_| AttributeError::BadString)?;
367
368 if str_bytes.is_empty() {
369 Err(AttributeError::BadString)
370 } else {
371 let result =
372 String::from_utf8(str_bytes).map_err(|_| AttributeError::BadString)?;
373
374 fieldcount += result.as_bytes().len() + 1;
376
377 Ok(result)
378 }
379 }};
380 }
381
382 let kind = AttributeKind::try_from(next!()?)?;
387 match kind {
388 AttributeKind::Enum => {
389 let key = AttributeId::try_from(next!()?)?;
391 Ok((fieldcount, Attribute::Enum(key.try_into()?)))
392 }
393 AttributeKind::IntKeyValue => {
394 let key = AttributeId::try_from(next!()?)?;
396 let value = next!()?;
397
398 Ok((fieldcount, Attribute::Int(TryInto::try_into((key, value))?)))
399 }
400 AttributeKind::StrKey => {
401 let key = take_string!()?;
403
404 Ok((fieldcount, Attribute::Str(key)))
405 }
406 AttributeKind::StrKeyValue => {
407 let key = take_string!()?;
409 let value = take_string!()?;
410
411 Ok((fieldcount, Attribute::StrKeyValue(key, value)))
412 }
413 }
414 }
415}
416
417#[derive(Debug)]
419pub struct AttributeEntry(Vec<AttributeGroup>);
420
421#[derive(Debug, Default)]
423pub struct Attributes(Vec<AttributeEntry>);
424
425impl Attributes {
426 pub(crate) fn get(&self, id: u64) -> Option<&AttributeEntry> {
427 self.0.get(id as usize)
428 }
429}
430
431impl IrBlock for Attributes {
432 type Error = AttributeError;
433
434 const BLOCK_ID: IrBlockId = IrBlockId::ParamAttr;
435
436 fn try_map_inner(block: &UnrolledBlock, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
437 let mut entries = vec![];
438
439 for record in block.records() {
440 let code = AttributeCode::try_from(record.code()).map_err(AttributeError::from)?;
441
442 match code {
443 AttributeCode::EntryOld => {
444 unimplemented!();
445 }
446 AttributeCode::Entry => {
447 let mut groups = vec![];
448 for group_id in record.fields() {
449 let group_id = *group_id as u32;
450 log::debug!("group id: {}", group_id);
451 groups.push(
452 ctx.attribute_groups()
453 .get(group_id)
454 .ok_or(AttributeError::BadAttributeGroup(group_id))?
455 .clone(),
456 );
457 }
458 entries.push(AttributeEntry(groups));
459 }
460 AttributeCode::GroupCodeEntry => {
461 return Err(AttributeError::WrongBlock(code));
463 }
464 }
465 }
466
467 Ok(Attributes(entries))
468 }
469}
470
471#[derive(Clone, Copy, Debug)]
474pub enum AttributeGroupDisposition {
475 Return,
477 Parameter(u32),
479 Function,
481}
482
483impl From<u32> for AttributeGroupDisposition {
484 fn from(value: u32) -> Self {
485 match value {
486 u32::MAX => Self::Function,
487 0 => Self::Return,
488 _ => Self::Parameter(value),
489 }
490 }
491}
492
493#[derive(Clone, Debug)]
495pub struct AttributeGroup {
496 pub disposition: AttributeGroupDisposition,
498 pub attributes: Vec<Attribute>,
500}
501
502#[derive(Debug, Default)]
504pub struct AttributeGroups(HashMap<u32, AttributeGroup>);
505
506impl AttributeGroups {
507 pub(crate) fn get(&self, group_id: u32) -> Option<&AttributeGroup> {
508 self.0.get(&group_id)
509 }
510}
511
512impl IrBlock for AttributeGroups {
513 type Error = AttributeError;
514
515 const BLOCK_ID: IrBlockId = IrBlockId::ParamAttrGroup;
516
517 fn try_map_inner(block: &UnrolledBlock, _ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
518 let mut groups = HashMap::new();
519
520 for record in block.records() {
521 let code = AttributeCode::try_from(record.code()).map_err(AttributeError::from)?;
522
523 if code != AttributeCode::GroupCodeEntry {
524 return Err(AttributeError::WrongBlock(code));
525 }
526
527 if record.fields().len() < 3 {
530 return Err(AttributeError::GroupTooShort(code, record.fields().len()));
531 }
532
533 let group_id = record.fields()[0] as u32;
535 let disposition: AttributeGroupDisposition = (record.fields()[1] as u32).into();
536
537 let mut fieldidx = 2;
541 let mut attributes = vec![];
542 while fieldidx < record.fields().len() {
543 let (count, attr) = Attribute::from_record(fieldidx, record)?;
544 attributes.push(attr);
545 fieldidx += count;
546 }
547
548 if fieldidx != record.fields().len() {
550 return Err(AttributeError::GroupSizeMismatch(
551 fieldidx,
552 record.fields().len(),
553 ));
554 }
555
556 groups.insert(
557 group_id,
558 AttributeGroup {
559 disposition,
560 attributes,
561 },
562 );
563 }
564
565 Ok(AttributeGroups(groups))
566 }
567}