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