├── .gitignore ├── .rustfmt.toml ├── tests ├── fixtures │ ├── llvm19.bc │ ├── simple.bc │ └── serialized.dia └── test_bitcode_reader.rs ├── Cargo.toml ├── src ├── lib.rs ├── visitor.rs ├── bitstream.rs ├── bits.rs ├── schema │ ├── values.rs │ └── blocks.rs ├── read.rs └── bitcode.rs ├── .github └── workflows │ └── CI.yml ├── README.md ├── LICENSE └── examples └── llvm_bca.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2024" 2 | -------------------------------------------------------------------------------- /tests/fixtures/llvm19.bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/messense/llvm-bitcode-rs/HEAD/tests/fixtures/llvm19.bc -------------------------------------------------------------------------------- /tests/fixtures/simple.bc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/messense/llvm-bitcode-rs/HEAD/tests/fixtures/simple.bc -------------------------------------------------------------------------------- /tests/fixtures/serialized.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/messense/llvm-bitcode-rs/HEAD/tests/fixtures/serialized.dia -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "llvm-bitcode" 3 | version = "0.2.0" 4 | authors = ["messense ", "Kornel "] 5 | description = "LLVM Bitcode parser in Rust" 6 | edition = "2024" 7 | keywords = ["llvm", "bitcode", "bc", "llvm-ir"] 8 | categories = ["parser-implementations"] 9 | readme = "README.md" 10 | license = "MIT AND Apache-2.0 WITH LLVM-exception" 11 | repository = "https://github.com/messense/llvm-bitcode-rs.git" 12 | 13 | [dependencies] 14 | num_enum = "0.7.2" 15 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! LLVM Bitcode parser in Rust 2 | 3 | /// Bitcode definitions 4 | pub mod bitcode; 5 | mod bits; 6 | /// Bitstream definitions 7 | pub mod bitstream; 8 | /// Bitstream reader 9 | pub mod read; 10 | /// Bitstream visitor 11 | pub mod visitor; 12 | 13 | /// Definitions from `LLVMBitCodes.h` 14 | pub mod schema { 15 | /// IDs of blocks and records inside the blocks 16 | pub mod blocks; 17 | /// Flags and values found in records 18 | pub mod values; 19 | } 20 | 21 | pub use self::bitcode::Bitcode; 22 | pub use self::bits::Cursor; 23 | pub use self::read::BitStreamReader; 24 | pub use self::visitor::BitStreamVisitor; 25 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | check: 7 | name: Checks 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: dtolnay/rust-toolchain@stable 12 | with: 13 | components: rustfmt 14 | - name: cargo fmt 15 | run: cargo fmt --all -- --check 16 | - name: cargo check 17 | run: cargo check 18 | 19 | test: 20 | name: Test Suite 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest, macos-latest, windows-latest] 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: dtolnay/rust-toolchain@stable 28 | - name: cargo test 29 | run: cargo test 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # llvm-bitcode-rs 2 | 3 | [![GitHub Actions](https://github.com/messense/llvm-bitcode-rs/workflows/CI/badge.svg)](https://github.com/messense/llvm-bitcode-rs/actions?query=workflow%3ACI) 4 | [![codecov](https://codecov.io/gh/messense/llvm-bitcode-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/messense/llvm-bitcode-rs) 5 | [![Crates.io](https://img.shields.io/crates/v/llvm-bitcode.svg)](https://crates.io/crates/llvm-bitcode) 6 | [![docs.rs](https://docs.rs/llvm-bitcode/badge.svg)](https://docs.rs/llvm-bitcode/) 7 | 8 | LLVM Bitcode parser in Rust. Supports iterating blocks and fields of records. 9 | 10 | ## Installation 11 | 12 | `cargo add llvm-bitcode` or add it to your `Cargo.toml`: 13 | 14 | ```toml 15 | [dependencies] 16 | llvm-bitcode = "0.2" 17 | ``` 18 | 19 | ## Usage 20 | 21 | See [examples](https://github.com/messense/llvm-bitcode-rs/tree/HEAD/examples) and [API docs](https://docs.rs/llvm-bitcode). 22 | 23 | ## License 24 | 25 | This work is released under the MIT license. A copy of the license is provided in the [LICENSE](./LICENSE) file. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 messense 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::BitStreamReader; 2 | use crate::bitcode::{BitcodeElement, Block, Record, Signature}; 3 | 4 | /// A visitor which receives callbacks while reading a bitstream. 5 | pub trait BitStreamVisitor { 6 | /// Validate a bitstream's signature or "magic number". 7 | fn validate(&self, _signature: Signature) -> bool { 8 | true 9 | } 10 | 11 | /// Called when a new block is encountered. Return `true` to enter the block 12 | /// and read its contents, or `false` to skip it. 13 | fn should_enter_block(&mut self, block_id: u32) -> bool; 14 | 15 | /// Called when a block is exited. 16 | fn did_exit_block(&mut self, block_id: u32); 17 | 18 | /// Called whenever a record is encountered. 19 | fn visit(&mut self, block_id: u32, record: Record); 20 | } 21 | 22 | /// A basic visitor that collects all the blocks and records in a stream. 23 | pub struct CollectingVisitor { 24 | stack: Vec<(u32, Vec)>, 25 | } 26 | 27 | impl CollectingVisitor { 28 | #[must_use] 29 | pub fn new() -> Self { 30 | Self { 31 | stack: vec![(BitStreamReader::TOP_LEVEL_BLOCK_ID, Vec::new())], 32 | } 33 | } 34 | 35 | #[must_use] 36 | pub fn finalize_top_level_elements(mut self) -> Vec { 37 | assert_eq!(self.stack.len(), 1); 38 | self.stack.pop().unwrap().1 39 | } 40 | } 41 | 42 | impl BitStreamVisitor for CollectingVisitor { 43 | fn should_enter_block(&mut self, id: u32) -> bool { 44 | self.stack.push((id, Vec::new())); 45 | true 46 | } 47 | 48 | fn did_exit_block(&mut self, block_id: u32) { 49 | if let Some((id, elements)) = self.stack.pop() { 50 | assert_eq!(id, block_id); 51 | let block = Block { id, elements }; 52 | let last = self.stack.last_mut().unwrap(); 53 | last.1.push(BitcodeElement::Block(block)); 54 | } 55 | } 56 | 57 | fn visit(&mut self, _block_id: u32, record: Record) { 58 | let last = self.stack.last_mut().unwrap(); 59 | last.1.push(BitcodeElement::Record(record)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/bitstream.rs: -------------------------------------------------------------------------------- 1 | use num_enum::TryFromPrimitive; 2 | 3 | /// An `Abbreviation` represents the encoding definition for a user-defined 4 | /// record. An `Abbreviation` is the primary form of compression available in 5 | /// a bitstream file. 6 | #[derive(Debug, Clone)] 7 | pub struct Abbreviation { 8 | /// All operands except the last that may be an array/string/blob 9 | pub fields: Vec, 10 | pub payload: Option, 11 | pub id: u32, 12 | } 13 | 14 | /// Abbreviation operand 15 | #[derive(Debug, Copy, Clone)] 16 | pub enum ScalarOperand { 17 | /// A literal value (emitted as a VBR8 field) 18 | Literal(u64), 19 | /// A fixed-width field 20 | Fixed(u8), 21 | /// A VBR-encoded value with the provided chunk width 22 | Vbr(u8), 23 | /// A char6-encoded ASCII character 24 | Char6, 25 | } 26 | 27 | /// Abbreviation operand 28 | #[derive(Debug, Copy, Clone)] 29 | pub enum PayloadOperand { 30 | /// Emitted as a vbr6 value, padded to a 32-bit boundary and then 31 | /// an array of 8-bit objects 32 | Blob, 33 | /// An array of values. This expects another operand encoded 34 | /// directly after indicating the element type. 35 | /// The array will begin with a vbr6 value indicating the length of 36 | /// the following array. 37 | Array(ScalarOperand), 38 | } 39 | 40 | #[derive(Debug, Copy, Clone)] 41 | pub enum Operand { 42 | Scalar(ScalarOperand), 43 | Payload(PayloadOperand), 44 | } 45 | 46 | impl Operand { 47 | /// Whether this case is payload 48 | #[must_use] 49 | pub fn is_payload(&self) -> bool { 50 | matches!(self, Self::Payload(_)) 51 | } 52 | 53 | /// Whether this case is the `literal` case 54 | #[must_use] 55 | pub fn is_literal(&self) -> bool { 56 | matches!(self, Self::Scalar(ScalarOperand::Literal(_))) 57 | } 58 | 59 | #[must_use] 60 | pub fn is_array(&self) -> bool { 61 | matches!(self, Self::Payload(PayloadOperand::Array(_))) 62 | } 63 | 64 | #[must_use] 65 | pub fn is_blob(&self) -> bool { 66 | matches!(self, Self::Payload(PayloadOperand::Blob)) 67 | } 68 | 69 | /// The `llvm::BitCodeAbbrevOp::Encoding` value this 70 | /// enum case represents. 71 | /// - note: Must match the encoding in 72 | /// 73 | #[must_use] 74 | pub fn encoded_kind(&self) -> u8 { 75 | match self { 76 | Self::Scalar(ScalarOperand::Literal(_)) => 0, 77 | Self::Scalar(ScalarOperand::Fixed(_)) => 1, 78 | Self::Scalar(ScalarOperand::Vbr(_)) => 2, 79 | Self::Payload(PayloadOperand::Array(_)) => 3, 80 | Self::Scalar(ScalarOperand::Char6) => 4, 81 | Self::Payload(PayloadOperand::Blob) => 5, 82 | } 83 | } 84 | } 85 | 86 | /// A `BlockInfoCode` enumerates the bits that occur in the metadata for 87 | /// a block or record. Of these bits, only `SetBid` is required. If 88 | /// a name is given to a block or record with `BlockName` or 89 | /// `SetRecordName`, debugging tools like `llvm-bcanalyzer` can be used to 90 | /// introspect the structure of blocks and records in the bitstream file. 91 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 92 | #[repr(u8)] 93 | pub enum BlockInfoCode { 94 | /// Indicates which block ID is being described. 95 | SetBid = 1, 96 | /// An optional element that records which bytes of the record are the 97 | /// name of the block. 98 | BlockName = 2, 99 | /// An optional element that records the record ID number and the bytes 100 | /// for the name of the corresponding record. 101 | SetRecordName = 3, 102 | } 103 | 104 | /// An abbreviation id is a fixed-width field that occurs at the start of 105 | /// abbreviated data records and inside block definitions. 106 | /// 107 | /// Bitstream reserves 4 special abbreviation IDs for its own bookkeeping. 108 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 109 | #[repr(u32)] 110 | pub enum BuiltinAbbreviationId { 111 | /// Marks the end of the current block. 112 | EndBlock = 0, 113 | /// Marks the beginning of a new block. 114 | EnterSubBlock = 1, 115 | /// Marks the definition of a new abbreviation. 116 | DefineAbbreviation = 2, 117 | /// Marks the definition of a new unabbreviated record. 118 | UnabbreviatedRecord = 3, 119 | } 120 | -------------------------------------------------------------------------------- /tests/test_bitcode_reader.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use llvm_bitcode::bitcode::{BitcodeElement, Payload, Record}; 4 | use llvm_bitcode::{BitStreamVisitor, Bitcode}; 5 | 6 | #[test] 7 | fn test_bitcode() { 8 | let data = fs::read("tests/fixtures/serialized.dia").unwrap(); 9 | let _bitcode = Bitcode::new(&data).unwrap(); 10 | 11 | let data = fs::read("tests/fixtures/simple.bc").unwrap(); 12 | let bitcode = Bitcode::new(&data).unwrap(); 13 | let module_block = bitcode 14 | .elements 15 | .iter() 16 | .find(|ele| match ele { 17 | BitcodeElement::Record(_) => false, 18 | BitcodeElement::Block(block) => block.id == 8, 19 | }) 20 | .unwrap(); 21 | let target_triple_record = module_block 22 | .as_block() 23 | .unwrap() 24 | .elements 25 | .iter() 26 | .find(|ele| match ele { 27 | BitcodeElement::Record(record) => record.id == 2, 28 | BitcodeElement::Block(_) => false, 29 | }) 30 | .unwrap() 31 | .as_record() 32 | .unwrap(); 33 | let fields: Vec = target_triple_record 34 | .fields() 35 | .iter() 36 | .map(|x| *x as u8) 37 | .collect(); 38 | let target_triple = std::str::from_utf8(&fields).unwrap(); 39 | assert_eq!(target_triple, "x86_64-apple-macosx11.0.0"); 40 | } 41 | 42 | #[test] 43 | fn test_bitstream_reader() { 44 | struct LoggingVisitor(Vec); 45 | 46 | impl BitStreamVisitor for LoggingVisitor { 47 | fn should_enter_block(&mut self, id: u32) -> bool { 48 | self.0.push(format!("entering block: {id}")); 49 | true 50 | } 51 | 52 | fn did_exit_block(&mut self, id: u32) { 53 | self.0.push(format!("exiting block: {id}")); 54 | } 55 | 56 | fn visit(&mut self, _block_id: u32, mut record: Record) { 57 | let payload = if let Some(payload) = record.take_payload() { 58 | match payload { 59 | Payload::Array(ele) => { 60 | format!("array({} elements)", ele.len()) 61 | } 62 | Payload::Blob(blob) => format!("blob({} bytes)", blob.len()), 63 | Payload::Char6String(s) => s, 64 | } 65 | } else { 66 | "none".to_string() 67 | }; 68 | let id = record.id; 69 | self.0.push(format!( 70 | "Record (id: {}, fields: {:?}, payload: {}", 71 | id, 72 | record.fields(), 73 | payload 74 | )); 75 | } 76 | } 77 | 78 | let data = fs::read("tests/fixtures/serialized.dia").unwrap(); 79 | let mut visitor = LoggingVisitor(Vec::new()); 80 | Bitcode::read(&data, &mut visitor).unwrap(); 81 | assert_eq!( 82 | visitor.0, 83 | vec![ 84 | "entering block: 8", 85 | "Record (id: 1, fields: [1], payload: none", 86 | "exiting block: 8", 87 | "entering block: 9", 88 | "Record (id: 6, fields: [1, 0, 0, 100], payload: blob(100 bytes)", 89 | "Record (id: 2, fields: [3, 1, 53, 28, 0, 0, 0, 34], payload: blob(34 bytes)", 90 | "exiting block: 9", 91 | "entering block: 9", 92 | "Record (id: 2, fields: [3, 1, 53, 28, 0, 0, 0, 59], payload: blob(59 bytes)", 93 | "exiting block: 9", 94 | "entering block: 9", 95 | "Record (id: 2, fields: [3, 1, 113, 1, 0, 0, 0, 38], payload: blob(38 bytes)", 96 | "exiting block: 9", 97 | "entering block: 9", 98 | "Record (id: 2, fields: [3, 1, 113, 1, 0, 0, 0, 20], payload: blob(20 bytes)", 99 | "exiting block: 9", 100 | "entering block: 9", 101 | "Record (id: 6, fields: [2, 0, 0, 98], payload: blob(98 bytes)", 102 | "Record (id: 2, fields: [3, 2, 21, 69, 0, 0, 0, 34], payload: blob(34 bytes)", 103 | "exiting block: 9", 104 | "entering block: 9", 105 | "Record (id: 2, fields: [3, 2, 21, 69, 0, 0, 0, 22], payload: blob(22 bytes)", 106 | "Record (id: 7, fields: [2, 21, 69, 0, 2, 21, 69, 0, 1], payload: blob(1 bytes)", 107 | "exiting block: 9", 108 | "entering block: 9", 109 | "Record (id: 2, fields: [3, 2, 21, 69, 0, 0, 0, 42], payload: blob(42 bytes)", 110 | "exiting block: 9", 111 | "entering block: 9", 112 | "Record (id: 2, fields: [3, 2, 21, 69, 0, 0, 0, 22], payload: blob(22 bytes)", 113 | "Record (id: 7, fields: [2, 21, 69, 0, 2, 21, 69, 0, 1], payload: blob(1 bytes)", 114 | "exiting block: 9", 115 | "entering block: 9", 116 | "Record (id: 6, fields: [3, 0, 0, 84], payload: blob(84 bytes)", 117 | "Record (id: 2, fields: [3, 3, 38, 28, 0, 0, 0, 34], payload: blob(34 bytes)", 118 | "exiting block: 9", 119 | "entering block: 9", 120 | "Record (id: 2, fields: [3, 3, 38, 28, 0, 0, 0, 59], payload: blob(59 bytes)", 121 | "exiting block: 9", 122 | "entering block: 9", 123 | "Record (id: 2, fields: [3, 3, 66, 1, 0, 0, 0, 38], payload: blob(38 bytes)", 124 | "exiting block: 9", 125 | "entering block: 9", 126 | "Record (id: 2, fields: [3, 3, 66, 1, 0, 0, 0, 20], payload: blob(20 bytes)", 127 | "exiting block: 9", 128 | "entering block: 9", 129 | "Record (id: 6, fields: [4, 0, 0, 93], payload: blob(93 bytes)", 130 | "Record (id: 2, fields: [3, 4, 15, 46, 0, 0, 0, 40], payload: blob(40 bytes)", 131 | "exiting block: 9", 132 | "entering block: 9", 133 | "Record (id: 2, fields: [3, 4, 15, 46, 0, 0, 0, 22], payload: blob(22 bytes)", 134 | "Record (id: 7, fields: [4, 15, 46, 0, 4, 15, 46, 0, 1], payload: blob(1 bytes)", 135 | "exiting block: 9", 136 | "entering block: 9", 137 | "Record (id: 2, fields: [3, 4, 15, 46, 0, 0, 0, 42], payload: blob(42 bytes)", 138 | "exiting block: 9", 139 | "entering block: 9", 140 | "Record (id: 2, fields: [3, 4, 15, 46, 0, 0, 0, 22], payload: blob(22 bytes)", 141 | "Record (id: 7, fields: [4, 15, 46, 0, 4, 15, 46, 0, 1], payload: blob(1 bytes)", 142 | "exiting block: 9", 143 | "entering block: 9", 144 | "Record (id: 6, fields: [5, 0, 0, 72], payload: blob(72 bytes)", 145 | "Record (id: 2, fields: [3, 5, 34, 13, 0, 0, 0, 44], payload: blob(44 bytes)", 146 | "Record (id: 3, fields: [5, 34, 13, 0, 5, 34, 26, 0], payload: none", 147 | "exiting block: 9", 148 | ] 149 | ); 150 | } 151 | 152 | #[test] 153 | fn test_block_skip() { 154 | struct No15(usize); 155 | 156 | impl BitStreamVisitor for No15 { 157 | fn should_enter_block(&mut self, id: u32) -> bool { 158 | id != 15 159 | } 160 | 161 | fn did_exit_block(&mut self, id: u32) { 162 | assert_ne!(15, id); 163 | } 164 | 165 | fn visit(&mut self, block_id: u32, _: Record) { 166 | assert_ne!(15, block_id); 167 | self.0 += 1; 168 | } 169 | } 170 | 171 | let data = fs::read("tests/fixtures/llvm19.bc").unwrap(); 172 | let mut test = No15(0); 173 | Bitcode::read(&data, &mut test).unwrap(); 174 | assert_eq!(179, test.0); 175 | } 176 | -------------------------------------------------------------------------------- /src/bits.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub enum Error { 5 | BufferOverflow, 6 | VbrOverflow, 7 | Alignment, 8 | } 9 | 10 | impl fmt::Display for Error { 11 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 12 | f.write_str(match self { 13 | Self::BufferOverflow => "buffer overflow", 14 | Self::VbrOverflow => "vbr overflow", 15 | Self::Alignment => "bad alignment", 16 | }) 17 | } 18 | } 19 | 20 | impl error::Error for Error {} 21 | 22 | #[derive(Clone)] 23 | pub struct Cursor<'input> { 24 | buffer: &'input [u8], 25 | offset: usize, 26 | } 27 | 28 | impl<'input> Cursor<'input> { 29 | #[must_use] 30 | pub fn new(buffer: &'input [u8]) -> Self { 31 | Self { buffer, offset: 0 } 32 | } 33 | 34 | #[must_use] 35 | pub fn is_at_end(&self) -> bool { 36 | self.offset >= (self.buffer.len() << 3) 37 | } 38 | 39 | #[inline] 40 | pub fn peek(&self, bits: u8) -> Result { 41 | self.read_bits(bits).ok_or(Error::BufferOverflow) 42 | } 43 | 44 | #[inline] 45 | pub fn read(&mut self, bits: u8) -> Result { 46 | if bits < 1 || bits > 64 { 47 | return Err(Error::VbrOverflow); 48 | } 49 | let res = self.peek(bits)?; 50 | self.offset += bits as usize; 51 | Ok(res) 52 | } 53 | 54 | fn read_bits(&self, count: u8) -> Option { 55 | let upper_bound = self.offset + count as usize; 56 | let top_byte_index = upper_bound >> 3; 57 | let mut res = 0; 58 | if upper_bound & 7 != 0 { 59 | let mask = (1u8 << (upper_bound & 7) as u8) - 1; 60 | res = u64::from(*self.buffer.get(top_byte_index)? & mask); 61 | } 62 | for i in ((self.offset >> 3)..(upper_bound >> 3)).rev() { 63 | res <<= 8; 64 | res |= u64::from(*self.buffer.get(i)?); 65 | } 66 | if self.offset & 7 != 0 { 67 | res >>= self.offset as u64 & 7; 68 | } 69 | Some(res) 70 | } 71 | 72 | pub fn read_bytes(&mut self, length_bytes: usize) -> Result<&'input [u8], Error> { 73 | if !self.offset.is_multiple_of(8) { 74 | return Err(Error::Alignment); 75 | } 76 | let byte_start = self.offset >> 3; 77 | let byte_end = byte_start + length_bytes; 78 | let bytes = self 79 | .buffer 80 | .get(byte_start..byte_end) 81 | .ok_or(Error::BufferOverflow)?; 82 | self.offset = byte_end << 3; 83 | Ok(bytes) 84 | } 85 | 86 | pub fn skip_bytes(&mut self, count: usize) -> Result<(), Error> { 87 | if !self.offset.is_multiple_of(8) { 88 | return Err(Error::Alignment); 89 | } 90 | let byte_end = (self.offset >> 3) + count; 91 | if byte_end > self.buffer.len() { 92 | return Err(Error::BufferOverflow); 93 | } 94 | self.offset = byte_end << 3; 95 | Ok(()) 96 | } 97 | 98 | /// Create a cursor for `length_bytes`, and skip over `length_bytes` 99 | /// Must be aligned to 32 bits. 100 | pub(crate) fn take_slice(&mut self, length_bytes: usize) -> Result { 101 | if !self.offset.is_multiple_of(32) { 102 | return Err(Error::Alignment); 103 | } 104 | Ok(Cursor { 105 | buffer: self.read_bytes(length_bytes)?, 106 | offset: 0, 107 | }) 108 | } 109 | 110 | /// Read a VBR number in `width`-wide encoding. 111 | /// The number may be up to 64-bit long regardless of the `width`. 112 | #[inline] 113 | pub fn read_vbr(&mut self, width: u8) -> Result { 114 | if width < 1 || width > 32 { 115 | // This is `MaxChunkSize` in LLVM 116 | return Err(Error::VbrOverflow); 117 | } 118 | let test_bit = 1u64 << (width - 1); 119 | let mask = test_bit - 1; 120 | let mut res = 0; 121 | let mut offset = 0; 122 | loop { 123 | let next = self.read(width)?; 124 | res |= (next & mask) << offset; 125 | offset += width - 1; 126 | // 64 may not be divisible by width 127 | if offset > 63 + width { 128 | return Err(Error::VbrOverflow); 129 | } 130 | if next & test_bit == 0 { 131 | break; 132 | } 133 | } 134 | Ok(res) 135 | } 136 | 137 | /// Skip bytes until a 32-bit boundary (no-op if already aligned) 138 | pub fn align32(&mut self) -> Result<(), Error> { 139 | let new_offset = if self.offset.is_multiple_of(32) { 140 | self.offset 141 | } else { 142 | (self.offset + 32) & !(32 - 1) 143 | }; 144 | self.buffer = self 145 | .buffer 146 | .get((new_offset >> 3)..) 147 | .ok_or(Error::BufferOverflow)?; 148 | self.offset = 0; 149 | Ok(()) 150 | } 151 | 152 | /// Maximum number of bits that can be read 153 | #[must_use] 154 | pub fn unconsumed_bit_len(&self) -> usize { 155 | (self.buffer.len() << 3) - self.offset 156 | } 157 | } 158 | 159 | struct CursorDebugBytes<'a>(&'a [u8]); 160 | 161 | impl fmt::Debug for CursorDebugBytes<'_> { 162 | #[cold] 163 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 164 | f.write_str("[0x")?; 165 | for &b in self.0.iter().take(200) { 166 | write!(f, "{b:02x}")?; 167 | } 168 | if self.0.len() > 200 { 169 | f.write_str("...")?; 170 | } 171 | write!(f, "; {}]", self.0.len()) 172 | } 173 | } 174 | 175 | impl fmt::Debug for Cursor<'_> { 176 | /// Debug-print only the accessible part of the internal buffer 177 | #[cold] 178 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 179 | let byte_offset = self.offset / 8; 180 | let bit_offset = self.offset % 8; 181 | let buffer = CursorDebugBytes(self.buffer.get(byte_offset..).unwrap_or_default()); 182 | f.debug_struct("Cursor") 183 | .field("offset", &bit_offset) 184 | .field("buffer", &buffer) 185 | .field("nextvbr6", &self.peek(6).ok()) 186 | .finish() 187 | } 188 | } 189 | 190 | #[test] 191 | fn test_cursor_bits() { 192 | let mut c = Cursor::new(&[0b1000_0000]); 193 | assert_eq!(0, c.peek(1).unwrap()); 194 | assert!(c.peek(9).is_err()); 195 | assert_eq!(0, c.peek(2).unwrap()); 196 | assert_eq!(0, c.peek(3).unwrap()); 197 | assert_eq!(0, c.peek(4).unwrap()); 198 | assert_eq!(0, c.peek(5).unwrap()); 199 | assert_eq!(0, c.peek(6).unwrap()); 200 | assert_eq!(0, c.peek(7).unwrap()); 201 | assert_eq!(0b1000_0000, c.peek(8).unwrap()); 202 | assert_eq!(0, c.read(6).unwrap()); 203 | assert_eq!(0b10, c.peek(2).unwrap()); 204 | assert_eq!(0, c.peek(1).unwrap()); 205 | assert_eq!(0, c.read(1).unwrap()); 206 | assert_eq!(0b1, c.peek(1).unwrap()); 207 | assert_eq!(0b1, c.read(1).unwrap()); 208 | 209 | let mut c = Cursor::new(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 0x55, 0x11, 0xff, 1, 127, 0x51]); 210 | assert_eq!(0, c.peek(1).unwrap()); 211 | assert_eq!(0b1_0000_0000, c.peek(9).unwrap()); 212 | assert_eq!(0, c.peek(2).unwrap()); 213 | assert_eq!(0, c.peek(3).unwrap()); 214 | assert_eq!(0, c.peek(4).unwrap()); 215 | assert_eq!(0, c.peek(5).unwrap()); 216 | assert_eq!(0, c.peek(6).unwrap()); 217 | assert_eq!(0, c.peek(7).unwrap()); 218 | assert_eq!(0, c.peek(8).unwrap()); 219 | assert_eq!(0b1_0000_0000, c.peek(9).unwrap()); 220 | 221 | assert_eq!(0, c.peek(7).unwrap()); 222 | assert!(c.read(0).is_err()); 223 | assert_eq!(0, c.read(1).unwrap()); 224 | assert_eq!(0, c.read(2).unwrap()); 225 | assert_eq!(0, c.read(3).unwrap()); 226 | assert_eq!(4, c.read(4).unwrap()); 227 | assert_eq!(0, c.read(5).unwrap()); 228 | assert_eq!(4, c.read(6).unwrap()); 229 | assert_eq!(24, c.read(7).unwrap()); 230 | assert_eq!(64, c.read(8).unwrap()); 231 | assert_eq!(80, c.read(9).unwrap()); 232 | c.align32().unwrap(); 233 | let mut d = c.take_slice(6).unwrap(); 234 | assert_eq!(0x51, c.read(8).unwrap()); 235 | assert!(d.read(0).is_err()); 236 | assert_eq!(0, d.read(1).unwrap()); 237 | assert_eq!(0, d.read(2).unwrap()); 238 | assert_eq!(1, d.read(3).unwrap()); 239 | assert_eq!(4, d.read(4).unwrap()); 240 | assert_eq!(21, d.read(5).unwrap()); 241 | assert_eq!(34, d.read(6).unwrap()); 242 | assert_eq!(120, d.read(7).unwrap()); 243 | assert_eq!(31, d.read(8).unwrap()); 244 | assert!(d.read(63).is_err()); 245 | assert_eq!(496, d.read(9).unwrap()); 246 | assert!(d.read(0).is_err()); 247 | assert_eq!(1, d.read(1).unwrap()); 248 | assert!(d.align32().is_err()); 249 | assert_eq!(1, d.read(2).unwrap()); 250 | assert!(d.align32().is_err()); 251 | assert!(d.read(1).is_err()); 252 | } 253 | 254 | #[test] 255 | fn test_cursor_bytes() { 256 | let mut c = Cursor::new(&[0, 1, 2, 3, 4, 5, 6, 7, 8]); 257 | c.align32().unwrap(); 258 | assert_eq!(0x0100, c.peek(16).unwrap()); 259 | assert_eq!(0x020100, c.peek(24).unwrap()); 260 | assert_eq!(0x03020100, c.peek(32).unwrap()); 261 | assert_eq!(0x0100, c.read(16).unwrap()); 262 | assert_eq!(0x02, c.read(8).unwrap()); 263 | assert_eq!([3, 4, 5, 6], c.read_bytes(4).unwrap()); 264 | c.skip_bytes(1).unwrap(); 265 | assert!(c.read_bytes(2).is_err()); 266 | assert_eq!([8], c.read_bytes(1).unwrap()); 267 | } 268 | -------------------------------------------------------------------------------- /src/schema/values.rs: -------------------------------------------------------------------------------- 1 | //! From the LLVM Project, under the [Apache License v2.0 with LLVM Exceptions](https://llvm.org/LICENSE.txt) 2 | 3 | use num_enum::TryFromPrimitive; 4 | 5 | #[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)] 6 | #[repr(u8)] 7 | pub enum AttrKind { 8 | // = 0 is unused 9 | Alignment = 1, 10 | AlwaysInline = 2, 11 | ByVal = 3, 12 | InlineHint = 4, 13 | InReg = 5, 14 | MinSize = 6, 15 | Naked = 7, 16 | Nest = 8, 17 | NoAlias = 9, 18 | NoBuiltin = 10, 19 | NoCapture = 11, 20 | NoDuplicate = 12, 21 | NoImplicitFloat = 13, 22 | NoInline = 14, 23 | NonLazyBind = 15, 24 | NoRedZone = 16, 25 | NoReturn = 17, 26 | NoUnwind = 18, 27 | OptimizeForSize = 19, 28 | ReadNone = 20, 29 | ReadOnly = 21, 30 | Returned = 22, 31 | ReturnsTwice = 23, 32 | SExt = 24, 33 | StackAlignment = 25, 34 | StackProtect = 26, 35 | StackProtectReq = 27, 36 | StackProtectStrong = 28, 37 | StructRet = 29, 38 | SanitizeAddress = 30, 39 | SanitizeThread = 31, 40 | SanitizeMemory = 32, 41 | UwTable = 33, 42 | ZExt = 34, 43 | Builtin = 35, 44 | Cold = 36, 45 | OptimizeNone = 37, 46 | InAlloca = 38, 47 | NonNull = 39, 48 | JumpTable = 40, 49 | Dereferenceable = 41, 50 | DereferenceableOrNull = 42, 51 | Convergent = 43, 52 | Safestack = 44, 53 | /// Unused 54 | ArgMemOnly = 45, 55 | SwiftSelf = 46, 56 | SwiftError = 47, 57 | NoRecurse = 48, 58 | /// Unused 59 | InaccessibleMemOnly = 49, 60 | /// Unused 61 | InaccessiblememOrArgMemOnly = 50, 62 | AllocSize = 51, 63 | Writeonly = 52, 64 | Speculatable = 53, 65 | StrictFp = 54, 66 | SanitizeHwaddress = 55, 67 | NocfCheck = 56, 68 | OptForFuzzing = 57, 69 | Shadowcallstack = 58, 70 | SpeculativeLoadHardening = 59, 71 | Immarg = 60, 72 | Willreturn = 61, 73 | Nofree = 62, 74 | Nosync = 63, 75 | SanitizeMemtag = 64, 76 | Preallocated = 65, 77 | NoMerge = 66, 78 | NullPointerIsValid = 67, 79 | Noundef = 68, 80 | Byref = 69, 81 | Mustprogress = 70, 82 | NoCallback = 71, 83 | Hot = 72, 84 | NoProfile = 73, 85 | VscaleRange = 74, 86 | SwiftAsync = 75, 87 | NoSanitizeCoverage = 76, 88 | Elementtype = 77, 89 | DisableSanitizerInstrumentation = 78, 90 | NoSanitizeBounds = 79, 91 | AllocAlign = 80, 92 | AllocatedPointer = 81, 93 | AllocKind = 82, 94 | PresplitCoroutine = 83, 95 | FnretthunkExtern = 84, 96 | SkipProfile = 85, 97 | Memory = 86, 98 | Nofpclass = 87, 99 | OptimizeForDebugging = 88, 100 | Writable = 89, 101 | CoroOnlyDestroyWhenComplete = 90, 102 | DeadOnUnwind = 91, 103 | Range = 92, 104 | SanitizeNumericalStability = 93, 105 | Initializes = 94, 106 | HybridPatchable = 95, 107 | } 108 | 109 | /// These are values used in the bitcode files to encode which 110 | /// cast a `CST_CODE_CE_CAST` refers to. 111 | #[derive(Debug, TryFromPrimitive)] 112 | #[repr(u8)] 113 | pub enum CastOpcode { 114 | Trunc = 0, 115 | ZExt = 1, 116 | SExt = 2, 117 | FpToUi = 3, 118 | FpToSi = 4, 119 | UiToFp = 5, 120 | SiToFp = 6, 121 | FpTrunc = 7, 122 | FpExt = 8, 123 | PtrToInt = 9, 124 | IntToPtr = 10, 125 | Bitcast = 11, 126 | Addrspace = 12, 127 | } 128 | 129 | #[derive(Debug, TryFromPrimitive)] 130 | #[repr(u8)] 131 | pub enum Linkage { 132 | External = 0, 133 | Weak = 1, 134 | Appending = 2, 135 | Internal = 3, 136 | Linkonce = 4, 137 | Dllimport = 5, 138 | Dllexport = 6, 139 | ExternWeak = 7, 140 | Common = 8, 141 | Private = 9, 142 | WeakOdr = 10, 143 | LinkonceOdr = 11, 144 | AvailableExternally = 12, 145 | Deprecated1 = 13, 146 | Deprecated2 = 14, 147 | } 148 | 149 | #[derive(Debug, TryFromPrimitive)] 150 | #[repr(u8)] 151 | pub enum DllStorageClass { 152 | Default = 0, 153 | Import = 1, 154 | Export = 2, 155 | } 156 | 157 | #[derive(Debug, TryFromPrimitive)] 158 | #[repr(u8)] 159 | pub enum CallConv { 160 | C = 0, 161 | Fast = 8, 162 | Cold = 9, 163 | GHC = 10, 164 | HiPE = 11, 165 | AnyReg = 13, 166 | PreserveMost = 14, 167 | PreserveAll = 15, 168 | Swift = 16, 169 | /// CXX_FAST_TLS 170 | CxxFastTls = 17, 171 | Tail = 18, 172 | /// CFGuard_Check 173 | CFGuardCheck = 19, 174 | SwiftTail = 20, 175 | PreserveNone = 21, 176 | /// X86_StdCall (first target cc) 177 | X86StdCall = 64, 178 | /// X86_FastCall 179 | X86FastCall = 65, 180 | /// ARM_APCS 181 | ArmApcs = 66, 182 | /// ARM_AAPCS 183 | ArmAapcs = 67, 184 | /// ARM_AAPCS_VFP 185 | ArmAapcsVfp = 68, 186 | /// MSP430_INTR 187 | Msp430Intr = 69, 188 | /// X86_ThisCall 189 | X86ThisCall = 70, 190 | /// PTX_Kernel 191 | PTXKernel = 71, 192 | /// PTX_Device 193 | PTXDevice = 72, 194 | /// SPIR_FUNC 195 | SpirFunc = 75, 196 | /// SPIR_KERNEL 197 | SpirKernel = 76, 198 | /// Intel_OCL_BI 199 | IntelOclBi = 77, 200 | /// X86_64_SysV 201 | X8664SysV = 78, 202 | /// Win64 203 | Win64 = 79, 204 | /// X86_VectorCall 205 | X86VectorCall = 80, 206 | /// DUMMY_HHVM 207 | DummyHhvm = 81, 208 | /// DUMMY_HHVM_C 209 | DummyHhvmC = 82, 210 | /// X86_INTR 211 | X86Intr = 83, 212 | /// AVR_INTR 213 | AvrIntr = 84, 214 | /// AVR_SIGNAL 215 | AvrSignal = 85, 216 | /// AVR_BUILTIN 217 | AvrBuiltin = 86, 218 | /// AMDGPU_VS 219 | AmdGpuVs = 87, 220 | /// AMDGPU_GS 221 | AmdGpuGs = 88, 222 | /// AMDGPU_PS 223 | AmdGpuPs = 89, 224 | /// AMDGPU_CS 225 | AmdGpuCs = 90, 226 | /// AMDGPU_KERNEL 227 | AmdGpuKernel = 91, 228 | /// X86_RegCall 229 | X86RegCall = 92, 230 | /// AMDGPU_HS 231 | AmdGpuHs = 93, 232 | /// MSP430_BUILTIN 233 | Msp430Builtin = 94, 234 | /// AMDGPU_LS 235 | AmdGpuLs = 95, 236 | /// AMDGPU_ES 237 | AmdGpuEs = 96, 238 | /// AArch64_VectorCall 239 | AArch64VectorCall = 97, 240 | /// AArch64_SVE_VectorCall 241 | AArch64SVEVectorCall = 98, 242 | /// WASM_EmscriptenInvoke 243 | WasmEmscriptenInvoke = 99, 244 | /// AMDGPU_Gfx 245 | AmdGpuGfx = 100, 246 | /// M68k_INTR 247 | M68kIntr = 101, 248 | AArch64SmeAbiSupportRoutinesPreserveMostFromX0 = 102, 249 | AArch64SmeAbiSupportRoutinesPreserveMostFromX2 = 103, 250 | AmdGpuCSChain = 104, 251 | AmdGpuCSChainPreserve = 105, 252 | M68kRTD = 106, 253 | Graal = 107, 254 | Arm64ECThunkX64 = 108, 255 | Arm64ECThunkNative = 109, 256 | RiscVVectorCall = 110, 257 | AArch64SmeAbiSupportRoutinesPreserveMostFromX1 = 111, 258 | } 259 | 260 | /// call conv field in bitcode is often mixed with flags 261 | impl CallConv { 262 | pub fn from_flags(ccinfo_flags: u64) -> Result { 263 | // static_cast((0x7ff & CCInfo) >> bitc::CALL_CCONV)); 264 | let id = u8::try_from((ccinfo_flags & 0x7ff) >> 1).map_err(|e| e.to_string())?; 265 | Self::try_from_primitive(id).map_err(|e| e.to_string()) 266 | } 267 | } 268 | 269 | /// These are values used in the bitcode files to encode which 270 | /// binop a `CST_CODE_CE_BINOP` refers to. 271 | #[derive(Debug, TryFromPrimitive)] 272 | #[repr(u8)] 273 | pub enum BinOpcode { 274 | Add = 0, 275 | Sub = 1, 276 | Mul = 2, 277 | Udiv = 3, 278 | /// overloaded for FP 279 | Sdiv = 4, 280 | Urem = 5, 281 | /// overloaded for FP 282 | Srem = 6, 283 | Shl = 7, 284 | Lshr = 8, 285 | Ashr = 9, 286 | And = 10, 287 | Or = 11, 288 | Xor = 12, 289 | } 290 | 291 | /// Encoded `AtomicOrdering` values. 292 | #[derive(Debug, TryFromPrimitive, Default)] 293 | #[repr(u8)] 294 | pub enum AtomicOrdering { 295 | #[default] 296 | Notatomic = 0, 297 | Unordered = 1, 298 | Monotonic = 2, 299 | Acquire = 3, 300 | Release = 4, 301 | AcqRel = 5, 302 | SeqCst = 6, 303 | } 304 | 305 | /// COMDATSELECTIONKIND enumerates the possible selection mechanisms for 306 | /// COMDAT sections. 307 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 308 | #[repr(u8)] 309 | pub enum ComdatSelectionKind { 310 | Any = 1, 311 | ExactMatch = 2, 312 | Largest = 3, 313 | NoDuplicates = 4, 314 | SameSize = 5, 315 | } 316 | 317 | /// Atomic read-modify-write operations 318 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 319 | #[repr(u8)] 320 | #[non_exhaustive] 321 | pub enum RmwOperation { 322 | /// `XCHG` 323 | Xchg = 0, 324 | 325 | /// `ADD` 326 | Add = 1, 327 | 328 | /// `SUB` 329 | Sub = 2, 330 | 331 | /// `AND` 332 | And = 3, 333 | 334 | /// `NAND` 335 | Nand = 4, 336 | 337 | /// `OR` 338 | Or = 5, 339 | 340 | /// `XOR` 341 | Xor = 6, 342 | 343 | /// `MAX` 344 | Max = 7, 345 | 346 | /// `MIN` 347 | Min = 8, 348 | 349 | /// `UMAX` 350 | Umax = 9, 351 | 352 | /// `UMIN` 353 | Umin = 10, 354 | 355 | /// `FADD` 356 | Fadd = 11, 357 | 358 | /// `FSUB` 359 | Fsub = 12, 360 | 361 | /// `FMAX` 362 | Fmax = 13, 363 | 364 | /// `FMIN` 365 | Fmin = 14, 366 | 367 | /// `UINC_WRAP` 368 | UincWrap = 15, 369 | 370 | /// `UDEC_WRAP` 371 | UdecWrap = 16, 372 | 373 | /// `USUB_COND` 374 | UsSubCond = 17, 375 | 376 | /// `USUB_SAT` 377 | UsSubSat = 18, 378 | } 379 | 380 | /// Unary Opcodes 381 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 382 | #[repr(u8)] 383 | #[non_exhaustive] 384 | pub enum UnaryOpcode { 385 | /// `UNOP_FNEG` 386 | Fneg = 0, 387 | } 388 | 389 | /// Flags for serializing 390 | /// OverflowingBinaryOperator's SubclassOptionalData contents. 391 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 392 | #[repr(u8)] 393 | #[non_exhaustive] 394 | pub enum OverflowingBinaryOperatorOptionalFlags { 395 | NoUnsignedWrap = 0, 396 | NoSignedWrap = 1, 397 | } 398 | 399 | /// Flags for serializing 400 | /// TruncInstOptionalFlags's SubclassOptionalData contents. 401 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 402 | #[repr(u8)] 403 | #[non_exhaustive] 404 | pub enum TruncInstOptionalFlags { 405 | NoUnsignedWrap = 0, 406 | NoSignedWrap = 1, 407 | } 408 | 409 | /// FastMath Flags 410 | /// This is a fixed layout derived from the bitcode emitted by LLVM 5.0 411 | /// intended to decouple the in-memory representation from the serialization. 412 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 413 | #[repr(u8)] 414 | pub enum FastMathMap { 415 | UnsafeAlgebra = 1 << 0, // Legacy 416 | NoNaNs = 1 << 1, 417 | NoInfs = 1 << 2, 418 | NoSignedZeros = 1 << 3, 419 | AllowReciprocal = 1 << 4, 420 | AllowContract = 1 << 5, 421 | ApproxFunc = 1 << 6, 422 | AllowReassoc = 1 << 7, 423 | } 424 | 425 | /// Flags for serializing 426 | /// GEPOperator's SubclassOptionalData contents. 427 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 428 | #[repr(u8)] 429 | #[non_exhaustive] 430 | pub enum GetElementPtrOptionalFlags { 431 | Inbounds = 0, 432 | Nusw = 1, 433 | Nuw = 2, 434 | } 435 | 436 | /// Markers and flags for call instruction. 437 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 438 | #[repr(u8)] 439 | #[non_exhaustive] 440 | pub enum CallMarkersFlags { 441 | Tail = 0, 442 | Cconv = 1, 443 | MustTail = 14, 444 | ExplicitType = 15, 445 | NoTail = 16, 446 | Fmf = 17, // Call has optional fast-math-flags. 447 | } 448 | -------------------------------------------------------------------------------- /src/read.rs: -------------------------------------------------------------------------------- 1 | use crate::bitstream::{PayloadOperand, ScalarOperand}; 2 | use std::string::FromUtf8Error; 3 | use std::sync::Arc; 4 | use std::{collections::HashMap, convert::TryFrom, error, fmt}; 5 | 6 | use crate::bitcode::{BlockInfo, RecordIter}; 7 | use crate::bits::{self, Cursor}; 8 | use crate::bitstream::{Abbreviation, BlockInfoCode, BuiltinAbbreviationId, Operand}; 9 | use crate::visitor::BitStreamVisitor; 10 | 11 | /// Bitstream reader errors 12 | #[derive(Debug, Clone)] 13 | pub enum Error { 14 | EndOfRecord, 15 | ValueOverflow, 16 | UnexpectedOperand(Option), 17 | InvalidSignature(u32), 18 | InvalidAbbrev, 19 | NestedBlockInBlockInfo, 20 | MissingSetBid, 21 | InvalidBlockInfoRecord(u64), 22 | NoSuchAbbrev { block_id: u32, abbrev_id: u32 }, 23 | UnexpectedBlock(u32), 24 | MissingEndBlock(u32), 25 | AbbrevWidthTooSmall(u8), 26 | ReadBits(bits::Error), 27 | Encoding(FromUtf8Error), 28 | Other(&'static str), 29 | } 30 | 31 | impl fmt::Display for Error { 32 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | match self { 34 | Self::EndOfRecord => write!(f, "read past end of record"), 35 | Self::ValueOverflow => write!(f, "read integer too big"), 36 | Self::UnexpectedOperand(op) => write!(f, "Unexpected operand {op:?}"), 37 | Self::InvalidSignature(sig) => { 38 | write!(f, "invalid signature (magic number): 0x{sig:x}") 39 | } 40 | Self::InvalidAbbrev => write!(f, "invalid abbreviation"), 41 | Self::NestedBlockInBlockInfo => { 42 | write!(f, "nested block in block info") 43 | } 44 | Self::UnexpectedBlock(id) => write!(f, "nested block {id}"), 45 | Self::MissingSetBid => write!(f, "missing SETBID"), 46 | Self::InvalidBlockInfoRecord(record_id) => { 47 | write!(f, "invalid block info record `{record_id}`") 48 | } 49 | Self::AbbrevWidthTooSmall(width) => { 50 | write!(f, "abbreviation width `{width}` is too small") 51 | } 52 | Self::NoSuchAbbrev { 53 | block_id, 54 | abbrev_id, 55 | } => write!( 56 | f, 57 | "no such abbreviation `{abbrev_id}` in block `{block_id}`" 58 | ), 59 | Self::MissingEndBlock(block_id) => { 60 | write!(f, "missing end block for `{block_id}`") 61 | } 62 | Self::ReadBits(err) => err.fmt(f), 63 | Self::Encoding(err) => err.fmt(f), 64 | Self::Other(err) => err.fmt(f), 65 | } 66 | } 67 | } 68 | 69 | impl error::Error for Error {} 70 | 71 | impl From for Error { 72 | fn from(err: bits::Error) -> Self { 73 | Self::ReadBits(err) 74 | } 75 | } 76 | 77 | /// A block can contain either nested blocks or records. 78 | /// LLVM writes blocks first, but the format allows them to be mixed freely. 79 | #[derive(Debug)] 80 | pub enum BlockItem<'cursor, 'input> { 81 | /// Recurse 82 | Block(BlockIter<'cursor, 'input>), 83 | /// Read a record from the current block 84 | Record(RecordIter<'cursor, 'input>), 85 | } 86 | 87 | /// Iterator content directly in a block 88 | #[derive(Debug)] 89 | pub struct BlockIter<'global_state, 'input> { 90 | /// ID of the block being iterated 91 | pub id: u32, 92 | cursor: Cursor<'input>, 93 | abbrev_width: u8, 94 | /// Abbreviations defined in this block 95 | block_local_abbrevs: Vec>, 96 | /// Global abbreviations and names 97 | reader: &'global_state mut BitStreamReader, 98 | } 99 | 100 | /// Bitstream reader 101 | #[derive(Debug, Clone)] 102 | pub struct BitStreamReader { 103 | /// Block information 104 | pub(crate) block_info: HashMap, 105 | global_abbrevs: HashMap>>, 106 | } 107 | 108 | impl BitStreamReader { 109 | /// Top level fake block ID 110 | pub const TOP_LEVEL_BLOCK_ID: u32 = u32::MAX; 111 | 112 | #[must_use] 113 | pub fn new() -> Self { 114 | Self { 115 | block_info: HashMap::new(), 116 | global_abbrevs: HashMap::new(), 117 | } 118 | } 119 | 120 | /// Skip `Signature` first 121 | pub fn iter_bitcode<'input>(&mut self, bitcode_data: &'input [u8]) -> BlockIter<'_, 'input> { 122 | BlockIter::new(self, Cursor::new(bitcode_data), Self::TOP_LEVEL_BLOCK_ID, 2) 123 | } 124 | 125 | fn visit_block( 126 | mut block: BlockIter<'_, '_>, 127 | visitor: &mut V, 128 | ) -> Result<(), Error> { 129 | let block_id = block.id; 130 | while let Some(item) = block.next()? { 131 | match item { 132 | BlockItem::Block(new_block) => { 133 | let new_id = new_block.id; 134 | if visitor.should_enter_block(new_id) { 135 | Self::visit_block(new_block, visitor)?; 136 | visitor.did_exit_block(new_id); 137 | } 138 | } 139 | BlockItem::Record(record) => { 140 | visitor.visit(block_id, record.into_record()?); 141 | } 142 | } 143 | } 144 | Ok(()) 145 | } 146 | 147 | /// Read abbreviated operand 148 | #[inline(never)] 149 | fn read_abbrev_op(cursor: &mut Cursor<'_>, num_ops_left: &mut usize) -> Result { 150 | if *num_ops_left == 0 { 151 | return Err(Error::InvalidAbbrev); 152 | } 153 | *num_ops_left -= 1; 154 | 155 | let is_literal = cursor.read(1)?; 156 | if is_literal == 1 { 157 | return Ok(Operand::Scalar(ScalarOperand::Literal(cursor.read_vbr(8)?))); 158 | } 159 | let op_type = cursor.read(3)?; 160 | Ok(match op_type { 161 | 1 => { 162 | let width = cursor.read_vbr(5)?; 163 | if width < 1 || width > 32 { 164 | return Err(Error::AbbrevWidthTooSmall(width as u8)); 165 | } 166 | Operand::Scalar(ScalarOperand::Fixed(width as u8)) 167 | } 168 | 2 => { 169 | let width = cursor.read_vbr(5)?; 170 | if width < 1 || width > 32 { 171 | return Err(Error::AbbrevWidthTooSmall(width as u8)); 172 | } 173 | Operand::Scalar(ScalarOperand::Vbr(width as u8)) 174 | } 175 | 3 if *num_ops_left == 1 => { 176 | let op = Self::read_abbrev_op(cursor, num_ops_left)?; 177 | if let Operand::Scalar(op) = op { 178 | Operand::Payload(PayloadOperand::Array(op)) 179 | } else { 180 | return Err(Error::UnexpectedOperand(Some(op))); 181 | } 182 | } 183 | 4 => Operand::Scalar(ScalarOperand::Char6), 184 | 5 if *num_ops_left == 0 => Operand::Payload(PayloadOperand::Blob), 185 | _ => return Err(Error::InvalidAbbrev), 186 | }) 187 | } 188 | 189 | /// Read abbreviation 190 | fn define_abbrev( 191 | cursor: &mut Cursor<'_>, 192 | abbrevs: &mut Vec>, 193 | ) -> Result<(), Error> { 194 | let mut num_ops = cursor.read_vbr(5)? as usize; 195 | 196 | let mut fields = Vec::with_capacity(num_ops); 197 | let mut payload = None; 198 | while num_ops > 0 && fields.len() != fields.capacity() { 199 | match Self::read_abbrev_op(cursor, &mut num_ops)? { 200 | Operand::Scalar(op) => { 201 | fields.push(op); 202 | } 203 | Operand::Payload(op) if num_ops == 0 => { 204 | payload = Some(op); 205 | } 206 | op => return Err(Error::UnexpectedOperand(Some(op))), 207 | } 208 | } 209 | let id = abbrevs.len() as u32; 210 | let abbrev = Arc::new(Abbreviation { 211 | fields, 212 | payload, 213 | id, 214 | }); 215 | abbrevs.push(abbrev); 216 | Ok(()) 217 | } 218 | 219 | /// Read block info block 220 | fn read_block_info_block( 221 | &mut self, 222 | cursor: &mut Cursor<'_>, 223 | abbrev_width: u8, 224 | ) -> Result<(), Error> { 225 | use BuiltinAbbreviationId::*; 226 | 227 | let mut current_block_id: Option = None; 228 | loop { 229 | let abbrev_id = cursor.read(abbrev_width)? as u32; 230 | match BuiltinAbbreviationId::try_from(abbrev_id).map_err(|_| Error::NoSuchAbbrev { 231 | block_id: 0, 232 | abbrev_id, 233 | })? { 234 | EndBlock => { 235 | cursor.align32()?; 236 | return Ok(()); 237 | } 238 | EnterSubBlock => { 239 | return Err(Error::NestedBlockInBlockInfo); 240 | } 241 | DefineAbbreviation => { 242 | let block_id = current_block_id.ok_or(Error::MissingSetBid)?; 243 | Self::define_abbrev(cursor, self.global_abbrevs.entry(block_id).or_default())?; 244 | } 245 | UnabbreviatedRecord => { 246 | let mut record = RecordIter::from_cursor(cursor)?; 247 | let block = u8::try_from(record.id) 248 | .ok() 249 | .and_then(|c| BlockInfoCode::try_from(c).ok()) 250 | .ok_or(Error::InvalidBlockInfoRecord(record.id))?; 251 | match block { 252 | BlockInfoCode::SetBid => { 253 | let id = record 254 | .u32() 255 | .ok() 256 | .filter(|_| record.is_empty()) 257 | .ok_or(Error::InvalidBlockInfoRecord(record.id))?; 258 | current_block_id = Some(id); 259 | } 260 | BlockInfoCode::BlockName => { 261 | let block_id = current_block_id.ok_or(Error::MissingSetBid)?; 262 | let block_info = self.block_info.entry(block_id).or_default(); 263 | if let Ok(name) = String::from_utf8(record.string()?) { 264 | block_info.name = name; 265 | } 266 | } 267 | BlockInfoCode::SetRecordName => { 268 | let block_id = current_block_id.ok_or(Error::MissingSetBid)?; 269 | let record_id = record 270 | .u64() 271 | .map_err(|_| Error::InvalidBlockInfoRecord(record.id))?; 272 | let block_info = self.block_info.entry(block_id).or_default(); 273 | if let Ok(name) = String::from_utf8(record.string()?) { 274 | block_info.record_names.insert(record_id, name); 275 | } 276 | } 277 | } 278 | } 279 | } 280 | } 281 | } 282 | 283 | /// Read block with visitor 284 | pub fn read_block( 285 | &mut self, 286 | cursor: Cursor<'_>, 287 | block_id: u32, 288 | abbrev_width: u8, 289 | visitor: &mut V, 290 | ) -> Result<(), Error> { 291 | Self::visit_block( 292 | BlockIter::new(self, cursor, block_id, abbrev_width), 293 | visitor, 294 | ) 295 | } 296 | } 297 | 298 | impl<'global_state, 'input> BlockIter<'global_state, 'input> { 299 | pub fn next_record<'parent>( 300 | &'parent mut self, 301 | ) -> Result>, Error> { 302 | match self.next()? { 303 | None => Ok(None), 304 | Some(BlockItem::Record(rec)) => Ok(Some(rec)), 305 | Some(BlockItem::Block(block)) => Err(Error::UnexpectedBlock(block.id)), 306 | } 307 | } 308 | 309 | /// Returns the next item (block or record) in this block 310 | pub fn next<'parent>(&'parent mut self) -> Result>, Error> { 311 | if self.cursor.is_at_end() { 312 | return if self.id == BitStreamReader::TOP_LEVEL_BLOCK_ID { 313 | Ok(None) 314 | } else { 315 | Err(Error::MissingEndBlock(self.id)) 316 | }; 317 | } 318 | 319 | let abbrev_id = self.cursor.read(self.abbrev_width)? as u32; 320 | 321 | if let Ok(builtin_abbrev) = BuiltinAbbreviationId::try_from(abbrev_id) { 322 | use BuiltinAbbreviationId::*; 323 | match builtin_abbrev { 324 | EndBlock => { 325 | self.cursor.align32()?; 326 | Ok(None) 327 | } 328 | EnterSubBlock => { 329 | let block_id = self.cursor.read_vbr(8)? as u32; 330 | let new_abbrev_width = self.cursor.read_vbr(4)? as u8; 331 | self.cursor.align32()?; 332 | let block_length = self.cursor.read(32)? as usize * 4; 333 | let mut cursor = self.cursor.take_slice(block_length)?; 334 | 335 | if block_id == 0 { 336 | self.reader 337 | .read_block_info_block(&mut cursor, new_abbrev_width)?; 338 | return self.next(); 339 | } 340 | 341 | // Create new block iterator 342 | let block_iter = 343 | BlockIter::new(self.reader, cursor, block_id, new_abbrev_width); 344 | Ok(Some(BlockItem::Block(block_iter))) 345 | } 346 | DefineAbbreviation => { 347 | BitStreamReader::define_abbrev( 348 | &mut self.cursor, 349 | &mut self.block_local_abbrevs, 350 | )?; 351 | self.next() 352 | } 353 | UnabbreviatedRecord => { 354 | let record_iter = RecordIter::from_cursor(&mut self.cursor)?; 355 | Ok(Some(BlockItem::Record(record_iter))) 356 | } 357 | } 358 | } else { 359 | let abbrev_index = abbrev_id as usize - 4; 360 | let global_abbrevs = self 361 | .reader 362 | .global_abbrevs 363 | .get(&self.id) 364 | .map(|v| v.as_slice()) 365 | .unwrap_or_default(); 366 | 367 | // > Any abbreviations defined in a BLOCKINFO record for the particular block type receive IDs first, 368 | // > followed by any abbreviations defined within the block itself. 369 | let abbrev = if let Some(local_index) = abbrev_index.checked_sub(global_abbrevs.len()) { 370 | self.block_local_abbrevs.get(local_index).cloned() 371 | } else { 372 | global_abbrevs.get(abbrev_index).cloned() 373 | }; 374 | 375 | let abbrev = abbrev.ok_or(Error::NoSuchAbbrev { 376 | block_id: self.id, 377 | abbrev_id, 378 | })?; 379 | 380 | Ok(Some(BlockItem::Record(RecordIter::from_cursor_abbrev( 381 | &mut self.cursor, 382 | abbrev, 383 | )?))) 384 | } 385 | } 386 | 387 | /// Bit width of abbreviation IDs in this block. 388 | /// 389 | /// This is an implementation detail, 390 | /// intended only for debugging or data dumps. 391 | #[must_use] 392 | pub fn debug_abbrev_width(&self) -> u8 { 393 | self.abbrev_width 394 | } 395 | 396 | /// Valid only before any record or subblock has been read. This is the block size in bytes. 397 | /// 398 | /// This is an implementation detail, 399 | /// intended only for debugging or data dumps. 400 | #[must_use] 401 | pub fn debug_data_len(&self) -> Option { 402 | let bits = self.cursor.unconsumed_bit_len(); 403 | (bits & 31 != 0).then_some(bits >> 3) 404 | } 405 | 406 | fn new( 407 | reader: &'global_state mut BitStreamReader, 408 | cursor: Cursor<'input>, 409 | block_id: u32, 410 | abbrev_width: u8, 411 | ) -> Self { 412 | Self { 413 | id: block_id, 414 | cursor, 415 | abbrev_width, 416 | block_local_abbrevs: Vec::new(), 417 | reader, 418 | } 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /src/bitcode.rs: -------------------------------------------------------------------------------- 1 | use crate::bits::Cursor; 2 | use crate::bitstream::{Abbreviation, Operand}; 3 | use crate::bitstream::{PayloadOperand, ScalarOperand}; 4 | use std::cell::RefCell; 5 | use std::collections::HashMap; 6 | use std::fmt; 7 | use std::num::NonZero; 8 | use std::ops::Range; 9 | use std::sync::Arc; 10 | 11 | use crate::read::{BitStreamReader, Error}; 12 | use crate::visitor::{BitStreamVisitor, CollectingVisitor}; 13 | 14 | const LLVM_BITCODE_WRAPPER_MAGIC: u32 = 0x0B17C0DE; 15 | 16 | /// Represents the contents of a file encoded using the 17 | /// [LLVM bitstream container format](https://llvm.org/docs/BitCodeFormat.html#bitstream-container-format) 18 | #[derive(Debug, Clone)] 19 | pub struct Bitcode { 20 | pub signature: Signature, 21 | pub elements: Vec, 22 | pub block_info: HashMap, 23 | } 24 | 25 | /// Blocks in a bitstream denote nested regions of the stream, 26 | /// and are identified by a content-specific id number 27 | /// 28 | /// Block IDs 0-7 are reserved for [standard blocks](https://llvm.org/docs/BitCodeFormat.html#standard-blocks) 29 | /// whose meaning is defined by Bitcode; 30 | /// block IDs 8 and greater are application specific. 31 | #[derive(Debug, Clone)] 32 | pub struct Block { 33 | /// Block ID 34 | pub id: u32, 35 | /// Block elements 36 | pub elements: Vec, 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub enum Payload { 41 | Array(Vec), 42 | Char6String(String), 43 | Blob(Vec), 44 | } 45 | 46 | /// Data records consist of a record code and a number of (up to) 64-bit integer values 47 | /// 48 | /// The interpretation of the code and values is application specific and may vary between different block types. 49 | #[derive(Debug, Clone)] 50 | pub struct Record { 51 | /// Record code 52 | pub id: u64, 53 | /// An abbreviated record has a abbreviation id followed by a set of fields 54 | fields: Vec, 55 | /// Array and Blob encoding has payload 56 | payload: Option, 57 | } 58 | 59 | impl Record { 60 | #[must_use] 61 | pub fn fields(&self) -> &[u64] { 62 | &self.fields 63 | } 64 | 65 | pub fn take_payload(&mut self) -> Option { 66 | self.payload.take() 67 | } 68 | } 69 | 70 | #[derive(Debug, Clone)] 71 | enum Ops { 72 | Abbrev { 73 | /// If under `abbrev.fields.len()`, then it's the next op to read 74 | /// If equals `abbrev.fields.len()`, then payload is next 75 | /// If greater than `abbrev.fields.len()`, then payload has been read 76 | state: usize, 77 | abbrev: Arc, 78 | }, 79 | /// Num ops left 80 | Full(usize), 81 | } 82 | 83 | /// Data records consist of a record code and a number of (up to) 64-bit integer values 84 | /// 85 | /// The interpretation of the code and values is application specific and may vary between different block types. 86 | pub struct RecordIter<'cursor, 'input> { 87 | /// Record code 88 | pub id: u64, 89 | cursor: &'cursor mut Cursor<'input>, 90 | ops: Ops, 91 | } 92 | 93 | impl<'cursor, 'input> RecordIter<'cursor, 'input> { 94 | pub(crate) fn into_record(mut self) -> Result { 95 | let mut fields = Vec::with_capacity(self.len()); 96 | while let Some(f) = self.next()? { 97 | fields.push(f); 98 | } 99 | Ok(Record { 100 | id: self.id, 101 | fields, 102 | payload: self.payload().ok().flatten(), 103 | }) 104 | } 105 | 106 | fn read_scalar_operand(cursor: &mut Cursor<'_>, operand: ScalarOperand) -> Result { 107 | match operand { 108 | ScalarOperand::Char6 => { 109 | let value = cursor.read(6)? as u8; 110 | Ok(u64::from(match value { 111 | 0..=25 => value + b'a', 112 | 26..=51 => value + (b'A' - 26), 113 | 52..=61 => value - (52 - b'0'), 114 | 62 => b'.', 115 | 63 => b'_', 116 | _ => return Err(Error::InvalidAbbrev), 117 | })) 118 | } 119 | ScalarOperand::Literal(value) => Ok(value), 120 | ScalarOperand::Fixed(width) => Ok(cursor.read(width)?), 121 | ScalarOperand::Vbr(width) => Ok(cursor.read_vbr(width)?), 122 | } 123 | } 124 | 125 | pub(crate) fn from_cursor_abbrev( 126 | cursor: &'cursor mut Cursor<'input>, 127 | abbrev: Arc, 128 | ) -> Result { 129 | let id = 130 | Self::read_scalar_operand(cursor, *abbrev.fields.first().ok_or(Error::InvalidAbbrev)?)?; 131 | Ok(Self { 132 | id, 133 | cursor, 134 | ops: Ops::Abbrev { state: 1, abbrev }, 135 | }) 136 | } 137 | 138 | pub(crate) fn from_cursor(cursor: &'cursor mut Cursor<'input>) -> Result { 139 | let id = cursor.read_vbr(6)?; 140 | let num_ops = cursor.read_vbr(6)? as usize; 141 | Ok(Self { 142 | id, 143 | cursor, 144 | ops: Ops::Full(num_ops), 145 | }) 146 | } 147 | 148 | pub fn payload(&mut self) -> Result, Error> { 149 | match &mut self.ops { 150 | Ops::Abbrev { state, abbrev } => { 151 | if *state > abbrev.fields.len() { 152 | return Ok(None); 153 | } 154 | Ok(match abbrev.payload { 155 | Some(PayloadOperand::Blob) => Some(Payload::Blob(self.blob()?.to_vec())), 156 | Some(PayloadOperand::Array(ScalarOperand::Char6)) => { 157 | Some(Payload::Char6String( 158 | String::from_utf8(self.string()?).map_err(|_| Error::InvalidAbbrev)?, 159 | )) 160 | } 161 | Some(PayloadOperand::Array(_)) => Some(Payload::Array(self.array()?)), 162 | None => None, 163 | }) 164 | } 165 | Ops::Full(_) => Ok(None), 166 | } 167 | } 168 | 169 | /// Number of unread fields, excludes string/array/blob payload 170 | #[must_use] 171 | pub fn len(&self) -> usize { 172 | match &self.ops { 173 | Ops::Abbrev { state, abbrev } => abbrev.fields.len().saturating_sub(*state), 174 | Ops::Full(num_ops) => *num_ops, 175 | } 176 | } 177 | 178 | /// Matches len, excludes string/array/blob payload 179 | #[must_use] 180 | pub fn is_empty(&self) -> bool { 181 | self.len() == 0 182 | } 183 | 184 | pub fn next(&mut self) -> Result, Error> { 185 | match &mut self.ops { 186 | Ops::Abbrev { state, abbrev } => { 187 | let Some(&op) = abbrev.fields.get(*state) else { 188 | return Ok(None); 189 | }; 190 | *state += 1; 191 | Ok(Some(Self::read_scalar_operand(self.cursor, op)?)) 192 | } 193 | Ops::Full(num_ops) => { 194 | if *num_ops == 0 { 195 | return Ok(None); 196 | } 197 | *num_ops -= 1; 198 | Ok(Some(self.cursor.read_vbr(6)?)) 199 | } 200 | } 201 | } 202 | 203 | pub fn u64(&mut self) -> Result { 204 | self.next()?.ok_or(Error::EndOfRecord) 205 | } 206 | 207 | pub fn nzu64(&mut self) -> Result>, Error> { 208 | self.u64().map(NonZero::new) 209 | } 210 | 211 | pub fn i64(&mut self) -> Result { 212 | let v = self.u64()?; 213 | let shifted = (v >> 1) as i64; 214 | Ok(if (v & 1) == 0 { 215 | shifted 216 | } else if v != 1 { 217 | -shifted 218 | } else { 219 | 1 << 63 220 | }) 221 | } 222 | 223 | pub fn u32(&mut self) -> Result { 224 | self.u64()?.try_into().map_err(|_| Error::ValueOverflow) 225 | } 226 | 227 | pub fn nzu32(&mut self) -> Result>, Error> { 228 | self.u32().map(NonZero::new) 229 | } 230 | 231 | pub fn u8(&mut self) -> Result { 232 | self.u64()?.try_into().map_err(|_| Error::ValueOverflow) 233 | } 234 | 235 | pub fn try_from, T: TryFrom>(&mut self) -> Result { 236 | T::try_from(self.u64()?.try_into().map_err(|_| Error::ValueOverflow)?) 237 | .map_err(|_| Error::ValueOverflow) 238 | } 239 | 240 | pub fn nzu8(&mut self) -> Result>, Error> { 241 | self.u8().map(NonZero::new) 242 | } 243 | 244 | pub fn bool(&mut self) -> Result { 245 | match self.u64()? { 246 | 0 => Ok(false), 247 | 1 => Ok(true), 248 | _ => Err(Error::ValueOverflow), 249 | } 250 | } 251 | 252 | pub fn range(&mut self) -> Result, Error> { 253 | let start = self.u64()? as usize; 254 | Ok(Range { 255 | start, 256 | end: start + self.u64()? as usize, 257 | }) 258 | } 259 | 260 | pub fn blob(&mut self) -> Result<&'input [u8], Error> { 261 | match &mut self.ops { 262 | Ops::Abbrev { state, abbrev } => match Self::take_payload_operand(state, abbrev)? { 263 | Some(PayloadOperand::Blob) => { 264 | let length = self.cursor.read_vbr(6)? as usize; 265 | self.cursor.align32()?; 266 | let data = self.cursor.read_bytes(length)?; 267 | self.cursor.align32()?; 268 | Ok(data) 269 | } 270 | other => Err(Error::UnexpectedOperand(other.map(Operand::Payload))), 271 | }, 272 | Ops::Full(_) => Err(Error::UnexpectedOperand(None)), 273 | } 274 | } 275 | 276 | pub fn array(&mut self) -> Result, Error> { 277 | match &mut self.ops { 278 | Ops::Abbrev { state, abbrev } => match Self::take_payload_operand(state, abbrev)? { 279 | Some(PayloadOperand::Array(op)) => { 280 | let len = self.cursor.read_vbr(6)? as usize; 281 | let mut out = Vec::with_capacity(len); 282 | for _ in 0..len { 283 | if out.len() == out.capacity() { 284 | debug_assert!(false); 285 | break; 286 | } 287 | out.push(Self::read_scalar_operand(self.cursor, op)?); 288 | } 289 | Ok(out) 290 | } 291 | other => Err(Error::UnexpectedOperand(other.map(Operand::Payload))), 292 | }, 293 | // Not a proper array payload, but this fallback pattern is used by LLVM 294 | Ops::Full(num_ops) => { 295 | let len = *num_ops; 296 | *num_ops = 0; 297 | let mut out = Vec::with_capacity(len); 298 | for _ in 0..len { 299 | if out.len() == out.capacity() { 300 | debug_assert!(false); 301 | break; 302 | } 303 | out.push(self.cursor.read_vbr(6)?); 304 | } 305 | Ok(out) 306 | } 307 | } 308 | } 309 | 310 | /// Mark payload as read, if there is one 311 | fn take_payload_operand( 312 | state: &mut usize, 313 | abbrev: &Abbreviation, 314 | ) -> Result, Error> { 315 | if *state == abbrev.fields.len() { 316 | if abbrev.payload.is_some() { 317 | *state += 1; 318 | } 319 | Ok(abbrev.payload) 320 | } else { 321 | Err(Error::UnexpectedOperand( 322 | abbrev.fields.get(*state).copied().map(Operand::Scalar), 323 | )) 324 | } 325 | } 326 | 327 | /// Read remainder of the fields as string chars. 328 | /// 329 | /// Interpret data as UTF-8. 330 | /// The string may contain NUL terminator, depending on context. 331 | pub fn string_utf8(&mut self) -> Result { 332 | String::from_utf8(self.string()?).map_err(Error::Encoding) 333 | } 334 | 335 | /// Read remainder of the fields as string chars 336 | /// 337 | /// The strings are just binary blobs. LLVM doesn't guarantee any encoding. 338 | /// The string may contain NUL terminator, depending on context. 339 | pub fn string(&mut self) -> Result, Error> { 340 | match &mut self.ops { 341 | Ops::Abbrev { state, abbrev } => match Self::take_payload_operand(state, abbrev)? { 342 | Some(PayloadOperand::Array(el)) => { 343 | *state += 1; 344 | let len = self.cursor.read_vbr(6)? as usize; 345 | let mut out = Vec::with_capacity(len); 346 | 347 | match el { 348 | ScalarOperand::Char6 => { 349 | for _ in 0..len { 350 | if out.len() == out.capacity() { 351 | debug_assert!(false); 352 | break; 353 | } 354 | let ch = match self.cursor.read(6)? as u8 { 355 | value @ 0..=25 => value + b'a', 356 | value @ 26..=51 => value + (b'A' - 26), 357 | value @ 52..=61 => value - (52 - b'0'), 358 | 62 => b'.', 359 | 63 => b'_', 360 | _ => return Err(Error::InvalidAbbrev), 361 | }; 362 | out.push(ch); 363 | } 364 | } 365 | ScalarOperand::Fixed(width @ 6..=8) => { 366 | for _ in 0..len { 367 | if out.len() == out.capacity() { 368 | debug_assert!(false); 369 | break; 370 | } 371 | out.push(self.cursor.read(width)? as u8); 372 | } 373 | } 374 | other => { 375 | return Err(Error::UnexpectedOperand(Some(Operand::Scalar(other)))); 376 | } 377 | } 378 | Ok(out) 379 | } 380 | other => Err(Error::UnexpectedOperand(other.map(Operand::Payload))), 381 | }, 382 | Ops::Full(num_ops) => { 383 | let len = std::mem::replace(num_ops, 0); 384 | let mut out = Vec::with_capacity(len); 385 | for _ in 0..len { 386 | let ch = self.cursor.read_vbr(6)?; 387 | out.push(u8::try_from(ch).map_err(|_| Error::ValueOverflow)?); 388 | } 389 | Ok(out) 390 | } 391 | } 392 | } 393 | 394 | /// Zero-terminated string, assumes latin1 encoding 395 | pub fn zstring(&mut self) -> Result { 396 | let mut s = String::new(); 397 | while let Some(b) = self.nzu8()? { 398 | s.push(b.get() as char); 399 | } 400 | Ok(s) 401 | } 402 | 403 | /// Internal ID of this record's abbreviation, if any. 404 | /// 405 | /// This is intended only for debugging and data dumps. 406 | /// This isn't a stable identifier, and may be block-specific. 407 | #[must_use] 408 | pub fn debug_abbrev_id(&self) -> Option { 409 | match &self.ops { 410 | Ops::Abbrev { abbrev, .. } => Some(abbrev.id), 411 | Ops::Full(_) => None, 412 | } 413 | } 414 | 415 | /// For debug printing 416 | fn from_cloned_cursor<'new_cursor>( 417 | &self, 418 | cursor: &'new_cursor mut Cursor<'input>, 419 | ) -> RecordIter<'new_cursor, 'input> { 420 | RecordIter { 421 | id: self.id, 422 | ops: self.ops.clone(), 423 | cursor, 424 | } 425 | } 426 | } 427 | 428 | impl Iterator for RecordIter<'_, '_> { 429 | type Item = Result; 430 | fn next(&mut self) -> Option { 431 | self.next().transpose() 432 | } 433 | } 434 | 435 | impl Drop for RecordIter<'_, '_> { 436 | /// Must drain the remaining records to advance the cursor to the next record 437 | fn drop(&mut self) { 438 | while let Ok(Some(_)) = self.next() {} 439 | if let Ops::Abbrev { abbrev, .. } = &self.ops 440 | && abbrev.payload.is_some() 441 | { 442 | let _ = self.payload(); 443 | } 444 | } 445 | } 446 | 447 | struct RecordIterDebugFields<'c, 'i>(RefCell>); 448 | struct RecordIterDebugResult(Result); 449 | 450 | impl fmt::Debug for RecordIter<'_, '_> { 451 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 452 | let mut c = self.cursor.clone(); 453 | let fields = RecordIterDebugFields(RefCell::new(self.from_cloned_cursor(&mut c))); 454 | 455 | f.debug_struct("RecordIter") 456 | .field("id", &self.id) 457 | .field("fields", &fields) 458 | .field("ops", &self.ops) 459 | .field("cursor", &self.cursor) 460 | .finish() 461 | } 462 | } 463 | 464 | impl fmt::Debug for RecordIterDebugFields<'_, '_> { 465 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 466 | let mut iter = self.0.borrow_mut(); 467 | let mut d = f.debug_list(); 468 | d.entries(iter.by_ref().map(RecordIterDebugResult)); 469 | if let Some(p) = iter.payload().transpose() { 470 | d.entries([RecordIterDebugResult(p)]); 471 | } 472 | d.finish() 473 | } 474 | } 475 | 476 | impl fmt::Debug for RecordIterDebugResult { 477 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 478 | match &self.0 { 479 | Ok(t) => t.fmt(f), 480 | Err(e) => e.fmt(f), 481 | } 482 | } 483 | } 484 | 485 | /// Bitcode element 486 | #[derive(Debug, Clone)] 487 | pub enum BitcodeElement { 488 | /// Block 489 | Block(Block), 490 | /// Data record 491 | Record(Record), 492 | } 493 | 494 | impl BitcodeElement { 495 | /// Returns true if it is a `Block` 496 | #[must_use] 497 | pub fn is_block(&self) -> bool { 498 | matches!(self, Self::Block(_)) 499 | } 500 | 501 | /// If it is a `Block`, returns the associated block. Returns `None` otherwise. 502 | #[must_use] 503 | pub fn as_block(&self) -> Option<&Block> { 504 | match self { 505 | Self::Block(block) => Some(block), 506 | Self::Record(_) => None, 507 | } 508 | } 509 | 510 | /// If it is a `Block`, returns the associated mutable block. Returns `None` otherwise. 511 | pub fn as_block_mut(&mut self) -> Option<&mut Block> { 512 | match self { 513 | Self::Block(block) => Some(block), 514 | Self::Record(_) => None, 515 | } 516 | } 517 | 518 | /// Returns true if it is a `Record` 519 | #[must_use] 520 | pub fn is_record(&self) -> bool { 521 | matches!(self, Self::Record(_)) 522 | } 523 | 524 | /// If it is a `Record`, returns the associated record. Returns `None` otherwise. 525 | #[must_use] 526 | pub fn as_record(&self) -> Option<&Record> { 527 | match self { 528 | Self::Block(_) => None, 529 | Self::Record(record) => Some(record), 530 | } 531 | } 532 | 533 | /// If it is a `Record`, returns the associated mutable record. Returns `None` otherwise. 534 | pub fn as_record_mut(&mut self) -> Option<&mut Record> { 535 | match self { 536 | Self::Block(_) => None, 537 | Self::Record(record) => Some(record), 538 | } 539 | } 540 | } 541 | 542 | /// Block information 543 | #[derive(Debug, Clone, Default)] 544 | pub struct BlockInfo { 545 | /// Block name 546 | pub name: String, 547 | /// Data record names 548 | pub record_names: HashMap, 549 | } 550 | 551 | /// aka. Magic number 552 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] 553 | pub struct Signature { 554 | pub magic: u32, 555 | pub magic2: u32, 556 | pub version: u32, 557 | pub offset: u32, 558 | pub size: u32, 559 | pub cpu_type: u32, 560 | } 561 | 562 | impl Signature { 563 | #[must_use] 564 | pub fn parse(data: &[u8]) -> Option<(Self, &[u8])> { 565 | let (signature, remaining_data) = data.split_first_chunk::<4>()?; 566 | let magic = u32::from_le_bytes(*signature); 567 | if magic != LLVM_BITCODE_WRAPPER_MAGIC { 568 | Some(( 569 | Self { 570 | version: 0, 571 | magic, 572 | magic2: 0, 573 | offset: 4, 574 | size: remaining_data.len() as _, 575 | cpu_type: 0, 576 | }, 577 | remaining_data, 578 | )) 579 | } else { 580 | // It is a LLVM Bitcode wrapper, remove wrapper header 581 | if data.len() < 20 { 582 | return None; 583 | } 584 | let mut words = data 585 | .chunks_exact(4) 586 | .skip(1) 587 | .map(|w| u32::from_le_bytes(w.try_into().unwrap())); 588 | let version = words.next()?; 589 | let offset = words.next()?; 590 | let size = words.next()?; 591 | let cpu_id = words.next()?; 592 | let data = data.get(offset as usize..offset as usize + size as usize)?; 593 | let (magic2, remaining_data) = data.split_first_chunk::<4>()?; 594 | let magic2 = u32::from_le_bytes(*magic2); 595 | Some(( 596 | Self { 597 | version, 598 | magic, 599 | magic2, 600 | offset, 601 | size, 602 | cpu_type: cpu_id, 603 | }, 604 | remaining_data, 605 | )) 606 | } 607 | } 608 | } 609 | 610 | impl Bitcode { 611 | /// Parse bitcode from bytes 612 | /// 613 | /// Accepts both LLVM bitcode and bitcode wrapper formats 614 | pub fn new(data: &[u8]) -> Result { 615 | let (signature, stream) = Signature::parse(data).ok_or(Error::InvalidSignature(0))?; 616 | let mut reader = BitStreamReader::new(); 617 | let mut visitor = CollectingVisitor::new(); 618 | reader.read_block( 619 | Cursor::new(stream), 620 | BitStreamReader::TOP_LEVEL_BLOCK_ID, 621 | 2, 622 | &mut visitor, 623 | )?; 624 | Ok(Self { 625 | signature, 626 | elements: visitor.finalize_top_level_elements(), 627 | block_info: reader.block_info, 628 | }) 629 | } 630 | 631 | /// Read bitcode from bytes with a visitor 632 | /// 633 | /// Accepts both LLVM bitcode and bitcode wrapper formats 634 | pub fn read(data: &[u8], visitor: &mut V) -> Result<(), Error> 635 | where 636 | V: BitStreamVisitor, 637 | { 638 | let (header, stream) = Signature::parse(data).ok_or(Error::InvalidSignature(0))?; 639 | if !visitor.validate(header) { 640 | return Err(Error::InvalidSignature(header.magic)); 641 | } 642 | let mut reader = BitStreamReader::new(); 643 | reader.read_block( 644 | Cursor::new(stream), 645 | BitStreamReader::TOP_LEVEL_BLOCK_ID, 646 | 2, 647 | visitor, 648 | ) 649 | } 650 | } 651 | -------------------------------------------------------------------------------- /src/schema/blocks.rs: -------------------------------------------------------------------------------- 1 | //! From the LLVM Project, under the [Apache License v2.0 with LLVM Exceptions](https://llvm.org/LICENSE.txt) 2 | 3 | use num_enum::TryFromPrimitive; 4 | 5 | /// Block IDs 6 | /// 7 | /// The only top-level block types are MODULE, IDENTIFICATION, STRTAB and SYMTAB. 8 | #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, TryFromPrimitive)] 9 | #[repr(u8)] 10 | #[non_exhaustive] 11 | pub enum BlockId { 12 | /// `MODULE` 13 | Module = 8, 14 | 15 | // Module sub-block IDs 16 | /// `PARAMATTR` 17 | /// 18 | /// See [`AttributeCode`] 19 | ParamAttr, 20 | 21 | /// `PARAMATTR_GROUP` 22 | ParamAttrGroup, 23 | 24 | /// `CONSTANTS` 25 | Constants, 26 | 27 | /// `FUNCTION` 28 | Function, 29 | 30 | /// Obsolete. 31 | /// 32 | /// Block intended to contain information on the bitcode versioning. Can be 33 | /// used to provide better error messages when we fail to parse a bitcode file. 34 | Identification, 35 | 36 | /// `VALUE_SYMTAB` 37 | ValueSymtab, 38 | 39 | /// `METADATA` 40 | Metadata, 41 | 42 | /// `METADATA_ATTACHMENT_ID` 43 | MetadataAttachment, 44 | 45 | /// `TYPE_BLOCK_ID_NEW = 17` 46 | Type = 17, 47 | 48 | /// `USELIST` 49 | Uselist, 50 | 51 | /// `MODULE_STRTAB` 52 | ModuleStrtab, 53 | 54 | /// Obsolete. 55 | /// 56 | /// `GLOBALVAL_SUMMARY` 57 | GlobalvalSummary, 58 | 59 | /// `OPERAND_BUNDLE_TAGS` 60 | OperandBundleTags, 61 | 62 | /// `METADATA_KIND` 63 | MetadataKind, 64 | 65 | /// `STRTAB` 66 | Strtab, 67 | 68 | /// `FULL_LTO_GLOBALVAL_SUMMARY` 69 | FullLtoGlobalvalSummary, 70 | 71 | /// `SYMTAB` 72 | Symtab, 73 | 74 | /// `SYNC_SCOPE_NAMES` 75 | SyncScopeNames, 76 | } 77 | 78 | /// `OperandBundle` tag codes 79 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 80 | #[repr(u8)] 81 | #[non_exhaustive] 82 | pub enum OperandBundleTagCode { 83 | /// `TAG` 84 | /// 85 | /// [strchr x N] 86 | Tag = 1, 87 | } 88 | 89 | /// Sync scope name codes 90 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 91 | #[repr(u8)] 92 | #[non_exhaustive] 93 | pub enum SyncScopeNameCode { 94 | /// `SYNC_SCOPE_NAME` 95 | Name = 1, 96 | } 97 | 98 | /// STRTAB block codes 99 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 100 | #[repr(u8)] 101 | #[non_exhaustive] 102 | pub enum StrtabCode { 103 | /// `STRTAB_BLOB` 104 | Blob = 1, 105 | } 106 | 107 | /// SYMTAB block codes 108 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 109 | #[repr(u8)] 110 | #[non_exhaustive] 111 | pub enum SymtabCode { 112 | /// `SYMTAB_BLOB` 113 | Blob = 1, 114 | } 115 | 116 | /// `MODULE` blocks have a number of optional fields and subblocks. 117 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 118 | #[repr(u8)] 119 | #[non_exhaustive] 120 | pub enum ModuleCode { 121 | /// `VERSION` 122 | /// 123 | /// [version#] 124 | Version = 1, 125 | 126 | /// `TRIPLE` 127 | /// 128 | /// [strchr x N] 129 | Triple = 2, 130 | 131 | /// `DATALAYOUT` 132 | /// 133 | /// [strchr x N] 134 | Datalayout = 3, 135 | 136 | /// `ASM` 137 | /// 138 | /// [strchr x N] 139 | Asm = 4, 140 | 141 | /// `SECTIONNAME` 142 | /// 143 | /// [strchr x N] 144 | SectionName = 5, 145 | 146 | /// Obsolete. 147 | /// 148 | /// `DEPLIB` 149 | /// 150 | /// [strchr x N] 151 | Deplib = 6, 152 | 153 | /// `GLOBALVAR` 154 | /// 155 | /// [pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal] 156 | GlobalVar = 7, 157 | 158 | /// `FUNCTION` 159 | /// 160 | /// [type, callingconv, isproto, linkage, paramattrs, alignment, section, visibility, gc, unnamed_addr] 161 | Function = 8, 162 | 163 | /// Obsolete alias record; replaced by `MODULE_CODE_ALIAS` 164 | /// 165 | /// `ALIAS` 166 | /// 167 | /// [alias type, aliasee val#, linkage, visibility] 168 | AliasOld = 9, 169 | 170 | /// `GCNAME` 171 | /// 172 | /// [strchr x N] 173 | GCName = 11, 174 | 175 | /// `COMDAT` 176 | /// 177 | /// [selection_kind, name] 178 | Comdat = 12, 179 | 180 | /// `VSTOFFSET` 181 | /// 182 | /// [offset] 183 | VstOffset = 13, 184 | 185 | /// `ALIAS` 186 | /// 187 | /// [alias value type, addrspace, aliasee val#, linkage, visibility] 188 | Alias = 14, 189 | 190 | /// Obsolete. 191 | MetadataValuesUnused = 15, 192 | 193 | /// `SOURCE_FILENAME` 194 | /// 195 | /// [namechar x N] 196 | SourceFilename = 16, 197 | 198 | /// `HASH` 199 | /// 200 | /// [5*i32] 201 | Hash = 17, 202 | 203 | /// `IFUNC` 204 | /// 205 | /// [ifunc value type, addrspace, resolver val#, linkage, visibility] 206 | Ifunc = 18, 207 | } 208 | 209 | /// The summary section uses different codes in the per-module 210 | /// and combined index cases. 211 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 212 | #[repr(u8)] 213 | #[non_exhaustive] 214 | pub enum GlobalValueSummaryCode { 215 | /// `PERMODULE` 216 | /// 217 | /// [valueid, flags, instcount, numrefs, numrefs x valueid, n x (valueid)] 218 | PerModule = 1, 219 | 220 | /// `PERMODULE_PROFILE` 221 | /// 222 | /// [valueid, flags, instcount, numrefs, numrefs x valueid, n x (valueid, hotness+tailcall)] 223 | PerModuleProfile = 2, 224 | 225 | /// `PERMODULE_GLOBALVAR_INIT_REFS` 226 | /// 227 | /// [valueid, flags, n x valueid] 228 | PerModuleGlobalvarInitRefs = 3, 229 | 230 | /// `COMBINED` 231 | /// 232 | /// [valueid, modid, flags, instcount, numrefs, numrefs x valueid, n x (valueid)] 233 | Combined = 4, 234 | 235 | /// `COMBINED_PROFILE` 236 | /// 237 | /// [valueid, modid, flags, instcount, numrefs, numrefs x valueid, n x (valueid, hotness+tailcall)] 238 | CombinedProfile = 5, 239 | 240 | /// `COMBINED_GLOBALVAR_INIT_REFS` 241 | /// 242 | /// [valueid, modid, flags, n x valueid] 243 | CombinedGlobalvarInitRefs = 6, 244 | 245 | /// `ALIAS` 246 | /// 247 | /// [valueid, flags, valueid] 248 | Alias = 7, 249 | 250 | /// `COMBINED_ALIAS` 251 | /// 252 | /// [valueid, modid, flags, valueid] 253 | CombinedAlias = 8, 254 | 255 | /// `COMBINED_ORIGINAL_NAME` 256 | /// 257 | /// [original_name_hash] 258 | CombinedOriginalName = 9, 259 | 260 | /// `VERSION` of the summary, bumped when adding flags for instance. 261 | Version = 10, 262 | 263 | /// The list of `llvm.type.test` type identifiers used by the following function that are used 264 | /// other than by an `llvm.assume`. 265 | /// 266 | /// [n x typeid] 267 | TypeTests = 11, 268 | 269 | /// The list of virtual calls made by this function using `llvm.assume(llvm.type.test)` intrinsics 270 | /// that do not have all constant integer arguments. 271 | /// 272 | /// [n x (typeid, offset)] 273 | TypeTestAssumeVCalls = 12, 274 | 275 | /// The list of virtual calls made by this function using `llvm.type.checked.load` intrinsics 276 | /// that do not have all constant integer arguments. 277 | /// 278 | /// [n x (typeid, offset)] 279 | TypeCheckedLoadVCalls = 13, 280 | 281 | /// Identifies a virtual call made by this function using an `llvm.assume(llvm.type.test)` 282 | /// intrinsic with all constant integer arguments. 283 | /// 284 | /// [typeid, offset, n x arg] 285 | TypeTestAssumeConstVCall = 14, 286 | 287 | /// Identifies a virtual call made by this function using an `llvm.type.checked.load` intrinsic 288 | /// with all constant integer arguments. 289 | /// 290 | /// [typeid, offset, n x arg] 291 | TypeCheckedLoadConstVCall = 15, 292 | 293 | /// Assigns a GUID to a value ID. This normally appears only in combined summaries, 294 | /// but it can also appear in per-module summaries for PGO data. 295 | /// 296 | /// [valueid, guid] 297 | ValueGuid = 16, 298 | 299 | /// The list of local functions with CFI jump tables. Function names are strings in `strtab`. 300 | /// 301 | /// [n * name] 302 | CfiFunctionDefs = 17, 303 | 304 | /// The list of external functions with CFI jump tables. Function names are strings in `strtab`. 305 | /// 306 | /// [n * name] 307 | CfiFunctionDecls = 18, 308 | 309 | /// Per-module summary that also adds relative block frequency to callee info. 310 | /// 311 | /// `PERMODULE_RELBF` 312 | /// 313 | /// [valueid, flags, instcount, numrefs, numrefs x valueid, n x (valueid, relblockfreq+tailcall)] 314 | PerModuleRelBf = 19, 315 | 316 | /// Index-wide flags 317 | Flags = 20, 318 | 319 | /// Maps type identifier to summary information for that type identifier. Produced by the thin link 320 | /// (only lives in combined index). 321 | /// 322 | /// `TYPE_ID` 323 | /// 324 | /// [typeid, kind, bitwidth, align, size, bitmask, inlinebits, n x (typeid, kind, name, numrba, numrba x (numarg, numarg x arg, kind, info, byte, bit)] 325 | TypeId = 21, 326 | 327 | /// Maps type identifier to summary information for that type identifier computed from type metadata: 328 | /// the valueid of each vtable definition decorated with a type metadata for that identifier, 329 | /// and the offset from the corresponding type metadata. 330 | /// Exists in the per-module summary to provide information to thin link for index-based whole 331 | /// program devirtualization. 332 | /// 333 | /// For background see overview at . 334 | /// The type metadata includes both the type identifier and the offset of 335 | /// the address point of the type (the address held by objects of that type 336 | /// which may not be the beginning of the virtual table). Vtable definitions 337 | /// are decorated with type metadata for the types they are compatible with. 338 | /// 339 | /// `TYPE_ID_METADATA` 340 | /// 341 | /// [typeid, n x (valueid, offset)] 342 | TypeIdMetadata = 22, 343 | 344 | /// Summarizes vtable definition for use in index-based whole program devirtualization during the thin link. 345 | /// 346 | /// `PERMODULE_VTABLE_GLOBALVAR_INIT_REFS` 347 | /// 348 | /// [valueid, flags, varflags, numrefs, numrefs x valueid, n x (valueid, offset)] 349 | PerModuleVtableGlobalvarInitRefs = 23, 350 | 351 | /// The total number of basic blocks in the module. 352 | /// 353 | /// This is for pre-allocation of storage. 354 | BlockCount = 24, 355 | 356 | /// Range information for accessed offsets for every argument. 357 | /// 358 | /// [n x (paramno, range, numcalls, numcalls x (callee_guid, paramno, range))] 359 | ParamAccess = 25, 360 | 361 | /// Summary of per-module memprof callsite metadata. 362 | /// 363 | /// [valueid, n x stackidindex] 364 | PerModuleCallsiteInfo = 26, 365 | 366 | /// Summary of per-module allocation memprof metadata. 367 | /// 368 | /// [nummib, nummib x (alloc type, context radix tree index), [nummib x (numcontext x total size)]?] 369 | PerModuleAllocInfo = 27, 370 | 371 | /// Summary of combined index memprof callsite metadata. 372 | /// 373 | /// [valueid, context radix tree index, numver, numver x version] 374 | CombinedCallsiteInfo = 28, 375 | 376 | /// Summary of combined index allocation memprof metadata. 377 | /// 378 | /// [nummib, numver, nummib x (alloc type, numstackids, numstackids x stackidindex), numver x version] 379 | CombinedAllocInfo = 29, 380 | 381 | /// List of all stack ids referenced by index in the callsite and alloc infos. 382 | /// 383 | /// [n x stack id] 384 | StackIds = 30, 385 | 386 | /// List of all full stack id pairs corresponding to the total sizes recorded 387 | /// at the end of the alloc info when reporting of hinted bytes is enabled. 388 | /// We use a fixed-width array, which is more efficient as these ids typically 389 | /// are close to 64 bits in size. The max fixed width value supported is 32 390 | /// bits so each 64-bit context id hash is recorded as a pair (upper 32 bits 391 | /// first). This record must immediately precede the associated alloc info, and 392 | /// the entries must be in the exact same order as the corresponding sizes. 393 | /// 394 | /// [nummib x (numcontext x full stack id)] 395 | AllocContextIds = 31, 396 | 397 | /// Linearized radix tree of allocation contexts. 398 | /// 399 | /// [n x entry] 400 | ContextRadixTreeArray = 32, 401 | } 402 | 403 | /// `METADATA` block codes 404 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 405 | #[repr(u8)] 406 | #[non_exhaustive] 407 | pub enum MetadataCode { 408 | /// `MDSTRING` 409 | /// 410 | /// [values] 411 | StringOld = 1, 412 | 413 | /// `VALUE` 414 | /// 415 | /// [type num, value num] 416 | Value = 2, 417 | 418 | /// `NODE` 419 | /// 420 | /// [n x md num] 421 | Node = 3, 422 | 423 | /// `STRING` 424 | /// 425 | /// [values] 426 | Name = 4, 427 | 428 | /// `DISTINCT_NODE` 429 | /// 430 | /// [n x md num] 431 | DistinctNode = 5, 432 | 433 | /// `KIND` 434 | /// 435 | /// [n x [id, name]] 436 | Kind = 6, 437 | 438 | /// `LOCATION` 439 | /// 440 | /// [distinct, line, col, scope, inlined-at?] 441 | Location = 7, 442 | 443 | /// `OLD_NODE` 444 | /// 445 | /// [n x (type num, value num)] 446 | OldNode = 8, 447 | 448 | /// `OLD_FN_NODE` 449 | /// 450 | /// [n x (type num, value num)] 451 | OldFnNode = 9, 452 | 453 | /// `NAMED_NODE` 454 | /// 455 | /// [n x mdnodes] 456 | NamedNode = 10, 457 | 458 | /// `ATTACHMENT` 459 | /// 460 | /// [m x [value, [n x [id, mdnode]]] 461 | Attachment = 11, 462 | 463 | /// `GENERIC_DEBUG` 464 | /// 465 | /// [distinct, tag, vers, header, n x md num] 466 | GenericDebug = 12, 467 | 468 | /// `SUBRANGE` 469 | /// 470 | /// [distinct, count, lo] 471 | Subrange = 13, 472 | 473 | /// `ENUMERATOR` 474 | /// 475 | /// [isUnsigned|distinct, value, name] 476 | Enumerator = 14, 477 | 478 | /// `BASIC_TYPE` 479 | /// 480 | /// [distinct, tag, name, size, align, enc] 481 | BasicType = 15, 482 | 483 | /// `FILE` 484 | /// 485 | /// [distinct, filename, directory, checksumkind, checksum] 486 | File = 16, 487 | 488 | /// `DERIVED_TYPE` 489 | /// 490 | /// [distinct, ...] 491 | DerivedType = 17, 492 | 493 | /// `COMPOSITE_TYPE` 494 | /// 495 | /// [distinct, ...] 496 | CompositeType = 18, 497 | 498 | /// `SUBROUTINE_TYPE` 499 | /// 500 | /// [distinct, flags, types, cc] 501 | SubroutineType = 19, 502 | 503 | /// `COMPILE_UNIT` 504 | /// 505 | /// [distinct, ...] 506 | CompileUnit = 20, 507 | 508 | /// `SUBPROGRAM` 509 | /// 510 | /// [distinct, ...] 511 | Subprogram = 21, 512 | 513 | /// `LEXICAL_BLOCK` 514 | /// 515 | /// [distinct, scope, file, line, column] 516 | LexicalBlock = 22, 517 | 518 | /// `LEXICAL_BLOCK_FILE` 519 | /// 520 | /// [distinct, scope, file, discriminator] 521 | LexicalBlockFile = 23, 522 | 523 | /// `NAMESPACE` 524 | /// 525 | /// [distinct, scope, file, name, line, exportSymbols] 526 | Namespace = 24, 527 | 528 | /// `TEMPLATE_TYPE` 529 | /// 530 | /// [distinct, scope, name, type, ...] 531 | TemplateType = 25, 532 | 533 | /// `TEMPLATE_VALUE` 534 | /// 535 | /// [distinct, scope, name, type, value, ...] 536 | TemplateValue = 26, 537 | 538 | /// `GLOBAL_VAR` 539 | /// 540 | /// [distinct, ...] 541 | GlobalVar = 27, 542 | 543 | /// `LOCAL_VAR` 544 | /// 545 | /// [distinct, ...] 546 | LocalVar = 28, 547 | 548 | /// `EXPRESSION` 549 | /// 550 | /// [distinct, n x element] 551 | Expression = 29, 552 | 553 | /// `OBJC_PROPERTY` 554 | /// 555 | /// [distinct, name, file, line, ...] 556 | ObjcProperty = 30, 557 | 558 | /// `IMPORTED_ENTITY` 559 | /// 560 | /// [distinct, tag, scope, entity, line, name] 561 | ImportedEntity = 31, 562 | 563 | /// `MODULE` 564 | /// 565 | /// [distinct, scope, name, ...] 566 | Module = 32, 567 | 568 | /// `MACRO` 569 | /// 570 | /// [distinct, macinfo, line, name, value] 571 | Macro = 33, 572 | 573 | /// `MACRO_FILE` 574 | /// 575 | /// [distinct, macinfo, line, file, ...] 576 | MacroFile = 34, 577 | 578 | /// `STRINGS` 579 | /// 580 | /// [count, offset] blob([lengths][chars]) 581 | Strings = 35, 582 | 583 | /// `GLOBAL_DECL_ATTACHMENT` 584 | /// 585 | /// [valueid, n x [id, mdnode]] 586 | GlobalDeclAttachment = 36, 587 | 588 | /// `GLOBAL_VAR_EXPR` 589 | /// 590 | /// [distinct, var, expr] 591 | GlobalVarExpr = 37, 592 | 593 | /// `INDEX_OFFSET` 594 | /// 595 | /// [offset] 596 | IndexOffset = 38, 597 | 598 | /// `INDEX` 599 | /// 600 | /// [bitpos] 601 | Index = 39, 602 | 603 | /// `LABEL` 604 | /// 605 | /// [distinct, scope, name, file, line] 606 | Label = 40, 607 | 608 | /// `STRING_TYPE` 609 | /// 610 | /// [distinct, name, size, align, ..] 611 | StringType = 41, 612 | 613 | /// `COMMON_BLOCK` 614 | /// 615 | /// [distinct, scope, name, variable, ..] 616 | CommonBlock = 44, 617 | 618 | /// `GENERIC_SUBRANGE` 619 | /// 620 | /// [distinct, count, lo, up, stride] 621 | GenericSubrange = 45, 622 | 623 | /// `ARG_LIST` 624 | /// 625 | /// [n x [type num, value num]] 626 | ArgList = 46, 627 | 628 | /// `ASSIGN_ID` 629 | /// 630 | /// [distinct, ...] 631 | AssignId = 47, 632 | } 633 | 634 | /// `USELISTBLOCK` encoded values for a value's use-list. 635 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 636 | #[repr(u8)] 637 | #[non_exhaustive] 638 | pub enum UselistCode { 639 | /// `DEFAULT` 640 | /// 641 | /// [index..., value-id] 642 | Default = 1, 643 | 644 | /// `BB` 645 | /// 646 | /// [index..., bb-id] 647 | BB = 2, 648 | } 649 | 650 | /// Identification block contains a string that describes the producer details, 651 | /// and an epoch that defines the auto-upgrade capability. 652 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 653 | #[repr(u8)] 654 | #[non_exhaustive] 655 | pub enum IdentificationCode { 656 | /// `IDENTIFICATION` 657 | /// 658 | /// [strchr x N] 659 | String = 1, 660 | 661 | /// `EPOCH` 662 | /// 663 | /// [epoch#] 664 | Epoch = 2, 665 | } 666 | 667 | /// `PARAMATTR` blocks have code for defining a parameter attribute set. 668 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 669 | #[repr(u8)] 670 | #[non_exhaustive] 671 | pub enum AttributeCode { 672 | /// Obsolete. 673 | /// 674 | /// `ENTRY_OLD` 675 | /// 676 | /// [paramidx0, attr0, paramidx1, attr1...] 677 | EntryOld = 1, 678 | 679 | /// `ENTRY` 680 | /// 681 | /// [attrgrp0, attrgrp1, ...] 682 | Entry = 2, 683 | 684 | /// `GRP_CODE_ENTRY` 685 | /// 686 | /// [grpid, idx, attr0, attr1, ...] 687 | GroupEntry = 3, 688 | } 689 | 690 | /// Value symbol table codes. 691 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 692 | #[repr(u8)] 693 | #[non_exhaustive] 694 | pub enum ValueSymtabCode { 695 | /// `VST_ENTRY` 696 | /// 697 | /// [valueid, namechar x N] 698 | Entry = 1, 699 | 700 | /// `VST_BBENTRY` 701 | /// 702 | /// [bbid, namechar x N] 703 | BbEntry = 2, 704 | 705 | /// `VST_FNENTRY` 706 | /// 707 | /// Unused when strtab is present 708 | /// 709 | /// [valueid, offset, namechar x N] 710 | FnEntry = 3, 711 | 712 | /// Obsolete. 713 | /// 714 | /// `VST_COMBINED_ENTRY` 715 | /// 716 | /// [valueid, refguid] 717 | CombinedEntry = 5, 718 | } 719 | 720 | /// `TYPE` blocks have codes for each type primitive they use. 721 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 722 | #[repr(u8)] 723 | #[non_exhaustive] 724 | pub enum TypeCode { 725 | /// `NUMENTRY` 726 | /// 727 | /// Allows pre-allocating storage 728 | /// 729 | /// [numentries] 730 | NumEntry = 1, 731 | 732 | /// `VOID` 733 | Void = 2, 734 | 735 | /// `FLOAT` 736 | Float = 3, 737 | 738 | /// `DOUBLE` 739 | Double = 4, 740 | 741 | /// `LABEL` 742 | Label = 5, 743 | 744 | /// `OPAQUE` 745 | Opaque = 6, 746 | 747 | /// `INTEGER` 748 | /// 749 | /// [width] 750 | Integer = 7, 751 | 752 | /// Typed pointers are obsolete. 753 | /// 754 | /// [pointee type] 755 | Pointer = 8, 756 | 757 | /// Obsolete 758 | /// 759 | /// [vararg, attrid, retty, paramty x N] 760 | FunctionOld = 9, 761 | 762 | /// `HALF` 763 | Half = 10, 764 | 765 | /// `ARRAY` 766 | /// 767 | /// [num_elements, elements_type] 768 | Array = 11, 769 | 770 | /// `VECTOR` 771 | /// 772 | /// [num_elements, elements_type] 773 | Vector = 12, 774 | 775 | // These are not with the other floating point types because they're 776 | // a late addition, and putting them in the right place breaks 777 | // binary compatibility. 778 | /// X86 LONG DOUBLE 779 | X86Fp80 = 13, 780 | 781 | /// LONG DOUBLE (112 bit mantissa) 782 | Fp128 = 14, 783 | 784 | /// PPC LONG DOUBLE (2 doubles) 785 | PpcFp128 = 15, 786 | 787 | /// `METADATA` 788 | Metadata = 16, 789 | 790 | /// Unused 791 | /// 792 | /// `X86 MMX` 793 | X86Mmx = 17, 794 | 795 | /// `STRUCT_ANON` 796 | /// 797 | /// [ispacked, elements_type x N] 798 | StructAnon = 18, 799 | 800 | /// `STRUCT_NAME` 801 | /// 802 | /// [strchr x N] 803 | StructName = 19, 804 | 805 | /// `STRUCT_NAMED` 806 | /// 807 | /// [ispacked, elements_type x N] 808 | StructNamed = 20, 809 | 810 | /// `FUNCTION` 811 | /// 812 | /// [vararg, retty, paramty x N] 813 | Function = 21, 814 | 815 | /// `TOKEN` 816 | Token = 22, 817 | 818 | /// `BRAIN FLOATING POINT` 819 | BFloat = 23, 820 | 821 | /// `X86 AMX` 822 | X86Amx = 24, 823 | 824 | /// `OPAQUE_POINTER` 825 | /// 826 | /// [addrspace] 827 | OpaquePointer = 25, 828 | 829 | /// `TARGET_TYPE` 830 | TargetType = 26, 831 | } 832 | 833 | /// The constants block (`CONSTANTS_BLOCK_ID` describes emission for each 834 | /// constant and maintains an implicit current type value. 835 | #[derive(PartialEq, Debug, Clone, Copy, TryFromPrimitive)] 836 | #[repr(u8)] 837 | #[non_exhaustive] 838 | pub enum ConstantsCodes { 839 | /// `SETTYPE` 840 | /// 841 | /// The initial implicit type is `i32` 842 | /// 843 | /// [typeid] 844 | Settype = 1, 845 | 846 | /// `NULL` 847 | Null = 2, 848 | 849 | /// `UNDEF` 850 | Undef = 3, 851 | 852 | /// `INTEGER` 853 | /// 854 | /// [intval] 855 | Integer = 4, 856 | 857 | /// `WIDE_INTEGER` 858 | /// 859 | /// [n x intval] 860 | WideInteger = 5, 861 | 862 | /// `FLOAT` 863 | /// 864 | /// [fpval] 865 | Float = 6, 866 | 867 | /// `AGGREGATE` 868 | /// 869 | /// [n x value number] 870 | Aggregate = 7, 871 | 872 | /// `STRING` 873 | /// 874 | /// [values] 875 | String = 8, 876 | 877 | /// `CSTRING` 878 | /// 879 | /// [values] 880 | CString = 9, 881 | 882 | /// `CE_BINOP` 883 | /// 884 | /// [opcode, opval, opval] 885 | BinOp = 10, 886 | 887 | /// `CE_CAST` 888 | /// 889 | /// [opcode, opty, opval] 890 | Cast = 11, 891 | 892 | /// Obsolete “constant expression” GEP record; replaced by `CST_CODE_CE_GEP` 893 | /// 894 | /// `CE_GEP` 895 | /// 896 | /// [n x operands] 897 | GepOld = 12, 898 | 899 | /// Unused 900 | /// 901 | /// `CE_SELECT` 902 | /// 903 | /// [opval, opval, opval] 904 | Select = 13, 905 | 906 | /// `CE_EXTRACTELT` 907 | /// 908 | /// [opty, opval, opval] 909 | ExtractElt = 14, 910 | 911 | /// `CE_INSERTELT` 912 | /// 913 | /// [opval, opval, opval] 914 | InsertElt = 15, 915 | 916 | /// `CE_SHUFFLEVEC` 917 | /// 918 | /// [opval, opval, opval] 919 | ShuffleVec = 16, 920 | 921 | /// Unused. 922 | /// 923 | /// `CE_CMP` 924 | /// 925 | /// [opty, opval, opval, pred] 926 | Cmp = 17, 927 | 928 | /// Obsolete inline asm record variant 929 | /// 930 | /// `INLINEASM` 931 | /// 932 | /// [sideeffect|alignstack, asmstr, onststr] 933 | InlineasmOld = 18, 934 | 935 | /// `SHUFVEC_EX` 936 | /// 937 | /// [opty, opval, opval, opval] 938 | ShufVecEx = 19, 939 | 940 | /// Obsolete. 941 | /// 942 | /// `INBOUNDS_GEP` 943 | /// 944 | /// [n x operands] 945 | InboundsGep = 20, 946 | 947 | /// `BLOCKADDRESS` 948 | /// 949 | /// [fnty, fnval, bb#] 950 | BlockAddress = 21, 951 | 952 | /// `DATA` 953 | /// 954 | /// [n x elements] 955 | Data = 22, 956 | 957 | /// Obsolete inline asm encoding variant 958 | /// 959 | /// `INLINEASM` 960 | /// 961 | /// [sideeffect|alignstack|asmdialect, smstr, onststr] 962 | InlineAsmOld2 = 23, 963 | 964 | /// [opty, flags, n x operands] 965 | GepWithInrangeIndexOld = 24, 966 | 967 | /// `CST_CODE_CE_UNOP` 968 | /// 969 | /// [opcode, opval] 970 | UnOp = 25, 971 | 972 | /// `POISON` 973 | Poison = 26, 974 | 975 | /// `DSO_LOCAL_EQUIVALENT` 976 | /// 977 | /// [gvty, gv] 978 | DsoLocalEquivalent = 27, 979 | 980 | /// Obsolete variant for inline asm 981 | /// 982 | /// `INLINEASM` 983 | /// 984 | /// [sideeffect|alignstack|asmdialect|unwind, asmstr, onststr] 985 | InlineAsmOld3 = 28, 986 | 987 | /// `NO_CFI` 988 | /// 989 | /// [fty, f] 990 | NoCfiValue = 29, 991 | 992 | /// `INLINEASM` 993 | /// 994 | /// [fnty, sideeffect|alignstack|asmdialect|unwind, asmstr, onststr] 995 | InlineAsm = 30, 996 | 997 | /// `CST_CODE_CE_GEP_WITH_INRANGE` 998 | /// [opty, flags, range, n x operands] 999 | GepWithInrange = 31, 1000 | 1001 | /// `CST_CODE_CE_GEP` 1002 | /// [opty, flags, n x operands] 1003 | Gep = 32, 1004 | 1005 | /// `CST_CODE_PTRAUTH` 1006 | /// [ptr, key, disc, addrdisc] 1007 | PtrAuth = 33, 1008 | } 1009 | 1010 | // The function body block (`FUNCTION_BLOCK_ID`) describes function bodies. It 1011 | // can contain a constant block (`CONSTANTS_BLOCK_ID`). 1012 | #[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)] 1013 | #[repr(u8)] 1014 | #[non_exhaustive] 1015 | pub enum FunctionCode { 1016 | /// `DECLAREBLOCKS` 1017 | /// 1018 | /// [n] 1019 | DeclareBlocks = 1, 1020 | 1021 | /// `BINOP` 1022 | /// 1023 | /// [opcode, ty, opval, opval] 1024 | BinOp = 2, 1025 | 1026 | /// `CAST` 1027 | /// 1028 | /// [opcode, ty, opty, opval] 1029 | Cast = 3, 1030 | 1031 | /// Old GEP instruction record; superseded by `FUNC_CODE_INST_GEP` 1032 | /// 1033 | /// `GEP` 1034 | /// 1035 | /// [n x operands] 1036 | GepOld = 4, 1037 | 1038 | /// Unused. 1039 | /// 1040 | /// `SELECT` 1041 | /// 1042 | /// [ty, opval, opval, opval] 1043 | SelectOld = 5, 1044 | 1045 | /// `EXTRACTELT` 1046 | /// 1047 | /// [opty, opval, opval] 1048 | ExtractElt = 6, 1049 | 1050 | /// `INSERTELT` 1051 | /// 1052 | /// [ty, opval, opval, opval] 1053 | InsertElt = 7, 1054 | 1055 | /// `SHUFFLEVEC` 1056 | /// 1057 | /// [ty, opval, opval, opval] 1058 | ShuffleVec = 8, 1059 | 1060 | /// `CMP` 1061 | /// 1062 | /// [opty, opval, opval, pred] 1063 | Cmp = 9, 1064 | 1065 | /// `RET` 1066 | /// 1067 | /// [opty, pval] 1068 | Ret = 10, 1069 | 1070 | /// `BR` 1071 | /// 1072 | /// [bb#, bb#, cond] or [bb#] 1073 | Br = 11, 1074 | 1075 | /// `SWITCH` 1076 | /// 1077 | /// [opty, op0, op1, ...] 1078 | Switch = 12, 1079 | 1080 | /// `INVOKE` 1081 | /// 1082 | /// [attr, fnty, op0, op1, ...] 1083 | Invoke = 13, 1084 | 1085 | /// `UNREACHABLE` 1086 | Unreachable = 15, 1087 | 1088 | /// `PHI` 1089 | /// 1090 | /// [ty, val0, b0, ...] 1091 | Phi = 16, 1092 | 1093 | /// `ALLOCA` 1094 | /// 1095 | /// [instty, opty, op, align] 1096 | Alloca = 19, 1097 | 1098 | /// `LOAD` 1099 | /// 1100 | /// [opty, op, align, vol] 1101 | Load = 20, 1102 | 1103 | /// `VAARG` 1104 | /// 1105 | /// [valistty, valist, instty] 1106 | VaArg = 23, 1107 | 1108 | // This store code encodes the pointer type, rather than the value type 1109 | // this is so information only available in the pointer type (e.g. address 1110 | // spaces) is retained. 1111 | /// Obsolete store record; replaced by `FUNC_CODE_INST_STORE` 1112 | /// 1113 | /// `STORE` 1114 | /// 1115 | /// [ptrty, tr, al, align, vol] 1116 | StoreOld = 24, 1117 | 1118 | /// `EXTRACTVAL` 1119 | /// 1120 | /// [n x operands] 1121 | ExtractVal = 26, 1122 | 1123 | /// `INSERTVAL` 1124 | /// 1125 | /// [n x operands] 1126 | InsertVal = 27, 1127 | 1128 | /// `CMP2` 1129 | /// 1130 | /// fcmp/icmp returning `Int1TY` or vector of `Int1Ty`. Same as `CMP`, exists to 1131 | /// support legacy vicmp/vfcmp instructions. 1132 | /// 1133 | /// [opty, opval, opval, pred] 1134 | Cmp2 = 28, 1135 | 1136 | /// `VSELECT` 1137 | /// 1138 | /// new select on i1 or [N x i1] 1139 | /// 1140 | /// [ty, pval, pval, redty, red] 1141 | Vselect = 29, 1142 | 1143 | /// Obsolete inbounds GEP record; replaced by the newer `FUNC_CODE_INST_GEP` 1144 | /// 1145 | /// `INBOUNDS_GEP` 1146 | /// 1147 | /// [n x operands] 1148 | InboundsGepOld = 30, 1149 | 1150 | /// `INDIRECTBR` 1151 | /// 1152 | /// [opty, op0, op1, ...] 1153 | IndirectBr = 31, 1154 | 1155 | /// `DEBUG_LOC_AGAIN` 1156 | DebugLocAgain = 33, 1157 | 1158 | /// `CALL` 1159 | /// 1160 | /// [attr, cc, fnty, fnid, args...] 1161 | Call = 34, 1162 | 1163 | /// `DEBUG_LOC` 1164 | /// 1165 | /// [Line, ol, copeVal, IAVal] 1166 | DebugLoc = 35, 1167 | 1168 | /// `FENCE` 1169 | /// 1170 | /// [ordering, synchscope] 1171 | Fence = 36, 1172 | 1173 | /// Old cmpxchg record; replaced by `FUNC_CODE_INST_CMPXCHG` 1174 | /// 1175 | /// `CMPXCHG` 1176 | /// 1177 | /// [ptrty, ptr, cmp, val, vol, ordering, synchscope, failure_ordering?, weak?] 1178 | CmpxchgOld = 37, 1179 | 1180 | /// Obsolete atomicrmw record; replaced by `FUNC_CODE_INST_ATOMICRMW` 1181 | /// 1182 | /// `ATOMICRMW` 1183 | /// 1184 | /// [ptrty, tr, al, operation, align, vol, ordering, synchscope] 1185 | AtomicRmwOld = 38, 1186 | 1187 | /// `RESUME` 1188 | /// 1189 | /// [opval] 1190 | Resume = 39, 1191 | 1192 | /// Obsolete landingpad record; replaced by `FUNC_CODE_INST_LANDINGPAD` 1193 | /// 1194 | /// `LANDINGPAD` 1195 | /// 1196 | /// [ty, al, al, um, d0, al0...] 1197 | LandingPadOld = 40, 1198 | 1199 | /// `LOAD` 1200 | /// 1201 | /// [opty, op, align, vol, ordering, synchscope] 1202 | LoadAtomic = 41, 1203 | 1204 | /// Obsolete store-atomic record; replaced by `FUNC_CODE_INST_STOREATOMIC` 1205 | /// 1206 | /// `STORE` 1207 | /// 1208 | /// [ptrty, tr, al, align, vol ordering, synchscope] 1209 | StoreAtomicOld = 42, 1210 | 1211 | /// `GEP` 1212 | /// 1213 | /// [inbounds, n x operands] 1214 | Gep = 43, 1215 | 1216 | /// `STORE` 1217 | /// 1218 | /// [ptrty, tr, alty, al, align, vol] 1219 | Store = 44, 1220 | 1221 | /// `STORE` 1222 | /// 1223 | /// [ptrty, tr, al, align, vol] 1224 | StoreAtomic = 45, 1225 | 1226 | /// `CMPXCHG` 1227 | /// 1228 | /// [ptrty, ptr, cmp, val, vol, success_ordering, synchscope, failure_ordering, weak] 1229 | Cmpxchg = 46, 1230 | 1231 | /// `LANDINGPAD` 1232 | /// 1233 | /// [ty, al, um, d0, al0...] 1234 | LandingPad = 47, 1235 | 1236 | /// `CLEANUPRET` 1237 | /// 1238 | /// [val] or [val, b#] 1239 | CleanupRet = 48, 1240 | 1241 | /// `CATCHRET` 1242 | /// 1243 | /// [val, b#] 1244 | CatchRet = 49, 1245 | 1246 | /// `CATCHPAD` 1247 | /// 1248 | /// [bb#, b#, um, rgs...] 1249 | CatchPad = 50, 1250 | 1251 | /// `CLEANUPPAD` 1252 | /// 1253 | /// [num, rgs...] 1254 | CleanupPad = 51, 1255 | 1256 | /// `CATCHSWITCH` 1257 | /// 1258 | /// [num, rgs...] or [num, rgs..., b] 1259 | CatchSwitch = 52, 1260 | 1261 | /// `OPERAND_BUNDLE` 1262 | /// 1263 | /// [tag#, value...] 1264 | OperandBundle = 55, 1265 | 1266 | /// `UNOP` 1267 | /// 1268 | /// [opcode, ty, opval] 1269 | UnOp = 56, 1270 | 1271 | /// `CALLBR` 1272 | /// 1273 | /// [attr, cc, norm, transfs, fnty, fnid, args...] 1274 | CallBr = 57, 1275 | 1276 | /// `FREEZE` 1277 | /// 1278 | /// [opty, opval] 1279 | Freeze = 58, 1280 | 1281 | /// `ATOMICRMW` 1282 | /// 1283 | /// [ptrty, ptr, valty, val, operation, align, vol, ordering, synchscope] 1284 | AtomicRmw = 59, 1285 | 1286 | /// `BLOCKADDR_USERS` 1287 | /// 1288 | /// [value...] 1289 | BlockaddrUsers = 60, 1290 | 1291 | /// [DILocation, DILocalVariable, DIExpression, ValueAsMetadata] 1292 | DebugRecordValue = 61, 1293 | 1294 | /// [DILocation, DILocalVariable, DIExpression, ValueAsMetadata] 1295 | DebugRecordDeclare = 62, 1296 | 1297 | /// [DILocation, DILocalVariable, DIExpression, ValueAsMetadata, DIAssignID, DIExpression (addr), ValueAsMetadata (addr)] 1298 | DebugRecordAssign = 63, 1299 | 1300 | /// [DILocation, DILocalVariable, DIExpression, Value] 1301 | DebugRecordValueSimple = 64, 1302 | 1303 | /// [DILocation, DILabel] 1304 | DebugRecordLabel = 65, 1305 | } 1306 | 1307 | /// The module path symbol table only has one code (`MST_CODE_ENTRY`). 1308 | #[derive(Debug, Clone, Copy, TryFromPrimitive)] 1309 | #[repr(u8)] 1310 | #[non_exhaustive] 1311 | pub enum ModulePathSymtabCode { 1312 | /// `MST_CODE_ENTRY` 1313 | /// 1314 | /// [modid, namechar x N] 1315 | Entry = 1, 1316 | 1317 | /// `MST_CODE_HASH` 1318 | /// 1319 | /// [5*i32] 1320 | Hash = 2, 1321 | } 1322 | -------------------------------------------------------------------------------- /examples/llvm_bca.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | use llvm_bitcode::BitStreamReader; 3 | use llvm_bitcode::bitcode::Signature; 4 | use llvm_bitcode::read::BlockItem; 5 | use llvm_bitcode::read::BlockIter; 6 | use llvm_bitcode::read::Error; 7 | use num_enum::TryFromPrimitive; 8 | 9 | fn main() { 10 | let path = std::env::args() 11 | .nth(1) 12 | .expect("Provide file path to a .bc file"); 13 | let file = std::fs::read(&path).unwrap(); 14 | 15 | let mut reader = BitStreamReader::new(); 16 | let (sig, bitcode) = Signature::parse(&file).unwrap(); 17 | 18 | println!( 19 | "", 20 | sig.magic, sig.version, sig.offset, sig.size, sig.cpu_type 21 | ); 22 | iter_block(reader.iter_bitcode(bitcode), 0).unwrap(); 23 | } 24 | 25 | fn iter_block(mut block: BlockIter, depth: usize) -> Result<(), Error> { 26 | let outer_block_id = block.id; 27 | while let Some(b) = block.next()? { 28 | match b { 29 | BlockItem::Block(b) => { 30 | let tag_name = block_tag_name(b.id as _); 31 | println!( 32 | "{:indent$}<{tag_name} NumWords={nw} BlockCodeSize={ab}>", 33 | "", 34 | nw = b.debug_data_len().unwrap_or(0) / 4, 35 | ab = b.debug_abbrev_width(), 36 | indent = depth * 2 37 | ); 38 | iter_block(b, depth + 1)?; 39 | println!("{:indent$}", "", indent = depth * 2); 40 | } 41 | BlockItem::Record(mut r) => { 42 | let tag_name = record_tag_name(outer_block_id as _, r.id as _); 43 | print!("{:indent$}<{tag_name}", "", indent = depth * 2); 44 | if let Some(a) = r.debug_abbrev_id() { 45 | print!(" abbrevid={a}"); 46 | } 47 | let fields = r 48 | .by_ref() 49 | .map(|f| f.map(|f| f as i64)) 50 | .collect::, _>>()?; 51 | for (i, &op) in fields.iter().enumerate() { 52 | print!(" op{i}={op}"); 53 | } 54 | let payload: Result<_, _> = r.payload(); 55 | match payload { 56 | Ok(Some(llvm_bitcode::bitcode::Payload::Array(a))) => { 57 | for (i, op) in a.iter().enumerate() { 58 | print!(" op{}={op}", i + fields.len()); 59 | } 60 | if !a.is_empty() && a.iter().all(|&c| (c as u8) >= 0x20 && (c as u8) < 0x7F) 61 | { 62 | // lol bug in the original 63 | let s: String = a.iter().map(|&c| c as u8 as char).collect(); 64 | println!("/> record string = '{s}'"); 65 | } else { 66 | println!("/>"); 67 | } 68 | } 69 | Ok(Some(llvm_bitcode::bitcode::Payload::Char6String(s))) => { 70 | for (i, op) in s.chars().enumerate() { 71 | print!(" op{}={}", i + fields.len(), op as u32); 72 | } 73 | if s.is_empty() { 74 | println!("/>"); 75 | } else { 76 | println!("/> record string = '{s}'"); 77 | } 78 | } 79 | Ok(None) => { 80 | if r.debug_abbrev_id().is_some() 81 | && fields.len() > 1 82 | && fields.iter().skip(1).all(|&c| (0x20..0x7F).contains(&c)) 83 | { 84 | let s: String = 85 | fields.iter().skip(1).map(|&c| c as u8 as char).collect(); 86 | println!("/> record string = '{s}'"); 87 | } else { 88 | println!("/>"); 89 | } 90 | } 91 | Ok(Some(llvm_bitcode::bitcode::Payload::Blob(b))) => { 92 | if b.len() < 10000 && b.iter().all(|&c| (0x20..0x7F).contains(&c)) { 93 | println!("/> blob data = {}", String::from_utf8_lossy(&b)); 94 | } else { 95 | print!("/> blob data = "); 96 | if b.len() > 50 { 97 | print!("unprintable, {} bytes.", b.len()); 98 | } else { 99 | print!("'"); 100 | for b in b { 101 | print!("{b:02x}"); 102 | } 103 | print!("'"); 104 | } 105 | println!(); 106 | } 107 | } 108 | Err(err) => print!("/> payload_err={err}"), 109 | } 110 | } 111 | } 112 | } 113 | Ok(()) 114 | } 115 | 116 | fn block_tag_name(id: u32) -> &'static str { 117 | use Blocks::*; 118 | match Blocks::try_from(id).unwrap() { 119 | MODULE_BLOCK_ID => "MODULE_BLOCK", 120 | PARAMATTR_BLOCK_ID => "PARAMATTR_BLOCK", 121 | PARAMATTR_GROUP_BLOCK_ID => "PARAMATTR_GROUP_BLOCK_ID", 122 | CONSTANTS_BLOCK_ID => "CONSTANTS_BLOCK", 123 | FUNCTION_BLOCK_ID => "FUNCTION_BLOCK", 124 | IDENTIFICATION_BLOCK_ID => "IDENTIFICATION_BLOCK_ID", 125 | VALUE_SYMTAB_BLOCK_ID => "VALUE_SYMTAB", 126 | METADATA_BLOCK_ID => "METADATA_BLOCK", 127 | METADATA_ATTACHMENT_ID => "METADATA_ATTACHMENT_BLOCK", 128 | TYPE_BLOCK_ID_NEW => "TYPE_BLOCK_ID", 129 | USELIST_BLOCK_ID => "USELIST_BLOCK", 130 | MODULE_STRTAB_BLOCK_ID => "MODULE_STRTAB_BLOCK", 131 | GLOBALVAL_SUMMARY_BLOCK_ID => "GLOBALVAL_SUMMARY_BLOCK", 132 | OPERAND_BUNDLE_TAGS_BLOCK_ID => "OPERAND_BUNDLE_TAGS_BLOCK", 133 | METADATA_KIND_BLOCK_ID => "METADATA_KIND_BLOCK", 134 | STRTAB_BLOCK_ID => "STRTAB_BLOCK", 135 | FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID => "FULL_LTO_GLOBALVAL_SUMMARY_BLOCK", 136 | SYMTAB_BLOCK_ID => "SYMTAB_BLOCK", 137 | SYNC_SCOPE_NAMES_BLOCK_ID => "UnknownBlock26", //"SYNC_SCOPE_NAMES_BLOCK", 138 | } 139 | } 140 | 141 | fn record_tag_name(block: u32, record: u32) -> &'static str { 142 | match Blocks::try_from(block).unwrap() { 143 | Blocks::MODULE_BLOCK_ID => match ModuleCodes::try_from(record).unwrap() { 144 | ModuleCodes::MODULE_CODE_VERSION => "VERSION", 145 | ModuleCodes::MODULE_CODE_TRIPLE => "TRIPLE", 146 | ModuleCodes::MODULE_CODE_DATALAYOUT => "DATALAYOUT", 147 | ModuleCodes::MODULE_CODE_ASM => "ASM", 148 | ModuleCodes::MODULE_CODE_SECTIONNAME => "SECTIONNAME", 149 | ModuleCodes::MODULE_CODE_DEPLIB => "DEPLIB", 150 | ModuleCodes::MODULE_CODE_GLOBALVAR => "GLOBALVAR", 151 | ModuleCodes::MODULE_CODE_FUNCTION => "FUNCTION", 152 | ModuleCodes::MODULE_CODE_ALIAS => "ALIAS", 153 | ModuleCodes::MODULE_CODE_GCNAME => "GCNAME", 154 | ModuleCodes::MODULE_CODE_COMDAT => "COMDAT", 155 | ModuleCodes::MODULE_CODE_VSTOFFSET => "VSTOFFSET", 156 | ModuleCodes::MODULE_CODE_METADATA_VALUES_UNUSED => "METADATA_VALUES_UNUSED", 157 | ModuleCodes::MODULE_CODE_SOURCE_FILENAME => "SOURCE_FILENAME", 158 | ModuleCodes::MODULE_CODE_HASH => "HASH", 159 | ModuleCodes::MODULE_CODE_ALIAS_OLD | ModuleCodes::MODULE_CODE_IFUNC => todo!(), 160 | }, 161 | Blocks::IDENTIFICATION_BLOCK_ID => match IdentificationCodes::try_from(record).unwrap() { 162 | IdentificationCodes::IDENTIFICATION_CODE_STRING => "STRING", 163 | IdentificationCodes::IDENTIFICATION_CODE_EPOCH => "EPOCH", 164 | }, 165 | crate::Blocks::PARAMATTR_GROUP_BLOCK_ID | crate::Blocks::PARAMATTR_BLOCK_ID => { 166 | match AttributeCodes::try_from(record).unwrap() { 167 | AttributeCodes::PARAMATTR_CODE_ENTRY_OLD => "ENTRY_OLD", 168 | AttributeCodes::PARAMATTR_CODE_ENTRY => "ENTRY", 169 | AttributeCodes::PARAMATTR_GRP_CODE_ENTRY => "ENTRY", 170 | } 171 | } 172 | Blocks::TYPE_BLOCK_ID_NEW => match TypeCodes::try_from(record).unwrap() { 173 | TypeCodes::TYPE_CODE_NUMENTRY => "NUMENTRY", 174 | TypeCodes::TYPE_CODE_VOID => "VOID", 175 | TypeCodes::TYPE_CODE_FLOAT => "FLOAT", 176 | TypeCodes::TYPE_CODE_DOUBLE => "DOUBLE", 177 | TypeCodes::TYPE_CODE_LABEL => "LABEL", 178 | TypeCodes::TYPE_CODE_OPAQUE => "OPAQUE", 179 | TypeCodes::TYPE_CODE_INTEGER => "INTEGER", 180 | TypeCodes::TYPE_CODE_POINTER => "POINTER", 181 | TypeCodes::TYPE_CODE_HALF => "HALF", 182 | TypeCodes::TYPE_CODE_ARRAY => "ARRAY", 183 | TypeCodes::TYPE_CODE_VECTOR => "VECTOR", 184 | TypeCodes::TYPE_CODE_X86_FP80 => "X86_FP80", 185 | TypeCodes::TYPE_CODE_FP128 => "FP128", 186 | TypeCodes::TYPE_CODE_PPC_FP128 => "PPC_FP128", 187 | TypeCodes::TYPE_CODE_METADATA => "METADATA", 188 | TypeCodes::TYPE_CODE_X86_MMX => "X86_MMX", 189 | TypeCodes::TYPE_CODE_STRUCT_ANON => "STRUCT_ANON", 190 | TypeCodes::TYPE_CODE_STRUCT_NAME => "STRUCT_NAME", 191 | TypeCodes::TYPE_CODE_STRUCT_NAMED => "STRUCT_NAMED", 192 | TypeCodes::TYPE_CODE_FUNCTION => "FUNCTION", 193 | TypeCodes::TYPE_CODE_TOKEN => "TOKEN", 194 | TypeCodes::TYPE_CODE_BFLOAT => "BFLOAT", 195 | TypeCodes::TYPE_CODE_FUNCTION_OLD => "FUNCTION_OLD", 196 | TypeCodes::TYPE_CODE_X86_AMX => "X86_AMX", 197 | TypeCodes::TYPE_CODE_OPAQUE_POINTER => "UnknownCode25", //"OPAQUE_POINTER", 198 | TypeCodes::TYPE_CODE_TARGET_TYPE => "TARGET_TYPE", 199 | }, 200 | Blocks::CONSTANTS_BLOCK_ID => match ConstantsCodes::try_from(record).unwrap() { 201 | ConstantsCodes::CST_CODE_SETTYPE => "SETTYPE", 202 | ConstantsCodes::CST_CODE_NULL => "NULL", 203 | ConstantsCodes::CST_CODE_UNDEF => "UNDEF", 204 | ConstantsCodes::CST_CODE_INTEGER => "INTEGER", 205 | ConstantsCodes::CST_CODE_WIDE_INTEGER => "WIDE_INTEGER", 206 | ConstantsCodes::CST_CODE_FLOAT => "FLOAT", 207 | ConstantsCodes::CST_CODE_AGGREGATE => "AGGREGATE", 208 | ConstantsCodes::CST_CODE_STRING => "STRING", 209 | ConstantsCodes::CST_CODE_CSTRING => "CSTRING", 210 | ConstantsCodes::CST_CODE_CE_BINOP => "CE_BINOP", 211 | ConstantsCodes::CST_CODE_CE_CAST => "CE_CAST", 212 | ConstantsCodes::CST_CODE_CE_GEP => "CE_GEP", 213 | ConstantsCodes::CST_CODE_CE_INBOUNDS_GEP => "CE_INBOUNDS_GEP", 214 | ConstantsCodes::CST_CODE_CE_SELECT => "CE_SELECT", 215 | ConstantsCodes::CST_CODE_CE_EXTRACTELT => "CE_EXTRACTELT", 216 | ConstantsCodes::CST_CODE_CE_INSERTELT => "CE_INSERTELT", 217 | ConstantsCodes::CST_CODE_CE_SHUFFLEVEC => "CE_SHUFFLEVEC", 218 | ConstantsCodes::CST_CODE_CE_CMP => "CE_CMP", 219 | ConstantsCodes::CST_CODE_INLINEASM => "INLINEASM", 220 | ConstantsCodes::CST_CODE_CE_SHUFVEC_EX => "CE_SHUFVEC_EX", 221 | ConstantsCodes::CST_CODE_CE_UNOP => "CE_UNOP", 222 | ConstantsCodes::CST_CODE_DSO_LOCAL_EQUIVALENT => "DSO_LOCAL_EQUIVALENT", 223 | ConstantsCodes::CST_CODE_NO_CFI_VALUE => "NO_CFI_VALUE", 224 | ConstantsCodes::CST_CODE_PTRAUTH => "PTRAUTH", 225 | ConstantsCodes::CST_CODE_BLOCKADDRESS => "BLOCKADDRESS", 226 | ConstantsCodes::CST_CODE_DATA => "DATA", 227 | ConstantsCodes::CST_CODE_CE_GEP_OLD => "CE_GEP_OLD", 228 | ConstantsCodes::CST_CODE_INLINEASM_OLD => "INLINEASM_OLD", 229 | ConstantsCodes::CST_CODE_INLINEASM_OLD2 => "INLINEASM_OLD2", 230 | ConstantsCodes::CST_CODE_CE_GEP_WITH_INRANGE_INDEX_OLD => { 231 | "CE_GEP_WITH_INRANGE_INDEX_OLD" 232 | } 233 | ConstantsCodes::CST_CODE_POISON => "UnknownCode26", //"POISON", 234 | ConstantsCodes::CST_CODE_INLINEASM_OLD3 => "INLINEASM_OLD3", 235 | ConstantsCodes::CST_CODE_CE_GEP_WITH_INRANGE => "CE_GEP_WITH_INRANGE", 236 | }, 237 | Blocks::FUNCTION_BLOCK_ID => match FunctionCodes::try_from(record).unwrap() { 238 | FunctionCodes::FUNC_CODE_DECLAREBLOCKS => "DECLAREBLOCKS", 239 | FunctionCodes::FUNC_CODE_INST_BINOP => "INST_BINOP", 240 | FunctionCodes::FUNC_CODE_INST_CAST => "INST_CAST", 241 | FunctionCodes::FUNC_CODE_INST_GEP_OLD => "INST_GEP_OLD", 242 | FunctionCodes::FUNC_CODE_INST_INBOUNDS_GEP_OLD => "INST_INBOUNDS_GEP_OLD", 243 | FunctionCodes::FUNC_CODE_INST_SELECT => "INST_SELECT", 244 | FunctionCodes::FUNC_CODE_INST_EXTRACTELT => "INST_EXTRACTELT", 245 | FunctionCodes::FUNC_CODE_INST_INSERTELT => "INST_INSERTELT", 246 | FunctionCodes::FUNC_CODE_INST_SHUFFLEVEC => "INST_SHUFFLEVEC", 247 | FunctionCodes::FUNC_CODE_INST_CMP => "INST_CMP", 248 | FunctionCodes::FUNC_CODE_INST_RET => "INST_RET", 249 | FunctionCodes::FUNC_CODE_INST_BR => "INST_BR", 250 | FunctionCodes::FUNC_CODE_INST_SWITCH => "INST_SWITCH", 251 | FunctionCodes::FUNC_CODE_INST_INVOKE => "INST_INVOKE", 252 | FunctionCodes::FUNC_CODE_INST_UNOP => "INST_UNOP", 253 | FunctionCodes::FUNC_CODE_INST_UNREACHABLE => "INST_UNREACHABLE", 254 | FunctionCodes::FUNC_CODE_INST_CLEANUPRET => "INST_CLEANUPRET", 255 | FunctionCodes::FUNC_CODE_INST_CATCHRET => "INST_CATCHRET", 256 | FunctionCodes::FUNC_CODE_INST_CATCHPAD => "INST_CATCHPAD", 257 | FunctionCodes::FUNC_CODE_INST_PHI => "INST_PHI", 258 | FunctionCodes::FUNC_CODE_INST_ALLOCA => "INST_ALLOCA", 259 | FunctionCodes::FUNC_CODE_INST_LOAD => "INST_LOAD", 260 | FunctionCodes::FUNC_CODE_INST_VAARG => "INST_VAARG", 261 | FunctionCodes::FUNC_CODE_INST_STORE => "INST_STORE", 262 | FunctionCodes::FUNC_CODE_INST_EXTRACTVAL => "INST_EXTRACTVAL", 263 | FunctionCodes::FUNC_CODE_INST_INSERTVAL => "INST_INSERTVAL", 264 | FunctionCodes::FUNC_CODE_INST_CMP2 => "INST_CMP2", 265 | FunctionCodes::FUNC_CODE_INST_VSELECT => "INST_VSELECT", 266 | FunctionCodes::FUNC_CODE_DEBUG_LOC_AGAIN => "DEBUG_LOC_AGAIN", 267 | FunctionCodes::FUNC_CODE_INST_CALL => "INST_CALL", 268 | FunctionCodes::FUNC_CODE_DEBUG_LOC => "DEBUG_LOC", 269 | FunctionCodes::FUNC_CODE_INST_GEP => "INST_GEP", 270 | FunctionCodes::FUNC_CODE_OPERAND_BUNDLE => "OPERAND_BUNDLE", 271 | FunctionCodes::FUNC_CODE_INST_FENCE => "INST_FENCE", 272 | FunctionCodes::FUNC_CODE_INST_ATOMICRMW => "INST_ATOMICRMW", 273 | FunctionCodes::FUNC_CODE_INST_LOADATOMIC => "INST_LOADATOMIC", 274 | FunctionCodes::FUNC_CODE_INST_STOREATOMIC => "INST_STOREATOMIC", 275 | FunctionCodes::FUNC_CODE_INST_CMPXCHG => "INST_CMPXCHG", 276 | FunctionCodes::FUNC_CODE_INST_CALLBR => "INST_CALLBR", 277 | FunctionCodes::FUNC_CODE_BLOCKADDR_USERS => "BLOCKADDR_USERS", 278 | FunctionCodes::FUNC_CODE_DEBUG_RECORD_DECLARE => "DEBUG_RECORD_DECLARE", 279 | FunctionCodes::FUNC_CODE_DEBUG_RECORD_VALUE => "DEBUG_RECORD_VALUE", 280 | FunctionCodes::FUNC_CODE_DEBUG_RECORD_ASSIGN => "DEBUG_RECORD_ASSIGN", 281 | FunctionCodes::FUNC_CODE_DEBUG_RECORD_VALUE_SIMPLE => "DEBUG_RECORD_VALUE_SIMPLE", 282 | FunctionCodes::FUNC_CODE_DEBUG_RECORD_LABEL => "DEBUG_RECORD_LABEL", 283 | 284 | FunctionCodes::FUNC_CODE_INST_STORE_OLD => "INST_STORE_OLD", 285 | FunctionCodes::FUNC_CODE_INST_INDIRECTBR => "INST_INDIRECTBR", 286 | FunctionCodes::FUNC_CODE_INST_CMPXCHG_OLD => "INST_CMPXCHG_OLD", 287 | FunctionCodes::FUNC_CODE_INST_ATOMICRMW_OLD => "INST_ATOMICRMW_OLD", 288 | FunctionCodes::FUNC_CODE_INST_RESUME => "UnknownCode39", //"INST_RESUME", 289 | FunctionCodes::FUNC_CODE_INST_LANDINGPAD_OLD => "INST_LANDINGPAD_OLD", 290 | FunctionCodes::FUNC_CODE_INST_STOREATOMIC_OLD => "INST_STOREATOMIC_OLD", 291 | FunctionCodes::FUNC_CODE_INST_LANDINGPAD => "UnknownCode47", //"INST_LANDINGPAD", 292 | FunctionCodes::FUNC_CODE_INST_CLEANUPPAD => "INST_CLEANUPPAD", 293 | FunctionCodes::FUNC_CODE_INST_CATCHSWITCH => "INST_CATCHSWITCH", 294 | FunctionCodes::FUNC_CODE_INST_FREEZE => "INST_FREEZE", 295 | }, 296 | Blocks::VALUE_SYMTAB_BLOCK_ID => match ValueSymtabCodes::try_from(record).unwrap() { 297 | ValueSymtabCodes::VST_CODE_ENTRY => "ENTRY", 298 | ValueSymtabCodes::VST_CODE_BBENTRY => "BBENTRY", 299 | ValueSymtabCodes::VST_CODE_FNENTRY => "FNENTRY", 300 | ValueSymtabCodes::VST_CODE_COMBINED_ENTRY => "COMBINED_ENTRY", 301 | }, 302 | Blocks::MODULE_STRTAB_BLOCK_ID => match ModulePathSymtabCodes::try_from(record).unwrap() { 303 | ModulePathSymtabCodes::MST_CODE_ENTRY => "ENTRY", 304 | ModulePathSymtabCodes::MST_CODE_HASH => "HASH", 305 | }, 306 | crate::Blocks::GLOBALVAL_SUMMARY_BLOCK_ID 307 | | crate::Blocks::FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID => { 308 | match GlobalValueSummarySymtabCodes::try_from(record).unwrap() { 309 | GlobalValueSummarySymtabCodes::FS_PERMODULE => "PERMODULE", 310 | GlobalValueSummarySymtabCodes::FS_PERMODULE_PROFILE => "PERMODULE_PROFILE", 311 | GlobalValueSummarySymtabCodes::FS_PERMODULE_RELBF => "PERMODULE_RELBF", 312 | GlobalValueSummarySymtabCodes::FS_PERMODULE_GLOBALVAR_INIT_REFS => { 313 | "PERMODULE_GLOBALVAR_INIT_REFS" 314 | } 315 | GlobalValueSummarySymtabCodes::FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS => { 316 | "PERMODULE_VTABLE_GLOBALVAR_INIT_REFS" 317 | } 318 | GlobalValueSummarySymtabCodes::FS_COMBINED => "COMBINED", 319 | GlobalValueSummarySymtabCodes::FS_COMBINED_PROFILE => "COMBINED_PROFILE", 320 | GlobalValueSummarySymtabCodes::FS_COMBINED_GLOBALVAR_INIT_REFS => { 321 | "COMBINED_GLOBALVAR_INIT_REFS" 322 | } 323 | GlobalValueSummarySymtabCodes::FS_ALIAS => "ALIAS", 324 | GlobalValueSummarySymtabCodes::FS_COMBINED_ALIAS => "COMBINED_ALIAS", 325 | GlobalValueSummarySymtabCodes::FS_COMBINED_ORIGINAL_NAME => { 326 | "COMBINED_ORIGINAL_NAME" 327 | } 328 | GlobalValueSummarySymtabCodes::FS_VERSION => "VERSION", 329 | GlobalValueSummarySymtabCodes::FS_FLAGS => "FLAGS", 330 | GlobalValueSummarySymtabCodes::FS_TYPE_TESTS => "TYPE_TESTS", 331 | GlobalValueSummarySymtabCodes::FS_TYPE_TEST_ASSUME_VCALLS => { 332 | "TYPE_TEST_ASSUME_VCALLS" 333 | } 334 | GlobalValueSummarySymtabCodes::FS_TYPE_CHECKED_LOAD_VCALLS => { 335 | "TYPE_CHECKED_LOAD_VCALLS" 336 | } 337 | GlobalValueSummarySymtabCodes::FS_TYPE_TEST_ASSUME_CONST_VCALL => { 338 | "TYPE_TEST_ASSUME_CONST_VCALL" 339 | } 340 | GlobalValueSummarySymtabCodes::FS_TYPE_CHECKED_LOAD_CONST_VCALL => { 341 | "TYPE_CHECKED_LOAD_CONST_VCALL" 342 | } 343 | GlobalValueSummarySymtabCodes::FS_VALUE_GUID => "VALUE_GUID", 344 | GlobalValueSummarySymtabCodes::FS_CFI_FUNCTION_DEFS => "CFI_FUNCTION_DEFS", 345 | GlobalValueSummarySymtabCodes::FS_CFI_FUNCTION_DECLS => "CFI_FUNCTION_DECLS", 346 | GlobalValueSummarySymtabCodes::FS_TYPE_ID => "TYPE_ID", 347 | GlobalValueSummarySymtabCodes::FS_TYPE_ID_METADATA => "TYPE_ID_METADATA", 348 | GlobalValueSummarySymtabCodes::FS_BLOCK_COUNT => "BLOCK_COUNT", 349 | GlobalValueSummarySymtabCodes::FS_PARAM_ACCESS => "PARAM_ACCESS", 350 | GlobalValueSummarySymtabCodes::FS_PERMODULE_CALLSITE_INFO => { 351 | "PERMODULE_CALLSITE_INFO" 352 | } 353 | GlobalValueSummarySymtabCodes::FS_PERMODULE_ALLOC_INFO => "PERMODULE_ALLOC_INFO", 354 | GlobalValueSummarySymtabCodes::FS_COMBINED_CALLSITE_INFO => { 355 | "COMBINED_CALLSITE_INFO" 356 | } 357 | GlobalValueSummarySymtabCodes::FS_COMBINED_ALLOC_INFO => "COMBINED_ALLOC_INFO", 358 | GlobalValueSummarySymtabCodes::FS_STACK_IDS => "STACK_IDS", 359 | GlobalValueSummarySymtabCodes::FS_ALLOC_CONTEXT_IDS => "ALLOC_CONTEXT_IDS", 360 | GlobalValueSummarySymtabCodes::FS_CONTEXT_RADIX_TREE_ARRAY => { 361 | "CONTEXT_RADIX_TREE_ARRAY" 362 | } 363 | } 364 | } 365 | crate::Blocks::METADATA_KIND_BLOCK_ID 366 | | crate::Blocks::METADATA_BLOCK_ID 367 | | Blocks::METADATA_ATTACHMENT_ID => match MetadataCodes::try_from(record).unwrap() { 368 | MetadataCodes::METADATA_ATTACHMENT => "ATTACHMENT", 369 | MetadataCodes::METADATA_STRING_OLD => "STRING_OLD", 370 | MetadataCodes::METADATA_VALUE => "VALUE", 371 | MetadataCodes::METADATA_NODE => "NODE", 372 | MetadataCodes::METADATA_NAME => "NAME", 373 | MetadataCodes::METADATA_DISTINCT_NODE => "DISTINCT_NODE", 374 | MetadataCodes::METADATA_KIND => "KIND", 375 | MetadataCodes::METADATA_LOCATION => "LOCATION", 376 | MetadataCodes::METADATA_OLD_NODE => "OLD_NODE", 377 | MetadataCodes::METADATA_OLD_FN_NODE => "OLD_FN_NODE", 378 | MetadataCodes::METADATA_NAMED_NODE => "NAMED_NODE", 379 | MetadataCodes::METADATA_GENERIC_DEBUG => "GENERIC_DEBUG", 380 | MetadataCodes::METADATA_SUBRANGE => "SUBRANGE", 381 | MetadataCodes::METADATA_ENUMERATOR => "ENUMERATOR", 382 | MetadataCodes::METADATA_BASIC_TYPE => "BASIC_TYPE", 383 | MetadataCodes::METADATA_FILE => "FILE", 384 | MetadataCodes::METADATA_DERIVED_TYPE => "DERIVED_TYPE", 385 | MetadataCodes::METADATA_COMPOSITE_TYPE => "COMPOSITE_TYPE", 386 | MetadataCodes::METADATA_SUBROUTINE_TYPE => "SUBROUTINE_TYPE", 387 | MetadataCodes::METADATA_COMPILE_UNIT => "COMPILE_UNIT", 388 | MetadataCodes::METADATA_SUBPROGRAM => "SUBPROGRAM", 389 | MetadataCodes::METADATA_LEXICAL_BLOCK => "LEXICAL_BLOCK", 390 | MetadataCodes::METADATA_LEXICAL_BLOCK_FILE => "LEXICAL_BLOCK_FILE", 391 | MetadataCodes::METADATA_NAMESPACE => "NAMESPACE", 392 | MetadataCodes::METADATA_TEMPLATE_TYPE => "TEMPLATE_TYPE", 393 | MetadataCodes::METADATA_TEMPLATE_VALUE => "TEMPLATE_VALUE", 394 | MetadataCodes::METADATA_GLOBAL_VAR => "GLOBAL_VAR", 395 | MetadataCodes::METADATA_LOCAL_VAR => "LOCAL_VAR", 396 | MetadataCodes::METADATA_EXPRESSION => "EXPRESSION", 397 | MetadataCodes::METADATA_OBJC_PROPERTY => "OBJC_PROPERTY", 398 | MetadataCodes::METADATA_IMPORTED_ENTITY => "IMPORTED_ENTITY", 399 | MetadataCodes::METADATA_MODULE => "MODULE", 400 | MetadataCodes::METADATA_MACRO => "MACRO", 401 | MetadataCodes::METADATA_MACRO_FILE => "MACRO_FILE", 402 | MetadataCodes::METADATA_STRINGS => "STRINGS", 403 | MetadataCodes::METADATA_GLOBAL_DECL_ATTACHMENT => "GLOBAL_DECL_ATTACHMENT", 404 | MetadataCodes::METADATA_GLOBAL_VAR_EXPR => "GLOBAL_VAR_EXPR", 405 | MetadataCodes::METADATA_INDEX_OFFSET => "INDEX_OFFSET", 406 | MetadataCodes::METADATA_INDEX => "INDEX", 407 | MetadataCodes::METADATA_ARG_LIST => "ARG_LIST", 408 | MetadataCodes::METADATA_LABEL => "LABEL", 409 | MetadataCodes::METADATA_STRING_TYPE => "STRING_TYPE", 410 | MetadataCodes::METADATA_COMMON_BLOCK => "COMMON_BLOCK", 411 | MetadataCodes::METADATA_GENERIC_SUBRANGE => "GENERIC_SUBRANGE", 412 | MetadataCodes::METADATA_ASSIGN_ID => "ASSIGN_ID", 413 | }, 414 | Blocks::USELIST_BLOCK_ID => match UseListCodes::try_from(record).unwrap() { 415 | UseListCodes::USELIST_CODE_DEFAULT => "DEFAULT", 416 | UseListCodes::USELIST_CODE_BB => "BB", 417 | }, 418 | Blocks::OPERAND_BUNDLE_TAGS_BLOCK_ID => { 419 | match OperandBundleTagCode::try_from(record).unwrap() { 420 | OperandBundleTagCode::OPERAND_BUNDLE_TAG => "OPERAND_BUNDLE_TAG", 421 | } 422 | } 423 | Blocks::STRTAB_BLOCK_ID => match StrtabCodes::try_from(record).unwrap() { 424 | StrtabCodes::STRTAB_BLOB => "BLOB", 425 | }, 426 | Blocks::SYMTAB_BLOCK_ID => match SymtabCodes::try_from(record).unwrap() { 427 | SymtabCodes::SYMTAB_BLOB => "BLOB", 428 | }, 429 | Blocks::SYNC_SCOPE_NAMES_BLOCK_ID => "UnknownCode1", 430 | } 431 | } 432 | 433 | #[derive(TryFromPrimitive)] 434 | #[repr(u32)] 435 | enum Blocks { 436 | MODULE_BLOCK_ID = 8, 437 | PARAMATTR_BLOCK_ID, 438 | PARAMATTR_GROUP_BLOCK_ID, 439 | CONSTANTS_BLOCK_ID, 440 | FUNCTION_BLOCK_ID, 441 | IDENTIFICATION_BLOCK_ID, 442 | VALUE_SYMTAB_BLOCK_ID, 443 | METADATA_BLOCK_ID, 444 | METADATA_ATTACHMENT_ID, 445 | TYPE_BLOCK_ID_NEW, 446 | USELIST_BLOCK_ID, 447 | MODULE_STRTAB_BLOCK_ID, 448 | GLOBALVAL_SUMMARY_BLOCK_ID, 449 | OPERAND_BUNDLE_TAGS_BLOCK_ID, 450 | METADATA_KIND_BLOCK_ID, 451 | STRTAB_BLOCK_ID, 452 | FULL_LTO_GLOBALVAL_SUMMARY_BLOCK_ID, 453 | SYMTAB_BLOCK_ID, 454 | SYNC_SCOPE_NAMES_BLOCK_ID, 455 | } 456 | 457 | /// Identification block contains a string that describes the producer details, 458 | /// and an epoch that defines the auto-upgrade capability. 459 | #[derive(TryFromPrimitive)] 460 | #[repr(u32)] 461 | enum IdentificationCodes { 462 | IDENTIFICATION_CODE_STRING = 1, // IDENTIFICATION: [strchr x N] 463 | IDENTIFICATION_CODE_EPOCH = 2, // EPOCH: [epoch#] 464 | } 465 | 466 | /// The epoch that defines the auto-upgrade compatibility for the bitcode. 467 | /// 468 | /// LLVM guarantees in a major release that a minor release can read bitcode 469 | /// generated by previous minor releases. We translate this by making the reader 470 | /// accepting only bitcode with the same epoch, except for the X.0 release which 471 | /// also accepts N-1. 472 | #[derive(TryFromPrimitive)] 473 | #[repr(u32)] 474 | enum EpochCode { 475 | BITCODE_CURRENT_EPOCH = 0, 476 | } 477 | 478 | /// MODULE blocks have a number of optional fields and subblocks. 479 | #[derive(TryFromPrimitive)] 480 | #[repr(u32)] 481 | enum ModuleCodes { 482 | MODULE_CODE_VERSION = 1, // VERSION: [version#] 483 | MODULE_CODE_TRIPLE = 2, // TRIPLE: [strchr x N] 484 | MODULE_CODE_DATALAYOUT = 3, // DATALAYOUT: [strchr x N] 485 | MODULE_CODE_ASM = 4, // ASM: [strchr x N] 486 | MODULE_CODE_SECTIONNAME = 5, // SECTIONNAME: [strchr x N] 487 | 488 | // Deprecated, but still needed to read old bitcode files. 489 | MODULE_CODE_DEPLIB = 6, // DEPLIB: [strchr x N] 490 | 491 | // GLOBALVAR: [pointer type, isconst, initid, 492 | // linkage, alignment, section, visibility, threadlocal] 493 | MODULE_CODE_GLOBALVAR = 7, 494 | 495 | // FUNCTION: [type, callingconv, isproto, linkage, paramattrs, alignment, 496 | // section, visibility, gc, unnamed_addr] 497 | MODULE_CODE_FUNCTION = 8, 498 | 499 | // ALIAS: [alias type, aliasee val#, linkage, visibility] 500 | MODULE_CODE_ALIAS_OLD = 9, 501 | 502 | MODULE_CODE_GCNAME = 11, // GCNAME: [strchr x N] 503 | MODULE_CODE_COMDAT = 12, // COMDAT: [selection_kind, name] 504 | 505 | MODULE_CODE_VSTOFFSET = 13, // VSTOFFSET: [offset] 506 | 507 | // ALIAS: [alias value type, addrspace, aliasee val#, linkage, visibility] 508 | MODULE_CODE_ALIAS = 14, 509 | 510 | MODULE_CODE_METADATA_VALUES_UNUSED = 15, 511 | 512 | // SOURCE_FILENAME: [namechar x N] 513 | MODULE_CODE_SOURCE_FILENAME = 16, 514 | 515 | // HASH: [5*i32] 516 | MODULE_CODE_HASH = 17, 517 | 518 | // IFUNC: [ifunc value type, addrspace, resolver val#, linkage, visibility] 519 | MODULE_CODE_IFUNC = 18, 520 | } 521 | 522 | /// PARAMATTR blocks have code for defining a parameter attribute set. 523 | #[derive(TryFromPrimitive)] 524 | #[repr(u32)] 525 | enum AttributeCodes { 526 | // Deprecated, but still needed to read old bitcode files. 527 | PARAMATTR_CODE_ENTRY_OLD = 1, // ENTRY: [paramidx0, attr0, 528 | // paramidx1, attr1...] 529 | PARAMATTR_CODE_ENTRY = 2, // ENTRY: [attrgrp0, attrgrp1, ...] 530 | PARAMATTR_GRP_CODE_ENTRY = 3, // ENTRY: [grpid, idx, attr0, attr1, ...] 531 | } 532 | 533 | /// TYPE blocks have codes for each type primitive they use. 534 | #[derive(TryFromPrimitive)] 535 | #[repr(u32)] 536 | enum TypeCodes { 537 | TYPE_CODE_NUMENTRY = 1, // NUMENTRY: [numentries] 538 | 539 | // Type Codes 540 | TYPE_CODE_VOID = 2, // VOID 541 | TYPE_CODE_FLOAT = 3, // FLOAT 542 | TYPE_CODE_DOUBLE = 4, // DOUBLE 543 | TYPE_CODE_LABEL = 5, // LABEL 544 | TYPE_CODE_OPAQUE = 6, // OPAQUE 545 | TYPE_CODE_INTEGER = 7, // INTEGER: [width] 546 | TYPE_CODE_POINTER = 8, // POINTER: [pointee type] 547 | 548 | TYPE_CODE_FUNCTION_OLD = 9, // FUNCTION: [vararg, attrid, retty, 549 | // paramty x N] 550 | TYPE_CODE_HALF = 10, // HALF 551 | 552 | TYPE_CODE_ARRAY = 11, // ARRAY: [num_elements, elements_type] 553 | TYPE_CODE_VECTOR = 12, // VECTOR: [num_elements, elements_type] 554 | 555 | // These are not with the other floating point types because they're 556 | // a late addition, and putting them in the right place breaks 557 | // binary compatibility. 558 | TYPE_CODE_X86_FP80 = 13, // X86 LONG DOUBLE 559 | TYPE_CODE_FP128 = 14, // LONG DOUBLE (112 bit mantissa) 560 | TYPE_CODE_PPC_FP128 = 15, // PPC LONG DOUBLE (2 doubles) 561 | 562 | TYPE_CODE_METADATA = 16, // METADATA 563 | 564 | TYPE_CODE_X86_MMX = 17, // X86 MMX 565 | 566 | TYPE_CODE_STRUCT_ANON = 18, // STRUCT_ANON: [ispacked, elements_type x N] 567 | TYPE_CODE_STRUCT_NAME = 19, // STRUCT_NAME: [strchr x N] 568 | TYPE_CODE_STRUCT_NAMED = 20, // STRUCT_NAMED: [ispacked, elements_type x N] 569 | 570 | TYPE_CODE_FUNCTION = 21, // FUNCTION: [vararg, retty, paramty x N] 571 | 572 | TYPE_CODE_TOKEN = 22, // TOKEN 573 | 574 | TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT 575 | TYPE_CODE_X86_AMX = 24, // X86 AMX 576 | 577 | TYPE_CODE_OPAQUE_POINTER = 25, // OPAQUE_POINTER: [addrspace] 578 | 579 | TYPE_CODE_TARGET_TYPE = 26, // TARGET_TYPE 580 | } 581 | 582 | #[derive(TryFromPrimitive)] 583 | #[repr(u32)] 584 | enum OperandBundleTagCode { 585 | OPERAND_BUNDLE_TAG = 1, // TAG: [strchr x N] 586 | } 587 | 588 | #[derive(TryFromPrimitive)] 589 | #[repr(u32)] 590 | enum SyncScopeNameCode { 591 | SYNC_SCOPE_NAME = 1, 592 | } 593 | 594 | // Value symbol table codes. 595 | #[derive(TryFromPrimitive)] 596 | #[repr(u32)] 597 | enum ValueSymtabCodes { 598 | VST_CODE_ENTRY = 1, // VST_ENTRY: [valueid, namechar x N] 599 | VST_CODE_BBENTRY = 2, // VST_BBENTRY: [bbid, namechar x N] 600 | VST_CODE_FNENTRY = 3, // VST_FNENTRY: [valueid, offset, namechar x N] 601 | // VST_COMBINED_ENTRY: [valueid, refguid] 602 | VST_CODE_COMBINED_ENTRY = 5, 603 | } 604 | 605 | // The module path symbol table only has one code (MST_CODE_ENTRY). 606 | #[derive(TryFromPrimitive)] 607 | #[repr(u32)] 608 | enum ModulePathSymtabCodes { 609 | MST_CODE_ENTRY = 1, // MST_ENTRY: [modid, namechar x N] 610 | MST_CODE_HASH = 2, // MST_HASH: [5*i32] 611 | } 612 | 613 | // The summary section uses different codes in the per-module 614 | // and combined index cases. 615 | #[derive(TryFromPrimitive)] 616 | #[repr(u32)] 617 | enum GlobalValueSummarySymtabCodes { 618 | // PERMODULE: [valueid, flags, instcount, numrefs, numrefs x valueid, 619 | // n x (valueid)] 620 | FS_PERMODULE = 1, 621 | // PERMODULE_PROFILE: [valueid, flags, instcount, numrefs, 622 | // numrefs x valueid, 623 | // n x (valueid, hotness+tailcall)] 624 | FS_PERMODULE_PROFILE = 2, 625 | // PERMODULE_GLOBALVAR_INIT_REFS: [valueid, flags, n x valueid] 626 | FS_PERMODULE_GLOBALVAR_INIT_REFS = 3, 627 | // COMBINED: [valueid, modid, flags, instcount, numrefs, numrefs x valueid, 628 | // n x (valueid)] 629 | FS_COMBINED = 4, 630 | // COMBINED_PROFILE: [valueid, modid, flags, instcount, numrefs, 631 | // numrefs x valueid, 632 | // n x (valueid, hotness+tailcall)] 633 | FS_COMBINED_PROFILE = 5, 634 | // COMBINED_GLOBALVAR_INIT_REFS: [valueid, modid, flags, n x valueid] 635 | FS_COMBINED_GLOBALVAR_INIT_REFS = 6, 636 | // ALIAS: [valueid, flags, valueid] 637 | FS_ALIAS = 7, 638 | // COMBINED_ALIAS: [valueid, modid, flags, valueid] 639 | FS_COMBINED_ALIAS = 8, 640 | // COMBINED_ORIGINAL_NAME: [original_name_hash] 641 | FS_COMBINED_ORIGINAL_NAME = 9, 642 | // VERSION of the summary, bumped when adding flags for instance. 643 | FS_VERSION = 10, 644 | // The list of llvm.type.test type identifiers used by the following function 645 | // that are used other than by an llvm.assume. 646 | // [n x typeid] 647 | FS_TYPE_TESTS = 11, 648 | // The list of virtual calls made by this function using 649 | // llvm.assume(llvm.type.test) intrinsics that do not have all constant 650 | // integer arguments. 651 | // [n x (typeid, offset)] 652 | FS_TYPE_TEST_ASSUME_VCALLS = 12, 653 | // The list of virtual calls made by this function using 654 | // llvm.type.checked.load intrinsics that do not have all constant integer 655 | // arguments. 656 | // [n x (typeid, offset)] 657 | FS_TYPE_CHECKED_LOAD_VCALLS = 13, 658 | // Identifies a virtual call made by this function using an 659 | // llvm.assume(llvm.type.test) intrinsic with all constant integer arguments. 660 | // [typeid, offset, n x arg] 661 | FS_TYPE_TEST_ASSUME_CONST_VCALL = 14, 662 | // Identifies a virtual call made by this function using an 663 | // llvm.type.checked.load intrinsic with all constant integer arguments. 664 | // [typeid, offset, n x arg] 665 | FS_TYPE_CHECKED_LOAD_CONST_VCALL = 15, 666 | // Assigns a GUID to a value ID. This normally appears only in combined 667 | // summaries, but it can also appear in per-module summaries for PGO data. 668 | // [valueid, guid] 669 | FS_VALUE_GUID = 16, 670 | // The list of local functions with CFI jump tables. Function names are 671 | // strings in strtab. 672 | // [n * name] 673 | FS_CFI_FUNCTION_DEFS = 17, 674 | // The list of external functions with CFI jump tables. Function names are 675 | // strings in strtab. 676 | // [n * name] 677 | FS_CFI_FUNCTION_DECLS = 18, 678 | // Per-module summary that also adds relative block frequency to callee info. 679 | // PERMODULE_RELBF: [valueid, flags, instcount, numrefs, 680 | // numrefs x valueid, 681 | // n x (valueid, relblockfreq+tailcall)] 682 | FS_PERMODULE_RELBF = 19, 683 | // Index-wide flags 684 | FS_FLAGS = 20, 685 | // Maps type identifier to summary information for that type identifier. 686 | // Produced by the thin link (only lives in combined index). 687 | // TYPE_ID: [typeid, kind, bitwidth, align, size, bitmask, inlinebits, 688 | // n x (typeid, kind, name, numrba, 689 | // numrba x (numarg, numarg x arg, kind, info, byte, bit))] 690 | FS_TYPE_ID = 21, 691 | // For background see overview at https://llvm.org/docs/TypeMetadata.html. 692 | // The type metadata includes both the type identifier and the offset of 693 | // the address point of the type (the address held by objects of that type 694 | // which may not be the beginning of the virtual table). Vtable definitions 695 | // are decorated with type metadata for the types they are compatible with. 696 | // 697 | // Maps type identifier to summary information for that type identifier 698 | // computed from type metadata: the valueid of each vtable definition 699 | // decorated with a type metadata for that identifier, and the offset from 700 | // the corresponding type metadata. 701 | // Exists in the per-module summary to provide information to thin link 702 | // for index-based whole program devirtualization. 703 | // TYPE_ID_METADATA: [typeid, n x (valueid, offset)] 704 | FS_TYPE_ID_METADATA = 22, 705 | // Summarizes vtable definition for use in index-based whole program 706 | // devirtualization during the thin link. 707 | // PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: [valueid, flags, varflags, 708 | // numrefs, numrefs x valueid, 709 | // n x (valueid, offset)] 710 | FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS = 23, 711 | // The total number of basic blocks in the module. 712 | FS_BLOCK_COUNT = 24, 713 | // Range information for accessed offsets for every argument. 714 | // [n x (paramno, range, numcalls, numcalls x (callee_guid, paramno, range))] 715 | FS_PARAM_ACCESS = 25, 716 | // Summary of per-module memprof callsite metadata. 717 | // [valueid, n x stackidindex] 718 | FS_PERMODULE_CALLSITE_INFO = 26, 719 | // Summary of per-module allocation memprof metadata. 720 | // [nummib, nummib x (alloc type, context radix tree index), 721 | // [nummib x (numcontext x total size)]?] 722 | FS_PERMODULE_ALLOC_INFO = 27, 723 | // Summary of combined index memprof callsite metadata. 724 | // [valueid, context radix tree index, numver, 725 | // numver x version] 726 | FS_COMBINED_CALLSITE_INFO = 28, 727 | // Summary of combined index allocation memprof metadata. 728 | // [nummib, numver, 729 | // nummib x (alloc type, numstackids, numstackids x stackidindex), 730 | // numver x version] 731 | FS_COMBINED_ALLOC_INFO = 29, 732 | // List of all stack ids referenced by index in the callsite and alloc infos. 733 | // [n x stack id] 734 | FS_STACK_IDS = 30, 735 | // List of all full stack id pairs corresponding to the total sizes recorded 736 | // at the end of the alloc info when reporting of hinted bytes is enabled. 737 | // We use a fixed-width array, which is more efficient as these ids typically 738 | // are close to 64 bits in size. The max fixed width value supported is 32 739 | // bits so each 64-bit context id hash is recorded as a pair (upper 32 bits 740 | // first). This record must immediately precede the associated alloc info, and 741 | // the entries must be in the exact same order as the corresponding sizes. 742 | // [nummib x (numcontext x full stack id)] 743 | FS_ALLOC_CONTEXT_IDS = 31, 744 | // Linearized radix tree of allocation contexts. See the description above the 745 | // CallStackRadixTreeBuilder class in ProfileData/MemProf.h for format. 746 | // [n x entry] 747 | FS_CONTEXT_RADIX_TREE_ARRAY = 32, 748 | } 749 | 750 | #[derive(TryFromPrimitive)] 751 | #[repr(u32)] 752 | enum MetadataCodes { 753 | METADATA_STRING_OLD = 1, // MDSTRING: [values] 754 | METADATA_VALUE = 2, // VALUE: [type num, value num] 755 | METADATA_NODE = 3, // NODE: [n x md num] 756 | METADATA_NAME = 4, // STRING: [values] 757 | METADATA_DISTINCT_NODE = 5, // DISTINCT_NODE: [n x md num] 758 | METADATA_KIND = 6, // [n x [id, name]] 759 | METADATA_LOCATION = 7, // [distinct, line, col, scope, inlined-at?] 760 | METADATA_OLD_NODE = 8, // OLD_NODE: [n x (type num, value num)] 761 | METADATA_OLD_FN_NODE = 9, // OLD_FN_NODE: [n x (type num, value num)] 762 | METADATA_NAMED_NODE = 10, // NAMED_NODE: [n x mdnodes] 763 | METADATA_ATTACHMENT = 11, // [m x [value, [n x [id, mdnode]]] 764 | METADATA_GENERIC_DEBUG = 12, // [distinct, tag, vers, header, n x md num] 765 | METADATA_SUBRANGE = 13, // [distinct, count, lo] 766 | METADATA_ENUMERATOR = 14, // [isUnsigned|distinct, value, name] 767 | METADATA_BASIC_TYPE = 15, // [distinct, tag, name, size, align, enc] 768 | METADATA_FILE = 16, // [distinct, filename, directory, checksumkind, checksum] 769 | METADATA_DERIVED_TYPE = 17, // [distinct, ...] 770 | METADATA_COMPOSITE_TYPE = 18, // [distinct, ...] 771 | METADATA_SUBROUTINE_TYPE = 19, // [distinct, flags, types, cc] 772 | METADATA_COMPILE_UNIT = 20, // [distinct, ...] 773 | METADATA_SUBPROGRAM = 21, // [distinct, ...] 774 | METADATA_LEXICAL_BLOCK = 22, // [distinct, scope, file, line, column] 775 | METADATA_LEXICAL_BLOCK_FILE = 23, //[distinct, scope, file, discriminator] 776 | METADATA_NAMESPACE = 24, // [distinct, scope, file, name, line, exportSymbols] 777 | METADATA_TEMPLATE_TYPE = 25, // [distinct, scope, name, type, ...] 778 | METADATA_TEMPLATE_VALUE = 26, // [distinct, scope, name, type, value, ...] 779 | METADATA_GLOBAL_VAR = 27, // [distinct, ...] 780 | METADATA_LOCAL_VAR = 28, // [distinct, ...] 781 | METADATA_EXPRESSION = 29, // [distinct, n x element] 782 | METADATA_OBJC_PROPERTY = 30, // [distinct, name, file, line, ...] 783 | METADATA_IMPORTED_ENTITY = 31, // [distinct, tag, scope, entity, line, name] 784 | METADATA_MODULE = 32, // [distinct, scope, name, ...] 785 | METADATA_MACRO = 33, // [distinct, macinfo, line, name, value] 786 | METADATA_MACRO_FILE = 34, // [distinct, macinfo, line, file, ...] 787 | METADATA_STRINGS = 35, // [count, offset] blob([lengths][chars]) 788 | METADATA_GLOBAL_DECL_ATTACHMENT = 36, // [valueid, n x [id, mdnode]] 789 | METADATA_GLOBAL_VAR_EXPR = 37, // [distinct, var, expr] 790 | METADATA_INDEX_OFFSET = 38, // [offset] 791 | METADATA_INDEX = 39, // [bitpos] 792 | METADATA_LABEL = 40, // [distinct, scope, name, file, line] 793 | METADATA_STRING_TYPE = 41, // [distinct, name, size, align,...] 794 | // Codes 42 and 43 are reserved for support for Fortran array specific debug 795 | // info. 796 | METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...] 797 | METADATA_GENERIC_SUBRANGE = 45, // [distinct, count, lo, up, stride] 798 | METADATA_ARG_LIST = 46, // [n x [type num, value num]] 799 | METADATA_ASSIGN_ID = 47, // [distinct, ...] 800 | } 801 | 802 | // The constants block (CONSTANTS_BLOCK_ID) describes emission for each 803 | // constant and maintains an implicit current type value. 804 | #[derive(TryFromPrimitive)] 805 | #[repr(u32)] 806 | enum ConstantsCodes { 807 | CST_CODE_SETTYPE = 1, // SETTYPE: [typeid] 808 | CST_CODE_NULL = 2, // NULL 809 | CST_CODE_UNDEF = 3, // UNDEF 810 | CST_CODE_INTEGER = 4, // INTEGER: [intval] 811 | CST_CODE_WIDE_INTEGER = 5, // WIDE_INTEGER: [n x intval] 812 | CST_CODE_FLOAT = 6, // FLOAT: [fpval] 813 | CST_CODE_AGGREGATE = 7, // AGGREGATE: [n x value number] 814 | CST_CODE_STRING = 8, // STRING: [values] 815 | CST_CODE_CSTRING = 9, // CSTRING: [values] 816 | CST_CODE_CE_BINOP = 10, // CE_BINOP: [opcode, opval, opval] 817 | CST_CODE_CE_CAST = 11, // CE_CAST: [opcode, opty, opval] 818 | CST_CODE_CE_GEP_OLD = 12, // CE_GEP: [n x operands] 819 | CST_CODE_CE_SELECT = 13, // CE_SELECT: [opval, opval, opval] 820 | CST_CODE_CE_EXTRACTELT = 14, // CE_EXTRACTELT: [opty, opval, opval] 821 | CST_CODE_CE_INSERTELT = 15, // CE_INSERTELT: [opval, opval, opval] 822 | CST_CODE_CE_SHUFFLEVEC = 16, // CE_SHUFFLEVEC: [opval, opval, opval] 823 | CST_CODE_CE_CMP = 17, // CE_CMP: [opty, opval, opval, pred] 824 | CST_CODE_INLINEASM_OLD = 18, // INLINEASM: [sideeffect|alignstack, 825 | // asmstr,conststr] 826 | CST_CODE_CE_SHUFVEC_EX = 19, // SHUFVEC_EX: [opty, opval, opval, opval] 827 | CST_CODE_CE_INBOUNDS_GEP = 20, // INBOUNDS_GEP: [n x operands] 828 | CST_CODE_BLOCKADDRESS = 21, // CST_CODE_BLOCKADDRESS [fnty, fnval, bb#] 829 | CST_CODE_DATA = 22, // DATA: [n x elements] 830 | CST_CODE_INLINEASM_OLD2 = 23, // INLINEASM: [sideeffect|alignstack| 831 | // asmdialect,asmstr,conststr] 832 | CST_CODE_CE_GEP_WITH_INRANGE_INDEX_OLD = 24, // [opty, flags, n x operands] 833 | CST_CODE_CE_UNOP = 25, // CE_UNOP: [opcode, opval] 834 | CST_CODE_POISON = 26, // POISON 835 | CST_CODE_DSO_LOCAL_EQUIVALENT = 27, // DSO_LOCAL_EQUIVALENT [gvty, gv] 836 | CST_CODE_INLINEASM_OLD3 = 28, // INLINEASM: [sideeffect|alignstack| 837 | // asmdialect|unwind, 838 | // asmstr,conststr] 839 | CST_CODE_NO_CFI_VALUE = 29, // NO_CFI [ fty, f ] 840 | CST_CODE_INLINEASM = 30, // INLINEASM: [fnty, 841 | // sideeffect|alignstack| 842 | // asmdialect|unwind, 843 | // asmstr,conststr] 844 | CST_CODE_CE_GEP_WITH_INRANGE = 31, // [opty, flags, range, n x operands] 845 | CST_CODE_CE_GEP = 32, // [opty, flags, n x operands] 846 | CST_CODE_PTRAUTH = 33, // [ptr, key, disc, addrdisc] 847 | } 848 | 849 | // The function body block (FUNCTION_BLOCK_ID) describes function bodies. It 850 | // can contain a constant block (CONSTANTS_BLOCK_ID). 851 | #[derive(TryFromPrimitive)] 852 | #[repr(u32)] 853 | enum FunctionCodes { 854 | FUNC_CODE_DECLAREBLOCKS = 1, // DECLAREBLOCKS: [n] 855 | 856 | FUNC_CODE_INST_BINOP = 2, // BINOP: [opcode, ty, opval, opval] 857 | FUNC_CODE_INST_CAST = 3, // CAST: [opcode, ty, opty, opval] 858 | FUNC_CODE_INST_GEP_OLD = 4, // GEP: [n x operands] 859 | FUNC_CODE_INST_SELECT = 5, // SELECT: [ty, opval, opval, opval] 860 | FUNC_CODE_INST_EXTRACTELT = 6, // EXTRACTELT: [opty, opval, opval] 861 | FUNC_CODE_INST_INSERTELT = 7, // INSERTELT: [ty, opval, opval, opval] 862 | FUNC_CODE_INST_SHUFFLEVEC = 8, // SHUFFLEVEC: [ty, opval, opval, opval] 863 | FUNC_CODE_INST_CMP = 9, // CMP: [opty, opval, opval, pred] 864 | 865 | FUNC_CODE_INST_RET = 10, // RET: [opty,opval] 866 | FUNC_CODE_INST_BR = 11, // BR: [bb#, bb#, cond] or [bb#] 867 | FUNC_CODE_INST_SWITCH = 12, // SWITCH: [opty, op0, op1, ...] 868 | FUNC_CODE_INST_INVOKE = 13, // INVOKE: [attr, fnty, op0,op1, ...] 869 | // 14 is unused. 870 | FUNC_CODE_INST_UNREACHABLE = 15, // UNREACHABLE 871 | 872 | FUNC_CODE_INST_PHI = 16, // PHI: [ty, val0,bb0, ...] 873 | // 17 is unused. 874 | // 18 is unused. 875 | FUNC_CODE_INST_ALLOCA = 19, // ALLOCA: [instty, opty, op, align] 876 | FUNC_CODE_INST_LOAD = 20, // LOAD: [opty, op, align, vol] 877 | // 21 is unused. 878 | // 22 is unused. 879 | FUNC_CODE_INST_VAARG = 23, // VAARG: [valistty, valist, instty] 880 | // This store code encodes the pointer type, rather than the value type 881 | // this is so information only available in the pointer type (e.g. address 882 | // spaces) is retained. 883 | FUNC_CODE_INST_STORE_OLD = 24, // STORE: [ptrty,ptr,val, align, vol] 884 | // 25 is unused. 885 | FUNC_CODE_INST_EXTRACTVAL = 26, // EXTRACTVAL: [n x operands] 886 | FUNC_CODE_INST_INSERTVAL = 27, // INSERTVAL: [n x operands] 887 | // fcmp/icmp returning Int1TY or vector of Int1Ty. Same as CMP, exists to 888 | // support legacy vicmp/vfcmp instructions. 889 | FUNC_CODE_INST_CMP2 = 28, // CMP2: [opty, opval, opval, pred] 890 | // new select on i1 or [N x i1] 891 | FUNC_CODE_INST_VSELECT = 29, // VSELECT: [ty,opval,opval,predty,pred] 892 | FUNC_CODE_INST_INBOUNDS_GEP_OLD = 30, // INBOUNDS_GEP: [n x operands] 893 | FUNC_CODE_INST_INDIRECTBR = 31, // INDIRECTBR: [opty, op0, op1, ...] 894 | // 32 is unused. 895 | FUNC_CODE_DEBUG_LOC_AGAIN = 33, // DEBUG_LOC_AGAIN 896 | 897 | FUNC_CODE_INST_CALL = 34, // CALL: [attr, cc, fnty, fnid, args...] 898 | 899 | FUNC_CODE_DEBUG_LOC = 35, // DEBUG_LOC: [Line,Col,ScopeVal, IAVal] 900 | FUNC_CODE_INST_FENCE = 36, // FENCE: [ordering, synchscope] 901 | FUNC_CODE_INST_CMPXCHG_OLD = 37, // CMPXCHG: [ptrty, ptr, cmp, val, vol, 902 | // ordering, synchscope, 903 | // failure_ordering?, weak?] 904 | FUNC_CODE_INST_ATOMICRMW_OLD = 38, // ATOMICRMW: [ptrty,ptr,val, operation, 905 | // align, vol, 906 | // ordering, synchscope] 907 | FUNC_CODE_INST_RESUME = 39, // RESUME: [opval] 908 | FUNC_CODE_INST_LANDINGPAD_OLD = 40, // LANDINGPAD: [ty,val,val,num,id0,val0...] 909 | FUNC_CODE_INST_LOADATOMIC = 41, // LOAD: [opty, op, align, vol, 910 | // ordering, synchscope] 911 | FUNC_CODE_INST_STOREATOMIC_OLD = 42, // STORE: [ptrty,ptr,val, align, vol 912 | // ordering, synchscope] 913 | FUNC_CODE_INST_GEP = 43, // GEP: [inbounds, n x operands] 914 | FUNC_CODE_INST_STORE = 44, // STORE: [ptrty,ptr,valty,val, align, vol] 915 | FUNC_CODE_INST_STOREATOMIC = 45, // STORE: [ptrty,ptr,val, align, vol 916 | FUNC_CODE_INST_CMPXCHG = 46, // CMPXCHG: [ptrty, ptr, cmp, val, vol, 917 | // success_ordering, synchscope, 918 | // failure_ordering, weak] 919 | FUNC_CODE_INST_LANDINGPAD = 47, // LANDINGPAD: [ty,val,num,id0,val0...] 920 | FUNC_CODE_INST_CLEANUPRET = 48, // CLEANUPRET: [val] or [val,bb#] 921 | FUNC_CODE_INST_CATCHRET = 49, // CATCHRET: [val,bb#] 922 | FUNC_CODE_INST_CATCHPAD = 50, // CATCHPAD: [bb#,bb#,num,args...] 923 | FUNC_CODE_INST_CLEANUPPAD = 51, // CLEANUPPAD: [num,args...] 924 | FUNC_CODE_INST_CATCHSWITCH = 52, // CATCHSWITCH: [num,args...] or [num,args...,bb] 925 | // 53 is unused. 926 | // 54 is unused. 927 | FUNC_CODE_OPERAND_BUNDLE = 55, // OPERAND_BUNDLE: [tag#, value...] 928 | FUNC_CODE_INST_UNOP = 56, // UNOP: [opcode, ty, opval] 929 | FUNC_CODE_INST_CALLBR = 57, // CALLBR: [attr, cc, norm, transfs, 930 | // fnty, fnid, args...] 931 | FUNC_CODE_INST_FREEZE = 58, // FREEZE: [opty, opval] 932 | FUNC_CODE_INST_ATOMICRMW = 59, // ATOMICRMW: [ptrty, ptr, valty, val, 933 | // operation, align, vol, 934 | // ordering, synchscope] 935 | FUNC_CODE_BLOCKADDR_USERS = 60, // BLOCKADDR_USERS: [value...] 936 | 937 | FUNC_CODE_DEBUG_RECORD_VALUE = 61, // [DILocation, DILocalVariable, DIExpression, ValueAsMetadata] 938 | FUNC_CODE_DEBUG_RECORD_DECLARE = 62, // [DILocation, DILocalVariable, DIExpression, ValueAsMetadata] 939 | FUNC_CODE_DEBUG_RECORD_ASSIGN = 63, // [DILocation, DILocalVariable, DIExpression, ValueAsMetadata, 940 | // DIAssignID, DIExpression (addr), ValueAsMetadata (addr)] 941 | FUNC_CODE_DEBUG_RECORD_VALUE_SIMPLE = 64, // [DILocation, DILocalVariable, DIExpression, Value] 942 | FUNC_CODE_DEBUG_RECORD_LABEL = 65, // [DILocation, DILabel] 943 | } 944 | 945 | #[derive(TryFromPrimitive)] 946 | #[repr(u32)] 947 | enum UseListCodes { 948 | USELIST_CODE_DEFAULT = 1, // DEFAULT: [index..., value-id] 949 | USELIST_CODE_BB = 2, // BB: [index..., bb-id] 950 | } 951 | 952 | #[derive(TryFromPrimitive)] 953 | #[repr(u32)] 954 | enum AttributeKindCodes { 955 | // = 0 is unused 956 | ATTR_KIND_ALIGNMENT = 1, 957 | ATTR_KIND_ALWAYS_INLINE = 2, 958 | ATTR_KIND_BY_VAL = 3, 959 | ATTR_KIND_INLINE_HINT = 4, 960 | ATTR_KIND_IN_REG = 5, 961 | ATTR_KIND_MIN_SIZE = 6, 962 | ATTR_KIND_NAKED = 7, 963 | ATTR_KIND_NEST = 8, 964 | ATTR_KIND_NO_ALIAS = 9, 965 | ATTR_KIND_NO_BUILTIN = 10, 966 | ATTR_KIND_NO_CAPTURE = 11, 967 | ATTR_KIND_NO_DUPLICATE = 12, 968 | ATTR_KIND_NO_IMPLICIT_FLOAT = 13, 969 | ATTR_KIND_NO_INLINE = 14, 970 | ATTR_KIND_NON_LAZY_BIND = 15, 971 | ATTR_KIND_NO_RED_ZONE = 16, 972 | ATTR_KIND_NO_RETURN = 17, 973 | ATTR_KIND_NO_UNWIND = 18, 974 | ATTR_KIND_OPTIMIZE_FOR_SIZE = 19, 975 | ATTR_KIND_READ_NONE = 20, 976 | ATTR_KIND_READ_ONLY = 21, 977 | ATTR_KIND_RETURNED = 22, 978 | ATTR_KIND_RETURNS_TWICE = 23, 979 | ATTR_KIND_S_EXT = 24, 980 | ATTR_KIND_STACK_ALIGNMENT = 25, 981 | ATTR_KIND_STACK_PROTECT = 26, 982 | ATTR_KIND_STACK_PROTECT_REQ = 27, 983 | ATTR_KIND_STACK_PROTECT_STRONG = 28, 984 | ATTR_KIND_STRUCT_RET = 29, 985 | ATTR_KIND_SANITIZE_ADDRESS = 30, 986 | ATTR_KIND_SANITIZE_THREAD = 31, 987 | ATTR_KIND_SANITIZE_MEMORY = 32, 988 | ATTR_KIND_UW_TABLE = 33, 989 | ATTR_KIND_Z_EXT = 34, 990 | ATTR_KIND_BUILTIN = 35, 991 | ATTR_KIND_COLD = 36, 992 | ATTR_KIND_OPTIMIZE_NONE = 37, 993 | ATTR_KIND_IN_ALLOCA = 38, 994 | ATTR_KIND_NON_NULL = 39, 995 | ATTR_KIND_JUMP_TABLE = 40, 996 | ATTR_KIND_DEREFERENCEABLE = 41, 997 | ATTR_KIND_DEREFERENCEABLE_OR_NULL = 42, 998 | ATTR_KIND_CONVERGENT = 43, 999 | ATTR_KIND_SAFESTACK = 44, 1000 | ATTR_KIND_ARGMEMONLY = 45, 1001 | ATTR_KIND_SWIFT_SELF = 46, 1002 | ATTR_KIND_SWIFT_ERROR = 47, 1003 | ATTR_KIND_NO_RECURSE = 48, 1004 | ATTR_KIND_INACCESSIBLEMEM_ONLY = 49, 1005 | ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50, 1006 | ATTR_KIND_ALLOC_SIZE = 51, 1007 | ATTR_KIND_WRITEONLY = 52, 1008 | ATTR_KIND_SPECULATABLE = 53, 1009 | ATTR_KIND_STRICT_FP = 54, 1010 | ATTR_KIND_SANITIZE_HWADDRESS = 55, 1011 | ATTR_KIND_NOCF_CHECK = 56, 1012 | ATTR_KIND_OPT_FOR_FUZZING = 57, 1013 | ATTR_KIND_SHADOWCALLSTACK = 58, 1014 | ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59, 1015 | ATTR_KIND_IMMARG = 60, 1016 | ATTR_KIND_WILLRETURN = 61, 1017 | ATTR_KIND_NOFREE = 62, 1018 | ATTR_KIND_NOSYNC = 63, 1019 | ATTR_KIND_SANITIZE_MEMTAG = 64, 1020 | ATTR_KIND_PREALLOCATED = 65, 1021 | ATTR_KIND_NO_MERGE = 66, 1022 | ATTR_KIND_NULL_POINTER_IS_VALID = 67, 1023 | ATTR_KIND_NOUNDEF = 68, 1024 | ATTR_KIND_BYREF = 69, 1025 | ATTR_KIND_MUSTPROGRESS = 70, 1026 | ATTR_KIND_NO_CALLBACK = 71, 1027 | ATTR_KIND_HOT = 72, 1028 | ATTR_KIND_NO_PROFILE = 73, 1029 | ATTR_KIND_VSCALE_RANGE = 74, 1030 | ATTR_KIND_SWIFT_ASYNC = 75, 1031 | ATTR_KIND_NO_SANITIZE_COVERAGE = 76, 1032 | ATTR_KIND_ELEMENTTYPE = 77, 1033 | ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78, 1034 | ATTR_KIND_NO_SANITIZE_BOUNDS = 79, 1035 | ATTR_KIND_ALLOC_ALIGN = 80, 1036 | ATTR_KIND_ALLOCATED_POINTER = 81, 1037 | ATTR_KIND_ALLOC_KIND = 82, 1038 | ATTR_KIND_PRESPLIT_COROUTINE = 83, 1039 | ATTR_KIND_FNRETTHUNK_EXTERN = 84, 1040 | ATTR_KIND_SKIP_PROFILE = 85, 1041 | ATTR_KIND_MEMORY = 86, 1042 | ATTR_KIND_NOFPCLASS = 87, 1043 | ATTR_KIND_OPTIMIZE_FOR_DEBUGGING = 88, 1044 | ATTR_KIND_WRITABLE = 89, 1045 | ATTR_KIND_CORO_ONLY_DESTROY_WHEN_COMPLETE = 90, 1046 | ATTR_KIND_DEAD_ON_UNWIND = 91, 1047 | ATTR_KIND_RANGE = 92, 1048 | ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93, 1049 | ATTR_KIND_INITIALIZES = 94, 1050 | ATTR_KIND_HYBRID_PATCHABLE = 95, 1051 | ATTR_KIND_SANITIZE_REALTIME = 96, 1052 | ATTR_KIND_SANITIZE_REALTIME_BLOCKING = 97, 1053 | ATTR_KIND_CORO_ELIDE_SAFE = 98, 1054 | ATTR_KIND_NO_EXT = 99, 1055 | ATTR_KIND_NO_DIVERGENCE_SOURCE = 100, 1056 | ATTR_KIND_SANITIZE_TYPE = 101, 1057 | ATTR_KIND_CAPTURES = 102, 1058 | } 1059 | 1060 | #[derive(TryFromPrimitive)] 1061 | #[repr(u32)] 1062 | enum ComdatSelectionKindCodes { 1063 | COMDAT_SELECTION_KIND_ANY = 1, 1064 | COMDAT_SELECTION_KIND_EXACT_MATCH = 2, 1065 | COMDAT_SELECTION_KIND_LARGEST = 3, 1066 | COMDAT_SELECTION_KIND_NO_DUPLICATES = 4, 1067 | COMDAT_SELECTION_KIND_SAME_SIZE = 5, 1068 | } 1069 | 1070 | #[derive(TryFromPrimitive)] 1071 | #[repr(u32)] 1072 | enum StrtabCodes { 1073 | STRTAB_BLOB = 1, 1074 | } 1075 | 1076 | #[derive(TryFromPrimitive)] 1077 | #[repr(u32)] 1078 | enum SymtabCodes { 1079 | SYMTAB_BLOB = 1, 1080 | } 1081 | --------------------------------------------------------------------------------