[build] Move settings generation from Python to Rust code;

This commit is contained in:
Benjamin Bouvier
2018-11-21 15:38:12 +01:00
committed by Dan Gohman
parent 4c8f1e7a5a
commit d94e027c2a
20 changed files with 1334 additions and 40 deletions

View File

@@ -82,20 +82,12 @@ fn main() {
// Now that the Python build process is complete, generate files that are // Now that the Python build process is complete, generate files that are
// emitted by the `meta` crate. // emitted by the `meta` crate.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
let isas = meta::isa::define_all();
if let Err(err) = meta::gen_types::generate("types.rs", &out_dir) { if let Err(err) = generate_meta(&out_dir) {
eprintln!("Error: {}", err); eprintln!("Error: {}", err);
process::exit(1); process::exit(1);
} }
for isa in isas {
if let Err(err) = meta::gen_registers::generate(isa, "registers", &out_dir) {
eprintln!("Error: {}", err);
process::exit(1);
}
}
if let Ok(_) = env::var("CRANELIFT_VERBOSE") { if let Ok(_) = env::var("CRANELIFT_VERBOSE") {
println!( println!(
"cargo:warning=Build step took {:?}.", "cargo:warning=Build step took {:?}.",
@@ -105,6 +97,20 @@ fn main() {
} }
} }
fn generate_meta(out_dir: &str) -> Result<(), meta::error::Error> {
let shared_settings = meta::gen_settings::generate_common("new_settings.rs", &out_dir)?;
let isas = meta::isa::define_all(&shared_settings);
meta::gen_types::generate("types.rs", &out_dir)?;
for isa in &isas {
meta::gen_registers::generate(&isa, "registers", &out_dir)?;
meta::gen_settings::generate(&isa, "new_settings", &out_dir)?;
}
Ok(())
}
fn identify_python() -> &'static str { fn identify_python() -> &'static str {
for python in &["python", "python3", "python2.7"] { for python in &["python", "python3", "python2.7"] {
if process::Command::new(python) if process::Command::new(python)

View File

@@ -340,7 +340,7 @@ class SettingGroup(object):
precomputed predicates. precomputed predicates.
This is the size of the byte-sized settings plus all the numbered This is the size of the byte-sized settings plus all the numbered
predcate bits rounded up to a whole number of bytes. predicate bits rounded up to a whole number of bytes.
""" """
return self.boolean_offset + (len(self.predicate_number) + 7) // 8 return self.boolean_offset + (len(self.predicate_number) + 7) // 8

View File

@@ -255,8 +255,8 @@ def gen_display(sgrp, fmt):
fmt.line('Ok(())') fmt.line('Ok(())')
def gen_constructor(sgrp, parent, fmt): def gen_constructor(sgrp, fmt):
# type: (SettingGroup, PredContext, srcgen.Formatter) -> None # type: (SettingGroup, srcgen.Formatter) -> None
""" """
Generate a Flags constructor. Generate a Flags constructor.
""" """
@@ -310,7 +310,7 @@ def gen_group(sgrp, fmt):
with fmt.indented('pub struct Flags {', '}'): with fmt.indented('pub struct Flags {', '}'):
fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size())) fmt.line('bytes: [u8; {}],'.format(sgrp.byte_size()))
gen_constructor(sgrp, None, fmt) gen_constructor(sgrp, fmt)
gen_enum_types(sgrp, fmt) gen_enum_types(sgrp, fmt)
gen_getters(sgrp, fmt) gen_getters(sgrp, fmt)
gen_descriptors(sgrp, fmt) gen_descriptors(sgrp, fmt)

View File

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

View File

@@ -0,0 +1,164 @@
use 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

@@ -3,19 +3,22 @@ use cranelift_entity::PrimaryMap;
use super::regs::{ use super::regs::{
RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto, RegBank, RegBankBuilder, RegBankIndex, RegClass, RegClassBuilder, RegClassIndex, RegClassProto,
}; };
use super::settings::SettingGroup;
pub struct TargetIsa { pub struct TargetIsa {
pub name: &'static str, pub name: &'static str,
pub reg_banks: PrimaryMap<RegBankIndex, RegBank>, pub reg_banks: PrimaryMap<RegBankIndex, RegBank>,
pub reg_classes: PrimaryMap<RegClassIndex, RegClass>, pub reg_classes: PrimaryMap<RegClassIndex, RegClass>,
pub settings: SettingGroup,
} }
impl TargetIsa { impl TargetIsa {
pub fn new(name: &'static str) -> Self { pub fn new(name: &'static str, settings: SettingGroup) -> Self {
Self { Self {
name, name,
reg_banks: PrimaryMap::new(), reg_banks: PrimaryMap::new(),
reg_classes: PrimaryMap::new(), reg_classes: PrimaryMap::new(),
settings,
} }
} }
} }
@@ -25,9 +28,9 @@ pub struct TargetIsaBuilder {
} }
impl TargetIsaBuilder { impl TargetIsaBuilder {
pub fn new(name: &'static str) -> Self { pub fn new(name: &'static str, settings: SettingGroup) -> Self {
Self { Self {
isa: TargetIsa::new(name), isa: TargetIsa::new(name, settings),
} }
} }

View File

@@ -5,10 +5,38 @@
pub mod isa; pub mod isa;
pub mod regs; pub mod regs;
pub mod settings;
pub mod types; 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. /// Convert the string `s` to CamelCase.
fn _camel_case(s: &str) -> String { pub fn camel_case(s: &str) -> String {
let mut output_chars = String::with_capacity(s.len()); let mut output_chars = String::with_capacity(s.len());
let mut capitalize = true; let mut capitalize = true;
@@ -30,7 +58,7 @@ fn _camel_case(s: &str) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::_camel_case as camel_case; use super::camel_case;
#[test] #[test]
fn camel_case_works() { fn camel_case_works() {

View File

@@ -0,0 +1,385 @@
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 (l_mask, 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(group_name, bool_name) => {
format!("{}.{}()", group_name, bool_name)
}
PredicateNode::And(lhs, 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,50 @@
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::new();
table.resize(size, None);
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

@@ -78,7 +78,7 @@ fn gen_regbank_units(reg_bank: &RegBank, fmt: &mut Formatter) {
} }
} }
fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) -> Result<(), error::Error> { fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) {
// Emit RegInfo. // Emit RegInfo.
fmt.line("pub static INFO: RegInfo = RegInfo {"); fmt.line("pub static INFO: RegInfo = RegInfo {");
@@ -128,13 +128,11 @@ fn gen_isa(isa: &TargetIsa, fmt: &mut Formatter) -> Result<(), error::Error> {
fmt.line("}") fmt.line("}")
}); });
fmt.line("}"); fmt.line("}");
Ok(())
} }
pub fn generate(isa: TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> { pub fn generate(isa: &TargetIsa, base_filename: &str, out_dir: &str) -> Result<(), error::Error> {
let mut fmt = Formatter::new(); let mut fmt = Formatter::new();
gen_isa(&isa, &mut fmt)?; gen_isa(&isa, &mut fmt);
fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?; fmt.update_file(&format!("{}-{}.rs", base_filename, isa.name), out_dir)?;
Ok(()) Ok(())
} }

View File

@@ -0,0 +1,447 @@
use base;
use cdsl::camel_case;
use cdsl::isa::TargetIsa;
use cdsl::settings::{BoolSetting, Predicate, Preset, Setting, SettingGroup, SpecificSetting};
use constant_hash::{generate_table, simple_hash};
use error;
use srcgen::{Formatter, Match};
use std::collections::HashMap;
use unique_table::UniqueTable;
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(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::new();
default_bytes.resize(group.settings_size as usize, 0);
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

@@ -1,8 +1,14 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use cdsl::settings::{SettingGroup, SettingGroupBuilder};
pub fn define() -> TargetIsa { fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut isa = TargetIsaBuilder::new("arm32"); 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") let builder = RegBankBuilder::new("FloatRegs", "s")
.units(64) .units(64)

View File

@@ -1,8 +1,14 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use cdsl::settings::{SettingGroup, SettingGroupBuilder};
pub fn define() -> TargetIsa { fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut isa = TargetIsaBuilder::new("arm64"); 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 // The `x31` regunit serves as the stack pointer / zero register depending on context. We
// reserve it and don't model the difference. // reserve it and don't model the difference.

View File

@@ -1,4 +1,5 @@
use cdsl::isa::TargetIsa; use cdsl::isa::TargetIsa;
use cdsl::settings::SettingGroup;
use std::fmt; use std::fmt;
mod arm32; mod arm32;
@@ -61,11 +62,11 @@ impl fmt::Display for Isa {
} }
} }
pub fn define_all() -> Vec<TargetIsa> { pub fn define_all(shared_settings: &SettingGroup) -> Vec<TargetIsa> {
vec![ vec![
riscv::define(), riscv::define(shared_settings),
arm32::define(), arm32::define(shared_settings),
arm64::define(), arm64::define(shared_settings),
x86::define(), x86::define(shared_settings),
] ]
} }

View File

@@ -1,8 +1,61 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
pub fn define() -> TargetIsa { fn define_settings(shared: &SettingGroup) -> SettingGroup {
let mut isa = TargetIsaBuilder::new("riscv"); 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") let builder = RegBankBuilder::new("IntRegs", "x")
.units(32) .units(32)

View File

@@ -1,9 +1,74 @@
use cdsl::isa::{TargetIsa, TargetIsaBuilder}; use cdsl::isa::{TargetIsa, TargetIsaBuilder};
use cdsl::regs::{RegBankBuilder, RegClassBuilder}; use cdsl::regs::{RegBankBuilder, RegClassBuilder};
use cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
pub fn define() -> TargetIsa { pub fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut isa = TargetIsaBuilder::new("x86"); 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") let builder = RegBankBuilder::new("IntRegs", "r")
.units(16) .units(16)
.names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"]) .names(vec!["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"])
@@ -38,6 +103,14 @@ pub fn define() -> TargetIsa {
let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8); let builder = RegClassBuilder::subclass_of("FPR8", fpr, 0, 8);
isa.add_reg_class(builder); 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() isa.finish()
} }

View File

@@ -1,11 +1,16 @@
#[macro_use] #[macro_use]
extern crate cranelift_entity; extern crate cranelift_entity;
#[macro_use]
mod cdsl;
pub mod error; pub mod error;
pub mod gen_registers; pub mod gen_registers;
pub mod gen_settings;
pub mod gen_types; pub mod gen_types;
pub mod isa; pub mod isa;
mod base; mod base;
mod cdsl; mod constant_hash;
mod srcgen; mod srcgen;
mod unique_table;

View File

@@ -99,7 +99,7 @@ impl Formatter {
} }
/// Add a comment line. /// Add a comment line.
pub fn _comment(&mut self, s: &str) { pub fn comment(&mut self, s: &str) {
let commented_line = format!("// {}", s); let commented_line = format!("// {}", s);
self.line(&commented_line); self.line(&commented_line);
} }
@@ -302,7 +302,7 @@ match x {
let mut fmt = Formatter::new(); let mut fmt = Formatter::new();
fmt.line("Hello line 1"); fmt.line("Hello line 1");
fmt.indent_push(); fmt.indent_push();
fmt._comment("Nested comment"); fmt.comment("Nested comment");
fmt.indent_pop(); fmt.indent_pop();
fmt.line("Back home again"); fmt.line("Back home again");
let expected_lines = vec![ let expected_lines = vec![

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

View File

@@ -56,7 +56,7 @@ pub fn probe<K: Copy + Eq, T: Table<K> + ?Sized>(
} }
/// A primitive hash function for matching opcodes. /// A primitive hash function for matching opcodes.
/// Must match `lib/codegen/meta-python/constant_hash.py`. /// Must match `lib/codegen/meta-python/constant_hash.py` and `lib/codegen/meta/constant_hash.rs`.
pub fn simple_hash(s: &str) -> usize { pub fn simple_hash(s: &str) -> usize {
let mut h: u32 = 5381; let mut h: u32 = 5381;
for c in s.chars() { for c in s.chars() {