moved crates in lib/ to src/, renamed crates, modified some files' text (#660)

moved crates in lib/ to src/, renamed crates, modified some files' text (#660)
This commit is contained in:
lazypassion
2019-01-28 18:56:54 -05:00
committed by Dan Gohman
parent 54959cf5bb
commit 747ad3c4c5
508 changed files with 94 additions and 92 deletions

View File

@@ -0,0 +1,16 @@
[package]
name = "cranelift-codegen-meta"
authors = ["The Cranelift Project Developers"]
version = "0.28.0"
description = "Metaprogram for cranelift-codegen code generator library"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/CraneStation/cranelift"
readme = "README.md"
edition = "2018"
[dependencies]
cranelift-entity = { path = "../../cranelift-entity", version = "0.28.0" }
[badges]
maintenance = { status = "experimental" }
travis-ci = { repository = "CraneStation/cranelift" }

View File

@@ -0,0 +1,220 @@
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,2 @@
This crate contains the metaprogram used by cranelift-codegen. It's not
useful on its own.

View File

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

View File

@@ -0,0 +1,164 @@
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
pub fn generate() -> SettingGroup {
let mut settings = SettingGroupBuilder::new("shared");
settings.add_enum(
"opt_level",
r#"
Optimization level:
- default: Very profitable optimizations enabled, none slow.
- best: Enable all optimizations
- fastest: Optimize for compile time by disabling most optimizations.
"#,
vec!["default", "best", "fastest"],
);
settings.add_bool(
"enable_verifier",
r#"
Run the Cranelift IR verifier at strategic times during compilation.
This makes compilation slower but catches many bugs. The verifier is
disabled by default, except when reading Cranelift IR from a text file.
"#,
true,
);
// Note that Cranelift doesn't currently need an is_pie flag, because PIE is
// just PIC where symbols can't be pre-empted, which can be expressed with the
// `colocated` flag on external functions and global values.
settings.add_bool(
"is_pic",
"Enable Position-Independent Code generation",
false,
);
settings.add_bool(
"colocated_libcalls",
r#"
Use colocated libcalls.
Generate code that assumes that libcalls can be declared "colocated",
meaning they will be defined along with the current function, such that
they can use more efficient addressing.
"#,
false,
);
settings.add_bool(
"avoid_div_traps",
r#"
Generate explicit checks around native division instructions to avoid
their trapping.
This is primarily used by SpiderMonkey which doesn't install a signal
handler for SIGFPE, but expects a SIGILL trap for division by zero.
On ISAs like ARM where the native division instructions don't trap,
this setting has no effect - explicit checks are always inserted.
"#,
false,
);
settings.add_bool(
"enable_float",
r#"
Enable the use of floating-point instructions
Disabling use of floating-point instructions is not yet implemented.
"#,
true,
);
settings.add_bool(
"enable_nan_canonicalization",
r#"
Enable NaN canonicalization
This replaces NaNs with a single canonical value, for users requiring
entirely deterministic WebAssembly computation. This is not required
by the WebAssembly spec, so it is not enabled by default.
"#,
false,
);
settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", true);
settings.add_bool(
"enable_atomics",
"Enable the use of atomic instructions",
true,
);
// Settings specific to the `baldrdash` calling convention.
settings.add_num(
"baldrdash_prologue_words",
r#"
Number of pointer-sized words pushed by the baldrdash prologue.
Functions with the `baldrdash` calling convention don't generate their
own prologue and epilogue. They depend on externally generated code
that pushes a fixed number of words in the prologue and restores them
in the epilogue.
This setting configures the number of pointer-sized words pushed on the
stack when the Cranelift-generated code is entered. This includes the
pushed return address on x86.
"#,
0,
);
// BaldrMonkey requires that not-yet-relocated function addresses be encoded
// as all-ones bitpatterns.
settings.add_bool(
"allones_funcaddrs",
"Emit not-yet-relocated function addresses as all-ones bit patterns.",
false,
);
// Stack probing options.
settings.add_bool(
"probestack_enabled",
r#"
Enable the use of stack probes, for calling conventions which support this
functionality.
"#,
true,
);
settings.add_bool(
"probestack_func_adjusts_sp",
r#"
Set this to true of the stack probe function modifies the stack pointer
itself.
"#,
false,
);
settings.add_num(
"probestack_size_log2",
r#"
The log2 of the size of the stack guard region.
Stack frames larger than this size will have stack overflow checked
by calling the probestack function.
The default is 12, which translates to a size of 4096.
"#,
12,
);
// Jump table options.
settings.add_bool(
"jump_tables_enabled",
"Enable the use of jump tables in generated machine code.",
true,
);
settings.finish()
}

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,192 @@
use cranelift_entity::PrimaryMap;
use super::regs::{
RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto,
};
use super::settings::SettingGroup;
pub struct TargetIsa {
pub name: &'static str,
pub reg_banks: PrimaryMap<RegBankIndex, RegBank>,
pub reg_classes: PrimaryMap<RegClassIndex, RegClass>,
pub settings: SettingGroup,
}
impl TargetIsa {
pub fn new(name: &'static str, settings: SettingGroup) -> Self {
Self {
name,
reg_banks: PrimaryMap::new(),
reg_classes: PrimaryMap::new(),
settings,
}
}
}
pub struct TargetIsaBuilder {
isa: TargetIsa,
}
impl TargetIsaBuilder {
pub fn new(name: &'static str, settings: SettingGroup) -> Self {
Self {
isa: TargetIsa::new(name, settings),
}
}
pub fn add_reg_bank(&mut self, builder: RegBankBuilder) -> RegBankIndex {
let first_unit = if self.isa.reg_banks.len() == 0 {
0
} else {
let last = &self.isa.reg_banks.last().unwrap();
let first_available_unit = (last.first_unit + last.units) as i8;
let units = builder.units;
let align = if units.is_power_of_two() {
units
} else {
units.next_power_of_two()
} as i8;
(first_available_unit + align - 1) & -align
} as u8;
self.isa.reg_banks.push(RegBank::new(
builder.name,
first_unit,
builder.units,
builder.names,
builder.prefix,
builder
.pressure_tracking
.expect("Pressure tracking must be explicitly set"),
))
}
pub fn add_reg_class(&mut self, builder: RegClassBuilder) -> RegClassIndex {
let class_index = self.isa.reg_classes.next_key();
// Finish delayed construction of RegClass.
let (bank, toprc, start, width) = match builder.proto {
RegClassProto::TopLevel(bank_index) => {
self.isa
.reg_banks
.get_mut(bank_index)
.unwrap()
.toprcs
.push(class_index);
(bank_index, class_index, builder.start, builder.width)
}
RegClassProto::SubClass(parent_class_index) => {
assert!(builder.width == 0);
let (bank, toprc, start, width) = {
let parent = self.isa.reg_classes.get(parent_class_index).unwrap();
(parent.bank, parent.toprc, parent.start, parent.width)
};
for reg_class in self.isa.reg_classes.values_mut() {
if reg_class.toprc == toprc {
reg_class.subclasses.push(class_index);
}
}
let subclass_start = start + builder.start * width;
(bank, toprc, subclass_start, width)
}
};
let reg_bank_units = self.isa.reg_banks.get(bank).unwrap().units;
assert!(start < reg_bank_units);
let count = if builder.count != 0 {
builder.count
} else {
reg_bank_units / width
};
let reg_class = RegClass::new(builder.name, class_index, width, bank, toprc, count, start);
self.isa.reg_classes.push(reg_class);
let reg_bank = self.isa.reg_banks.get_mut(bank).unwrap();
reg_bank.classes.push(class_index);
class_index
}
/// Checks that the set of register classes satisfies:
///
/// 1. Closed under intersection: The intersection of any two register
/// classes in the set is either empty or identical to a member of the
/// set.
/// 2. There are no identical classes under different names.
/// 3. Classes are sorted topologically such that all subclasses have a
/// higher index that the superclass.
pub fn finish(self) -> TargetIsa {
for reg_bank in self.isa.reg_banks.values() {
for i1 in reg_bank.classes.iter() {
for i2 in reg_bank.classes.iter() {
if i1 >= i2 {
continue;
}
let rc1 = self.isa.reg_classes.get(*i1).unwrap();
let rc2 = self.isa.reg_classes.get(*i2).unwrap();
let rc1_mask = rc1.mask(0);
let rc2_mask = rc2.mask(0);
assert!(
rc1.width != rc2.width || rc1_mask != rc2_mask,
"no duplicates"
);
if rc1.width != rc2.width {
continue;
}
let mut intersect = Vec::new();
for (a, b) in rc1_mask.iter().zip(rc2_mask.iter()) {
intersect.push(a & b);
}
if intersect == vec![0; intersect.len()] {
continue;
}
// Classes must be topologically ordered, so the intersection can't be the
// superclass.
assert!(intersect != rc1_mask);
// If the intersection is the second one, then it must be a subclass.
if intersect == rc2_mask {
assert!(self
.isa
.reg_classes
.get(*i1)
.unwrap()
.subclasses
.iter()
.find(|x| **x == *i2)
.is_some());
}
}
}
}
// This limit should be coordinated with the `RegClassMask` and `RegClassIndex` types in
// isa/registers.rs of the non-meta code.
assert!(
self.isa.reg_classes.len() <= 32,
"Too many register classes"
);
// The maximum number of top-level register classes which have pressure tracking should be
// kept in sync with the MAX_TRACKED_TOPRCS constant in isa/registers.rs of the non-meta
// code.
let num_toplevel = self
.isa
.reg_classes
.values()
.filter(|x| {
x.toprc == x.index && self.isa.reg_banks.get(x.bank).unwrap().pressure_tracking
})
.count();
assert!(num_toplevel <= 4, "Too many top-level register classes");
self.isa
}
}

View File

@@ -0,0 +1,68 @@
//! Cranelift DSL classes.
//!
//! This module defines the classes that are used to define Cranelift
//! instructions and other entities.
pub mod isa;
pub mod regs;
pub mod settings;
pub mod types;
/// A macro that converts boolean settings into predicates to look more natural.
#[macro_export]
macro_rules! predicate {
($a:ident && $($b:tt)*) => {
PredicateNode::And(Box::new($a.into()), Box::new(predicate!($($b)*)))
};
($a:ident) => {
$a.into()
};
}
#[macro_export]
macro_rules! preset {
() => {
vec![]
};
($($x:ident)&&*) => {
{
let mut v = Vec::new();
$(
v.push($x.into());
)*
v
}
};
}
/// Convert the string `s` to CamelCase.
pub 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;
#[test]
fn camel_case_works() {
assert_eq!(camel_case("x"), "X");
assert_eq!(camel_case("camel_case"), "CamelCase");
}
}

View File

@@ -0,0 +1,180 @@
use cranelift_entity::entity_impl;
use cranelift_entity::EntityRef;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RegBankIndex(u32);
entity_impl!(RegBankIndex);
pub struct RegBank {
pub name: &'static str,
pub first_unit: u8,
pub units: u8,
pub names: Vec<&'static str>,
pub prefix: &'static str,
pub pressure_tracking: bool,
pub toprcs: Vec<RegClassIndex>,
pub classes: Vec<RegClassIndex>,
}
impl RegBank {
pub fn new(
name: &'static str,
first_unit: u8,
units: u8,
names: Vec<&'static str>,
prefix: &'static str,
pressure_tracking: bool,
) -> Self {
RegBank {
name,
first_unit,
units,
names,
prefix,
pressure_tracking,
toprcs: Vec::new(),
classes: Vec::new(),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RegClassIndex(u32);
entity_impl!(RegClassIndex);
pub struct RegClass {
pub name: &'static str,
pub index: RegClassIndex,
pub width: u8,
pub bank: RegBankIndex,
pub toprc: RegClassIndex,
pub count: u8,
pub start: u8,
pub subclasses: Vec<RegClassIndex>,
}
impl RegClass {
pub fn new(
name: &'static str,
index: RegClassIndex,
width: u8,
bank: RegBankIndex,
toprc: RegClassIndex,
count: u8,
start: u8,
) -> Self {
Self {
name,
index,
width,
bank,
toprc,
count,
start,
subclasses: Vec::new(),
}
}
/// Compute a bit-mask of subclasses, including self.
pub fn subclass_mask(&self) -> u64 {
let mut m = 1 << self.index.index();
for rc in self.subclasses.iter() {
m |= 1 << rc.index();
}
m
}
/// Compute a bit-mask of the register units allocated by this register class.
pub fn mask(&self, bank_first_unit: u8) -> Vec<u32> {
let mut u = (self.start + bank_first_unit) as usize;
let mut out_mask = vec![0, 0, 0];
for _ in 0..self.count {
out_mask[u / 32] |= 1 << (u % 32);
u += self.width as usize;
}
out_mask
}
}
pub enum RegClassProto {
TopLevel(RegBankIndex),
SubClass(RegClassIndex),
}
pub struct RegClassBuilder {
pub name: &'static str,
pub width: u8,
pub count: u8,
pub start: u8,
pub proto: RegClassProto,
}
impl RegClassBuilder {
pub fn new_toplevel(name: &'static str, bank: RegBankIndex) -> Self {
Self {
name,
width: 1,
count: 0,
start: 0,
proto: RegClassProto::TopLevel(bank),
}
}
pub fn subclass_of(
name: &'static str,
parent_index: RegClassIndex,
start: u8,
stop: u8,
) -> Self {
assert!(stop >= start);
Self {
name,
width: 0,
count: stop - start,
start: start,
proto: RegClassProto::SubClass(parent_index),
}
}
pub fn count(mut self, count: u8) -> Self {
self.count = count;
self
}
pub fn width(mut self, width: u8) -> Self {
match self.proto {
RegClassProto::TopLevel(_) => self.width = width,
RegClassProto::SubClass(_) => panic!("Subclasses inherit their parent's width."),
}
self
}
}
pub struct RegBankBuilder {
pub name: &'static str,
pub units: u8,
pub names: Vec<&'static str>,
pub prefix: &'static str,
pub pressure_tracking: Option<bool>,
}
impl RegBankBuilder {
pub fn new(name: &'static str, prefix: &'static str) -> Self {
Self {
name,
units: 0,
names: vec![],
prefix,
pressure_tracking: None,
}
}
pub fn units(mut self, units: u8) -> Self {
self.units = units;
self
}
pub fn names(mut self, names: Vec<&'static str>) -> Self {
self.names = names;
self
}
pub fn track_pressure(mut self, track: bool) -> Self {
self.pressure_tracking = Some(track);
self
}
}

View File

@@ -0,0 +1,387 @@
use std::iter;
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct BoolSettingIndex(usize);
#[derive(Hash, PartialEq, Eq)]
pub struct BoolSetting {
pub default: bool,
pub bit_offset: u8,
pub predicate_number: u8,
}
#[derive(Hash, PartialEq, Eq)]
pub enum SpecificSetting {
Bool(BoolSetting),
Enum(Vec<&'static str>),
Num(u8),
}
#[derive(Hash, PartialEq, Eq)]
pub struct Setting {
pub name: &'static str,
pub comment: &'static str,
pub specific: SpecificSetting,
pub byte_offset: u8,
}
impl Setting {
pub fn default_byte(&self) -> u8 {
match self.specific {
SpecificSetting::Bool(BoolSetting {
default,
bit_offset,
..
}) => {
if default {
1 << bit_offset
} else {
0
}
}
SpecificSetting::Enum(_) => 0,
SpecificSetting::Num(default) => default,
}
}
fn byte_for_value(&self, v: bool) -> u8 {
match self.specific {
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
if v {
1 << bit_offset
} else {
0
}
}
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
}
}
fn byte_mask(&self) -> u8 {
match self.specific {
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
}
}
}
#[derive(Hash, PartialEq, Eq)]
pub struct PresetIndex(usize);
#[derive(Hash, PartialEq, Eq)]
pub enum PresetType {
BoolSetting(BoolSettingIndex),
OtherPreset(PresetIndex),
}
impl Into<PresetType> for BoolSettingIndex {
fn into(self) -> PresetType {
PresetType::BoolSetting(self)
}
}
impl Into<PresetType> for PresetIndex {
fn into(self) -> PresetType {
PresetType::OtherPreset(self)
}
}
#[derive(Hash, PartialEq, Eq)]
pub struct Preset {
pub name: &'static str,
values: Vec<BoolSettingIndex>,
}
impl Preset {
pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
.take(group.settings_size as usize)
.collect();
for bool_index in &self.values {
let setting = &group.settings[bool_index.0];
let mask = setting.byte_mask();
let val = setting.byte_for_value(true);
assert!((val & !mask) == 0);
let (ref mut l_mask, ref mut l_val) =
*layout.get_mut(setting.byte_offset as usize).unwrap();
*l_mask |= mask;
*l_val = (*l_val & !mask) | val;
}
layout
}
}
pub struct SettingGroup {
pub name: &'static str,
pub settings: Vec<Setting>,
pub bool_start_byte_offset: u8,
pub settings_size: u8,
pub presets: Vec<Preset>,
pub predicates: Vec<Predicate>,
}
impl SettingGroup {
fn num_bool_settings(&self) -> u8 {
self.settings
.iter()
.filter(|s| {
if let SpecificSetting::Bool(_) = s.specific {
true
} else {
false
}
})
.count() as u8
}
pub fn byte_size(&self) -> u8 {
let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
self.bool_start_byte_offset + (num_predicates + 7) / 8
}
pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
for (i, s) in self.settings.iter().enumerate() {
if let SpecificSetting::Bool(_) = s.specific {
if s.name == name {
return (BoolSettingIndex(i), self);
}
}
}
panic!("Should have found bool setting by name.");
}
}
/// This is the basic information needed to track the specific parts of a setting when building
/// them.
pub enum ProtoSpecificSetting {
Bool(bool, u8),
Enum(Vec<&'static str>),
Num(u8),
}
/// This is the information provided during building for a setting.
struct ProtoSetting {
name: &'static str,
comment: &'static str,
specific: ProtoSpecificSetting,
}
#[derive(Hash, PartialEq, Eq)]
pub enum PredicateNode {
OwnedBool(BoolSettingIndex),
SharedBool(&'static str, &'static str),
And(Box<PredicateNode>, Box<PredicateNode>),
}
impl Into<PredicateNode> for BoolSettingIndex {
fn into(self) -> PredicateNode {
PredicateNode::OwnedBool(self)
}
}
impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
fn into(self) -> PredicateNode {
let (index, group) = (self.0, self.1);
let setting = &group.settings[index.0];
PredicateNode::SharedBool(group.name, setting.name)
}
}
impl PredicateNode {
fn render(&self, group: &SettingGroup) -> String {
match *self {
PredicateNode::OwnedBool(bool_setting_index) => format!(
"{}.{}()",
group.name, group.settings[bool_setting_index.0].name
),
PredicateNode::SharedBool(ref group_name, ref bool_name) => {
format!("{}.{}()", group_name, bool_name)
}
PredicateNode::And(ref lhs, ref rhs) => {
format!("{} && {}", lhs.render(group), rhs.render(group))
}
}
}
}
pub struct Predicate {
pub name: &'static str,
node: PredicateNode,
pub number: u8,
}
impl Predicate {
pub fn render(&self, group: &SettingGroup) -> String {
self.node.render(group)
}
}
pub struct SettingGroupBuilder {
name: &'static str,
settings: Vec<ProtoSetting>,
presets: Vec<Preset>,
predicates: Vec<Predicate>,
predicate_number: u8,
}
impl SettingGroupBuilder {
pub fn new(name: &'static str) -> Self {
Self {
name,
settings: Vec::new(),
presets: Vec::new(),
predicates: Vec::new(),
predicate_number: 0,
}
}
fn add_setting(
&mut self,
name: &'static str,
comment: &'static str,
specific: ProtoSpecificSetting,
) {
self.settings.push(ProtoSetting {
name,
comment,
specific,
})
}
pub fn add_bool(
&mut self,
name: &'static str,
comment: &'static str,
default: bool,
) -> BoolSettingIndex {
assert!(
self.predicates.len() == 0,
"predicates must be added after the boolean settings"
);
let predicate_number = self.predicate_number;
self.predicate_number += 1;
self.add_setting(
name,
comment,
ProtoSpecificSetting::Bool(default, predicate_number),
);
BoolSettingIndex(self.settings.len() - 1)
}
pub fn add_enum(
&mut self,
name: &'static str,
comment: &'static str,
values: Vec<&'static str>,
) {
self.add_setting(name, comment, ProtoSpecificSetting::Enum(values));
}
pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) {
self.add_setting(name, comment, ProtoSpecificSetting::Num(default));
}
pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
let number = self.predicate_number;
self.predicate_number += 1;
self.predicates.push(Predicate { name, node, number });
}
pub fn add_preset(&mut self, name: &'static str, args: Vec<PresetType>) -> PresetIndex {
let mut values = Vec::new();
for arg in args {
match arg {
PresetType::OtherPreset(index) => {
values.extend(self.presets[index.0].values.iter());
}
PresetType::BoolSetting(index) => values.push(index),
}
}
self.presets.push(Preset { name, values });
PresetIndex(self.presets.len() - 1)
}
/// Compute the layout of the byte vector used to represent this settings
/// group.
///
/// The byte vector contains the following entries in order:
///
/// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
/// 2. `BoolSetting` settings.
/// 3. Precomputed named predicates.
/// 4. Other numbered predicates, including anonymous predicates and parent
/// predicates that need to be accessible by number.
///
/// Set `self.settings_size` to the length of the byte vector prefix that
/// contains the settings. All bytes after that are computed, not
/// configured.
///
/// Set `self.boolean_offset` to the beginning of the numbered predicates,
/// 2. in the list above.
///
/// Assign `byte_offset` and `bit_offset` fields in all settings.
///
/// After calling this method, no more settings can be added, but
/// additional predicates can be made accessible with `number_predicate()`.
pub fn finish(self) -> SettingGroup {
let mut group = SettingGroup {
name: self.name,
settings: Vec::new(),
bool_start_byte_offset: 0,
settings_size: 0,
presets: Vec::new(),
predicates: Vec::new(),
};
let mut byte_offset = 0;
// Assign the non-boolean settings first.
for s in &self.settings {
let specific = match s.specific {
ProtoSpecificSetting::Bool(..) => continue,
ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
};
group.settings.push(Setting {
name: s.name,
comment: s.comment,
byte_offset,
specific,
});
byte_offset += 1;
}
group.bool_start_byte_offset = byte_offset;
// Then the boolean settings.
for s in &self.settings {
let (default, predicate_number) = match s.specific {
ProtoSpecificSetting::Bool(default, predicate_number) => {
(default, predicate_number)
}
ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
};
group.settings.push(Setting {
name: s.name,
comment: s.comment,
byte_offset: byte_offset + predicate_number / 8,
specific: SpecificSetting::Bool(BoolSetting {
default,
bit_offset: predicate_number % 8,
predicate_number,
}),
});
}
assert!(
group.predicates.len() == 0,
"settings_size is the byte size before adding predicates"
);
group.settings_size = group.byte_size();
group.predicates.extend(self.predicates);
group.presets.extend(self.presets);
group
}
}

View File

@@ -0,0 +1,473 @@
//! Cranelift ValueType hierarchy
// Temporary disabled: Unused at the moment.
// use std::collections::HashMap;
use std::fmt;
use crate::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
}
/// 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.to_string().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 {
match *self {
ValueType::BV(ref b) => b.fmt(f),
ValueType::Lane(l) => l.fmt(f),
ValueType::Special(s) => s.fmt(f),
ValueType::Vector(ref v) => v.fmt(f),
}
}
}
/// 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,
}
}
/// 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::Display for LaneType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LaneType::BoolType(_) => write!(f, "b{}", self.lane_bits()),
LaneType::FloatType(_) => write!(f, "f{}", self.lane_bits()),
LaneType::IntType(_) => write!(f, "i{}", self.lane_bits()),
}
}
}
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) -> Self {
Self { 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
)
}
/// 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
}
/// 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::Display for VectorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}x{}", self.base, self.lane_count())
}
}
impl fmt::Debug for VectorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"VectorType(base={}, lanes={})",
self.base,
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
}
}
impl fmt::Display for BVType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "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,
}
}
/// 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::Display for SpecialType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SpecialType::Flag(base_types::Flag::IFlags) => write!(f, "iflags"),
SpecialType::Flag(base_types::Flag::FFlags) => write!(f, "fflags"),
}
}
}
impl fmt::Debug for SpecialType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
SpecialType::Flag(_) => format!("FlagsType({})", self),
}
)
}
}
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,49 @@
pub fn simple_hash(s: &str) -> usize {
let mut h: u32 = 5381;
for c in s.chars() {
h = (h ^ c as u32).wrapping_add(h.rotate_right(6));
}
h as usize
}
/// Compute an open addressed, quadratically probed hash table containing
/// `items`. The returned table is a list containing the elements of the
/// iterable `items` and `None` in unused slots.
pub fn generate_table<T, H: Fn(&T) -> usize>(items: &Vec<T>, hash_function: H) -> Vec<Option<&T>> {
let size = (1.20 * items.len() as f64) as usize;
// TODO do we really need the multiply by two here?
let size = if size.is_power_of_two() {
size * 2
} else {
size.next_power_of_two()
};
let mut table: Vec<Option<&T>> = vec![None; size];
for i in items {
let mut h = hash_function(i) % size;
let mut s = 0;
while table[h].is_some() {
s += 1;
h = (h + s) % size;
}
table[h] = Some(i);
}
table
}
#[test]
fn test_generate_table() {
let v = vec!["Hello".to_string(), "world".to_string()];
let table = generate_table(&v, |s| simple_hash(&s));
assert_eq!(
table,
vec![
None,
Some(&"Hello".to_string()),
Some(&"world".to_string()),
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,138 @@
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{RegBank, RegClass};
use crate::error;
use crate::srcgen::Formatter;
use cranelift_entity::EntityRef;
fn gen_regbank(fmt: &mut Formatter, reg_bank: &RegBank) {
let names = if reg_bank.names.len() > 0 {
format!(r#""{}""#, reg_bank.names.join(r#"", ""#))
} else {
"".to_string()
};
fmt.line("RegBank {");
fmt.indent(|fmt| {
fmt.line(&format!(r#"name: "{}","#, reg_bank.name));
fmt.line(&format!("first_unit: {},", reg_bank.first_unit));
fmt.line(&format!("units: {},", reg_bank.units));
fmt.line(&format!("names: &[{}],", names));
fmt.line(&format!(r#"prefix: "{}","#, reg_bank.prefix));
fmt.line(&format!("first_toprc: {},", reg_bank.toprcs[0].index()));
fmt.line(&format!("num_toprcs: {},", reg_bank.toprcs.len()));
fmt.line(&format!(
"pressure_tracking: {},",
if reg_bank.pressure_tracking {
"true"
} else {
"false"
}
));
});
fmt.line("},");
}
fn gen_regclass(isa: &TargetIsa, reg_class: &RegClass, fmt: &mut Formatter) {
let reg_bank = isa.reg_banks.get(reg_class.bank).unwrap();
let mask: Vec<String> = reg_class
.mask(reg_bank.first_unit)
.iter()
.map(|x| format!("0x{:08x}", x))
.collect();
let mask = mask.join(", ");
fmt.line(&format!(
"pub static {}_DATA: RegClassData = RegClassData {{",
reg_class.name
));
fmt.indent(|fmt| {
fmt.line(&format!(r#"name: "{}","#, reg_class.name));
fmt.line(&format!("index: {},", reg_class.index.index()));
fmt.line(&format!("width: {},", reg_class.width));
fmt.line(&format!("bank: {},", reg_class.bank.index()));
fmt.line(&format!("toprc: {},", reg_class.toprc.index()));
fmt.line(&format!(
"first: {},",
reg_bank.first_unit + reg_class.start
));
fmt.line(&format!("subclasses: {:#x},", reg_class.subclass_mask()));
fmt.line(&format!("mask: [{}],", mask));
fmt.line("info: &INFO,");
});
fmt.line("};");
fmt.line("#[allow(dead_code)]");
fmt.line(&format!(
"pub static {}: RegClass = &{}_DATA;",
reg_class.name, reg_class.name
));
}
fn gen_regbank_units(reg_bank: &RegBank, fmt: &mut Formatter) {
for unit in 0..reg_bank.units {
let v = unit + reg_bank.first_unit;
if (unit as usize) < reg_bank.names.len() {
fmt.line(&format!("{} = {},", reg_bank.names[unit as usize], v));
continue;
}
fmt.line(&format!("{}{} = {},", reg_bank.prefix, unit, v));
}
}
fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) {
// Emit RegInfo.
fmt.line("pub static INFO: RegInfo = RegInfo {");
fmt.indent(|fmt| {
fmt.line("banks: &[");
// Bank descriptors.
fmt.indent(|fmt| {
for reg_bank in isa.reg_banks.values() {
gen_regbank(fmt, &reg_bank);
}
});
fmt.line("],");
// References to register classes.
fmt.line("classes: &[");
fmt.indent(|fmt| {
for reg_class in isa.reg_classes.values() {
fmt.line(&format!("&{}_DATA,", reg_class.name));
}
});
fmt.line("],");
});
fmt.line("};");
// Register class descriptors.
for rc in isa.reg_classes.values() {
gen_regclass(&isa, rc, fmt);
}
// Emit constants for all the register units.
fmt.line("#[allow(dead_code, non_camel_case_types)]");
fmt.line("#[derive(Clone, Copy)]");
fmt.line("pub enum RU {");
fmt.indent(|fmt| {
for reg_bank in isa.reg_banks.values() {
gen_regbank_units(reg_bank, fmt);
}
});
fmt.line("}");
// Emit Into conversion for the RU class.
fmt.line("impl Into<RegUnit> for RU {");
fmt.indent(|fmt| {
fmt.line("fn into(self) -> RegUnit {");
fmt.indent(|fmt| {
fmt.line("self as RegUnit");
});
fmt.line("}")
});
fmt.line("}");
}
pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
let mut fmt = Formatter::new();
gen_isa(&isa, &mut fmt);
fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?;
Ok(())
}

View File

@@ -0,0 +1,448 @@
use crate::base;
use crate::cdsl::camel_case;
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::settings::{
BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting,
};
use crate::constant_hash::{generate_table, simple_hash};
use crate::error;
use crate::srcgen::{Formatter, Match};
use crate::unique_table::UniqueTable;
use std::collections::HashMap;
enum ParentGroup {
None,
Shared,
}
/// Emits the constructor of the Flags structure.
fn gen_constructor(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
let args = match parent {
ParentGroup::None => "builder: Builder",
ParentGroup::Shared => "shared: &settings::Flags, builder: Builder",
};
fmt.line("impl Flags {");
fmt.indent(|fmt| {
fmt.doc_comment(&format!("Create flags {} settings group.", group.name));
fmt.line("#[allow(unused_variables)]");
fmt.line(&format!("pub fn new({}) -> Self {{", args));
fmt.indent(|fmt| {
fmt.line(&format!(
"let bvec = builder.state_for(\"{}\");",
group.name
));
fmt.line(&format!(
"let mut {} = Self {{ bytes: [0; {}] }};",
group.name,
group.byte_size()
));
fmt.line(&format!(
"debug_assert_eq!(bvec.len(), {});",
group.settings_size
));
fmt.line(&format!(
"{}.bytes[0..{}].copy_from_slice(&bvec);",
group.name, group.settings_size
));
// Now compute the predicates.
for p in &group.predicates {
fmt.comment(&format!("Precompute #{}.", p.number));
fmt.line(&format!("if {} {{", p.render(group)));
fmt.indent(|fmt| {
fmt.line(&format!(
"{}.bytes[{}] |= 1 << {};",
group.name,
group.bool_start_byte_offset + p.number / 8,
p.number % 8
));
});
fmt.line("}");
}
fmt.line(group.name);
});
fmt.line("}");
});
fmt.line("}");
}
/// Emit Display and FromStr implementations for enum settings.
fn gen_to_and_from_str(name: &str, values: &[&'static str], fmt: &mut Formatter) {
fmt.line(&format!("impl fmt::Display for {} {{", name));
fmt.indent(|fmt| {
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
fmt.indent(|fmt| {
fmt.line("f.write_str(match *self {");
fmt.indent(|fmt| {
for v in values.iter() {
fmt.line(&format!("{}::{} => \"{}\",", name, camel_case(v), v));
}
});
fmt.line("})");
});
fmt.line("}");
});
fmt.line("}");
fmt.line(&format!("impl str::FromStr for {} {{", name));
fmt.indent(|fmt| {
fmt.line("type Err = ();");
fmt.line("fn from_str(s: &str) -> Result<Self, Self::Err> {");
fmt.indent(|fmt| {
fmt.line("match s {");
fmt.indent(|fmt| {
for v in values.iter() {
fmt.line(&format!("\"{}\" => Ok({}::{}),", v, name, camel_case(v)));
}
fmt.line("_ => Err(()),");
});
fmt.line("}");
});
fmt.line("}");
});
fmt.line("}");
}
/// Emit real enum for the Enum settings.
fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) {
for setting in group.settings.iter() {
let values = match setting.specific {
SpecificSetting::Bool(_) | SpecificSetting::Num(_) => continue,
SpecificSetting::Enum(ref values) => values,
};
let name = camel_case(setting.name);
fmt.doc_comment(&format!("Values for `{}.{}`.", group.name, setting.name));
fmt.line("#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]");
fmt.line(&format!("pub enum {} {{", name));
fmt.indent(|fmt| {
for v in values.iter() {
fmt.doc_comment(&format!("`{}`.", v));
fmt.line(&format!("{},", camel_case(v)));
}
});
fmt.line("}");
gen_to_and_from_str(&name, values, fmt);
}
}
/// Emit a getter function for `setting`.
fn gen_getter(setting: &Setting, fmt: &mut Formatter) {
fmt.doc_comment(setting.comment);
match setting.specific {
SpecificSetting::Bool(BoolSetting {
predicate_number, ..
}) => {
fmt.line(&format!("pub fn {}(&self) -> bool {{", setting.name));
fmt.indent(|fmt| {
fmt.line(&format!("self.numbered_predicate({})", predicate_number));
});
fmt.line("}");
}
SpecificSetting::Enum(ref values) => {
let ty = camel_case(setting.name);
fmt.line(&format!("pub fn {}(&self) -> {} {{", setting.name, ty));
fmt.indent(|fmt| {
let mut m = Match::new(format!("self.bytes[{}]", setting.byte_offset));
for (i, v) in values.iter().enumerate() {
m.arm(
format!("{}", i),
vec![],
format!("{}::{}", ty, camel_case(v)),
);
}
m.arm("_", vec![], "panic!(\"Invalid enum value\")");
fmt.add_match(m);
});
fmt.line("}");
}
SpecificSetting::Num(_) => {
fmt.line(&format!("pub fn {}(&self) -> u8 {{", setting.name));
fmt.indent(|fmt| {
fmt.line(&format!("self.bytes[{}]", setting.byte_offset));
});
fmt.line("}");
}
}
}
fn gen_pred_getter(predicate: &Predicate, group: &SettingGroup, fmt: &mut Formatter) {
fmt.doc_comment(&format!(
"Computed predicate `{}`.",
predicate.render(group)
));
fmt.line(&format!("pub fn {}(&self) -> bool {{", predicate.name));
fmt.indent(|fmt| {
fmt.line(&format!("self.numbered_predicate({})", predicate.number));
});
fmt.line("}");
}
/// Emits getters for each setting value.
fn gen_getters(group: &SettingGroup, fmt: &mut Formatter) {
fmt.doc_comment("User-defined settings.");
fmt.line("#[allow(dead_code)]");
fmt.line("impl Flags {");
fmt.indent(|fmt| {
fmt.doc_comment("Get a view of the boolean predicates.");
fmt.line("pub fn predicate_view(&self) -> ::settings::PredicateView {");
fmt.indent(|fmt| {
fmt.line(&format!(
"::settings::PredicateView::new(&self.bytes[{}..])",
group.bool_start_byte_offset
));
});
fmt.line("}");
if group.settings.len() > 0 {
fmt.doc_comment("Dynamic numbered predicate getter.");
fmt.line("fn numbered_predicate(&self, p: usize) -> bool {");
fmt.indent(|fmt| {
fmt.line(&format!(
"self.bytes[{} + p / 8] & (1 << (p % 8)) != 0",
group.bool_start_byte_offset
));
});
fmt.line("}");
}
for setting in &group.settings {
gen_getter(&setting, fmt);
}
for predicate in &group.predicates {
gen_pred_getter(&predicate, &group, fmt);
}
});
fmt.line("}");
}
#[derive(Hash, PartialEq, Eq)]
enum SettingOrPreset<'a> {
Setting(&'a Setting),
Preset(&'a Preset),
}
impl<'a> SettingOrPreset<'a> {
fn name(&self) -> &str {
match *self {
SettingOrPreset::Setting(s) => s.name,
SettingOrPreset::Preset(p) => p.name,
}
}
}
/// Emits DESCRIPTORS, ENUMERATORS, HASH_TABLE and PRESETS.
fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) {
let mut enum_table: UniqueTable<&'static str> = UniqueTable::new();
let mut descriptor_index_map: HashMap<SettingOrPreset, usize> = HashMap::new();
// Generate descriptors.
fmt.line(&format!(
"static DESCRIPTORS: [detail::Descriptor; {}] = [",
group.settings.len() + group.presets.len()
));
fmt.indent(|fmt| {
for (idx, setting) in group.settings.iter().enumerate() {
fmt.line("detail::Descriptor {");
fmt.indent(|fmt| {
fmt.line(&format!("name: \"{}\",", setting.name));
fmt.line(&format!("offset: {},", setting.byte_offset));
match setting.specific {
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
fmt.line(&format!(
"detail: detail::Detail::Bool {{ bit: {} }},",
bit_offset
));
}
SpecificSetting::Enum(ref values) => {
let offset = enum_table.add(values);
fmt.line(&format!(
"detail: detail::Detail::Enum {{ last: {}, enumerators: {} }},",
values.len() - 1,
offset
));
}
SpecificSetting::Num(_) => {
fmt.line("detail: detail::Detail::Num,");
}
}
descriptor_index_map.insert(SettingOrPreset::Setting(setting), idx);
});
fmt.line("},");
}
for (idx, preset) in group.presets.iter().enumerate() {
fmt.line("detail::Descriptor {");
fmt.indent(|fmt| {
fmt.line(&format!("name: \"{}\",", preset.name));
fmt.line(&format!("offset: {},", (idx as u8) * group.settings_size));
fmt.line("detail: detail::Detail::Preset,");
});
fmt.line("},");
descriptor_index_map.insert(SettingOrPreset::Preset(preset), idx);
}
});
fmt.line("];");
// Generate enumerators.
fmt.line(&format!(
"static ENUMERATORS: [&str; {}] = [",
enum_table.len()
));
fmt.indent(|fmt| {
for enum_val in enum_table.iter() {
fmt.line(&format!("\"{}\",", enum_val));
}
});
fmt.line("];");
// Generate hash table.
let mut hash_entries: Vec<SettingOrPreset> = Vec::new();
hash_entries.extend(
group
.settings
.iter()
.map(|x| SettingOrPreset::Setting(x))
.collect::<Vec<SettingOrPreset>>(),
);
hash_entries.extend(
group
.presets
.iter()
.map(|x| SettingOrPreset::Preset(x))
.collect::<Vec<SettingOrPreset>>(),
);
let hash_table = generate_table(&hash_entries, |entry| simple_hash(entry.name()));
fmt.line(&format!(
"static HASH_TABLE: [u16; {}] = [",
hash_table.len()
));
fmt.indent(|fmt| {
for h in &hash_table {
match *h {
Some(setting_or_preset) => fmt.line(&format!(
"{},",
&descriptor_index_map
.get(setting_or_preset)
.unwrap()
.to_string()
)),
None => fmt.line("0xffff,"),
}
}
});
fmt.line("];");
// Generate presets.
fmt.line(&format!(
"static PRESETS: [(u8, u8); {}] = [",
group.presets.len()
));
fmt.indent(|fmt| {
for preset in &group.presets {
fmt.comment(preset.name);
for (mask, value) in preset.layout(&group) {
fmt.line(&format!("(0b{:08b}, 0b{:08b}),", mask, value));
}
}
});
fmt.line("];");
}
fn gen_template(group: &SettingGroup, fmt: &mut Formatter) {
let mut default_bytes: Vec<u8> = vec![0; group.settings_size as usize];
for setting in &group.settings {
*default_bytes.get_mut(setting.byte_offset as usize).unwrap() |= setting.default_byte();
}
let default_bytes: Vec<String> = default_bytes
.iter()
.map(|x| format!("{:#04x}", x))
.collect();
let default_bytes_str = default_bytes.join(", ");
fmt.line("static TEMPLATE: detail::Template = detail::Template {");
fmt.indent(|fmt| {
fmt.line(&format!("name: \"{}\",", group.name));
fmt.line("descriptors: &DESCRIPTORS,");
fmt.line("enumerators: &ENUMERATORS,");
fmt.line("hash_table: &HASH_TABLE,");
fmt.line(&format!("defaults: &[{}],", default_bytes_str));
fmt.line("presets: &PRESETS,");
});
fmt.line("};");
fmt.doc_comment(&format!(
"Create a `settings::Builder` for the {} settings group.",
group.name
));
fmt.line("pub fn builder() -> Builder {");
fmt.indent(|fmt| {
fmt.line("Builder::new(&TEMPLATE)");
});
fmt.line("}");
}
fn gen_display(group: &SettingGroup, fmt: &mut Formatter) {
fmt.line("impl fmt::Display for Flags {");
fmt.indent(|fmt| {
fmt.line("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {");
fmt.indent(|fmt| {
fmt.line(&format!("writeln!(f, \"[{}]\")?;", group.name));
fmt.line("for d in &DESCRIPTORS {");
fmt.indent(|fmt| {
fmt.line("if !d.detail.is_preset() {");
fmt.indent(|fmt| {
fmt.line("write!(f, \"{} = \", d.name)?;");
fmt.line(
"TEMPLATE.format_toml_value(d.detail, self.bytes[d.offset as usize], f)?;",
);
fmt.line("writeln!(f)?;");
});
fmt.line("}");
});
fmt.line("}");
fmt.line("Ok(())");
});
fmt.line("}")
});
fmt.line("}");
}
fn gen_group(group: &SettingGroup, parent: ParentGroup, fmt: &mut Formatter) {
// Generate struct.
fmt.line("#[derive(Clone)]");
fmt.doc_comment(&format!("Flags group `{}`.", group.name));
fmt.line("pub struct Flags {");
fmt.indent(|fmt| {
fmt.line(&format!("bytes: [u8; {}],", group.byte_size()));
});
fmt.line("}");
gen_constructor(group, parent, fmt);
gen_enum_types(group, fmt);
gen_getters(group, fmt);
gen_descriptors(group, fmt);
gen_template(group, fmt);
gen_display(group, fmt);
}
pub fn generate_common(filename: &str, out_dir: &str) -> Result<SettingGroup, error::Error> {
let settings = base::settings::generate();
let mut fmt = Formatter::new();
gen_group(&settings, ParentGroup::None, &mut fmt);
fmt.update_file(filename, out_dir)?;
Ok(settings)
}
pub fn generate(isa: &TargetIsa, prefix: &str, out_dir: &str) -> Result<(), error::Error> {
let mut fmt = Formatter::new();
gen_group(&isa.settings, ParentGroup::Shared, &mut fmt);
fmt.update_file(&format!("{}-{}.rs", prefix, isa.name), out_dir)?;
Ok(())
}

View File

@@ -0,0 +1,73 @@
//! Generate sources with type info.
//!
//! This generates a `types.rs` file which is included in
//! `cranelift-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 crate::cdsl::types as cdsl_types;
use crate::error;
use crate::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.to_string().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(cdsl_types::ValueType::from) {
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,45 @@
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let setting = SettingGroupBuilder::new("arm32");
setting.finish()
}
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
let mut isa = TargetIsaBuilder::new("arm32", define_settings(shared_settings));
let builder = RegBankBuilder::new("FloatRegs", "s")
.units(64)
.track_pressure(true);
let float_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("IntRegs", "r")
.units(16)
.track_pressure(true);
let int_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FlagRegs", "")
.units(1)
.names(vec!["nzcv"])
.track_pressure(false);
let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel("S", float_regs).count(32);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("D", float_regs).width(2);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("Q", float_regs).width(4);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder);
isa.finish()
}

View File

@@ -0,0 +1,41 @@
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let setting = SettingGroupBuilder::new("arm64");
setting.finish()
}
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
let mut isa = TargetIsaBuilder::new("arm64", define_settings(shared_settings));
// The `x31` regunit serves as the stack pointer / zero register depending on context. We
// reserve it and don't model the difference.
let builder = RegBankBuilder::new("IntRegs", "x")
.units(32)
.track_pressure(true);
let int_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FloatRegs", "v")
.units(32)
.track_pressure(true);
let float_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FlagRegs", "")
.units(1)
.names(vec!["nzcv"])
.track_pressure(false);
let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder);
isa.finish()
}

View File

@@ -0,0 +1,72 @@
use crate::cdsl::isa::TargetIsa;
use crate::cdsl::settings::SettingGroup;
use std::fmt;
mod arm32;
mod arm64;
mod riscv;
mod x86;
/// Represents known ISA target.
#[derive(Copy, Clone)]
pub enum Isa {
Riscv,
X86,
Arm32,
Arm64,
}
impl Isa {
/// Creates isa target using name.
pub fn new(name: &str) -> Option<Self> {
Isa::all()
.iter()
.cloned()
.filter(|isa| isa.to_string() == name)
.next()
}
/// Creates isa target from arch.
pub fn from_arch(arch: &str) -> Option<Isa> {
Isa::all()
.iter()
.cloned()
.filter(|isa| isa.is_arch_applicable(arch))
.next()
}
/// Returns all supported isa targets.
pub fn all() -> [Isa; 4] {
[Isa::Riscv, Isa::X86, Isa::Arm32, Isa::Arm64]
}
/// Checks if arch is applicable for the isa target.
fn is_arch_applicable(&self, arch: &str) -> bool {
match *self {
Isa::Riscv => arch == "riscv",
Isa::X86 => ["x86_64", "i386", "i586", "i686"].contains(&arch),
Isa::Arm32 => arch.starts_with("arm") || arch.starts_with("thumb"),
Isa::Arm64 => arch == "aarch64",
}
}
}
impl fmt::Display for Isa {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Isa::Riscv => write!(f, "riscv"),
Isa::X86 => write!(f, "x86"),
Isa::Arm32 => write!(f, "arm32"),
Isa::Arm64 => write!(f, "arm64"),
}
}
}
pub fn define_all(shared_settings: &SettingGroup) -> Vec<TargetIsa> {
vec![
riscv::define(shared_settings),
arm32::define(shared_settings),
arm64::define(shared_settings),
x86::define(shared_settings),
]
}

View File

@@ -0,0 +1,77 @@
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
fn define_settings(shared: &SettingGroup) -> SettingGroup {
let mut setting = SettingGroupBuilder::new("riscv");
let supports_m = setting.add_bool(
"supports_m",
"CPU supports the 'M' extension (mul/div)",
false,
);
let supports_a = setting.add_bool(
"supports_a",
"CPU supports the 'A' extension (atomics)",
false,
);
let supports_f = setting.add_bool(
"supports_f",
"CPU supports the 'F' extension (float)",
false,
);
let supports_d = setting.add_bool(
"supports_d",
"CPU supports the 'D' extension (double)",
false,
);
let enable_m = setting.add_bool(
"enable_m",
"Enable the use of 'M' instructions if available",
true,
);
setting.add_bool(
"enable_e",
"Enable the 'RV32E' instruction set with only 16 registers",
true,
);
let shared_enable_atomics = shared.get_bool("enable_atomics");
let shared_enable_float = shared.get_bool("enable_float");
let shared_enable_simd = shared.get_bool("enable_simd");
setting.add_predicate("use_m", predicate!(supports_m && enable_m));
setting.add_predicate("use_a", predicate!(supports_a && shared_enable_atomics));
setting.add_predicate("use_f", predicate!(supports_f && shared_enable_float));
setting.add_predicate("use_d", predicate!(supports_d && shared_enable_float));
setting.add_predicate(
"full_float",
predicate!(shared_enable_simd && supports_f && supports_d),
);
setting.finish()
}
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
let mut isa = TargetIsaBuilder::new("riscv", define_settings(shared_settings));
let builder = RegBankBuilder::new("IntRegs", "x")
.units(32)
.track_pressure(true);
let int_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FloatRegs", "f")
.units(32)
.track_pressure(true);
let float_regs = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
isa.add_reg_class(builder);
isa.finish()
}

View File

@@ -0,0 +1,116 @@
use crate::cdsl::isa::{TargetIsa, TargetIsaBuilder};
use crate::cdsl::regs::{RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
pub fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut settings = SettingGroupBuilder::new("x86");
// CPUID.01H:ECX
let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);
let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false);
let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false);
let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false);
settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false);
// CPUID.(EAX=07H, ECX=0H):EBX
let has_bmi1 = settings.add_bool(
"has_bmi1",
"BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]",
false,
);
let has_bmi2 = settings.add_bool(
"has_bmi2",
"BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]",
false,
);
// CPUID.EAX=80000001H:ECX
let has_lzcnt = settings.add_bool(
"has_lzcnt",
"LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]",
false,
);
settings.add_predicate("use_sse41", predicate!(has_sse41));
settings.add_predicate("use_sse42", predicate!(has_sse41 && has_sse42));
settings.add_predicate("use_popcnt", predicate!(has_popcnt && has_sse42));
settings.add_predicate("use_bmi1", predicate!(has_bmi1));
settings.add_predicate("use_lznct", predicate!(has_lzcnt));
settings.add_preset("baseline", preset!());
let nehalem = settings.add_preset(
"nehalem",
preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt),
);
let haswell = settings.add_preset(
"haswell",
preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt),
);
let broadwell = settings.add_preset("broadwell", preset!(haswell));
let skylake = settings.add_preset("skylake", preset!(broadwell));
let cannonlake = settings.add_preset("cannonlake", preset!(skylake));
settings.add_preset("icelake", preset!(cannonlake));
settings.add_preset(
"znver1",
preset!(
has_sse3
&& has_ssse3
&& has_sse41
&& has_sse42
&& has_popcnt
&& has_bmi1
&& has_bmi2
&& has_lzcnt
),
);
settings.finish()
}
fn define_registers(isa: &mut TargetIsaBuilder) {
let builder = RegBankBuilder::new("IntRegs", "r")
.units(16)
.names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"])
.track_pressure(true);
let int_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FloatRegs", "xmm")
.units(16)
.track_pressure(true);
let float_regs = isa.add_reg_bank(builder);
let builder = RegBankBuilder::new("FlagRegs", "")
.units(1)
.names(vec!["rflags"])
.track_pressure(false);
let flag_reg = isa.add_reg_bank(builder);
let builder = RegClassBuilder::new_toplevel("GPR", int_regs);
let gpr = isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FPR", float_regs);
let fpr = isa.add_reg_class(builder);
let builder = RegClassBuilder::new_toplevel("FLAG", flag_reg);
isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of("GPR8", gpr, 0, 8);
let gpr8 = isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of("ABCD", gpr8, 0, 4);
isa.add_reg_class(builder);
let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
isa.add_reg_class(builder);
}
pub fn define(shared_settings: &SettingGroup) -> TargetIsa {
let settings = define_settings(shared_settings);
let mut isa = TargetIsaBuilder::new("x86", settings);
define_registers(&mut isa);
isa.finish()
}

View File

@@ -0,0 +1,13 @@
#[macro_use]
mod cdsl;
pub mod error;
pub mod gen_registers;
pub mod gen_settings;
pub mod gen_types;
pub mod isa;
mod base;
mod constant_hash;
mod srcgen;
mod unique_table;

View File

@@ -0,0 +1,382 @@
//! Source code generator.
//!
//! The `srcgen` module contains generic helper routines and classes for
//! generating source code.
use std::cmp;
use std::collections::{BTreeMap, BTreeSet};
use std::fs;
use std::io::Write;
use std::path;
use crate::error;
static SHIFTWIDTH: usize = 4;
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() -> Self {
Self {
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;
}
pub fn indent<T, F: FnOnce(&mut Formatter) -> T>(&mut self, f: F) -> T {
self.indent_push();
let ret = f(self);
self.indent_pop();
ret
}
/// 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(())
}
/// 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| {
if l.len() == 0 {
"///".into()
} else {
format!("/// {}", l)
}
})
.for_each(|s| self.line(s.as_str()));
}
/// Add a match expression.
pub fn add_match(&mut self, m: Match) {
self.line(&format!("match {} {{", m.expr));
self.indent(|fmt| {
for (&(ref fields, ref body), ref names) in m.arms.iter() {
// name { fields } | name { fields } => { body }
let conditions: Vec<String> = names
.iter()
.map(|name| {
if fields.len() > 0 {
format!("{} {{ {} }}", name, fields.join(", "))
} else {
name.clone()
}
})
.collect();
let lhs = conditions.join(" | ");
fmt.line(&format!("{} => {{", lhs));
fmt.indent(|fmt| {
fmt.line(body);
});
fmt.line("}");
}
});
self.line("}");
}
}
/// 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_start();
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 and empty lines.
let indent = lines
.iter()
.skip(1)
.filter(|l| !l.trim().is_empty())
.map(|l| l.len() - l.trim_start().len())
.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 {
// Note that empty lines may have fewer than `indent` chars.
lines_iter
.map(|l| &l[cmp::min(indent, l.len())..])
.map(|l| l.trim_end())
.map(|l| l.to_string())
.collect::<Vec<_>>()
} else {
lines_iter
.map(|l| l.trim_end())
.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. BTreeMap/BTreeSet are used to
/// represent the arms in order to make the order deterministic.
pub struct Match {
expr: String,
arms: BTreeMap<(Vec<String>, String), BTreeSet<String>>,
}
impl Match {
/// Create a new match statement on `expr`.
pub fn new<T: Into<String>>(expr: T) -> Self {
Self {
expr: expr.into(),
arms: BTreeMap::new(),
}
}
/// Add an arm to the Match statement.
pub fn arm<T: Into<String>>(&mut self, name: T, fields: Vec<T>, body: T) {
// let key = (fields, body);
let body = body.into();
let fields = fields.into_iter().map(|x| x.into()).collect();
let match_arm = self
.arms
.entry((fields, body))
.or_insert_with(BTreeSet::new);
match_arm.insert(name.into());
}
}
#[cfg(test)]
mod srcgen_tests {
use super::parse_multiline;
use super::Formatter;
use super::Match;
fn from_raw_string<S: Into<String>>(s: S) -> Vec<String> {
s.into()
.trim()
.split("\n")
.into_iter()
.map(|x| format!("{}\n", x))
.collect()
}
#[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);
let mut fmt = Formatter::new();
fmt.add_match(m);
let expected_lines = from_raw_string(
r#"
match x {
Green { a, b } => {
different body
}
Orange { a, b } | Yellow { a, b } => {
some body
}
Blue { x, y } => {
some body
}
}
"#,
);
assert_eq!(fmt.lines, expected_lines);
}
#[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);
}
#[test]
fn fmt_can_add_doc_comments_with_empty_lines() {
let mut fmt = Formatter::new();
fmt.doc_comment(
r#"documentation
can be really good.
If you stick to writing it.
"#,
);
let expected_lines = from_raw_string(
r#"
/// documentation
/// can be really good.
///
/// If you stick to writing it."#,
);
assert_eq!(fmt.lines, expected_lines);
}
}

View File

@@ -0,0 +1,68 @@
use std::slice;
/// A table of sequences which tries to avoid common subsequences.
pub struct UniqueTable<T: PartialEq + Clone> {
table: Vec<T>,
}
impl<T: PartialEq + Clone> UniqueTable<T> {
pub fn new() -> Self {
Self { table: Vec::new() }
}
pub fn add(&mut self, values: &Vec<T>) -> usize {
if let Some(offset) = find_subsequence(values, &self.table) {
offset
} else {
let offset = self.table.len();
self.table.extend((*values).clone());
offset
}
}
pub fn len(&self) -> usize {
self.table.len()
}
pub fn iter(&self) -> slice::Iter<T> {
self.table.iter()
}
}
/// Try to find the subsequence `sub` in the `whole` sequence. Returns None if
/// it's not been found, or Some(index) if it has been. Naive implementation
/// until proven we need something better.
fn find_subsequence<T: PartialEq>(sub: &Vec<T>, whole: &Vec<T>) -> Option<usize> {
assert!(sub.len() > 0);
// We want i + sub.len() <= whole.len(), i.e. i < whole.len() + 1 - sub.len().
if whole.len() < sub.len() {
return None;
}
let max = whole.len() + 1 - sub.len();
for i in 0..max {
let mut found: Option<usize> = Some(i);
for j in 0..sub.len() {
if sub[j] != whole[i + j] {
found = None;
break;
}
}
if found.is_some() {
return found;
}
}
return None;
}
#[test]
fn test_find_subsequence() {
assert_eq!(find_subsequence(&vec![1], &vec![4]), None);
assert_eq!(find_subsequence(&vec![1], &vec![1]), Some(0));
assert_eq!(find_subsequence(&vec![1, 2], &vec![1]), None);
assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 2]), Some(0));
assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 3]), None);
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 2]), Some(1));
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1]), None);
assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1, 2]), Some(3));
assert_eq!(
find_subsequence(&vec![1, 1, 3], &vec![1, 1, 1, 3, 3]),
Some(1)
);
}