lib/codegen-meta moved into lib/codegen. (#423)

* lib/codegen-meta moved into lib/codegen.

* Renamed codegen-meta and existing meta.
This commit is contained in:
data-pup
2018-07-31 10:56:26 -04:00
committed by Dan Gohman
parent 65a1a6bb28
commit d9d40e1cdf
89 changed files with 7 additions and 9 deletions

View File

@@ -21,9 +21,8 @@ target-lexicon = { version = "0.0.3", default-features = false }
# machine code. Integration tests that need external dependencies can be
# accomodated in `tests`.
# Temporarily disable this while we work out how to publish this crate.
#[build-dependencies]
#cranelift-codegen-meta = { path = "../codegen-meta", version = "0.17.0-alpha" }
[build-dependencies]
cranelift-meta = { path = "meta", version = "0.17.0-alpha" }
[features]
# The "std" feature enables use of libstd. The "core" feature enables use

View File

@@ -18,8 +18,7 @@
// 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.
// Temporarily disable this while we work out how to publish this crate.
//extern crate cranelift_codegen_meta as meta;
extern crate cranelift_meta as meta;
use std::env;
use std::process;
@@ -57,8 +56,8 @@ fn main() {
crate_dir.join("build.rs").to_str().unwrap()
);
// Scripts are in `$crate_dir/meta`.
let meta_dir = crate_dir.join("meta");
// Scripts are in `$crate_dir/meta-python`.
let meta_dir = crate_dir.join("meta-python");
let build_script = meta_dir.join("build.py");
// Launch build script with Python. We'll just find python in the path.
@@ -79,7 +78,7 @@ fn main() {
// DEVELOPMENT:
// ------------------------------------------------------------------------
// Now that the Python build process is complete, generate files that are
// emitted by the `cretonne_codegen_meta` crate.
// emitted by the `meta` crate.
// ------------------------------------------------------------------------
// Temporarily disable this while we work out how to publish this crate.
//if let Err(err) = meta::gen_types::generate("new_types.rs", &out_dir) {

View File

@@ -0,0 +1,18 @@
[package]
name = "cranelift-meta"
authors = ["The Cranelift Project Developers"]
version = "0.17.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" }

219
lib/codegen/meta/LICENSE Normal file
View File

@@ -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.

View File

@@ -0,0 +1,3 @@
//! Definitions for the base Cranelift language.
pub mod types;

View File

@@ -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<Self::Item> {
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<Self::Item> {
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<Self::Item> {
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<Self::Item> {
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);
}
}

View File

@@ -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");
}
}

View File

@@ -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<u8> {
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<BVType> for ValueType {
fn from(bv: BVType) -> Self {
ValueType::BV(bv)
}
}
/// Create a ValueType from a given lane type.
impl From<LaneType> for ValueType {
fn from(lane: LaneType) -> Self {
ValueType::Lane(lane)
}
}
/// Create a ValueType from a given special type.
impl From<SpecialType> for ValueType {
fn from(spec: SpecialType) -> Self {
ValueType::Special(spec)
}
}
/// Create a ValueType from a given vector type.
impl From<VectorType> 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<base_types::Bool> for LaneType {
fn from(b: base_types::Bool) -> Self {
LaneType::BoolType(b)
}
}
/// Create a LaneType from a given float variant.
impl From<base_types::Float> for LaneType {
fn from(f: base_types::Float) -> Self {
LaneType::FloatType(f)
}
}
/// Create a LaneType from a given int variant.
impl From<base_types::Int> 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<Self::Item> {
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<base_types::Flag> 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<Self::Item> {
if let Some(f) = self.flag_iter.next() {
Some(SpecialType::from(f))
} else {
None
}
}
}

View File

@@ -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<ErrorInner>,
}
impl Error {
/// Create a new error object with the given message.
pub fn with_msg<S: Into<String>>(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<io::Error> 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),
}
}
}

View File

@@ -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(())
}

View File

@@ -0,0 +1,6 @@
pub mod error;
pub mod gen_types;
mod base;
mod cdsl;
mod srcgen;

View File

@@ -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<String>,
}
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<String>,
}
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<usize> {
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<String> {
// Convert tabs into spaces.
let expanded_tab = format!("{:-1$}", " ", SHIFTWIDTH);
let lines: Vec<String> = 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::<Vec<_>>()
} else {
lines_iter
.map(|l| l.trim_right())
.map(|l| l.to_string())
.collect::<Vec<_>>()
};
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);
}
}