From 06319b415a1a836e9d854b305314fd86df2b990a Mon Sep 17 00:00:00 2001 From: data-pup Date: Thu, 19 Jul 2018 12:56:23 -0400 Subject: [PATCH] Added initial Rust codegen-meta implementation. (#403) * Added initial Rust codegen-meta implementation. * Replace 'Cretonne' in comments. * Prevent iterator overflow. * 1.25.0 compatibility changes. * Implemented debug traits for type variants. * Added consistent comments. * Cleaned up a loop via clippy fix. * Added new license to codegen-meta Cargo.toml * Edited lane type iterator `next` method. * Removed functions that are not needed in Rust, and edited desc. * Debug trait derived for valuetype. * Added comments for iterator types in the base types submodule. * Numbering is now handled in the cdsl/types.rs file. * Moved type number logic into cdsl/types. * Repeating the lane change cleanup. * Removed codegen-meta crate from codegen deps. * Typo fix. * Addressing a patch note. * Addressing patch note. * Lowercase in vector names. * Fixing a comment bug. * Added a copy of the license file. * Formatting changes. * Cleaned up the vector type numbering. * 1.25 compatibility. * Fixed pattern match arms. --- lib/codegen-meta/Cargo.toml | 18 + lib/codegen-meta/LICENSE | 219 ++++++++++++ lib/codegen-meta/src/base/mod.rs | 3 + lib/codegen-meta/src/base/types.rs | 188 ++++++++++ lib/codegen-meta/src/cdsl/mod.rs | 38 ++ lib/codegen-meta/src/cdsl/types.rs | 473 +++++++++++++++++++++++++ lib/codegen-meta/src/error.rs | 47 +++ lib/codegen-meta/src/gen_build_deps.rs | 48 +++ lib/codegen-meta/src/gen_types.rs | 74 ++++ lib/codegen-meta/src/lib.rs | 7 + lib/codegen-meta/src/srcgen.rs | 316 +++++++++++++++++ lib/codegen/Cargo.toml | 3 + lib/codegen/build.rs | 27 +- 13 files changed, 1460 insertions(+), 1 deletion(-) create mode 100644 lib/codegen-meta/Cargo.toml create mode 100644 lib/codegen-meta/LICENSE create mode 100644 lib/codegen-meta/src/base/mod.rs create mode 100644 lib/codegen-meta/src/base/types.rs create mode 100644 lib/codegen-meta/src/cdsl/mod.rs create mode 100644 lib/codegen-meta/src/cdsl/types.rs create mode 100644 lib/codegen-meta/src/error.rs create mode 100644 lib/codegen-meta/src/gen_build_deps.rs create mode 100644 lib/codegen-meta/src/gen_types.rs create mode 100644 lib/codegen-meta/src/lib.rs create mode 100644 lib/codegen-meta/src/srcgen.rs diff --git a/lib/codegen-meta/Cargo.toml b/lib/codegen-meta/Cargo.toml new file mode 100644 index 0000000000..9d75e91ce8 --- /dev/null +++ b/lib/codegen-meta/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cranelift-codegen-meta" +authors = ["The Cranelift Project Developers"] +version = "0.15.0" +description = "Metaprogram for cranelift-codegen code generator library" +license = "Apache-2.0 WITH LLVM-exception" +documentation = "https://cranelift.readthedocs.io/" +repository = "https://github.com/CraneStation/cranelift" +keywords = ["compile", "compiler", "jit"] + +[dependencies] +# It is a goal of the cranelift-codegen crate to have minimal external dependencies. +# Please don't add any unless they are essential to the task of creating binary +# machine code. + +[badges] +maintenance = { status = "experimental" } +travis-ci = { repository = "CraneStation/cranelift" } diff --git a/lib/codegen-meta/LICENSE b/lib/codegen-meta/LICENSE new file mode 100644 index 0000000000..be1d7c438a --- /dev/null +++ b/lib/codegen-meta/LICENSE @@ -0,0 +1,219 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. diff --git a/lib/codegen-meta/src/base/mod.rs b/lib/codegen-meta/src/base/mod.rs new file mode 100644 index 0000000000..a75614d9c8 --- /dev/null +++ b/lib/codegen-meta/src/base/mod.rs @@ -0,0 +1,3 @@ +//! Definitions for the base Cranelift language. + +pub mod types; diff --git a/lib/codegen-meta/src/base/types.rs b/lib/codegen-meta/src/base/types.rs new file mode 100644 index 0000000000..7ce42b1978 --- /dev/null +++ b/lib/codegen-meta/src/base/types.rs @@ -0,0 +1,188 @@ +//! This module predefines all the Cranelift scalar types. + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Bool { + /// 1-bit bool. + B1 = 1, + /// 8-bit bool. + B8 = 8, + /// 16-bit bool. + B16 = 16, + /// 32-bit bool. + B32 = 32, + /// 64-bit bool. + B64 = 64, +} + +/// This provides an iterator through all of the supported bool variants. +pub struct BoolIterator { + index: u8, +} + +impl BoolIterator { + pub fn new() -> Self { + Self { index: 0 } + } +} + +impl Iterator for BoolIterator { + type Item = Bool; + fn next(&mut self) -> Option { + let res = match self.index { + 0 => Some(Bool::B1), + 1 => Some(Bool::B8), + 2 => Some(Bool::B16), + 3 => Some(Bool::B32), + 4 => Some(Bool::B64), + _ => return None, + }; + self.index += 1; + res + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Int { + /// 8-bit int. + I8 = 8, + /// 16-bit int. + I16 = 16, + /// 32-bit int. + I32 = 32, + /// 64-bit int. + I64 = 64, +} + +/// This provides an iterator through all of the supported int variants. +pub struct IntIterator { + index: u8, +} + +impl IntIterator { + pub fn new() -> Self { + Self { index: 0 } + } +} + +impl Iterator for IntIterator { + type Item = Int; + fn next(&mut self) -> Option { + let res = match self.index { + 0 => Some(Int::I8), + 1 => Some(Int::I16), + 2 => Some(Int::I32), + 3 => Some(Int::I64), + _ => return None, + }; + self.index += 1; + res + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Float { + F32 = 32, + F64 = 64, +} + +/// Iterator through the variants of the Float enum. +pub struct FloatIterator { + index: u8, +} + +impl FloatIterator { + pub fn new() -> Self { + Self { index: 0 } + } +} + +/// This provides an iterator through all of the supported float variants. +impl Iterator for FloatIterator { + type Item = Float; + fn next(&mut self) -> Option { + let res = match self.index { + 0 => Some(Float::F32), + 1 => Some(Float::F64), + _ => return None, + }; + self.index += 1; + res + } +} + +/// A type representing CPU flags. +/// +/// Flags can't be stored in memory. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Flag { + /// CPU flags from an integer comparison. + IFlags, + /// CPU flags from a floating point comparison. + FFlags, +} + +/// Iterator through the variants of the Flag enum. +pub struct FlagIterator { + index: u8, +} + +impl FlagIterator { + pub fn new() -> Self { + Self { index: 0 } + } +} + +impl Iterator for FlagIterator { + type Item = Flag; + fn next(&mut self) -> Option { + let res = match self.index { + 0 => Some(Flag::IFlags), + 1 => Some(Flag::FFlags), + _ => return None, + }; + self.index += 1; + res + } +} + +#[cfg(test)] +mod iter_tests { + use super::*; + + #[test] + fn bool_iter_works() { + let mut bool_iter = BoolIterator::new(); + assert_eq!(bool_iter.next(), Some(Bool::B1)); + assert_eq!(bool_iter.next(), Some(Bool::B8)); + assert_eq!(bool_iter.next(), Some(Bool::B16)); + assert_eq!(bool_iter.next(), Some(Bool::B32)); + assert_eq!(bool_iter.next(), Some(Bool::B64)); + assert_eq!(bool_iter.next(), None); + } + + #[test] + fn int_iter_works() { + let mut int_iter = IntIterator::new(); + assert_eq!(int_iter.next(), Some(Int::I8)); + assert_eq!(int_iter.next(), Some(Int::I16)); + assert_eq!(int_iter.next(), Some(Int::I32)); + assert_eq!(int_iter.next(), Some(Int::I64)); + assert_eq!(int_iter.next(), None); + } + + #[test] + fn float_iter_works() { + let mut float_iter = FloatIterator::new(); + assert_eq!(float_iter.next(), Some(Float::F32)); + assert_eq!(float_iter.next(), Some(Float::F64)); + assert_eq!(float_iter.next(), None); + } + + #[test] + fn flag_iter_works() { + let mut flag_iter = FlagIterator::new(); + assert_eq!(flag_iter.next(), Some(Flag::IFlags)); + assert_eq!(flag_iter.next(), Some(Flag::FFlags)); + assert_eq!(flag_iter.next(), None); + } +} diff --git a/lib/codegen-meta/src/cdsl/mod.rs b/lib/codegen-meta/src/cdsl/mod.rs new file mode 100644 index 0000000000..8f087a7c3d --- /dev/null +++ b/lib/codegen-meta/src/cdsl/mod.rs @@ -0,0 +1,38 @@ +//! Cranelift DSL classes. +//! +//! This module defines the classes that are used to define Cranelift +//! instructions and other entitties. + +pub mod types; + +/// Convert the string `s` to CamelCase. +fn _camel_case(s: &str) -> String { + let mut output_chars = String::with_capacity(s.len()); + + let mut capitalize = true; + for curr_char in s.chars() { + if curr_char == '_' { + capitalize = true; + } else { + if capitalize { + output_chars.extend(curr_char.to_uppercase()); + } else { + output_chars.push(curr_char); + } + capitalize = false; + } + } + + output_chars +} + +#[cfg(test)] +mod tests { + use super::_camel_case as camel_case; + + #[test] + fn camel_case_works() { + assert_eq!(camel_case("x"), "X"); + assert_eq!(camel_case("camel_case"), "CamelCase"); + } +} diff --git a/lib/codegen-meta/src/cdsl/types.rs b/lib/codegen-meta/src/cdsl/types.rs new file mode 100644 index 0000000000..90438e9179 --- /dev/null +++ b/lib/codegen-meta/src/cdsl/types.rs @@ -0,0 +1,473 @@ +//! Cranelift ValueType hierarchy + +// Temporary disabled: Unused at the moment. +// use std::collections::HashMap; + +use std::fmt; + +use base::types as base_types; + +// Numbering scheme for value types: +// +// 0: Void +// 0x01-0x6f: Special types +// 0x70-0x7f: Lane types +// 0x80-0xff: Vector types +// +// Vector types are encoded with the lane type in the low 4 bits and log2(lanes) +// in the high 4 bits, giving a range of 2-256 lanes. +static LANE_BASE: u8 = 0x70; + +// Rust name prefix used for the `rust_name` method. +static _RUST_NAME_PREFIX: &'static str = "ir::types::"; + +// ValueType variants (i8, i32, ...) are provided in `base::types.rs`. + +/// A concrete SSA value type. +/// +/// All SSA values have a type that is described by an instance of `ValueType` +/// or one of its subclasses. +#[derive(Debug)] +pub enum ValueType { + BV(BVType), + Lane(LaneType), + Special(SpecialType), + Vector(VectorType), +} + +impl ValueType { + /// Iterate through all of the lane types. + pub fn all_lane_types() -> LaneTypeIterator { + LaneTypeIterator::new() + } + + /// Iterate through all of the special types (neither lanes nor vectors). + pub fn all_special_types() -> SpecialTypeIterator { + SpecialTypeIterator::new() + } + + /// Return a string containing the documentation comment for this type. + pub fn doc(&self) -> String { + match *self { + ValueType::BV(ref b) => b.doc(), + ValueType::Lane(l) => l.doc(), + ValueType::Special(s) => s.doc(), + ValueType::Vector(ref v) => v.doc(), + } + } + + /// Return the number of bits in a lane. + pub fn lane_bits(&self) -> u64 { + match *self { + ValueType::BV(ref b) => b.lane_bits(), + ValueType::Lane(l) => l.lane_bits(), + ValueType::Special(s) => s.lane_bits(), + ValueType::Vector(ref v) => v.lane_bits(), + } + } + + /// Return the number of lanes. + pub fn lane_count(&self) -> u64 { + match *self { + ValueType::Vector(ref v) => v.lane_count(), + _ => 1, + } + } + + /// Find the number of bytes that this type occupies in memory. + pub fn membytes(&self) -> u64 { + self.width() / 8 + } + + /// Get the name of this type. + pub fn name(&self) -> String { + match *self { + ValueType::BV(ref b) => b.name(), + ValueType::Lane(l) => l.name(), + ValueType::Special(s) => s.name(), + ValueType::Vector(ref v) => v.name(), + } + } + + /// Find the unique number associated with this type. + pub fn number(&self) -> Option { + match *self { + ValueType::BV(_) => None, + ValueType::Lane(l) => Some(l.number()), + ValueType::Special(s) => Some(s.number()), + ValueType::Vector(ref v) => Some(v.number()), + } + } + + /// Return the name of this type for generated Rust source files. + pub fn _rust_name(&self) -> String { + format!("{}{}", _RUST_NAME_PREFIX, self.name().to_uppercase()) + } + + /// Return true iff: + /// 1. self and other have equal number of lanes + /// 2. each lane in self has at least as many bits as a lane in other + pub fn _wider_or_equal(&self, rhs: &ValueType) -> bool { + (self.lane_count() == rhs.lane_count()) && (self.lane_bits() >= rhs.lane_bits()) + } + + /// Return the total number of bits of an instance of this type. + pub fn width(&self) -> u64 { + self.lane_count() * self.lane_bits() + } +} + +impl fmt::Display for ValueType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + +/// Create a ValueType from a given bitvector type. +impl From for ValueType { + fn from(bv: BVType) -> Self { + ValueType::BV(bv) + } +} + +/// Create a ValueType from a given lane type. +impl From for ValueType { + fn from(lane: LaneType) -> Self { + ValueType::Lane(lane) + } +} + +/// Create a ValueType from a given special type. +impl From for ValueType { + fn from(spec: SpecialType) -> Self { + ValueType::Special(spec) + } +} + +/// Create a ValueType from a given vector type. +impl From for ValueType { + fn from(vector: VectorType) -> Self { + ValueType::Vector(vector) + } +} + +/// A concrete scalar type that can appear as a vector lane too. +#[derive(Clone, Copy)] +pub enum LaneType { + BoolType(base_types::Bool), + FloatType(base_types::Float), + IntType(base_types::Int), +} + +impl LaneType { + /// Return a string containing the documentation comment for this lane type. + pub fn doc(&self) -> String { + match *self { + LaneType::BoolType(_) => format!("A boolean type with {} bits.", self.lane_bits()), + LaneType::FloatType(base_types::Float::F32) => String::from( + "A 32-bit floating point type represented in the IEEE 754-2008 + *binary32* interchange format. This corresponds to the :c:type:`float` + type in most C implementations.", + ), + LaneType::FloatType(base_types::Float::F64) => String::from( + "A 64-bit floating point type represented in the IEEE 754-2008 + *binary64* interchange format. This corresponds to the :c:type:`double` + type in most C implementations.", + ), + LaneType::IntType(_) if self.lane_bits() < 32 => format!( + "An integer type with {} bits. + WARNING: arithmetic on {}bit integers is incomplete", + self.lane_bits(), + self.lane_bits() + ), + LaneType::IntType(_) => format!("An integer type with {} bits.", self.lane_bits()), + } + } + + /// Return the number of bits in a lane. + pub fn lane_bits(&self) -> u64 { + match *self { + LaneType::BoolType(ref b) => *b as u64, + LaneType::FloatType(ref f) => *f as u64, + LaneType::IntType(ref i) => *i as u64, + } + } + + /// Get the name of this lane type. + pub fn name(&self) -> String { + match *self { + LaneType::BoolType(_) => format!("b{}", self.lane_bits()), + LaneType::FloatType(_) => format!("f{}", self.lane_bits()), + LaneType::IntType(_) => format!("i{}", self.lane_bits()), + } + } + + /// Find the unique number associated with this lane type. + pub fn number(&self) -> u8 { + LANE_BASE + match *self { + LaneType::BoolType(base_types::Bool::B1) => 0, + LaneType::BoolType(base_types::Bool::B8) => 1, + LaneType::BoolType(base_types::Bool::B16) => 2, + LaneType::BoolType(base_types::Bool::B32) => 3, + LaneType::BoolType(base_types::Bool::B64) => 4, + LaneType::IntType(base_types::Int::I8) => 5, + LaneType::IntType(base_types::Int::I16) => 6, + LaneType::IntType(base_types::Int::I32) => 7, + LaneType::IntType(base_types::Int::I64) => 8, + LaneType::FloatType(base_types::Float::F32) => 9, + LaneType::FloatType(base_types::Float::F64) => 10, + } + } +} + +impl fmt::Debug for LaneType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let inner_msg = format!("bits={}", self.lane_bits()); + write!( + f, + "{}", + match *self { + LaneType::BoolType(_) => format!("BoolType({})", inner_msg), + LaneType::FloatType(_) => format!("FloatType({})", inner_msg), + LaneType::IntType(_) => format!("IntType({})", inner_msg), + } + ) + } +} + +/// Create a LaneType from a given bool variant. +impl From for LaneType { + fn from(b: base_types::Bool) -> Self { + LaneType::BoolType(b) + } +} + +/// Create a LaneType from a given float variant. +impl From for LaneType { + fn from(f: base_types::Float) -> Self { + LaneType::FloatType(f) + } +} + +/// Create a LaneType from a given int variant. +impl From for LaneType { + fn from(i: base_types::Int) -> Self { + LaneType::IntType(i) + } +} + +/// An iterator for different lane types. +pub struct LaneTypeIterator { + bool_iter: base_types::BoolIterator, + int_iter: base_types::IntIterator, + float_iter: base_types::FloatIterator, +} + +impl LaneTypeIterator { + /// Create a new lane type iterator. + fn new() -> Self { + Self { + bool_iter: base_types::BoolIterator::new(), + int_iter: base_types::IntIterator::new(), + float_iter: base_types::FloatIterator::new(), + } + } +} + +impl Iterator for LaneTypeIterator { + type Item = LaneType; + fn next(&mut self) -> Option { + if let Some(b) = self.bool_iter.next() { + Some(LaneType::from(b)) + } else if let Some(i) = self.int_iter.next() { + Some(LaneType::from(i)) + } else if let Some(f) = self.float_iter.next() { + Some(LaneType::from(f)) + } else { + None + } + } +} + +/// A concrete SIMD vector type. +/// +/// A vector type has a lane type which is an instance of `LaneType`, +/// and a positive number of lanes. +pub struct VectorType { + base: LaneType, + lanes: u64, +} + +impl VectorType { + /// Initialize a new integer type with `n` bits. + pub fn new(base: LaneType, lanes: u64) -> VectorType { + VectorType { base, lanes } + } + + /// Return a string containing the documentation comment for this vector type. + pub fn doc(&self) -> String { + format!( + "A SIMD vector with {} lanes containing a `{}` each.", + self.lane_count(), + self.base.name() + ) + } + + /// Return the number of bits in a lane. + pub fn lane_bits(&self) -> u64 { + self.base.lane_bits() + } + + /// Return the number of lanes. + pub fn lane_count(&self) -> u64 { + self.lanes + } + + /// Get the name of this vector type. + pub fn name(&self) -> String { + format!("{}x{}", self.base.name(), self.lane_count()) + } + + /// Find the unique number associated with this vector type. + /// + /// Vector types are encoded with the lane type in the low 4 bits and + /// log2(lanes) in the high 4 bits, giving a range of 2-256 lanes. + pub fn number(&self) -> u8 { + let lanes_log_2: u32 = 63 - self.lane_count().leading_zeros(); + let base_num = u32::from(self.base.number()); + let num = (lanes_log_2 << 4) + base_num; + num as u8 + } +} + +impl fmt::Debug for VectorType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "VectorType(base={}, lanes={})", + self.base.name(), + self.lane_count() + ) + } +} + +/// A flat bitvector type. Used for semantics description only. +pub struct BVType { + bits: u64, +} + +impl BVType { + /// Initialize a new bitvector type with `n` bits. + pub fn _new(bits: u64) -> Self { + Self { bits } + } + + /// Return a string containing the documentation comment for this bitvector type. + pub fn doc(&self) -> String { + format!("A bitvector type with {} bits.", self.bits) + } + + /// Return the number of bits in a lane. + pub fn lane_bits(&self) -> u64 { + self.bits + } + + /// Get the name of this bitvector type. + pub fn name(&self) -> String { + format!("bv{}", self.bits) + } +} + +impl fmt::Debug for BVType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "BVType(bits={})", self.lane_bits()) + } +} + +/// A concrete scalar type that is neither a vector nor a lane type. +/// +/// Special types cannot be used to form vectors. +#[derive(Clone, Copy)] +pub enum SpecialType { + Flag(base_types::Flag), +} + +impl SpecialType { + /// Return a string containing the documentation comment for this special type. + pub fn doc(&self) -> String { + match *self { + SpecialType::Flag(base_types::Flag::IFlags) => String::from( + "CPU flags representing the result of an integer comparison. These flags + can be tested with an :type:`intcc` condition code.", + ), + SpecialType::Flag(base_types::Flag::FFlags) => String::from( + "CPU flags representing the result of a floating point comparison. These + flags can be tested with a :type:`floatcc` condition code.", + ), + } + } + + /// Return the number of bits in a lane. + pub fn lane_bits(&self) -> u64 { + match *self { + SpecialType::Flag(_) => 0, + } + } + + /// Get the name of this special type. + pub fn name(&self) -> String { + match *self { + SpecialType::Flag(base_types::Flag::IFlags) => "iflags".to_string(), + SpecialType::Flag(base_types::Flag::FFlags) => "fflags".to_string(), + } + } + + /// Find the unique number associated with this special type. + pub fn number(&self) -> u8 { + match *self { + SpecialType::Flag(base_types::Flag::IFlags) => 1, + SpecialType::Flag(base_types::Flag::FFlags) => 2, + } + } +} + +impl fmt::Debug for SpecialType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + match *self { + SpecialType::Flag(_) => format!("FlagsType({})", self.name()), + } + ) + } +} + +impl From for SpecialType { + fn from(f: base_types::Flag) -> Self { + SpecialType::Flag(f) + } +} + +pub struct SpecialTypeIterator { + flag_iter: base_types::FlagIterator, +} + +impl SpecialTypeIterator { + fn new() -> Self { + Self { + flag_iter: base_types::FlagIterator::new(), + } + } +} + +impl Iterator for SpecialTypeIterator { + type Item = SpecialType; + fn next(&mut self) -> Option { + if let Some(f) = self.flag_iter.next() { + Some(SpecialType::from(f)) + } else { + None + } + } +} diff --git a/lib/codegen-meta/src/error.rs b/lib/codegen-meta/src/error.rs new file mode 100644 index 0000000000..316e69bbc5 --- /dev/null +++ b/lib/codegen-meta/src/error.rs @@ -0,0 +1,47 @@ +use std::fmt; +use std::io; + +/// An error that occurred when the cranelift_codegen_meta crate was generating +/// source files for the cranelift_codegen crate. +#[derive(Debug)] +pub struct Error { + inner: Box, +} + +impl Error { + /// Create a new error object with the given message. + pub fn with_msg>(msg: S) -> Error { + Error { + inner: Box::new(ErrorInner::Msg(msg.into())), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error { + inner: Box::new(ErrorInner::IoError(e)), + } + } +} + +#[derive(Debug)] +enum ErrorInner { + Msg(String), + IoError(io::Error), +} + +impl fmt::Display for ErrorInner { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ErrorInner::Msg(ref s) => write!(f, "{}", s), + ErrorInner::IoError(ref e) => write!(f, "{}", e), + } + } +} diff --git a/lib/codegen-meta/src/gen_build_deps.rs b/lib/codegen-meta/src/gen_build_deps.rs new file mode 100644 index 0000000000..a267bc664e --- /dev/null +++ b/lib/codegen-meta/src/gen_build_deps.rs @@ -0,0 +1,48 @@ +//! Generate build dependencies for Cargo. +//! +//! The `build.rs` script is invoked by cargo when building lib/codegen to +//! generate Rust code from the instruction descriptions. Cargo needs to know when +//! it is necessary to rerun the build script. +//! +//! If the build script outputs lines of the form: +//! cargo:rerun-if-changed=/path/to/file +//! +//! cargo will rerun the build script when those files have changed since the last +//! build. + +use error; + +use std::fs; +use std::path; + +/// Recursively find all interesting source files and directories in the +/// directory tree starting at `dir`. Yield a path to each file. +fn source_files(dir: &path::PathBuf) -> Result, error::Error> { + let mut files = Vec::new(); + if dir.is_dir() { + for entry in fs::read_dir(&dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + let mut child_dir_files = source_files(&path)?; + files.append(&mut child_dir_files); + } else if let Some(ext) = path.extension() { + if ext == "rs" { + files.push(path.to_str().unwrap().to_string()); + } + } + } + } + Ok(files) +} + +/// Generate the lines of `cargo:rerun-if-changed` output, for each Rust source +/// file inside of the cranelift-codegen-meta crate. +pub fn generate(meta_dir: &path::PathBuf) -> Result<(), error::Error> { + println!("Dependencies from Rust meta language directory:"); + source_files(&meta_dir)? + .into_iter() + .for_each(|p| println!("cargo:rerun-if-changed={}", p)); + + Ok(()) +} diff --git a/lib/codegen-meta/src/gen_types.rs b/lib/codegen-meta/src/gen_types.rs new file mode 100644 index 0000000000..1055f0de0e --- /dev/null +++ b/lib/codegen-meta/src/gen_types.rs @@ -0,0 +1,74 @@ +//! Generate sources with type info. +//! +//! This generates a `types.rs` file which is included in +//! `lib/codegen/ir/types.rs`. The file provides constant definitions for the +//! most commonly used types, including all of the scalar types. +//! +//! This ensures that the metaprogram and the generated program see the same +//! type numbering. + +use cdsl::types as cdsl_types; +use error; +use srcgen; + +/// Emit a constant definition of a single value type. +fn emit_type(ty: &cdsl_types::ValueType, fmt: &mut srcgen::Formatter) -> Result<(), error::Error> { + let name = ty.name().to_uppercase(); + let number = ty.number().ok_or_else(|| { + error::Error::with_msg(format!( + "Could not emit type `{}` which has no number.", + name + )) + })?; + + let definition = format!("pub const {}: Type = Type({:#x});\n", name, number); + + fmt.doc_comment(&ty.doc()); + fmt.line(&definition); + + Ok(()) +} + +/// Emit definition for all vector types with `bits` total size. +fn emit_vectors(bits: u64, fmt: &mut srcgen::Formatter) -> Result<(), error::Error> { + let vec_size: u64 = bits / 8; + for vec in cdsl_types::ValueType::all_lane_types() + .map(|ty| (ty, cdsl_types::ValueType::from(ty).membytes())) + .filter(|&(_, lane_size)| lane_size != 0 && lane_size < vec_size) + .map(|(ty, lane_size)| (ty, vec_size / lane_size)) + .map(|(ty, lanes)| cdsl_types::VectorType::new(ty, lanes)) + { + emit_type(&cdsl_types::ValueType::from(vec), fmt)?; + } + + Ok(()) +} + +/// Emit types using the given formatter object. +fn emit_types(fmt: &mut srcgen::Formatter) -> Result<(), error::Error> { + // Emit all of the special types, such as types for CPU flags. + for spec in cdsl_types::ValueType::all_special_types().map(|ty| cdsl_types::ValueType::from(ty)) + { + emit_type(&spec, fmt)?; + } + + // Emit all of the lane types, such integers, floats, and booleans. + for ty in cdsl_types::ValueType::all_lane_types().map(cdsl_types::ValueType::from) { + emit_type(&ty, fmt)?; + } + + // Emit vector definitions for common SIMD sizes. + for vec_size in &[64_u64, 128, 256, 512] { + emit_vectors(*vec_size, fmt)?; + } + + Ok(()) +} + +/// Generate the types file. +pub fn generate(filename: &str, out_dir: &str) -> Result<(), error::Error> { + let mut fmt = srcgen::Formatter::new(); + emit_types(&mut fmt)?; + fmt.update_file(filename, out_dir)?; + Ok(()) +} diff --git a/lib/codegen-meta/src/lib.rs b/lib/codegen-meta/src/lib.rs new file mode 100644 index 0000000000..7a199be9f5 --- /dev/null +++ b/lib/codegen-meta/src/lib.rs @@ -0,0 +1,7 @@ +pub mod error; +pub mod gen_build_deps; +pub mod gen_types; + +mod base; +mod cdsl; +mod srcgen; diff --git a/lib/codegen-meta/src/srcgen.rs b/lib/codegen-meta/src/srcgen.rs new file mode 100644 index 0000000000..53a4dc5f75 --- /dev/null +++ b/lib/codegen-meta/src/srcgen.rs @@ -0,0 +1,316 @@ +//! Source code generator. +//! +//! The `srcgen` module contains generic helper routines and classes for +//! generating source code. + +use std::collections::{BTreeMap, HashSet}; +use std::fs; +use std::io::Write; +use std::path; + +use error; + +static SHIFTWIDTH: usize = 4; + +struct _IndentedScope { + fmt: Formatter, + after: Option, +} + +impl _IndentedScope { + fn _enter(&mut self) { + self.fmt._indent_push(); + } + + fn _exit(&mut self) { + self.fmt._indent_pop(); + if let Some(ref s) = self.after { + self.fmt.line(&s); + } + } +} + +pub struct Formatter { + indent: usize, + lines: Vec, +} + +impl Formatter { + /// Source code formatter class. Used to collect source code to be written + /// to a file, and keep track of indentation. + pub fn new() -> Formatter { + Formatter { + indent: 0, + lines: Vec::new(), + } + } + + /// Increase current indentation level by one. + pub fn _indent_push(&mut self) { + self.indent += 1; + } + + /// Decrease indentation by one level. + pub fn _indent_pop(&mut self) { + assert!(self.indent > 0, "Already at top level indentation"); + self.indent -= 1; + } + + /// Get the current whitespace indentation in the form of a String. + fn get_indent(&self) -> String { + if self.indent == 0 { + String::new() + } else { + format!("{:-1$}", " ", self.indent * SHIFTWIDTH) + } + } + + /// Get a string containing whitespace outdented one level. Used for + /// lines of code that are inside a single indented block. + fn _get_outdent(&mut self) -> String { + self._indent_push(); + let s = self.get_indent(); + self._indent_pop(); + s + } + + /// Add an indented line. + pub fn line(&mut self, contents: &str) { + let indented_line = format!("{}{}\n", self.get_indent(), contents); + self.lines.push(indented_line); + } + + /// Emit a line outdented one level. + pub fn _outdented_line(&mut self, s: &str) { + let new_line = format!("{}{}", self._get_outdent(), s); + self.lines.push(new_line); + } + + /// Write `self.lines` to a file. + pub fn update_file(&self, filename: &str, directory: &str) -> Result<(), error::Error> { + #[cfg(target_family = "windows")] + let path_str = format!("{}\\{}", directory, filename); + #[cfg(not(target_family = "windows"))] + let path_str = format!("{}/{}", directory, filename); + + let path = path::Path::new(&path_str); + let mut f = fs::File::create(path)?; + + for l in self.lines.iter().map(|l| l.as_bytes()) { + f.write_all(l)?; + } + + Ok(()) + } + + /// Return a scope object for use with a `with` statement. + /// The optional `before` and `after` parameters are surrounding lines + /// which are *not* indented. + fn _indented(&self, _before: Option<&str>, _after: Option<&str>) -> _IndentedScope { + unimplemented!(); + } + + /// Add one or more lines after stripping common indentation. + pub fn _multi_line(&mut self, s: &str) { + parse_multiline(s).into_iter().for_each(|l| self.line(&l)); + } + + /// Add a comment line. + pub fn _comment(&mut self, s: &str) { + let commented_line = format!("// {}", s); + self.line(&commented_line); + } + + /// Add a (multi-line) documentation comment. + pub fn doc_comment(&mut self, contents: &str) { + parse_multiline(contents) + .iter() + .map(|l| format!("/// {}", l)) + .for_each(|s| self.line(s.as_str())); + } + + /// Add a match expression. + fn _add_match(&mut self, _m: &_Match) { + unimplemented!(); + } +} + +/// Compute the indentation of s, or None of an empty line. +fn _indent(s: &str) -> Option { + if s.is_empty() { + None + } else { + let t = s.trim_left(); + Some(s.len() - t.len()) + } +} + +/// Given a multi-line string, split it into a sequence of lines after +/// stripping a common indentation. This is useful for strings defined with +/// doc strings. +fn parse_multiline(s: &str) -> Vec { + // Convert tabs into spaces. + let expanded_tab = format!("{:-1$}", " ", SHIFTWIDTH); + let lines: Vec = s.lines().map(|l| l.replace("\t", &expanded_tab)).collect(); + + // Determine minimum indentation, ignoring the first line. + let indent = lines + .iter() + .skip(1) + .map(|l| l.len() - l.trim_left().len()) + .filter(|&i| i > 0) + .min(); + + // Strip off leading blank lines. + let mut lines_iter = lines.iter().skip_while(|l| l.is_empty()); + let mut trimmed = Vec::with_capacity(lines.len()); + + // Remove indentation (first line is special) + if let Some(s) = lines_iter.next().map(|l| l.trim()).map(|l| l.to_string()) { + trimmed.push(s); + } + + // Remove trailing whitespace from other lines. + let mut other_lines = if let Some(indent) = indent { + lines_iter + .map(|l| &l[indent..]) + .map(|l| l.trim_right()) + .map(|l| l.to_string()) + .collect::>() + } else { + lines_iter + .map(|l| l.trim_right()) + .map(|l| l.to_string()) + .collect::>() + }; + + trimmed.append(&mut other_lines); + + // Strip off trailing blank lines. + while let Some(s) = trimmed.pop() { + if s.is_empty() { + continue; + } else { + trimmed.push(s); + break; + } + } + + trimmed +} + +/// Match formatting class. +/// +/// Match objects collect all the information needed to emit a Rust `match` +/// expression, automatically deduplicating overlapping identical arms. +/// +/// Note that this class is ignorant of Rust types, and considers two fields +/// with the same name to be equivalent. A BTreeMap is used to represent the +/// arms in order to make the order deterministic. +struct _Match<'a> { + _expr: &'a str, + arms: BTreeMap<(Vec<&'a str>, &'a str), HashSet<&'a str>>, +} + +impl<'a> _Match<'a> { + /// Create a new match statement on `expr`. + fn _new(expr: &'a str) -> Self { + Self { + _expr: expr, + arms: BTreeMap::new(), + } + } + + /// Add an arm to the Match statement. + fn _arm(&mut self, name: &'a str, fields: Vec<&'a str>, body: &'a str) { + // let key = (fields, body); + let match_arm = self.arms.entry((fields, body)).or_insert_with(HashSet::new); + match_arm.insert(name); + } +} + +#[cfg(test)] +mod srcgen_tests { + use super::_Match; + use super::parse_multiline; + use super::Formatter; + + #[test] + fn adding_arms_works() { + let mut m = _Match::_new("x"); + m._arm("Orange", vec!["a", "b"], "some body"); + m._arm("Yellow", vec!["a", "b"], "some body"); + m._arm("Green", vec!["a", "b"], "different body"); + m._arm("Blue", vec!["x", "y"], "some body"); + assert_eq!(m.arms.len(), 3); + } + + #[test] + fn parse_multiline_works() { + let input = "\n hello\n world\n"; + let expected = vec!["hello", "world"]; + let output = parse_multiline(input); + assert_eq!(output, expected); + } + + #[test] + fn formatter_basic_example_works() { + let mut fmt = Formatter::new(); + fmt.line("Hello line 1"); + fmt._indent_push(); + fmt._comment("Nested comment"); + fmt._indent_pop(); + fmt.line("Back home again"); + let expected_lines = vec![ + "Hello line 1\n", + " // Nested comment\n", + "Back home again\n", + ]; + assert_eq!(fmt.lines, expected_lines); + } + + #[test] + fn get_indent_works() { + let mut fmt = Formatter::new(); + let expected_results = vec!["", " ", " ", ""]; + + let actual_results = Vec::with_capacity(4); + (0..3).for_each(|_| { + fmt.get_indent(); + fmt._indent_push(); + }); + (0..3).for_each(|_| fmt._indent_pop()); + fmt.get_indent(); + + actual_results + .into_iter() + .zip(expected_results.into_iter()) + .for_each(|(actual, expected): (String, &str)| assert_eq!(&actual, expected)); + } + + #[test] + fn fmt_can_add_type_to_lines() { + let mut fmt = Formatter::new(); + fmt.line(&format!("pub const {}: Type = Type({:#x});", "example", 0,)); + let expected_lines = vec!["pub const example: Type = Type(0x0);\n"]; + assert_eq!(fmt.lines, expected_lines); + } + + #[test] + fn fmt_can_add_indented_line() { + let mut fmt = Formatter::new(); + fmt.line("hello"); + fmt._indent_push(); + fmt.line("world"); + let expected_lines = vec!["hello\n", " world\n"]; + assert_eq!(fmt.lines, expected_lines); + } + + #[test] + fn fmt_can_add_doc_comments() { + let mut fmt = Formatter::new(); + fmt.doc_comment("documentation\nis\ngood"); + let expected_lines = vec!["/// documentation\n", "/// is\n", "/// good\n"]; + assert_eq!(fmt.lines, expected_lines); + } +} diff --git a/lib/codegen/Cargo.toml b/lib/codegen/Cargo.toml index fbd442b5b1..18cf15822a 100644 --- a/lib/codegen/Cargo.toml +++ b/lib/codegen/Cargo.toml @@ -21,6 +21,9 @@ target-lexicon = { version = "0.0.3", default-features = false } # machine code. Integration tests that need external dependencies can be # accomodated in `tests`. +[build-dependencies] +cranelift-codegen-meta = { path = "../codegen-meta", version = "0.15.0" } + [features] # The "std" feature enables use of libstd. The "core" feature enables use # of some minimal std-like replacement libraries. At least one of these two diff --git a/lib/codegen/build.rs b/lib/codegen/build.rs index b5a2f5b435..f9ba9b8e4a 100644 --- a/lib/codegen/build.rs +++ b/lib/codegen/build.rs @@ -18,6 +18,8 @@ // The build script expects to be run from the directory where this build.rs file lives. The // current directory is used to find the sources. +extern crate cranelift_codegen_meta as meta; + use std::env; use std::process; @@ -66,12 +68,35 @@ fn main() { .arg("-B") .arg(build_script) .arg("--out-dir") - .arg(out_dir) + .arg(out_dir.clone()) .status() .expect("Failed to launch second-level build script; is python installed?"); if !status.success() { process::exit(status.code().unwrap()); } + + // DEVELOPMENT: + // ------------------------------------------------------------------------ + // Now that the Python build process is complete, generate files that are + // emitted by the `cretonne_codegen_meta` crate. + // ------------------------------------------------------------------------ + + // Identify the directory of the Rust codegen-meta external crate. + let rust_meta_dir = crate_dir + .parent() + .map(|d| d.join("codegen-meta")) + .unwrap_or_else(|| { + eprintln!("Error: Could not find path to lib/codegen-meta crate."); + process::exit(1); + }); + + if let Err(err) = meta::gen_types::generate("new_types.rs", &out_dir) { + eprintln!("Error: {}", err); + process::exit(1); + } else if let Err(err) = meta::gen_build_deps::generate(&rust_meta_dir) { + eprintln!("Error: {}", err); + process::exit(1); + } } fn identify_python() -> &'static str {