tblgen/
lib.rs

1// Original work Copyright 2016 Alexander Stocko <as@coder.gg>.
2// Modified work Copyright 2023 Daan Vanoverloop
3// See the COPYRIGHT file at the top-level directory of this distribution.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! This crate provides raw bindings and a safe wrapper for [TableGen](https://llvm.org/docs/TableGen/),
12//! a domain-specific language used by the [LLVM project](https://llvm.org/).
13//!
14//! The goal of this crate is to enable users to develop custom [TableGen backends](https://llvm.org/docs/TableGen/BackGuide.html)
15//! in Rust. Hence the primary use case of this crate are procedural macros that
16//! generate Rust code from TableGen description files.
17//!
18//! # Safety
19//!
20//! This crate aims to be completely safe.
21//!
22//! # Supported LLVM Versions
23//!
24//! An installation of LLVM is required to use this crate.
25//! The versions of LLVM currently supported are 16.x.x (default) and 17.x.x.
26//! Different LLVM version can be selected using features flags (llvm16-0 or
27//! llvm17-0).
28//!
29//! The `TABLEGEN_<version>_PREFIX` environment variable can be used to specify
30//! a custom directory of the LLVM installation.
31//!
32//! # Examples
33//!
34//! The following example parse simple TableGen code provided as a `&str` and
35//! iterates over classes and defs defined in this file.
36//!
37//! ```rust
38//! use tblgen::{RecordKeeper, TableGenParser};
39//!
40//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
41//! let keeper: RecordKeeper = TableGenParser::new()
42//!     .add_source(
43//!         r#"
44//!         class A;
45//!         def D: A;
46//!         "#,
47//!     )?
48//!     .parse()?;
49//! assert_eq!(keeper.classes().next().unwrap().0, Ok("A"));
50//! assert_eq!(keeper.defs().next().unwrap().0, Ok("D"));
51//! assert_eq!(
52//!     keeper.all_derived_definitions("A").next().unwrap().name(),
53//!     Ok("D")
54//! );
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! By adding include paths, external TableGen files can be included.
60//!
61//! ```rust
62//! use std::path::Path;
63//! use tblgen::{RecordKeeper, TableGenParser};
64//!
65//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
66//! let keeper: RecordKeeper = TableGenParser::new()
67//!     .add_source(r#"include "mlir/IR/OpBase.td""#)?
68//!     .add_include_directory(&format!(
69//!         "{}/include",
70//!         std::env::var("TABLEGEN_200_PREFIX")?
71//!     ))
72//!     .parse()?;
73//! let i32_def = keeper.def("I32").expect("has I32 def");
74//! assert!(i32_def.subclass_of("I"));
75//! assert_eq!(i32_def.int_value("bitwidth"), Ok(32));
76//! # Ok(())
77//! # }
78//! ```
79//!
80//! You can also pass an included filename directly.
81//!
82//! ```rust
83//! use std::path::Path;
84//! use tblgen::{RecordKeeper, TableGenParser};
85//!
86//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
87//! let keeper: RecordKeeper = TableGenParser::new()
88//!     .add_source_file("mlir/IR/OpBase.td")
89//!     .add_include_directory(&format!(
90//!         "{}/include",
91//!         std::env::var("TABLEGEN_200_PREFIX")?
92//!     ))
93//!     .parse()?;
94//! let i32_def = keeper.def("I32").expect("has I32 def");
95//! assert!(i32_def.subclass_of("I"));
96//! assert_eq!(i32_def.int_value("bitwidth"), Ok(32));
97//! # Ok(())
98//! # }
99//! ```
100//!
101//! # API Stability
102//!
103//! LLVM does not provide a stable C API for TableGen, and the C API provided by
104//! this crate is not stable. Furthermore, the safe wrapper does not provide a
105//! stable interface either, since this crate is still in early development.
106
107pub mod error;
108pub mod init;
109/// TableGen records and record values.
110pub mod record;
111/// TableGen record keeper.
112pub mod record_keeper;
113mod string_ref;
114mod util;
115
116/// This module contains raw bindings for TableGen. Note that these bindings are
117/// unstable and can change at any time.
118#[allow(non_upper_case_globals)]
119#[allow(non_camel_case_types)]
120#[allow(non_snake_case)]
121pub mod raw {
122    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
123}
124
125use std::{
126    ffi::{CStr, CString},
127    marker::PhantomData,
128    sync::Mutex,
129};
130
131pub use error::Error;
132use error::TableGenError;
133pub use init::TypedInit;
134pub use record::{Record, RecordValue};
135pub use record_keeper::RecordKeeper;
136
137use raw::{
138    TableGenParserRef, tableGenAddIncludeDirectory, tableGenAddSource, tableGenAddSourceFile,
139    tableGenFree, tableGenGet, tableGenParse,
140};
141use string_ref::StringRef;
142
143// TableGen only exposes `TableGenParseFile` in its API.
144// However, this function uses global state and therefore it is not thread safe.
145// Until they remove this hack, we have to deal with it ourselves.
146static TABLEGEN_PARSE_LOCK: Mutex<()> = Mutex::new(());
147
148/// Builder struct that parses TableGen source files and builds a
149/// [`RecordKeeper`].
150#[derive(Debug, PartialEq, Eq)]
151pub struct TableGenParser<'s> {
152    raw: TableGenParserRef,
153    source_strings: Vec<CString>,
154    _source_ref: PhantomData<&'s str>,
155}
156
157impl Default for TableGenParser<'_> {
158    fn default() -> Self {
159        Self::new()
160    }
161}
162
163impl<'s> TableGenParser<'s> {
164    /// Initalizes a new TableGen parser.
165    pub fn new() -> Self {
166        Self {
167            raw: unsafe { tableGenGet() },
168            source_strings: Vec::new(),
169            _source_ref: PhantomData,
170        }
171    }
172
173    /// Adds the given path to the list of included directories.
174    pub fn add_include_directory(self, include: &str) -> Self {
175        unsafe { tableGenAddIncludeDirectory(self.raw, StringRef::from(include).to_raw()) }
176        self
177    }
178
179    /// Reads TableGen source code from the file at the given path.
180    pub fn add_source_file(self, source: &str) -> Self {
181        unsafe { tableGenAddSourceFile(self.raw, StringRef::from(source).to_raw()) }
182        self
183    }
184
185    /// Adds the given TableGen source string.
186    ///
187    /// The string must be null-terminated and is not copied, hence it is
188    /// required to live until the source code is parsed.
189    pub fn add_source_raw(self, source: &'s CStr) -> Result<Self, Error> {
190        if unsafe { tableGenAddSource(self.raw, source.as_ptr()) > 0 } {
191            Ok(self)
192        } else {
193            Err(TableGenError::InvalidSource.into())
194        }
195    }
196
197    /// Adds the given TableGen source string.
198    ///
199    /// The string is copied into a null-terminated [`CString`].
200    pub fn add_source(mut self, source: &str) -> Result<Self, Error> {
201        let string = CString::new(source).map_err(TableGenError::from)?;
202        self.source_strings.push(string);
203        if unsafe {
204            tableGenAddSource(
205                self.raw,
206                self.source_strings.last().expect("not empty").as_ptr(),
207            ) > 0
208        } {
209            Ok(self)
210        } else {
211            Err(TableGenError::InvalidSource.into())
212        }
213    }
214
215    pub fn source_info(&self) -> SourceInfo<'_> {
216        SourceInfo(self)
217    }
218
219    /// Parses the TableGen source files and returns a [`RecordKeeper`].
220    ///
221    /// Due to limitations of TableGen, parsing TableGen is not thread-safe.
222    /// In order to provide thread-safety, this method ensures that any
223    /// concurrent parse operations are executed sequentially.
224    pub fn parse(self) -> Result<RecordKeeper<'s>, Error> {
225        unsafe {
226            let guard = TABLEGEN_PARSE_LOCK.lock().unwrap();
227            let keeper = tableGenParse(self.raw);
228            let res = if !keeper.is_null() {
229                Ok(RecordKeeper::from_raw(keeper, self))
230            } else {
231                Err(TableGenError::Parse.into())
232            };
233            drop(guard);
234            res
235        }
236    }
237}
238
239impl Drop for TableGenParser<'_> {
240    fn drop(&mut self) {
241        unsafe {
242            tableGenFree(self.raw);
243        }
244    }
245}
246
247/// Reference to TableGen source file.
248///
249/// See [`TableGenParser::source_info`](TableGenParser::source_info) and
250/// [`RecordKeeper::source_info`](RecordKeeper::source_info).
251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
252pub struct SourceInfo<'a>(pub(crate) &'a TableGenParser<'a>);