Update to rustfmt-preview (#348)
* Update to rustfmt-preview. * Run "cargo fmt --all" with rustfmt 0.4.1. rustfmt 0.4.1 is the latest release of rustfmt-preview available on the stable channel. * Fix a long line that rustfmt 0.4.1 can't handle. * Remove unneeded commas left behind by rustfmt.
This commit is contained in:
11
.travis.yml
11
.travis.yml
@@ -16,7 +16,16 @@ addons:
|
|||||||
install:
|
install:
|
||||||
- pip3 install --user --upgrade mypy flake8
|
- pip3 install --user --upgrade mypy flake8
|
||||||
- mypy --version
|
- mypy --version
|
||||||
- travis_wait ./check-rustfmt.sh --install
|
before_script:
|
||||||
|
- cargo uninstall rustfmt || true
|
||||||
|
- cargo install --list
|
||||||
|
- rustup toolchain install stable
|
||||||
|
- rustup component add --toolchain=stable rustfmt-preview
|
||||||
|
- rustup component list --toolchain=stable
|
||||||
|
- rustup show
|
||||||
|
- rustfmt +stable --version || echo fail
|
||||||
|
- rustup update
|
||||||
|
- rustfmt +stable --version
|
||||||
script: ./test-all.sh
|
script: ./test-all.sh
|
||||||
cache:
|
cache:
|
||||||
cargo: true
|
cargo: true
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Usage: check-rustfmt.sh [--install]
|
|
||||||
#
|
|
||||||
# Check that the desired version of rustfmt is installed.
|
|
||||||
#
|
|
||||||
# Rustfmt is still immature enough that its formatting decisions can change
|
|
||||||
# between versions. This makes it difficult to enforce a certain style in a
|
|
||||||
# test script since not all developers will upgrade rustfmt at the same time.
|
|
||||||
# To work around this, we only verify formatting when a specific version of
|
|
||||||
# rustfmt is installed.
|
|
||||||
#
|
|
||||||
# Exits 0 if the right version of rustfmt is installed, 1 otherwise.
|
|
||||||
#
|
|
||||||
# With the --install option, also tries to install the right version.
|
|
||||||
|
|
||||||
# This version should always be bumped to the newest version available that
|
|
||||||
# works with stable Rust.
|
|
||||||
# ... but not 0.10.0, since it's the same as 0.9.0 except for a deprecation
|
|
||||||
# error (and it requires --force to disable the error and enable normal
|
|
||||||
# operation, however that doesn't appear to be possible through "cargo fmt").
|
|
||||||
VERS="0.9.0"
|
|
||||||
|
|
||||||
if cargo install --list | tee /dev/null | grep -q "^rustfmt v$VERS"; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${1:-""} != "--install" ]]; then
|
|
||||||
echo "********************************************************************"
|
|
||||||
echo "* Please install rustfmt v$VERS to verify formatting. *"
|
|
||||||
echo "* If a newer version of rustfmt is available, update this script. *"
|
|
||||||
echo "********************************************************************"
|
|
||||||
echo "$0 --install"
|
|
||||||
sleep 1
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Installing rustfmt v$VERS."
|
|
||||||
cargo install --force --vers="$VERS" rustfmt
|
|
||||||
@@ -8,4 +8,4 @@ cd $(dirname "$0")
|
|||||||
# Make sure we can find rustfmt.
|
# Make sure we can find rustfmt.
|
||||||
export PATH="$PATH:$HOME/.cargo/bin"
|
export PATH="$PATH:$HOME/.cargo/bin"
|
||||||
|
|
||||||
exec cargo fmt --all -- "$@"
|
exec cargo +stable fmt --all -- "$@"
|
||||||
|
|||||||
@@ -18,12 +18,8 @@ pub fn run(files: &[String]) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cat_one(filename: &str) -> CommandResult {
|
fn cat_one(filename: &str) -> CommandResult {
|
||||||
let buffer = read_to_string(&filename).map_err(
|
let buffer = read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
|e| format!("{}: {}", filename, e),
|
let items = parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
)?;
|
|
||||||
let items = parse_functions(&buffer).map_err(
|
|
||||||
|e| format!("{}: {}", filename, e),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for (idx, func) in items.into_iter().enumerate() {
|
for (idx, func) in items.into_iter().enumerate() {
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
//! CLI tool to read Cretonne IR files and compile them into native code.
|
//! CLI tool to read Cretonne IR files and compile them into native code.
|
||||||
|
|
||||||
use capstone::prelude::*;
|
use capstone::prelude::*;
|
||||||
|
use cretonne_codegen::Context;
|
||||||
use cretonne_codegen::isa::TargetIsa;
|
use cretonne_codegen::isa::TargetIsa;
|
||||||
use cretonne_codegen::print_errors::pretty_error;
|
use cretonne_codegen::print_errors::pretty_error;
|
||||||
use cretonne_codegen::settings::FlagsOrIsa;
|
use cretonne_codegen::settings::FlagsOrIsa;
|
||||||
use cretonne_codegen::Context;
|
|
||||||
use cretonne_codegen::{binemit, ir};
|
use cretonne_codegen::{binemit, ir};
|
||||||
use cretonne_reader::parse_test;
|
use cretonne_reader::parse_test;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -80,9 +80,7 @@ fn handle_module(
|
|||||||
name: &str,
|
name: &str,
|
||||||
fisa: FlagsOrIsa,
|
fisa: FlagsOrIsa,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let buffer = read_to_string(&path).map_err(
|
let buffer = read_to_string(&path).map_err(|e| format!("{}: {}", name, e))?;
|
||||||
|e| format!("{}: {}", name, e),
|
|
||||||
)?;
|
|
||||||
let test_file = parse_test(&buffer).map_err(|e| format!("{}: {}", name, e))?;
|
let test_file = parse_test(&buffer).map_err(|e| format!("{}: {}", name, e))?;
|
||||||
|
|
||||||
// If we have an isa from the command-line, use that. Otherwise if the
|
// If we have an isa from the command-line, use that. Otherwise if the
|
||||||
@@ -154,12 +152,10 @@ fn get_disassembler(isa: &TargetIsa) -> Result<Capstone, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"arm32" => Capstone::new().arm().mode(arch::arm::ArchMode::Arm).build(),
|
"arm32" => Capstone::new().arm().mode(arch::arm::ArchMode::Arm).build(),
|
||||||
"arm64" => {
|
"arm64" => Capstone::new()
|
||||||
Capstone::new()
|
|
||||||
.arm64()
|
.arm64()
|
||||||
.mode(arch::arm64::ArchMode::Arm)
|
.mode(arch::arm64::ArchMode::Arm)
|
||||||
.build()
|
.build(),
|
||||||
}
|
|
||||||
_ => return Err(String::from("Unknown ISA")),
|
_ => return Err(String::from("Unknown ISA")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
#![deny(trivial_numeric_casts)]
|
#![deny(trivial_numeric_casts)]
|
||||||
#![warn(unused_import_braces, unstable_features, unused_extern_crates)]
|
#![warn(unused_import_braces, unstable_features, unused_extern_crates)]
|
||||||
#![cfg_attr(feature="cargo-clippy", warn(
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
float_arithmetic,
|
warn(float_arithmetic, mut_mut, nonminimal_bool, option_map_unwrap_or,
|
||||||
mut_mut,
|
option_map_unwrap_or_else, unicode_not_nfc, use_self))]
|
||||||
nonminimal_bool,
|
|
||||||
option_map_unwrap_or,
|
|
||||||
option_map_unwrap_or_else,
|
|
||||||
unicode_not_nfc,
|
|
||||||
use_self,
|
|
||||||
))]
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate cfg_if;
|
extern crate cfg_if;
|
||||||
@@ -134,9 +128,7 @@ fn cton_util() -> CommandResult {
|
|||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "wasm"))]
|
#[cfg(not(feature = "wasm"))]
|
||||||
let result = Err(
|
let result = Err("Error: cton-util was compiled without wasm support.".to_owned());
|
||||||
"Error: cton-util was compiled without wasm support.".to_owned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,12 +19,8 @@ pub fn run(files: &[String]) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_cfg(filename: &str) -> CommandResult {
|
fn print_cfg(filename: &str) -> CommandResult {
|
||||||
let buffer = read_to_string(filename).map_err(
|
let buffer = read_to_string(filename).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
|e| format!("{}: {}", filename, e),
|
let items = parse_functions(&buffer).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
)?;
|
|
||||||
let items = parse_functions(&buffer).map_err(
|
|
||||||
|e| format!("{}: {}", filename, e),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for (idx, func) in items.into_iter().enumerate() {
|
for (idx, func) in items.into_iter().enumerate() {
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ pub fn run(files: &[String], verbose: bool) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
io::stdin().read_to_string(&mut buffer).map_err(|e| {
|
io::stdin()
|
||||||
format!("stdin: {}", e)
|
.read_to_string(&mut buffer)
|
||||||
})?;
|
.map_err(|e| format!("stdin: {}", e))?;
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
let (success, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(
|
let (success, explain) = checker
|
||||||
|e| e.to_string(),
|
.explain(&buffer, NO_VARIABLES)
|
||||||
)?;
|
.map_err(|e| e.to_string())?;
|
||||||
print!("{}", explain);
|
print!("{}", explain);
|
||||||
if success {
|
if success {
|
||||||
println!("OK");
|
println!("OK");
|
||||||
@@ -37,27 +37,25 @@ pub fn run(files: &[String], verbose: bool) -> CommandResult {
|
|||||||
} else {
|
} else {
|
||||||
Err("Check failed".to_string())
|
Err("Check failed".to_string())
|
||||||
}
|
}
|
||||||
} else if checker.check(&buffer, NO_VARIABLES).map_err(
|
} else if checker
|
||||||
|e| e.to_string(),
|
.check(&buffer, NO_VARIABLES)
|
||||||
)?
|
.map_err(|e| e.to_string())?
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let (_, explain) = checker.explain(&buffer, NO_VARIABLES).map_err(
|
let (_, explain) = checker
|
||||||
|e| e.to_string(),
|
.explain(&buffer, NO_VARIABLES)
|
||||||
)?;
|
.map_err(|e| e.to_string())?;
|
||||||
print!("{}", explain);
|
print!("{}", explain);
|
||||||
Err("Check failed".to_string())
|
Err("Check failed".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_checkfile(filename: &str) -> Result<Checker, String> {
|
fn read_checkfile(filename: &str) -> Result<Checker, String> {
|
||||||
let buffer = read_to_string(&filename).map_err(
|
let buffer = read_to_string(&filename).map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
|e| format!("{}: {}", filename, e),
|
|
||||||
)?;
|
|
||||||
let mut builder = CheckerBuilder::new();
|
let mut builder = CheckerBuilder::new();
|
||||||
builder.text(&buffer).map_err(
|
builder
|
||||||
|e| format!("{}: {}", filename, e),
|
.text(&buffer)
|
||||||
)?;
|
.map_err(|e| format!("{}: {}", filename, e))?;
|
||||||
Ok(builder.finish())
|
Ok(builder.finish())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,19 @@ function banner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Run rustfmt if we have it.
|
# Run rustfmt if we have it.
|
||||||
if $topdir/check-rustfmt.sh; then
|
|
||||||
banner "Rust formatting"
|
banner "Rust formatting"
|
||||||
$topdir/format-all.sh --write-mode=diff
|
if command -v rustfmt > /dev/null; then
|
||||||
|
# In newer versions of rustfmt, replace --write-mode=diff with --check.
|
||||||
|
if ! $topdir/format-all.sh --write-mode=diff ; then
|
||||||
|
echo "Formatting diffs detected! Run \"cargo fmt --all\" to correct."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "rustfmt not available; formatting not checked!"
|
||||||
|
echo
|
||||||
|
echo "If you are using rustup, rustfmt can be installed via"
|
||||||
|
echo "\"rustup component add --toolchain=stable rustfmt-preview\", or see"
|
||||||
|
echo "https://github.com/rust-lang-nursery/rustfmt for more information."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if any Python files have changed since we last checked them.
|
# Check if any Python files have changed since we last checked them.
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ fn main() {
|
|||||||
.arg("--out-dir")
|
.arg("--out-dir")
|
||||||
.arg(out_dir)
|
.arg(out_dir)
|
||||||
.status()
|
.status()
|
||||||
.expect(
|
.expect("Failed to launch second-level build script; is python installed?");
|
||||||
"Failed to launch second-level build script; is python installed?",
|
|
||||||
);
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
process::exit(status.code().unwrap());
|
process::exit(status.code().unwrap());
|
||||||
}
|
}
|
||||||
@@ -132,16 +130,14 @@ impl Isa {
|
|||||||
/// Returns isa targets to configure conditional compilation.
|
/// Returns isa targets to configure conditional compilation.
|
||||||
fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Vec<Isa>, String> {
|
fn isa_targets(cretonne_targets: Option<&str>, target_triple: &str) -> Result<Vec<Isa>, String> {
|
||||||
match cretonne_targets {
|
match cretonne_targets {
|
||||||
Some("native") => {
|
Some("native") => Isa::from_arch(target_triple.split('-').next().unwrap())
|
||||||
Isa::from_arch(target_triple.split('-').next().unwrap())
|
|
||||||
.map(|isa| vec![isa])
|
.map(|isa| vec![isa])
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"no supported isa found for target triple `{}`",
|
"no supported isa found for target triple `{}`",
|
||||||
target_triple
|
target_triple
|
||||||
)
|
)
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
Some(targets) => {
|
Some(targets) => {
|
||||||
let unknown_isa_targets = targets
|
let unknown_isa_targets = targets
|
||||||
.split(',')
|
.split(',')
|
||||||
|
|||||||
@@ -62,16 +62,14 @@ impl ValueConversion {
|
|||||||
ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
|
ValueConversion::IntSplit => ty.half_width().expect("Integer type too small to split"),
|
||||||
ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
|
ValueConversion::VectorSplit => ty.half_vector().expect("Not a vector"),
|
||||||
ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
|
ValueConversion::IntBits => Type::int(ty.bits()).expect("Bad integer size"),
|
||||||
ValueConversion::Sext(nty) |
|
ValueConversion::Sext(nty) | ValueConversion::Uext(nty) => nty,
|
||||||
ValueConversion::Uext(nty) => nty,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this a split conversion that results in two arguments?
|
/// Is this a split conversion that results in two arguments?
|
||||||
pub fn is_split(self) -> bool {
|
pub fn is_split(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
ValueConversion::IntSplit |
|
ValueConversion::IntSplit | ValueConversion::VectorSplit => true,
|
||||||
ValueConversion::VectorSplit => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use super::{Comparator, Forest, Node, NodeData, NodePool, Path, INNER_SIZE};
|
use super::{Comparator, Forest, Node, NodeData, NodePool, Path, INNER_SIZE};
|
||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
use std::marker::PhantomData;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
@@ -50,7 +50,9 @@ where
|
|||||||
{
|
{
|
||||||
/// Create a new empty forest.
|
/// Create a new empty forest.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { nodes: NodePool::new() }
|
Self {
|
||||||
|
nodes: NodePool::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all maps in the forest.
|
/// Clear all maps in the forest.
|
||||||
@@ -101,9 +103,9 @@ where
|
|||||||
|
|
||||||
/// Get the value stored for `key`.
|
/// Get the value stored for `key`.
|
||||||
pub fn get(&self, key: K, forest: &MapForest<K, V, C>, comp: &C) -> Option<V> {
|
pub fn get(&self, key: K, forest: &MapForest<K, V, C>, comp: &C) -> Option<V> {
|
||||||
self.root.expand().and_then(|root| {
|
self.root
|
||||||
Path::default().find(key, root, &forest.nodes, comp)
|
.expand()
|
||||||
})
|
.and_then(|root| Path::default().find(key, root, &forest.nodes, comp))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up the value stored for `key`.
|
/// Look up the value stored for `key`.
|
||||||
@@ -292,30 +294,30 @@ where
|
|||||||
///
|
///
|
||||||
/// If the cursor is already pointing at the first entry, leave it there and return `None`.
|
/// If the cursor is already pointing at the first entry, leave it there and return `None`.
|
||||||
pub fn prev(&mut self) -> Option<(K, V)> {
|
pub fn prev(&mut self) -> Option<(K, V)> {
|
||||||
self.root.expand().and_then(
|
self.root
|
||||||
|root| self.path.prev(root, self.pool),
|
.expand()
|
||||||
)
|
.and_then(|root| self.path.prev(root, self.pool))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current key, or `None` if the cursor is at the end.
|
/// Get the current key, or `None` if the cursor is at the end.
|
||||||
pub fn key(&self) -> Option<K> {
|
pub fn key(&self) -> Option<K> {
|
||||||
self.path.leaf_pos().and_then(|(node, entry)| {
|
self.path
|
||||||
self.pool[node].unwrap_leaf().0.get(entry).cloned()
|
.leaf_pos()
|
||||||
})
|
.and_then(|(node, entry)| self.pool[node].unwrap_leaf().0.get(entry).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current value, or `None` if the cursor is at the end.
|
/// Get the current value, or `None` if the cursor is at the end.
|
||||||
pub fn value(&self) -> Option<V> {
|
pub fn value(&self) -> Option<V> {
|
||||||
self.path.leaf_pos().and_then(|(node, entry)| {
|
self.path
|
||||||
self.pool[node].unwrap_leaf().1.get(entry).cloned()
|
.leaf_pos()
|
||||||
})
|
.and_then(|(node, entry)| self.pool[node].unwrap_leaf().1.get(entry).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the current value, or `None` if the cursor is at the end.
|
/// Get a mutable reference to the current value, or `None` if the cursor is at the end.
|
||||||
pub fn value_mut(&mut self) -> Option<&mut V> {
|
pub fn value_mut(&mut self) -> Option<&mut V> {
|
||||||
self.path.leaf_pos().and_then(move |(node, entry)| {
|
self.path
|
||||||
self.pool[node].unwrap_leaf_mut().1.get_mut(entry)
|
.leaf_pos()
|
||||||
})
|
.and_then(move |(node, entry)| self.pool[node].unwrap_leaf_mut().1.get_mut(entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move this cursor to `key`.
|
/// Move this cursor to `key`.
|
||||||
|
|||||||
@@ -362,7 +362,8 @@ impl<F: Forest> NodeData<F> {
|
|||||||
/// right sibling node is returned.
|
/// right sibling node is returned.
|
||||||
pub fn balance(&mut self, crit_key: F::Key, rhs: &mut Self) -> Option<F::Key> {
|
pub fn balance(&mut self, crit_key: F::Key, rhs: &mut Self) -> Option<F::Key> {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(&mut NodeData::Inner {
|
(
|
||||||
|
&mut NodeData::Inner {
|
||||||
size: ref mut l_size,
|
size: ref mut l_size,
|
||||||
keys: ref mut l_keys,
|
keys: ref mut l_keys,
|
||||||
tree: ref mut l_tree,
|
tree: ref mut l_tree,
|
||||||
@@ -371,7 +372,8 @@ impl<F: Forest> NodeData<F> {
|
|||||||
size: ref mut r_size,
|
size: ref mut r_size,
|
||||||
keys: ref mut r_keys,
|
keys: ref mut r_keys,
|
||||||
tree: ref mut r_tree,
|
tree: ref mut r_tree,
|
||||||
}) => {
|
},
|
||||||
|
) => {
|
||||||
let l_ents = usize::from(*l_size) + 1;
|
let l_ents = usize::from(*l_size) + 1;
|
||||||
let r_ents = usize::from(*r_size) + 1;
|
let r_ents = usize::from(*r_size) + 1;
|
||||||
let ents = l_ents + r_ents;
|
let ents = l_ents + r_ents;
|
||||||
@@ -408,7 +410,8 @@ impl<F: Forest> NodeData<F> {
|
|||||||
Some(new_crit)
|
Some(new_crit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(&mut NodeData::Leaf {
|
(
|
||||||
|
&mut NodeData::Leaf {
|
||||||
size: ref mut l_size,
|
size: ref mut l_size,
|
||||||
keys: ref mut l_keys,
|
keys: ref mut l_keys,
|
||||||
vals: ref mut l_vals,
|
vals: ref mut l_vals,
|
||||||
@@ -417,7 +420,8 @@ impl<F: Forest> NodeData<F> {
|
|||||||
size: ref mut r_size,
|
size: ref mut r_size,
|
||||||
keys: ref mut r_keys,
|
keys: ref mut r_keys,
|
||||||
vals: ref mut r_vals,
|
vals: ref mut r_vals,
|
||||||
}) => {
|
},
|
||||||
|
) => {
|
||||||
let l_ents = usize::from(*l_size);
|
let l_ents = usize::from(*l_size);
|
||||||
let l_keys = l_keys.borrow_mut();
|
let l_keys = l_keys.borrow_mut();
|
||||||
let l_vals = l_vals.borrow_mut();
|
let l_vals = l_vals.borrow_mut();
|
||||||
@@ -677,11 +681,7 @@ mod test {
|
|||||||
assert!(leaf.try_leaf_insert(2, 'c', SetValue()));
|
assert!(leaf.try_leaf_insert(2, 'c', SetValue()));
|
||||||
assert_eq!(leaf.to_string(), "[ a b c d ]");
|
assert_eq!(leaf.to_string(), "[ a b c d ]");
|
||||||
for i in 4..15 {
|
for i in 4..15 {
|
||||||
assert!(leaf.try_leaf_insert(
|
assert!(leaf.try_leaf_insert(usize::from(i), ('a' as u8 + i) as char, SetValue()));
|
||||||
usize::from(i),
|
|
||||||
('a' as u8 + i) as char,
|
|
||||||
SetValue(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
assert_eq!(leaf.to_string(), "[ a b c d e f g h i j k l m n o ]");
|
assert_eq!(leaf.to_string(), "[ a b c d e f g h i j k l m n o ]");
|
||||||
|
|
||||||
@@ -779,21 +779,13 @@ mod test {
|
|||||||
fn leaf_balance() {
|
fn leaf_balance() {
|
||||||
let mut lhs = NodeData::<TF>::leaf('a', SetValue());
|
let mut lhs = NodeData::<TF>::leaf('a', SetValue());
|
||||||
for i in 1..6 {
|
for i in 1..6 {
|
||||||
assert!(lhs.try_leaf_insert(
|
assert!(lhs.try_leaf_insert(usize::from(i), ('a' as u8 + i) as char, SetValue()));
|
||||||
usize::from(i),
|
|
||||||
('a' as u8 + i) as char,
|
|
||||||
SetValue(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
assert_eq!(lhs.to_string(), "[ a b c d e f ]");
|
assert_eq!(lhs.to_string(), "[ a b c d e f ]");
|
||||||
|
|
||||||
let mut rhs = NodeData::<TF>::leaf('0', SetValue());
|
let mut rhs = NodeData::<TF>::leaf('0', SetValue());
|
||||||
for i in 1..8 {
|
for i in 1..8 {
|
||||||
assert!(rhs.try_leaf_insert(
|
assert!(rhs.try_leaf_insert(usize::from(i), ('0' as u8 + i) as char, SetValue()));
|
||||||
usize::from(i),
|
|
||||||
('0' as u8 + i) as char,
|
|
||||||
SetValue(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
assert_eq!(rhs.to_string(), "[ 0 1 2 3 4 5 6 7 ]");
|
assert_eq!(rhs.to_string(), "[ 0 1 2 3 4 5 6 7 ]");
|
||||||
|
|
||||||
|
|||||||
@@ -303,9 +303,9 @@ impl<F: Forest> Path<F> {
|
|||||||
// When inserting into an inner node (`ins_node.is_some()`), we must point to a valid
|
// When inserting into an inner node (`ins_node.is_some()`), we must point to a valid
|
||||||
// entry in the current node since the new entry is inserted *after* the insert
|
// entry in the current node since the new entry is inserted *after* the insert
|
||||||
// location.
|
// location.
|
||||||
if entry > split.lhs_entries ||
|
if entry > split.lhs_entries
|
||||||
(entry == split.lhs_entries &&
|
|| (entry == split.lhs_entries
|
||||||
(split.lhs_entries > split.rhs_entries || ins_node.is_some()))
|
&& (split.lhs_entries > split.rhs_entries || ins_node.is_some()))
|
||||||
{
|
{
|
||||||
node = rhs_node;
|
node = rhs_node;
|
||||||
entry -= split.lhs_entries;
|
entry -= split.lhs_entries;
|
||||||
@@ -406,7 +406,9 @@ impl<F: Forest> Path<F> {
|
|||||||
let crit_node = self.node[crit_level];
|
let crit_node = self.node[crit_level];
|
||||||
|
|
||||||
match pool[crit_node] {
|
match pool[crit_node] {
|
||||||
NodeData::Inner { size, ref mut keys, .. } => {
|
NodeData::Inner {
|
||||||
|
size, ref mut keys, ..
|
||||||
|
} => {
|
||||||
debug_assert!(crit_kidx < size);
|
debug_assert!(crit_kidx < size);
|
||||||
keys[usize::from(crit_kidx)] = crit_key;
|
keys[usize::from(crit_kidx)] = crit_key;
|
||||||
}
|
}
|
||||||
@@ -436,7 +438,10 @@ impl<F: Forest> Path<F> {
|
|||||||
|
|
||||||
// Discard the root node if it has shrunk to a single sub-tree.
|
// Discard the root node if it has shrunk to a single sub-tree.
|
||||||
let mut ns = 0;
|
let mut ns = 0;
|
||||||
while let NodeData::Inner { size: 0, ref tree, .. } = pool[self.node[ns]] {
|
while let NodeData::Inner {
|
||||||
|
size: 0, ref tree, ..
|
||||||
|
} = pool[self.node[ns]]
|
||||||
|
{
|
||||||
ns += 1;
|
ns += 1;
|
||||||
self.node[ns] = tree[0];
|
self.node[ns] = tree[0];
|
||||||
}
|
}
|
||||||
@@ -616,9 +621,8 @@ impl<F: Forest> Path<F> {
|
|||||||
|
|
||||||
/// Update the critical key for the right sibling node at `level`.
|
/// Update the critical key for the right sibling node at `level`.
|
||||||
fn update_right_crit_key(&self, level: usize, crit_key: F::Key, pool: &mut NodePool<F>) {
|
fn update_right_crit_key(&self, level: usize, crit_key: F::Key, pool: &mut NodePool<F>) {
|
||||||
let bl = self.right_sibling_branch_level(level, pool).expect(
|
let bl = self.right_sibling_branch_level(level, pool)
|
||||||
"No right sibling exists",
|
.expect("No right sibling exists");
|
||||||
);
|
|
||||||
match pool[self.node[bl]] {
|
match pool[self.node[bl]] {
|
||||||
NodeData::Inner { ref mut keys, .. } => {
|
NodeData::Inner { ref mut keys, .. } => {
|
||||||
keys[usize::from(self.entry[bl])] = crit_key;
|
keys[usize::from(self.entry[bl])] = crit_key;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use super::{Forest, Node, NodeData};
|
use super::{Forest, Node, NodeData};
|
||||||
use entity::PrimaryMap;
|
use entity::PrimaryMap;
|
||||||
use std::ops::{Index, IndexMut};
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
/// A pool of nodes, including a free list.
|
/// A pool of nodes, including a free list.
|
||||||
pub(super) struct NodePool<F: Forest> {
|
pub(super) struct NodePool<F: Forest> {
|
||||||
@@ -51,7 +51,9 @@ impl<F: Forest> NodePool<F> {
|
|||||||
pub fn free_node(&mut self, node: Node) {
|
pub fn free_node(&mut self, node: Node) {
|
||||||
// Quick check for a double free.
|
// Quick check for a double free.
|
||||||
debug_assert!(!self.nodes[node].is_free(), "{} is already free", node);
|
debug_assert!(!self.nodes[node].is_free(), "{} is already free", node);
|
||||||
self.nodes[node] = NodeData::Free { next: self.freelist };
|
self.nodes[node] = NodeData::Free {
|
||||||
|
next: self.freelist,
|
||||||
|
};
|
||||||
self.freelist = Some(node);
|
self.freelist = Some(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use super::{Comparator, Forest, Node, NodeData, NodePool, Path, SetValue, INNER_SIZE};
|
use super::{Comparator, Forest, Node, NodeData, NodePool, Path, SetValue, INNER_SIZE};
|
||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
use std::marker::PhantomData;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
@@ -47,7 +47,9 @@ where
|
|||||||
{
|
{
|
||||||
/// Create a new empty forest.
|
/// Create a new empty forest.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { nodes: NodePool::new() }
|
Self {
|
||||||
|
nodes: NodePool::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all sets in the forest.
|
/// Clear all sets in the forest.
|
||||||
@@ -232,16 +234,16 @@ where
|
|||||||
///
|
///
|
||||||
/// If the cursor is already pointing at the first element, leave it there and return `None`.
|
/// If the cursor is already pointing at the first element, leave it there and return `None`.
|
||||||
pub fn prev(&mut self) -> Option<K> {
|
pub fn prev(&mut self) -> Option<K> {
|
||||||
self.root.expand().and_then(|root| {
|
self.root
|
||||||
self.path.prev(root, self.pool).map(|(k, _)| k)
|
.expand()
|
||||||
})
|
.and_then(|root| self.path.prev(root, self.pool).map(|(k, _)| k))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current element, or `None` if the cursor is at the end.
|
/// Get the current element, or `None` if the cursor is at the end.
|
||||||
pub fn elem(&self) -> Option<K> {
|
pub fn elem(&self) -> Option<K> {
|
||||||
self.path.leaf_pos().and_then(|(node, entry)| {
|
self.path
|
||||||
self.pool[node].unwrap_leaf().0.get(entry).cloned()
|
.leaf_pos()
|
||||||
})
|
.and_then(|(node, entry)| self.pool[node].unwrap_leaf().0.get(entry).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move this cursor to `elem`.
|
/// Move this cursor to `elem`.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod memorysink;
|
|||||||
mod relaxation;
|
mod relaxation;
|
||||||
mod shrink;
|
mod shrink;
|
||||||
|
|
||||||
pub use self::memorysink::{MemoryCodeSink, RelocSink, TrapSink, NullTrapSink};
|
pub use self::memorysink::{MemoryCodeSink, NullTrapSink, RelocSink, TrapSink};
|
||||||
pub use self::relaxation::relax_branches;
|
pub use self::relaxation::relax_branches;
|
||||||
pub use self::shrink::shrink_instructions;
|
pub use self::shrink::shrink_instructions;
|
||||||
pub use regalloc::RegDiversions;
|
pub use regalloc::RegDiversions;
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> Result<CodeOffset
|
|||||||
let dest_offset = cur.func.offsets[dest];
|
let dest_offset = cur.func.offsets[dest];
|
||||||
// This could be an out-of-range branch.
|
// This could be an out-of-range branch.
|
||||||
// Relax it unless the destination offset has not been computed yet.
|
// Relax it unless the destination offset has not been computed yet.
|
||||||
if !range.contains(offset, dest_offset) &&
|
if !range.contains(offset, dest_offset)
|
||||||
(dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
|
&& (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
|
||||||
{
|
{
|
||||||
offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa);
|
offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa);
|
||||||
continue;
|
continue;
|
||||||
@@ -148,14 +148,14 @@ fn relax_branch(
|
|||||||
// Pick the first encoding that can handle the branch range.
|
// Pick the first encoding that can handle the branch range.
|
||||||
let dfg = &cur.func.dfg;
|
let dfg = &cur.func.dfg;
|
||||||
let ctrl_type = dfg.ctrl_typevar(inst);
|
let ctrl_type = dfg.ctrl_typevar(inst);
|
||||||
if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type).find(
|
if let Some(enc) = isa.legal_encodings(cur.func, &dfg[inst], ctrl_type)
|
||||||
|&enc| {
|
.find(|&enc| {
|
||||||
let range = encinfo.branch_range(enc).expect("Branch with no range");
|
let range = encinfo.branch_range(enc).expect("Branch with no range");
|
||||||
if !range.contains(offset, dest_offset) {
|
if !range.contains(offset, dest_offset) {
|
||||||
dbg!(" trying [{}]: out of range", encinfo.display(enc));
|
dbg!(" trying [{}]: out of range", encinfo.display(enc));
|
||||||
false
|
false
|
||||||
} else if encinfo.operand_constraints(enc) !=
|
} else if encinfo.operand_constraints(enc)
|
||||||
encinfo.operand_constraints(cur.func.encodings[inst])
|
!= encinfo.operand_constraints(cur.func.encodings[inst])
|
||||||
{
|
{
|
||||||
// Conservatively give up if the encoding has different constraints
|
// Conservatively give up if the encoding has different constraints
|
||||||
// than the original, so that we don't risk picking a new encoding
|
// than the original, so that we don't risk picking a new encoding
|
||||||
@@ -168,9 +168,7 @@ fn relax_branch(
|
|||||||
dbg!(" trying [{}]: OK", encinfo.display(enc));
|
dbg!(" trying [{}]: OK", encinfo.display(enc));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
},
|
}) {
|
||||||
)
|
|
||||||
{
|
|
||||||
cur.func.encodings[inst] = enc;
|
cur.func.encodings[inst] = enc;
|
||||||
return encinfo.bytes(enc);
|
return encinfo.bytes(enc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
|
|||||||
|
|
||||||
// Pick the last encoding with constraints that are satisfied.
|
// Pick the last encoding with constraints that are satisfied.
|
||||||
let best_enc = isa.legal_encodings(func, &func.dfg[inst], ctrl_type)
|
let best_enc = isa.legal_encodings(func, &func.dfg[inst], ctrl_type)
|
||||||
.filter(|e| {
|
.filter(|e| encinfo.constraints[e.recipe()].satisfied(inst, &divert, &func))
|
||||||
encinfo.constraints[e.recipe()].satisfied(inst, &divert, &func)
|
|
||||||
})
|
|
||||||
.min_by_key(|e| encinfo.bytes(*e))
|
.min_by_key(|e| encinfo.bytes(*e))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -41,7 +39,6 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
|
|||||||
encinfo.bytes(best_enc)
|
encinfo.bytes(best_enc)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
divert.apply(&func.dfg[inst]);
|
divert.apply(&func.dfg[inst]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,8 +107,9 @@ mod tests {
|
|||||||
|
|
||||||
let s4 = BitSet::<u16>(4 | 8 | 256 | 1024);
|
let s4 = BitSet::<u16>(4 | 8 | 256 | 1024);
|
||||||
assert!(
|
assert!(
|
||||||
!s4.contains(0) && !s4.contains(1) && !s4.contains(4) && !s4.contains(5) &&
|
!s4.contains(0) && !s4.contains(1) && !s4.contains(4) && !s4.contains(5)
|
||||||
!s4.contains(6) && !s4.contains(7) && !s4.contains(9) && !s4.contains(11)
|
&& !s4.contains(6) && !s4.contains(7) && !s4.contains(9)
|
||||||
|
&& !s4.contains(11)
|
||||||
);
|
);
|
||||||
assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10));
|
assert!(s4.contains(2) && s4.contains(3) && s4.contains(8) && s4.contains(10));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ use preopt::do_preopt;
|
|||||||
use regalloc;
|
use regalloc;
|
||||||
use result::{CtonError, CtonResult};
|
use result::{CtonError, CtonResult};
|
||||||
use settings::{FlagsOrIsa, OptLevel};
|
use settings::{FlagsOrIsa, OptLevel};
|
||||||
use std::vec::Vec;
|
|
||||||
use simple_gvn::do_simple_gvn;
|
use simple_gvn::do_simple_gvn;
|
||||||
|
use std::vec::Vec;
|
||||||
use timing;
|
use timing;
|
||||||
use unreachable_code::eliminate_unreachable_code;
|
use unreachable_code::eliminate_unreachable_code;
|
||||||
use verifier;
|
use verifier;
|
||||||
@@ -251,11 +251,8 @@ impl Context {
|
|||||||
|
|
||||||
/// Compute the loop analysis.
|
/// Compute the loop analysis.
|
||||||
pub fn compute_loop_analysis(&mut self) {
|
pub fn compute_loop_analysis(&mut self) {
|
||||||
self.loop_analysis.compute(
|
self.loop_analysis
|
||||||
&self.func,
|
.compute(&self.func, &self.cfg, &self.domtree)
|
||||||
&self.cfg,
|
|
||||||
&self.domtree,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the control flow graph and dominator tree.
|
/// Compute the control flow graph and dominator tree.
|
||||||
@@ -292,12 +289,8 @@ impl Context {
|
|||||||
|
|
||||||
/// Run the register allocator.
|
/// Run the register allocator.
|
||||||
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
|
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
|
||||||
self.regalloc.run(
|
self.regalloc
|
||||||
isa,
|
.run(isa, &mut self.func, &self.cfg, &mut self.domtree)
|
||||||
&mut self.func,
|
|
||||||
&self.cfg,
|
|
||||||
&mut self.domtree,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert prologue and epilogues after computing the stack frame layout.
|
/// Insert prologue and epilogues after computing the stack frame layout.
|
||||||
|
|||||||
@@ -246,9 +246,11 @@ pub trait Cursor {
|
|||||||
let new_pos = if let Some(next) = self.layout().next_inst(inst) {
|
let new_pos = if let Some(next) = self.layout().next_inst(inst) {
|
||||||
CursorPosition::At(next)
|
CursorPosition::At(next)
|
||||||
} else {
|
} else {
|
||||||
CursorPosition::After(self.layout().inst_ebb(inst).expect(
|
CursorPosition::After(
|
||||||
"current instruction removed?",
|
self.layout()
|
||||||
))
|
.inst_ebb(inst)
|
||||||
|
.expect("current instruction removed?"),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
self.set_position(new_pos);
|
self.set_position(new_pos);
|
||||||
}
|
}
|
||||||
@@ -413,9 +415,11 @@ pub trait Cursor {
|
|||||||
self.set_position(At(next));
|
self.set_position(At(next));
|
||||||
Some(next)
|
Some(next)
|
||||||
} else {
|
} else {
|
||||||
let pos = After(self.layout().inst_ebb(inst).expect(
|
let pos = After(
|
||||||
"current instruction removed?",
|
self.layout()
|
||||||
));
|
.inst_ebb(inst)
|
||||||
|
.expect("current instruction removed?"),
|
||||||
|
);
|
||||||
self.set_position(pos);
|
self.set_position(pos);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -465,9 +469,11 @@ pub trait Cursor {
|
|||||||
self.set_position(At(prev));
|
self.set_position(At(prev));
|
||||||
Some(prev)
|
Some(prev)
|
||||||
} else {
|
} else {
|
||||||
let pos = Before(self.layout().inst_ebb(inst).expect(
|
let pos = Before(
|
||||||
"current instruction removed?",
|
self.layout()
|
||||||
));
|
.inst_ebb(inst)
|
||||||
|
.expect("current instruction removed?"),
|
||||||
|
);
|
||||||
self.set_position(pos);
|
self.set_position(pos);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -746,11 +752,9 @@ impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut EncCursor<'f> {
|
|||||||
// Assign an encoding.
|
// Assign an encoding.
|
||||||
// XXX Is there a way to describe this error to the user?
|
// XXX Is there a way to describe this error to the user?
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
#[cfg_attr(feature = "cargo-clippy", allow(match_wild_err_arm))]
|
||||||
match self.isa.encode(
|
match self.isa
|
||||||
&self.func,
|
.encode(&self.func, &self.func.dfg[inst], ctrl_typevar)
|
||||||
&self.func.dfg[inst],
|
{
|
||||||
ctrl_typevar,
|
|
||||||
) {
|
|
||||||
Ok(e) => self.func.encodings[inst] = e,
|
Ok(e) => self.func.encodings[inst] = e,
|
||||||
Err(_) => panic!("can't encode {}", self.display_inst(inst)),
|
Err(_) => panic!("can't encode {}", self.display_inst(inst)),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ macro_rules! dbg {
|
|||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! dbg {
|
macro_rules! dbg {
|
||||||
($($arg:tt)+) => {}
|
($($arg:tt)+) => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for printing lists.
|
/// Helper for printing lists.
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ use timing;
|
|||||||
|
|
||||||
/// Test whether the given opcode is unsafe to even consider for DCE.
|
/// Test whether the given opcode is unsafe to even consider for DCE.
|
||||||
fn trivially_unsafe_for_dce(opcode: Opcode) -> bool {
|
fn trivially_unsafe_for_dce(opcode: Opcode) -> bool {
|
||||||
opcode.is_call() || opcode.is_branch() || opcode.is_terminator() ||
|
opcode.is_call() || opcode.is_branch() || opcode.is_terminator() || opcode.is_return()
|
||||||
opcode.is_return() || opcode.can_trap() || opcode.other_side_effects() ||
|
|| opcode.can_trap() || opcode.other_side_effects() || opcode.can_store()
|
||||||
opcode.can_store()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preserve instructions with used result values.
|
/// Preserve instructions with used result values.
|
||||||
@@ -51,9 +50,8 @@ pub fn do_dce(func: &mut Function, domtree: &mut DominatorTree) {
|
|||||||
{
|
{
|
||||||
let data = &pos.func.dfg[inst];
|
let data = &pos.func.dfg[inst];
|
||||||
let opcode = data.opcode();
|
let opcode = data.opcode();
|
||||||
if trivially_unsafe_for_dce(opcode) ||
|
if trivially_unsafe_for_dce(opcode) || is_load_with_defined_trapping(opcode, &data)
|
||||||
is_load_with_defined_trapping(opcode, &data) ||
|
|| any_inst_results_used(inst, &live, &pos.func.dfg)
|
||||||
any_inst_results_used(inst, &live, &pos.func.dfg)
|
|
||||||
{
|
{
|
||||||
for arg in pos.func.dfg.inst_args(inst) {
|
for arg in pos.func.dfg.inst_args(inst) {
|
||||||
let v = pos.func.dfg.resolve_aliases(*arg);
|
let v = pos.func.dfg.resolve_aliases(*arg);
|
||||||
|
|||||||
@@ -101,9 +101,8 @@ impl DominatorTree {
|
|||||||
{
|
{
|
||||||
let a = a.into();
|
let a = a.into();
|
||||||
let b = b.into();
|
let b = b.into();
|
||||||
self.rpo_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b)).then(
|
self.rpo_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b))
|
||||||
layout.cmp(a, b),
|
.then(layout.cmp(a, b))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `a` dominates `b`.
|
/// Returns `true` if `a` dominates `b`.
|
||||||
@@ -145,9 +144,7 @@ impl DominatorTree {
|
|||||||
let (mut ebb_b, mut inst_b) = match b.into() {
|
let (mut ebb_b, mut inst_b) = match b.into() {
|
||||||
ExpandedProgramPoint::Ebb(ebb) => (ebb, None),
|
ExpandedProgramPoint::Ebb(ebb) => (ebb, None),
|
||||||
ExpandedProgramPoint::Inst(inst) => (
|
ExpandedProgramPoint::Inst(inst) => (
|
||||||
layout.inst_ebb(inst).expect(
|
layout.inst_ebb(inst).expect("Instruction not in layout."),
|
||||||
"Instruction not in layout.",
|
|
||||||
),
|
|
||||||
Some(inst),
|
Some(inst),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -163,7 +160,11 @@ impl DominatorTree {
|
|||||||
ebb_b = layout.inst_ebb(idom).expect("Dominator got removed.");
|
ebb_b = layout.inst_ebb(idom).expect("Dominator got removed.");
|
||||||
inst_b = Some(idom);
|
inst_b = Some(idom);
|
||||||
}
|
}
|
||||||
if a == ebb_b { inst_b } else { None }
|
if a == ebb_b {
|
||||||
|
inst_b
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the common dominator of two basic blocks.
|
/// Compute the common dominator of two basic blocks.
|
||||||
@@ -418,14 +419,13 @@ impl DominatorTree {
|
|||||||
// Get an iterator with just the reachable, already visited predecessors to `ebb`.
|
// Get an iterator with just the reachable, already visited predecessors to `ebb`.
|
||||||
// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
|
// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
|
||||||
// been visited yet, 0 for unreachable blocks.
|
// been visited yet, 0 for unreachable blocks.
|
||||||
let mut reachable_preds = cfg.pred_iter(ebb).filter(|&(pred, _)| {
|
let mut reachable_preds = cfg.pred_iter(ebb)
|
||||||
self.nodes[pred].rpo_number > 1
|
.filter(|&(pred, _)| self.nodes[pred].rpo_number > 1);
|
||||||
});
|
|
||||||
|
|
||||||
// The RPO must visit at least one predecessor before this node.
|
// The RPO must visit at least one predecessor before this node.
|
||||||
let mut idom = reachable_preds.next().expect(
|
let mut idom = reachable_preds
|
||||||
"EBB node must have one reachable predecessor",
|
.next()
|
||||||
);
|
.expect("EBB node must have one reachable predecessor");
|
||||||
|
|
||||||
for pred in reachable_preds {
|
for pred in reachable_preds {
|
||||||
idom = self.common_dominator(idom, pred, layout);
|
idom = self.common_dominator(idom, pred, layout);
|
||||||
@@ -450,8 +450,7 @@ impl DominatorTree {
|
|||||||
}
|
}
|
||||||
// We use the RPO comparison on the postorder list so we invert the operands of the
|
// We use the RPO comparison on the postorder list so we invert the operands of the
|
||||||
// comparison
|
// comparison
|
||||||
let old_ebb_postorder_index =
|
let old_ebb_postorder_index = self.postorder
|
||||||
self.postorder
|
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.binary_search_by(|probe| self.rpo_cmp_ebb(old_ebb, *probe))
|
.binary_search_by(|probe| self.rpo_cmp_ebb(old_ebb, *probe))
|
||||||
.expect("the old ebb is not declared to the dominator tree");
|
.expect("the old ebb is not declared to the dominator tree");
|
||||||
@@ -471,11 +470,10 @@ impl DominatorTree {
|
|||||||
// If there is no gaps in RPo numbers to insert this new number, we iterate
|
// If there is no gaps in RPo numbers to insert this new number, we iterate
|
||||||
// forward in RPO numbers and backwards in the postorder list of EBBs, renumbering the Ebbs
|
// forward in RPO numbers and backwards in the postorder list of EBBs, renumbering the Ebbs
|
||||||
// until we find a gap
|
// until we find a gap
|
||||||
for (¤t_ebb, current_rpo) in
|
for (¤t_ebb, current_rpo) in self.postorder[0..ebb_postorder_index]
|
||||||
self.postorder[0..ebb_postorder_index].iter().rev().zip(
|
.iter()
|
||||||
inserted_rpo_number +
|
.rev()
|
||||||
1..,
|
.zip(inserted_rpo_number + 1..)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if self.nodes[current_ebb].rpo_number < current_rpo {
|
if self.nodes[current_ebb].rpo_number < current_rpo {
|
||||||
// There is no gap, we renumber
|
// There is no gap, we renumber
|
||||||
@@ -644,9 +642,8 @@ impl DominatorTreePreorder {
|
|||||||
{
|
{
|
||||||
let a = a.into();
|
let a = a.into();
|
||||||
let b = b.into();
|
let b = b.into();
|
||||||
self.pre_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b)).then(
|
self.pre_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b))
|
||||||
layout.cmp(a, b),
|
.then(layout.cmp(a, b))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare two value defs according to the dominator tree pre-order.
|
/// Compare two value defs according to the dominator tree pre-order.
|
||||||
@@ -658,9 +655,8 @@ impl DominatorTreePreorder {
|
|||||||
pub fn pre_cmp_def(&self, a: Value, b: Value, func: &Function) -> Ordering {
|
pub fn pre_cmp_def(&self, a: Value, b: Value, func: &Function) -> Ordering {
|
||||||
let da = func.dfg.value_def(a);
|
let da = func.dfg.value_def(a);
|
||||||
let db = func.dfg.value_def(b);
|
let db = func.dfg.value_def(b);
|
||||||
self.pre_cmp(da, db, &func.layout).then_with(
|
self.pre_cmp(da, db, &func.layout)
|
||||||
|| da.num().cmp(&db.num()),
|
.then_with(|| da.num().cmp(&db.num()))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,10 +128,9 @@ impl ControlFlowGraph {
|
|||||||
// our iteration over successors.
|
// our iteration over successors.
|
||||||
let mut successors = mem::replace(&mut self.data[ebb].successors, Default::default());
|
let mut successors = mem::replace(&mut self.data[ebb].successors, Default::default());
|
||||||
for succ in successors.iter(&self.succ_forest) {
|
for succ in successors.iter(&self.succ_forest) {
|
||||||
self.data[succ].predecessors.retain(
|
self.data[succ]
|
||||||
&mut self.pred_forest,
|
.predecessors
|
||||||
|_, &mut e| e != ebb,
|
.retain(&mut self.pred_forest, |_, &mut e| e != ebb);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
successors.clear(&mut self.succ_forest);
|
successors.clear(&mut self.succ_forest);
|
||||||
}
|
}
|
||||||
@@ -149,17 +148,12 @@ impl ControlFlowGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_edge(&mut self, from: BasicBlock, to: Ebb) {
|
fn add_edge(&mut self, from: BasicBlock, to: Ebb) {
|
||||||
self.data[from.0].successors.insert(
|
self.data[from.0]
|
||||||
to,
|
.successors
|
||||||
&mut self.succ_forest,
|
.insert(to, &mut self.succ_forest, &());
|
||||||
&(),
|
self.data[to]
|
||||||
);
|
.predecessors
|
||||||
self.data[to].predecessors.insert(
|
.insert(from.1, from.0, &mut self.pred_forest, &());
|
||||||
from.1,
|
|
||||||
from.0,
|
|
||||||
&mut self.pred_forest,
|
|
||||||
&(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator over the CFG predecessors to `ebb`.
|
/// Get an iterator over the CFG predecessors to `ebb`.
|
||||||
|
|||||||
@@ -166,9 +166,9 @@ impl DataFlowGraph {
|
|||||||
/// Get the type of a value.
|
/// Get the type of a value.
|
||||||
pub fn value_type(&self, v: Value) -> Type {
|
pub fn value_type(&self, v: Value) -> Type {
|
||||||
match self.values[v] {
|
match self.values[v] {
|
||||||
ValueData::Inst { ty, .. } |
|
ValueData::Inst { ty, .. }
|
||||||
ValueData::Param { ty, .. } |
|
| ValueData::Param { ty, .. }
|
||||||
ValueData::Alias { ty, .. } => ty,
|
| ValueData::Alias { ty, .. } => ty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,11 +235,9 @@ impl DataFlowGraph {
|
|||||||
// This also avoids the creation of loops.
|
// This also avoids the creation of loops.
|
||||||
let original = self.resolve_aliases(src);
|
let original = self.resolve_aliases(src);
|
||||||
debug_assert_ne!(
|
debug_assert_ne!(
|
||||||
dest,
|
dest, original,
|
||||||
original,
|
|
||||||
"Aliasing {} to {} would create a loop",
|
"Aliasing {} to {} would create a loop",
|
||||||
dest,
|
dest, src
|
||||||
src
|
|
||||||
);
|
);
|
||||||
let ty = self.value_type(original);
|
let ty = self.value_type(original);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
@@ -267,8 +265,7 @@ impl DataFlowGraph {
|
|||||||
///
|
///
|
||||||
pub fn replace_with_aliases(&mut self, dest_inst: Inst, src_inst: Inst) {
|
pub fn replace_with_aliases(&mut self, dest_inst: Inst, src_inst: Inst) {
|
||||||
debug_assert_ne!(
|
debug_assert_ne!(
|
||||||
dest_inst,
|
dest_inst, src_inst,
|
||||||
src_inst,
|
|
||||||
"Replacing {} with itself would create a loop",
|
"Replacing {} with itself would create a loop",
|
||||||
dest_inst
|
dest_inst
|
||||||
);
|
);
|
||||||
@@ -342,8 +339,7 @@ impl ValueDef {
|
|||||||
/// this value.
|
/// this value.
|
||||||
pub fn num(self) -> usize {
|
pub fn num(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
ValueDef::Result(_, n) |
|
ValueDef::Result(_, n) | ValueDef::Param(_, n) => n,
|
||||||
ValueDef::Param(_, n) => n,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,9 +570,9 @@ impl DataFlowGraph {
|
|||||||
///
|
///
|
||||||
/// Panics if the instruction doesn't support arguments.
|
/// Panics if the instruction doesn't support arguments.
|
||||||
pub fn append_inst_arg(&mut self, inst: Inst, new_arg: Value) {
|
pub fn append_inst_arg(&mut self, inst: Inst, new_arg: Value) {
|
||||||
let mut branch_values = self.insts[inst].take_value_list().expect(
|
let mut branch_values = self.insts[inst]
|
||||||
"the instruction doesn't have value arguments",
|
.take_value_list()
|
||||||
);
|
.expect("the instruction doesn't have value arguments");
|
||||||
branch_values.push(new_arg, &mut self.value_lists);
|
branch_values.push(new_arg, &mut self.value_lists);
|
||||||
self.insts[inst].put_value_list(branch_values)
|
self.insts[inst].put_value_list(branch_values)
|
||||||
}
|
}
|
||||||
@@ -585,9 +581,9 @@ impl DataFlowGraph {
|
|||||||
///
|
///
|
||||||
/// This function panics if the instruction doesn't have any result.
|
/// This function panics if the instruction doesn't have any result.
|
||||||
pub fn first_result(&self, inst: Inst) -> Value {
|
pub fn first_result(&self, inst: Inst) -> Value {
|
||||||
self.results[inst].first(&self.value_lists).expect(
|
self.results[inst]
|
||||||
"Instruction has no results",
|
.first(&self.value_lists)
|
||||||
)
|
.expect("Instruction has no results")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if `inst` has any result values currently.
|
/// Test if `inst` has any result values currently.
|
||||||
@@ -653,9 +649,11 @@ impl DataFlowGraph {
|
|||||||
} else if constraints.requires_typevar_operand() {
|
} else if constraints.requires_typevar_operand() {
|
||||||
// Not all instruction formats have a designated operand, but in that case
|
// Not all instruction formats have a designated operand, but in that case
|
||||||
// `requires_typevar_operand()` should never be true.
|
// `requires_typevar_operand()` should never be true.
|
||||||
self.value_type(self[inst].typevar_operand(&self.value_lists).expect(
|
self.value_type(
|
||||||
"Instruction format doesn't have a designated operand, bad opcode.",
|
self[inst]
|
||||||
))
|
.typevar_operand(&self.value_lists)
|
||||||
|
.expect("Instruction format doesn't have a designated operand, bad opcode."),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
self.value_type(self.first_result(inst))
|
self.value_type(self.first_result(inst))
|
||||||
}
|
}
|
||||||
@@ -721,13 +719,16 @@ impl DataFlowGraph {
|
|||||||
} else {
|
} else {
|
||||||
panic!("{} must be an EBB parameter", val);
|
panic!("{} must be an EBB parameter", val);
|
||||||
};
|
};
|
||||||
self.ebbs[ebb].params.swap_remove(
|
self.ebbs[ebb]
|
||||||
num as usize,
|
.params
|
||||||
&mut self.value_lists,
|
.swap_remove(num as usize, &mut self.value_lists);
|
||||||
);
|
|
||||||
if let Some(last_arg_val) = self.ebbs[ebb].params.get(num as usize, &self.value_lists) {
|
if let Some(last_arg_val) = self.ebbs[ebb].params.get(num as usize, &self.value_lists) {
|
||||||
// We update the position of the old last arg.
|
// We update the position of the old last arg.
|
||||||
if let ValueData::Param { num: ref mut old_num, .. } = self.values[last_arg_val] {
|
if let ValueData::Param {
|
||||||
|
num: ref mut old_num,
|
||||||
|
..
|
||||||
|
} = self.values[last_arg_val]
|
||||||
|
{
|
||||||
*old_num = num;
|
*old_num = num;
|
||||||
} else {
|
} else {
|
||||||
panic!("{} should be an Ebb parameter", last_arg_val);
|
panic!("{} should be an Ebb parameter", last_arg_val);
|
||||||
@@ -744,27 +745,25 @@ impl DataFlowGraph {
|
|||||||
} else {
|
} else {
|
||||||
panic!("{} must be an EBB parameter", val);
|
panic!("{} must be an EBB parameter", val);
|
||||||
};
|
};
|
||||||
self.ebbs[ebb].params.remove(
|
self.ebbs[ebb]
|
||||||
num as usize,
|
.params
|
||||||
&mut self.value_lists,
|
.remove(num as usize, &mut self.value_lists);
|
||||||
);
|
|
||||||
for index in num..(self.num_ebb_params(ebb) as u16) {
|
for index in num..(self.num_ebb_params(ebb) as u16) {
|
||||||
match self.values[self.ebbs[ebb]
|
match self.values[self.ebbs[ebb]
|
||||||
.params
|
.params
|
||||||
.get(index as usize, &self.value_lists)
|
.get(index as usize, &self.value_lists)
|
||||||
.unwrap()] {
|
.unwrap()]
|
||||||
|
{
|
||||||
ValueData::Param { ref mut num, .. } => {
|
ValueData::Param { ref mut num, .. } => {
|
||||||
*num -= 1;
|
*num -= 1;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => panic!(
|
||||||
panic!(
|
|
||||||
"{} must be an EBB parameter",
|
"{} must be an EBB parameter",
|
||||||
self.ebbs[ebb]
|
self.ebbs[ebb]
|
||||||
.params
|
.params
|
||||||
.get(index as usize, &self.value_lists)
|
.get(index as usize, &self.value_lists)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,7 +834,9 @@ struct EbbData {
|
|||||||
|
|
||||||
impl EbbData {
|
impl EbbData {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { params: ValueList::new() }
|
Self {
|
||||||
|
params: ValueList::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,9 +879,9 @@ impl DataFlowGraph {
|
|||||||
"this function is only for assigning types to previously invalid values"
|
"this function is only for assigning types to previously invalid values"
|
||||||
);
|
);
|
||||||
match self.values[v] {
|
match self.values[v] {
|
||||||
ValueData::Inst { ref mut ty, .. } |
|
ValueData::Inst { ref mut ty, .. }
|
||||||
ValueData::Param { ref mut ty, .. } |
|
| ValueData::Param { ref mut ty, .. }
|
||||||
ValueData::Alias { ref mut ty, .. } => *ty = t,
|
| ValueData::Alias { ref mut ty, .. } => *ty = t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ impl Ebb {
|
|||||||
///
|
///
|
||||||
/// This method is for use by the parser.
|
/// This method is for use by the parser.
|
||||||
pub fn with_number(n: u32) -> Option<Self> {
|
pub fn with_number(n: u32) -> Option<Self> {
|
||||||
if n < u32::MAX { Some(Ebb(n)) } else { None }
|
if n < u32::MAX {
|
||||||
|
Some(Ebb(n))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +128,11 @@ impl FuncRef {
|
|||||||
///
|
///
|
||||||
/// This method is for use by the parser.
|
/// This method is for use by the parser.
|
||||||
pub fn with_number(n: u32) -> Option<Self> {
|
pub fn with_number(n: u32) -> Option<Self> {
|
||||||
if n < u32::MAX { Some(FuncRef(n)) } else { None }
|
if n < u32::MAX {
|
||||||
|
Some(FuncRef(n))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +146,11 @@ impl SigRef {
|
|||||||
///
|
///
|
||||||
/// This method is for use by the parser.
|
/// This method is for use by the parser.
|
||||||
pub fn with_number(n: u32) -> Option<Self> {
|
pub fn with_number(n: u32) -> Option<Self> {
|
||||||
if n < u32::MAX { Some(SigRef(n)) } else { None }
|
if n < u32::MAX {
|
||||||
|
Some(SigRef(n))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +164,11 @@ impl Heap {
|
|||||||
///
|
///
|
||||||
/// This method is for use by the parser.
|
/// This method is for use by the parser.
|
||||||
pub fn with_number(n: u32) -> Option<Self> {
|
pub fn with_number(n: u32) -> Option<Self> {
|
||||||
if n < u32::MAX { Some(Heap(n)) } else { None }
|
if n < u32::MAX {
|
||||||
|
Some(Heap(n))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -384,8 +384,7 @@ mod tests {
|
|||||||
CallConv::SystemV,
|
CallConv::SystemV,
|
||||||
CallConv::WindowsFastcall,
|
CallConv::WindowsFastcall,
|
||||||
CallConv::Baldrdash,
|
CallConv::Baldrdash,
|
||||||
]
|
] {
|
||||||
{
|
|
||||||
assert_eq!(Ok(cc), cc.to_string().parse())
|
assert_eq!(Ok(cc), cc.to_string().parse())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use ir::{DataFlowGraph, ExternalName, Layout, Signature};
|
|||||||
use ir::{Ebb, ExtFuncData, FuncRef, GlobalVar, GlobalVarData, Heap, HeapData, JumpTable,
|
use ir::{Ebb, ExtFuncData, FuncRef, GlobalVar, GlobalVarData, Heap, HeapData, JumpTable,
|
||||||
JumpTableData, SigRef, StackSlot, StackSlotData};
|
JumpTableData, SigRef, StackSlot, StackSlotData};
|
||||||
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
||||||
use isa::{EncInfo, Legalize, TargetIsa, Encoding};
|
use isa::{EncInfo, Encoding, Legalize, TargetIsa};
|
||||||
use settings::CallConv;
|
use settings::CallConv;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use write::write_function;
|
use write::write_function;
|
||||||
@@ -151,9 +151,9 @@ impl Function {
|
|||||||
/// Returns the value of the last `purpose` parameter, or `None` if no such parameter exists.
|
/// Returns the value of the last `purpose` parameter, or `None` if no such parameter exists.
|
||||||
pub fn special_param(&self, purpose: ir::ArgumentPurpose) -> Option<ir::Value> {
|
pub fn special_param(&self, purpose: ir::ArgumentPurpose) -> Option<ir::Value> {
|
||||||
let entry = self.layout.entry_block().expect("Function is empty");
|
let entry = self.layout.entry_block().expect("Function is empty");
|
||||||
self.signature.special_param_index(purpose).map(|i| {
|
self.signature
|
||||||
self.dfg.ebb_params(entry)[i]
|
.special_param_index(purpose)
|
||||||
})
|
.map(|i| self.dfg.ebb_params(entry)[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator over the instructions in `ebb`, including offsets and encoded instruction
|
/// Get an iterator over the instructions in `ebb`, including offsets and encoded instruction
|
||||||
|
|||||||
@@ -192,10 +192,12 @@ impl FromStr for Uimm32 {
|
|||||||
|
|
||||||
// Parse a decimal or hexadecimal `Uimm32`, formatted as above.
|
// Parse a decimal or hexadecimal `Uimm32`, formatted as above.
|
||||||
fn from_str(s: &str) -> Result<Self, &'static str> {
|
fn from_str(s: &str) -> Result<Self, &'static str> {
|
||||||
parse_i64(s).and_then(|x| if 0 <= x && x <= i64::from(u32::MAX) {
|
parse_i64(s).and_then(|x| {
|
||||||
|
if 0 <= x && x <= i64::from(u32::MAX) {
|
||||||
Ok(Uimm32(x as u32))
|
Ok(Uimm32(x as u32))
|
||||||
} else {
|
} else {
|
||||||
Err("Uimm32 out of range")
|
Err("Uimm32 out of range")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,12 +261,12 @@ impl FromStr for Offset32 {
|
|||||||
if !(s.starts_with('-') || s.starts_with('+')) {
|
if !(s.starts_with('-') || s.starts_with('+')) {
|
||||||
return Err("Offset must begin with sign");
|
return Err("Offset must begin with sign");
|
||||||
}
|
}
|
||||||
parse_i64(s).and_then(|x| if i64::from(i32::MIN) <= x &&
|
parse_i64(s).and_then(|x| {
|
||||||
x <= i64::from(i32::MAX)
|
if i64::from(i32::MIN) <= x && x <= i64::from(i32::MAX) {
|
||||||
{
|
|
||||||
Ok(Self::new(x as i32))
|
Ok(Self::new(x as i32))
|
||||||
} else {
|
} else {
|
||||||
Err("Offset out of range")
|
Err("Offset out of range")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,8 +449,7 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result<u64, &'static str> {
|
|||||||
Err(_) => return Err("Bad exponent"),
|
Err(_) => return Err("Bad exponent"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => match ch.to_digit(16) {
|
||||||
match ch.to_digit(16) {
|
|
||||||
Some(digit) => {
|
Some(digit) => {
|
||||||
digits += 1;
|
digits += 1;
|
||||||
if digits > 16 {
|
if digits > 16 {
|
||||||
@@ -457,8 +458,7 @@ fn parse_float(s: &str, w: u8, t: u8) -> Result<u64, &'static str> {
|
|||||||
significand = (significand << 4) | u64::from(digit);
|
significand = (significand << 4) | u64::from(digit);
|
||||||
}
|
}
|
||||||
None => return Err("Invalid character"),
|
None => return Err("Invalid character"),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,9 +546,7 @@ impl Ieee32 {
|
|||||||
let n = n.into();
|
let n = n.into();
|
||||||
debug_assert!(n < 32);
|
debug_assert!(n < 32);
|
||||||
debug_assert!(23 + 1 - n < 32);
|
debug_assert!(23 + 1 - n < 32);
|
||||||
Self::with_bits(
|
Self::with_bits((1u32 << (32 - 1)) | Self::pow2(n - 1).0 | (1u32 << (23 + 1 - n)))
|
||||||
(1u32 << (32 - 1)) | Self::pow2(n - 1).0 | (1u32 << (23 + 1 - n)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return self negated.
|
/// Return self negated.
|
||||||
@@ -609,9 +607,7 @@ impl Ieee64 {
|
|||||||
let n = n.into();
|
let n = n.into();
|
||||||
debug_assert!(n < 64);
|
debug_assert!(n < 64);
|
||||||
debug_assert!(52 + 1 - n < 64);
|
debug_assert!(52 + 1 - n < 64);
|
||||||
Self::with_bits(
|
Self::with_bits((1u64 << (64 - 1)) | Self::pow2(n - 1).0 | (1u64 << (52 + 1 - n)))
|
||||||
(1u64 << (64 - 1)) | Self::pow2(n - 1).0 | (1u64 << (52 + 1 - n)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return self negated.
|
/// Return self negated.
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ impl InstructionData {
|
|||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
InstructionData::BranchFloat {
|
| InstructionData::BranchFloat {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
InstructionData::Branch {
|
| InstructionData::Branch {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
@@ -208,11 +208,11 @@ impl InstructionData {
|
|||||||
/// Multi-destination branches like `br_table` return `None`.
|
/// Multi-destination branches like `br_table` return `None`.
|
||||||
pub fn branch_destination(&self) -> Option<Ebb> {
|
pub fn branch_destination(&self) -> Option<Ebb> {
|
||||||
match *self {
|
match *self {
|
||||||
InstructionData::Jump { destination, .. } |
|
InstructionData::Jump { destination, .. }
|
||||||
InstructionData::Branch { destination, .. } |
|
| InstructionData::Branch { destination, .. }
|
||||||
InstructionData::BranchInt { destination, .. } |
|
| InstructionData::BranchInt { destination, .. }
|
||||||
InstructionData::BranchFloat { destination, .. } |
|
| InstructionData::BranchFloat { destination, .. }
|
||||||
InstructionData::BranchIcmp { destination, .. } => Some(destination),
|
| InstructionData::BranchIcmp { destination, .. } => Some(destination),
|
||||||
InstructionData::BranchTable { .. } => None,
|
InstructionData::BranchTable { .. } => None,
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_branch());
|
debug_assert!(!self.opcode().is_branch());
|
||||||
@@ -227,11 +227,26 @@ impl InstructionData {
|
|||||||
/// Multi-destination branches like `br_table` return `None`.
|
/// Multi-destination branches like `br_table` return `None`.
|
||||||
pub fn branch_destination_mut(&mut self) -> Option<&mut Ebb> {
|
pub fn branch_destination_mut(&mut self) -> Option<&mut Ebb> {
|
||||||
match *self {
|
match *self {
|
||||||
InstructionData::Jump { ref mut destination, .. } |
|
InstructionData::Jump {
|
||||||
InstructionData::Branch { ref mut destination, .. } |
|
ref mut destination,
|
||||||
InstructionData::BranchInt { ref mut destination, .. } |
|
..
|
||||||
InstructionData::BranchFloat { ref mut destination, .. } |
|
}
|
||||||
InstructionData::BranchIcmp { ref mut destination, .. } => Some(destination),
|
| InstructionData::Branch {
|
||||||
|
ref mut destination,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| InstructionData::BranchInt {
|
||||||
|
ref mut destination,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| InstructionData::BranchFloat {
|
||||||
|
ref mut destination,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| InstructionData::BranchIcmp {
|
||||||
|
ref mut destination,
|
||||||
|
..
|
||||||
|
} => Some(destination),
|
||||||
InstructionData::BranchTable { .. } => None,
|
InstructionData::BranchTable { .. } => None,
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_branch());
|
debug_assert!(!self.opcode().is_branch());
|
||||||
@@ -245,12 +260,12 @@ impl InstructionData {
|
|||||||
/// Any instruction that can call another function reveals its call signature here.
|
/// Any instruction that can call another function reveals its call signature here.
|
||||||
pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> {
|
pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
InstructionData::Call { func_ref, ref args, .. } => {
|
InstructionData::Call {
|
||||||
CallInfo::Direct(func_ref, args.as_slice(pool))
|
func_ref, ref args, ..
|
||||||
}
|
} => CallInfo::Direct(func_ref, args.as_slice(pool)),
|
||||||
InstructionData::CallIndirect { sig_ref, ref args, .. } => {
|
InstructionData::CallIndirect {
|
||||||
CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..])
|
sig_ref, ref args, ..
|
||||||
}
|
} => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]),
|
||||||
_ => {
|
_ => {
|
||||||
debug_assert!(!self.opcode().is_call());
|
debug_assert!(!self.opcode().is_call());
|
||||||
CallInfo::NotACall
|
CallInfo::NotACall
|
||||||
@@ -512,12 +527,16 @@ impl OperandConstraint {
|
|||||||
LaneOf => Bound(ctrl_type.lane_type()),
|
LaneOf => Bound(ctrl_type.lane_type()),
|
||||||
AsBool => Bound(ctrl_type.as_bool()),
|
AsBool => Bound(ctrl_type.as_bool()),
|
||||||
HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
|
HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")),
|
||||||
DoubleWidth => Bound(ctrl_type.double_width().expect(
|
DoubleWidth => Bound(
|
||||||
"invalid type for double_width",
|
ctrl_type
|
||||||
)),
|
.double_width()
|
||||||
HalfVector => Bound(ctrl_type.half_vector().expect(
|
.expect("invalid type for double_width"),
|
||||||
"invalid type for half_vector",
|
),
|
||||||
)),
|
HalfVector => Bound(
|
||||||
|
ctrl_type
|
||||||
|
.half_vector()
|
||||||
|
.expect("invalid type for half_vector"),
|
||||||
|
),
|
||||||
DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")),
|
DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ impl JumpTableData {
|
|||||||
|
|
||||||
/// Checks if any of the entries branch to `ebb`.
|
/// Checks if any of the entries branch to `ebb`.
|
||||||
pub fn branches_to(&self, ebb: Ebb) -> bool {
|
pub fn branches_to(&self, ebb: Ebb) -> bool {
|
||||||
self.table.iter().any(|target_ebb| {
|
self.table
|
||||||
target_ebb.expand() == Some(ebb)
|
.iter()
|
||||||
})
|
.any(|target_ebb| target_ebb.expand() == Some(ebb))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the whole table as a mutable slice.
|
/// Access the whole table as a mutable slice.
|
||||||
|
|||||||
@@ -90,7 +90,11 @@ fn midpoint(a: SequenceNumber, b: SequenceNumber) -> Option<SequenceNumber> {
|
|||||||
debug_assert!(a < b);
|
debug_assert!(a < b);
|
||||||
// Avoid integer overflow.
|
// Avoid integer overflow.
|
||||||
let m = a + (b - a) / 2;
|
let m = a + (b - a) / 2;
|
||||||
if m > a { Some(m) } else { None }
|
if m > a {
|
||||||
|
Some(m)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -178,9 +182,8 @@ impl Layout {
|
|||||||
/// Assign a valid sequence number to `inst` such that the numbers are still monotonic. This may
|
/// Assign a valid sequence number to `inst` such that the numbers are still monotonic. This may
|
||||||
/// require renumbering.
|
/// require renumbering.
|
||||||
fn assign_inst_seq(&mut self, inst: Inst) {
|
fn assign_inst_seq(&mut self, inst: Inst) {
|
||||||
let ebb = self.inst_ebb(inst).expect(
|
let ebb = self.inst_ebb(inst)
|
||||||
"inst must be inserted before assigning an seq",
|
.expect("inst must be inserted before assigning an seq");
|
||||||
);
|
|
||||||
|
|
||||||
// Get the sequence number immediately before `inst`.
|
// Get the sequence number immediately before `inst`.
|
||||||
let prev_seq = match self.insts[inst].prev.expand() {
|
let prev_seq = match self.insts[inst].prev.expand() {
|
||||||
@@ -566,9 +569,8 @@ impl Layout {
|
|||||||
/// Insert `inst` before the instruction `before` in the same EBB.
|
/// Insert `inst` before the instruction `before` in the same EBB.
|
||||||
pub fn insert_inst(&mut self, inst: Inst, before: Inst) {
|
pub fn insert_inst(&mut self, inst: Inst, before: Inst) {
|
||||||
debug_assert_eq!(self.inst_ebb(inst), None);
|
debug_assert_eq!(self.inst_ebb(inst), None);
|
||||||
let ebb = self.inst_ebb(before).expect(
|
let ebb = self.inst_ebb(before)
|
||||||
"Instruction before insertion point not in the layout",
|
.expect("Instruction before insertion point not in the layout");
|
||||||
);
|
|
||||||
let after = self.insts[before].prev;
|
let after = self.insts[before].prev;
|
||||||
{
|
{
|
||||||
let inst_node = &mut self.insts[inst];
|
let inst_node = &mut self.insts[inst];
|
||||||
@@ -641,9 +643,8 @@ impl Layout {
|
|||||||
/// i4
|
/// i4
|
||||||
/// ```
|
/// ```
|
||||||
pub fn split_ebb(&mut self, new_ebb: Ebb, before: Inst) {
|
pub fn split_ebb(&mut self, new_ebb: Ebb, before: Inst) {
|
||||||
let old_ebb = self.inst_ebb(before).expect(
|
let old_ebb = self.inst_ebb(before)
|
||||||
"The `before` instruction must be in the layout",
|
.expect("The `before` instruction must be in the layout");
|
||||||
);
|
|
||||||
debug_assert!(!self.is_ebb_inserted(new_ebb));
|
debug_assert!(!self.is_ebb_inserted(new_ebb));
|
||||||
|
|
||||||
// Insert new_ebb after old_ebb.
|
// Insert new_ebb after old_ebb.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! Naming well-known routines in the runtime library.
|
//! Naming well-known routines in the runtime library.
|
||||||
|
|
||||||
use ir::{types, Opcode, Type, Inst, Function, FuncRef, ExternalName, Signature, AbiParam,
|
use ir::{types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst,
|
||||||
ExtFuncData, ArgumentPurpose};
|
Opcode, Signature, Type};
|
||||||
|
use isa::{RegUnit, TargetIsa};
|
||||||
use settings::CallConv;
|
use settings::CallConv;
|
||||||
use isa::{TargetIsa, RegUnit};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -82,24 +82,20 @@ impl LibCall {
|
|||||||
/// Returns `None` if no well-known library routine name exists for that instruction.
|
/// Returns `None` if no well-known library routine name exists for that instruction.
|
||||||
pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
|
pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option<Self> {
|
||||||
Some(match ctrl_type {
|
Some(match ctrl_type {
|
||||||
types::F32 => {
|
types::F32 => match opcode {
|
||||||
match opcode {
|
|
||||||
Opcode::Ceil => LibCall::CeilF32,
|
Opcode::Ceil => LibCall::CeilF32,
|
||||||
Opcode::Floor => LibCall::FloorF32,
|
Opcode::Floor => LibCall::FloorF32,
|
||||||
Opcode::Trunc => LibCall::TruncF32,
|
Opcode::Trunc => LibCall::TruncF32,
|
||||||
Opcode::Nearest => LibCall::NearestF32,
|
Opcode::Nearest => LibCall::NearestF32,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
},
|
||||||
}
|
types::F64 => match opcode {
|
||||||
types::F64 => {
|
|
||||||
match opcode {
|
|
||||||
Opcode::Ceil => LibCall::CeilF64,
|
Opcode::Ceil => LibCall::CeilF64,
|
||||||
Opcode::Floor => LibCall::FloorF64,
|
Opcode::Floor => LibCall::FloorF64,
|
||||||
Opcode::Trunc => LibCall::TruncF64,
|
Opcode::Trunc => LibCall::TruncF64,
|
||||||
Opcode::Nearest => LibCall::NearestF64,
|
Opcode::Nearest => LibCall::NearestF64,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
},
|
||||||
}
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -127,9 +123,8 @@ pub fn get_probestack_funcref(
|
|||||||
arg_reg: RegUnit,
|
arg_reg: RegUnit,
|
||||||
isa: &TargetIsa,
|
isa: &TargetIsa,
|
||||||
) -> FuncRef {
|
) -> FuncRef {
|
||||||
find_funcref(LibCall::Probestack, func).unwrap_or_else(|| {
|
find_funcref(LibCall::Probestack, func)
|
||||||
make_funcref_for_probestack(func, reg_type, arg_reg, isa)
|
.unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the existing function reference for `libcall` in `func` if it exists.
|
/// Get the existing function reference for `libcall` in `func` if it exists.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub use ir::heap::{HeapBase, HeapData, HeapStyle};
|
|||||||
pub use ir::instructions::{InstructionData, Opcode, ValueList, ValueListPool, VariableArgs};
|
pub use ir::instructions::{InstructionData, Opcode, ValueList, ValueListPool, VariableArgs};
|
||||||
pub use ir::jumptable::JumpTableData;
|
pub use ir::jumptable::JumpTableData;
|
||||||
pub use ir::layout::Layout;
|
pub use ir::layout::Layout;
|
||||||
pub use ir::libcall::{LibCall, get_libcall_funcref, get_probestack_funcref};
|
pub use ir::libcall::{get_libcall_funcref, get_probestack_funcref, LibCall};
|
||||||
pub use ir::memflags::MemFlags;
|
pub use ir::memflags::MemFlags;
|
||||||
pub use ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
pub use ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||||
pub use ir::sourceloc::SourceLoc;
|
pub use ir::sourceloc::SourceLoc;
|
||||||
|
|||||||
@@ -66,12 +66,10 @@ impl<'a> fmt::Display for DisplayValueLoc<'a> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
ValueLoc::Unassigned => write!(f, "-"),
|
ValueLoc::Unassigned => write!(f, "-"),
|
||||||
ValueLoc::Reg(ru) => {
|
ValueLoc::Reg(ru) => match self.1 {
|
||||||
match self.1 {
|
|
||||||
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
|
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
|
||||||
None => write!(f, "%{}", ru),
|
None => write!(f, "%{}", ru),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
ValueLoc::Stack(ss) => write!(f, "{}", ss),
|
ValueLoc::Stack(ss) => write!(f, "{}", ss),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,12 +151,10 @@ impl<'a> fmt::Display for DisplayArgumentLoc<'a> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
ArgumentLoc::Unassigned => write!(f, "-"),
|
ArgumentLoc::Unassigned => write!(f, "-"),
|
||||||
ArgumentLoc::Reg(ru) => {
|
ArgumentLoc::Reg(ru) => match self.1 {
|
||||||
match self.1 {
|
|
||||||
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
|
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
|
||||||
None => write!(f, "%{}", ru),
|
None => write!(f, "%{}", ru),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
ArgumentLoc::Stack(offset) => write!(f, "{}", offset),
|
ArgumentLoc::Stack(offset) => write!(f, "{}", offset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ pub fn legalize_signature(
|
|||||||
|
|
||||||
/// Get register class for a type appearing in a legalized signature.
|
/// Get register class for a type appearing in a legalized signature.
|
||||||
pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass {
|
pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass {
|
||||||
if ty.is_int() { GPR } else { FPR }
|
if ty.is_int() {
|
||||||
|
GPR
|
||||||
|
} else {
|
||||||
|
FPR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the set of allocatable registers for `func`.
|
/// Get the set of allocatable registers for `func`.
|
||||||
|
|||||||
@@ -30,16 +30,14 @@ impl OperandConstraint {
|
|||||||
/// counterpart operand has the same value location.
|
/// counterpart operand has the same value location.
|
||||||
pub fn satisfied(&self, loc: ValueLoc) -> bool {
|
pub fn satisfied(&self, loc: ValueLoc) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg | ConstraintKind::Tied(_) => {
|
||||||
ConstraintKind::Tied(_) => {
|
|
||||||
if let ValueLoc::Reg(reg) = loc {
|
if let ValueLoc::Reg(reg) = loc {
|
||||||
self.regclass.contains(reg)
|
self.regclass.contains(reg)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstraintKind::FixedReg(reg) |
|
ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => {
|
||||||
ConstraintKind::FixedTied(reg) => {
|
|
||||||
loc == ValueLoc::Reg(reg) && self.regclass.contains(reg)
|
loc == ValueLoc::Reg(reg) && self.regclass.contains(reg)
|
||||||
}
|
}
|
||||||
ConstraintKind::Stack => {
|
ConstraintKind::Stack => {
|
||||||
|
|||||||
@@ -122,10 +122,9 @@ impl EncInfo {
|
|||||||
///
|
///
|
||||||
/// Returns 0 for illegal encodings.
|
/// Returns 0 for illegal encodings.
|
||||||
pub fn bytes(&self, enc: Encoding) -> CodeOffset {
|
pub fn bytes(&self, enc: Encoding) -> CodeOffset {
|
||||||
self.sizing.get(enc.recipe()).map_or(
|
self.sizing
|
||||||
0,
|
.get(enc.recipe())
|
||||||
|s| CodeOffset::from(s.bytes),
|
.map_or(0, |s| CodeOffset::from(s.bytes))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the branch range that is supported by `enc`, if any.
|
/// Get the branch range that is supported by `enc`, if any.
|
||||||
|
|||||||
@@ -142,11 +142,8 @@ impl settings::Configurable for Builder {
|
|||||||
/// legalize it?
|
/// legalize it?
|
||||||
///
|
///
|
||||||
/// The `Encodings` iterator returns a legalization function to call.
|
/// The `Encodings` iterator returns a legalization function to call.
|
||||||
pub type Legalize = fn(ir::Inst,
|
pub type Legalize =
|
||||||
&mut ir::Function,
|
fn(ir::Inst, &mut ir::Function, &mut flowgraph::ControlFlowGraph, &TargetIsa) -> bool;
|
||||||
&mut flowgraph::ControlFlowGraph,
|
|
||||||
&TargetIsa)
|
|
||||||
-> bool;
|
|
||||||
|
|
||||||
/// Methods that are specialized to a target ISA. Implies a Display trait that shows the
|
/// Methods that are specialized to a target ISA. Implies a Display trait that shows the
|
||||||
/// shared flags, as well as any isa-specific flags.
|
/// shared flags, as well as any isa-specific flags.
|
||||||
|
|||||||
@@ -89,10 +89,12 @@ impl RegBank {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.and_then(|offset| if offset < self.units {
|
}.and_then(|offset| {
|
||||||
|
if offset < self.units {
|
||||||
Some(offset + self.first_unit)
|
Some(offset + self.first_unit)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,11 @@ pub fn legalize_signature(
|
|||||||
|
|
||||||
/// Get register class for a type appearing in a legalized signature.
|
/// Get register class for a type appearing in a legalized signature.
|
||||||
pub fn regclass_for_abi_type(ty: Type) -> RegClass {
|
pub fn regclass_for_abi_type(ty: Type) -> RegClass {
|
||||||
if ty.is_float() { FPR } else { GPR }
|
if ty.is_float() {
|
||||||
|
FPR
|
||||||
|
} else {
|
||||||
|
GPR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocatable_registers(_func: &ir::Function, isa_flags: &settings::Flags) -> RegisterSet {
|
pub fn allocatable_registers(_func: &ir::Function, isa_flags: &settings::Flags) -> RegisterSet {
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ impl StackRef {
|
|||||||
|
|
||||||
/// Get a reference to `ss` using the stack pointer as a base.
|
/// Get a reference to `ss` using the stack pointer as a base.
|
||||||
pub fn sp(ss: StackSlot, frame: &StackSlots) -> Self {
|
pub fn sp(ss: StackSlot, frame: &StackSlots) -> Self {
|
||||||
let size = frame.frame_size.expect(
|
let size = frame
|
||||||
"Stack layout must be computed before referencing stack slots",
|
.frame_size
|
||||||
);
|
.expect("Stack layout must be computed before referencing stack slots");
|
||||||
let slot = &frame[ss];
|
let slot = &frame[ss];
|
||||||
let offset = if slot.kind == StackSlotKind::OutgoingArg {
|
let offset = if slot.kind == StackSlotKind::OutgoingArg {
|
||||||
// Outgoing argument slots have offsets relative to our stack pointer.
|
// Outgoing argument slots have offsets relative to our stack pointer.
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use cursor::{Cursor, CursorPosition, EncCursor};
|
|||||||
use ir;
|
use ir;
|
||||||
use ir::immediates::Imm64;
|
use ir::immediates::Imm64;
|
||||||
use ir::stackslot::{StackOffset, StackSize};
|
use ir::stackslot::{StackOffset, StackSize};
|
||||||
use ir::{AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, InstBuilder, ValueLoc,
|
use ir::{get_probestack_funcref, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose,
|
||||||
get_probestack_funcref};
|
InstBuilder, ValueLoc};
|
||||||
use isa::{RegClass, RegUnit, TargetIsa};
|
use isa::{RegClass, RegUnit, TargetIsa};
|
||||||
use regalloc::RegisterSet;
|
use regalloc::RegisterSet;
|
||||||
use result;
|
use result;
|
||||||
@@ -97,7 +97,8 @@ impl ArgAssigner for Args {
|
|||||||
RU::r14
|
RU::r14
|
||||||
} else {
|
} else {
|
||||||
RU::rsi
|
RU::rsi
|
||||||
} as RegUnit).into()
|
} as RegUnit)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
// This is SpiderMonkey's `WasmTableCallSigReg`.
|
// This is SpiderMonkey's `WasmTableCallSigReg`.
|
||||||
ArgumentPurpose::SignatureId => return ArgumentLoc::Reg(RU::rbx as RegUnit).into(),
|
ArgumentPurpose::SignatureId => return ArgumentLoc::Reg(RU::rbx as RegUnit).into(),
|
||||||
@@ -235,8 +236,8 @@ fn callee_saved_gprs_used(flags: &shared_settings::Flags, func: &ir::Function) -
|
|||||||
for ebb in &func.layout {
|
for ebb in &func.layout {
|
||||||
for inst in func.layout.ebb_insts(ebb) {
|
for inst in func.layout.ebb_insts(ebb) {
|
||||||
match func.dfg[inst] {
|
match func.dfg[inst] {
|
||||||
ir::instructions::InstructionData::RegMove { dst, .. } |
|
ir::instructions::InstructionData::RegMove { dst, .. }
|
||||||
ir::instructions::InstructionData::RegFill { dst, .. } => {
|
| ir::instructions::InstructionData::RegFill { dst, .. } => {
|
||||||
if !used.is_avail(GPR, dst) {
|
if !used.is_avail(GPR, dst) {
|
||||||
used.free(GPR, dst);
|
used.free(GPR, dst);
|
||||||
}
|
}
|
||||||
@@ -431,10 +432,8 @@ fn insert_common_prologue(
|
|||||||
pos.func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit);
|
pos.func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit);
|
||||||
|
|
||||||
pos.ins().x86_push(fp);
|
pos.ins().x86_push(fp);
|
||||||
pos.ins().copy_special(
|
pos.ins()
|
||||||
RU::rsp as RegUnit,
|
.copy_special(RU::rsp as RegUnit, RU::rbp as RegUnit);
|
||||||
RU::rbp as RegUnit,
|
|
||||||
);
|
|
||||||
|
|
||||||
for reg in csrs.iter(GPR) {
|
for reg in csrs.iter(GPR) {
|
||||||
// Append param to entry EBB
|
// Append param to entry EBB
|
||||||
@@ -449,8 +448,8 @@ fn insert_common_prologue(
|
|||||||
|
|
||||||
// Allocate stack frame storage.
|
// Allocate stack frame storage.
|
||||||
if stack_size > 0 {
|
if stack_size > 0 {
|
||||||
if isa.flags().probestack_enabled() &&
|
if isa.flags().probestack_enabled()
|
||||||
stack_size > (1 << isa.flags().probestack_size_log2())
|
&& stack_size > (1 << isa.flags().probestack_size_log2())
|
||||||
{
|
{
|
||||||
// Emit a stack probe.
|
// Emit a stack probe.
|
||||||
let rax = RU::rax as RegUnit;
|
let rax = RU::rax as RegUnit;
|
||||||
@@ -464,8 +463,8 @@ fn insert_common_prologue(
|
|||||||
let callee = get_probestack_funcref(pos.func, reg_type, rax, isa);
|
let callee = get_probestack_funcref(pos.func, reg_type, rax, isa);
|
||||||
|
|
||||||
// Make the call.
|
// Make the call.
|
||||||
let call = if !isa.flags().is_pic() && isa.flags().is_64bit() &&
|
let call = if !isa.flags().is_pic() && isa.flags().is_64bit()
|
||||||
!pos.func.dfg.ext_funcs[callee].colocated
|
&& !pos.func.dfg.ext_funcs[callee].colocated
|
||||||
{
|
{
|
||||||
// 64-bit non-PIC non-colocated calls need to be legalized to call_indirect.
|
// 64-bit non-PIC non-colocated calls need to be legalized to call_indirect.
|
||||||
// Use r11 as it may be clobbered under all supported calling conventions.
|
// Use r11 as it may be clobbered under all supported calling conventions.
|
||||||
|
|||||||
@@ -84,11 +84,8 @@ fn expand_sdivrem(
|
|||||||
// Explicitly check for overflow: Trap when x == INT_MIN.
|
// Explicitly check for overflow: Trap when x == INT_MIN.
|
||||||
debug_assert!(avoid_div_traps, "Native trapping divide handled above");
|
debug_assert!(avoid_div_traps, "Native trapping divide handled above");
|
||||||
let f = pos.ins().ifcmp_imm(x, -1 << (ty.lane_bits() - 1));
|
let f = pos.ins().ifcmp_imm(x, -1 << (ty.lane_bits() - 1));
|
||||||
pos.ins().trapif(
|
pos.ins()
|
||||||
IntCC::Equal,
|
.trapif(IntCC::Equal, f, ir::TrapCode::IntegerOverflow);
|
||||||
f,
|
|
||||||
ir::TrapCode::IntegerOverflow,
|
|
||||||
);
|
|
||||||
// x / -1 = -x.
|
// x / -1 = -x.
|
||||||
pos.ins().irsub_imm(x, 0)
|
pos.ins().irsub_imm(x, 0)
|
||||||
};
|
};
|
||||||
@@ -348,11 +345,8 @@ fn expand_fcvt_to_sint(
|
|||||||
let mut pos = FuncCursor::new(func).after_inst(inst);
|
let mut pos = FuncCursor::new(func).after_inst(inst);
|
||||||
pos.use_srcloc(inst);
|
pos.use_srcloc(inst);
|
||||||
|
|
||||||
let is_done = pos.ins().icmp_imm(
|
let is_done = pos.ins()
|
||||||
IntCC::NotEqual,
|
.icmp_imm(IntCC::NotEqual, result, 1 << (ty.lane_bits() - 1));
|
||||||
result,
|
|
||||||
1 << (ty.lane_bits() - 1),
|
|
||||||
);
|
|
||||||
pos.ins().brnz(is_done, done, &[]);
|
pos.ins().brnz(is_done, done, &[]);
|
||||||
|
|
||||||
// We now have the following possibilities:
|
// We now have the following possibilities:
|
||||||
@@ -364,10 +358,8 @@ fn expand_fcvt_to_sint(
|
|||||||
|
|
||||||
// Check for NaN.
|
// Check for NaN.
|
||||||
let is_nan = pos.ins().fcmp(FloatCC::Unordered, x, x);
|
let is_nan = pos.ins().fcmp(FloatCC::Unordered, x, x);
|
||||||
pos.ins().trapnz(
|
pos.ins()
|
||||||
is_nan,
|
.trapnz(is_nan, ir::TrapCode::BadConversionToInteger);
|
||||||
ir::TrapCode::BadConversionToInteger,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check for case 1: INT_MIN is the correct result.
|
// Check for case 1: INT_MIN is the correct result.
|
||||||
// Determine the smallest floating point number that would convert to INT_MIN.
|
// Determine the smallest floating point number that would convert to INT_MIN.
|
||||||
@@ -376,14 +368,12 @@ fn expand_fcvt_to_sint(
|
|||||||
let flimit = match xty {
|
let flimit = match xty {
|
||||||
// An f32 can represent `i16::min_value() - 1` exactly with precision to spare, so
|
// An f32 can represent `i16::min_value() - 1` exactly with precision to spare, so
|
||||||
// there are values less than -2^(N-1) that convert correctly to INT_MIN.
|
// there are values less than -2^(N-1) that convert correctly to INT_MIN.
|
||||||
ir::types::F32 => {
|
ir::types::F32 => pos.ins().f32const(if output_bits < 32 {
|
||||||
pos.ins().f32const(if output_bits < 32 {
|
|
||||||
overflow_cc = FloatCC::LessThanOrEqual;
|
overflow_cc = FloatCC::LessThanOrEqual;
|
||||||
Ieee32::fcvt_to_sint_negative_overflow(output_bits)
|
Ieee32::fcvt_to_sint_negative_overflow(output_bits)
|
||||||
} else {
|
} else {
|
||||||
Ieee32::pow2(output_bits - 1).neg()
|
Ieee32::pow2(output_bits - 1).neg()
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
ir::types::F64 => {
|
ir::types::F64 => {
|
||||||
// An f64 can represent `i32::min_value() - 1` exactly with precision to spare, so
|
// An f64 can represent `i32::min_value() - 1` exactly with precision to spare, so
|
||||||
// there are values less than -2^(N-1) that convert correctly to INT_MIN.
|
// there are values less than -2^(N-1) that convert correctly to INT_MIN.
|
||||||
@@ -458,12 +448,8 @@ fn expand_fcvt_to_uint(
|
|||||||
_ => panic!("Can't convert {}", xty),
|
_ => panic!("Can't convert {}", xty),
|
||||||
};
|
};
|
||||||
let is_large = pos.ins().ffcmp(x, pow2nm1);
|
let is_large = pos.ins().ffcmp(x, pow2nm1);
|
||||||
pos.ins().brff(
|
pos.ins()
|
||||||
FloatCC::GreaterThanOrEqual,
|
.brff(FloatCC::GreaterThanOrEqual, is_large, large, &[]);
|
||||||
is_large,
|
|
||||||
large,
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
|
|
||||||
// We need to generate a specific trap code when `x` is NaN, so reuse the flags from the
|
// We need to generate a specific trap code when `x` is NaN, so reuse the flags from the
|
||||||
// previous comparison.
|
// previous comparison.
|
||||||
@@ -476,12 +462,8 @@ fn expand_fcvt_to_uint(
|
|||||||
// Now we know that x < 2^(N-1) and not NaN.
|
// Now we know that x < 2^(N-1) and not NaN.
|
||||||
let sres = pos.ins().x86_cvtt2si(ty, x);
|
let sres = pos.ins().x86_cvtt2si(ty, x);
|
||||||
let is_neg = pos.ins().ifcmp_imm(sres, 0);
|
let is_neg = pos.ins().ifcmp_imm(sres, 0);
|
||||||
pos.ins().brif(
|
pos.ins()
|
||||||
IntCC::SignedGreaterThanOrEqual,
|
.brif(IntCC::SignedGreaterThanOrEqual, is_neg, done, &[sres]);
|
||||||
is_neg,
|
|
||||||
done,
|
|
||||||
&[sres],
|
|
||||||
);
|
|
||||||
pos.ins().trap(ir::TrapCode::IntegerOverflow);
|
pos.ins().trap(ir::TrapCode::IntegerOverflow);
|
||||||
|
|
||||||
// Handle the case where x >= 2^(N-1) and not NaN.
|
// Handle the case where x >= 2^(N-1) and not NaN.
|
||||||
@@ -489,11 +471,8 @@ fn expand_fcvt_to_uint(
|
|||||||
let adjx = pos.ins().fsub(x, pow2nm1);
|
let adjx = pos.ins().fsub(x, pow2nm1);
|
||||||
let lres = pos.ins().x86_cvtt2si(ty, adjx);
|
let lres = pos.ins().x86_cvtt2si(ty, adjx);
|
||||||
let is_neg = pos.ins().ifcmp_imm(lres, 0);
|
let is_neg = pos.ins().ifcmp_imm(lres, 0);
|
||||||
pos.ins().trapif(
|
pos.ins()
|
||||||
IntCC::SignedLessThan,
|
.trapif(IntCC::SignedLessThan, is_neg, ir::TrapCode::IntegerOverflow);
|
||||||
is_neg,
|
|
||||||
ir::TrapCode::IntegerOverflow,
|
|
||||||
);
|
|
||||||
let lfinal = pos.ins().iadd_imm(lres, 1 << (ty.lane_bits() - 1));
|
let lfinal = pos.ins().iadd_imm(lres, 1 << (ty.lane_bits() - 1));
|
||||||
|
|
||||||
// Recycle the original instruction as a jump.
|
// Recycle the original instruction as a jump.
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ mod tests {
|
|||||||
vec![]
|
vec![]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
[]
|
[].iter()
|
||||||
.iter()
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.adjacent_pairs()
|
.adjacent_pairs()
|
||||||
.collect::<Vec<(i32, i32)>>(),
|
.collect::<Vec<(i32, i32)>>(),
|
||||||
|
|||||||
@@ -133,8 +133,7 @@ fn legalize_entry_params(func: &mut Function, entry: Ebb) {
|
|||||||
}
|
}
|
||||||
// The callee-save parameters should not appear until after register allocation is
|
// The callee-save parameters should not appear until after register allocation is
|
||||||
// done.
|
// done.
|
||||||
ArgumentPurpose::FramePointer |
|
ArgumentPurpose::FramePointer | ArgumentPurpose::CalleeSaved => {
|
||||||
ArgumentPurpose::CalleeSaved => {
|
|
||||||
panic!("Premature callee-saved arg {}", arg);
|
panic!("Premature callee-saved arg {}", arg);
|
||||||
}
|
}
|
||||||
// These can be meaningfully added by `legalize_signature()`.
|
// These can be meaningfully added by `legalize_signature()`.
|
||||||
@@ -174,9 +173,8 @@ fn legalize_inst_results<ResType>(pos: &mut FuncCursor, mut get_abi_type: ResTyp
|
|||||||
where
|
where
|
||||||
ResType: FnMut(&Function, usize) -> AbiParam,
|
ResType: FnMut(&Function, usize) -> AbiParam,
|
||||||
{
|
{
|
||||||
let call = pos.current_inst().expect(
|
let call = pos.current_inst()
|
||||||
"Cursor must point to a call instruction",
|
.expect("Cursor must point to a call instruction");
|
||||||
);
|
|
||||||
|
|
||||||
// We theoretically allow for call instructions that return a number of fixed results before
|
// We theoretically allow for call instructions that return a number of fixed results before
|
||||||
// the call return values. In practice, it doesn't happen.
|
// the call return values. In practice, it doesn't happen.
|
||||||
@@ -377,8 +375,8 @@ fn check_call_signature(dfg: &DataFlowGraph, inst: Inst) -> Result<(), SigRef> {
|
|||||||
};
|
};
|
||||||
let sig = &dfg.signatures[sig_ref];
|
let sig = &dfg.signatures[sig_ref];
|
||||||
|
|
||||||
if check_arg_types(dfg, args, &sig.params[..]) &&
|
if check_arg_types(dfg, args, &sig.params[..])
|
||||||
check_arg_types(dfg, dfg.inst_results(inst), &sig.returns[..])
|
&& check_arg_types(dfg, dfg.inst_results(inst), &sig.returns[..])
|
||||||
{
|
{
|
||||||
// All types check out.
|
// All types check out.
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -407,14 +405,13 @@ fn legalize_inst_arguments<ArgType>(
|
|||||||
) where
|
) where
|
||||||
ArgType: FnMut(&Function, usize) -> AbiParam,
|
ArgType: FnMut(&Function, usize) -> AbiParam,
|
||||||
{
|
{
|
||||||
let inst = pos.current_inst().expect(
|
let inst = pos.current_inst()
|
||||||
"Cursor must point to a call instruction",
|
.expect("Cursor must point to a call instruction");
|
||||||
);
|
|
||||||
|
|
||||||
// Lift the value list out of the call instruction so we modify it.
|
// Lift the value list out of the call instruction so we modify it.
|
||||||
let mut vlist = pos.func.dfg[inst].take_value_list().expect(
|
let mut vlist = pos.func.dfg[inst]
|
||||||
"Call must have a value list",
|
.take_value_list()
|
||||||
);
|
.expect("Call must have a value list");
|
||||||
|
|
||||||
// The value list contains all arguments to the instruction, including the callee on an
|
// The value list contains all arguments to the instruction, including the callee on an
|
||||||
// indirect call which isn't part of the call arguments that must match the ABI signature.
|
// indirect call which isn't part of the call arguments that must match the ABI signature.
|
||||||
@@ -544,8 +541,8 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.take_while(|&rt| {
|
.take_while(|&rt| {
|
||||||
rt.purpose == ArgumentPurpose::Link || rt.purpose == ArgumentPurpose::StructReturn ||
|
rt.purpose == ArgumentPurpose::Link || rt.purpose == ArgumentPurpose::StructReturn
|
||||||
rt.purpose == ArgumentPurpose::VMContext
|
|| rt.purpose == ArgumentPurpose::VMContext
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
let abi_args = func.signature.returns.len() - special_args;
|
let abi_args = func.signature.returns.len() - special_args;
|
||||||
@@ -570,9 +567,9 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
|
|||||||
let mut vlist = pos.func.dfg[inst].take_value_list().unwrap();
|
let mut vlist = pos.func.dfg[inst].take_value_list().unwrap();
|
||||||
for arg in &pos.func.signature.returns[abi_args..] {
|
for arg in &pos.func.signature.returns[abi_args..] {
|
||||||
match arg.purpose {
|
match arg.purpose {
|
||||||
ArgumentPurpose::Link |
|
ArgumentPurpose::Link
|
||||||
ArgumentPurpose::StructReturn |
|
| ArgumentPurpose::StructReturn
|
||||||
ArgumentPurpose::VMContext => {}
|
| ArgumentPurpose::VMContext => {}
|
||||||
ArgumentPurpose::Normal => panic!("unexpected return value {}", arg),
|
ArgumentPurpose::Normal => panic!("unexpected return value {}", arg),
|
||||||
_ => panic!("Unsupported special purpose return value {}", arg),
|
_ => panic!("Unsupported special purpose return value {}", arg),
|
||||||
}
|
}
|
||||||
@@ -587,10 +584,9 @@ pub fn handle_return_abi(inst: Inst, func: &mut Function, cfg: &ControlFlowGraph
|
|||||||
.expect("No matching special purpose argument.");
|
.expect("No matching special purpose argument.");
|
||||||
// Get the corresponding entry block value and add it to the return instruction's
|
// Get the corresponding entry block value and add it to the return instruction's
|
||||||
// arguments.
|
// arguments.
|
||||||
let val = pos.func.dfg.ebb_params(
|
let val = pos.func
|
||||||
pos.func.layout.entry_block().unwrap(),
|
.dfg
|
||||||
)
|
.ebb_params(pos.func.layout.entry_block().unwrap())[idx];
|
||||||
[idx];
|
|
||||||
debug_assert_eq!(pos.func.dfg.value_type(val), arg.value_type);
|
debug_assert_eq!(pos.func.dfg.value_type(val), arg.value_type);
|
||||||
vlist.push(val, &mut pos.func.dfg.value_lists);
|
vlist.push(val, &mut pos.func.dfg.value_lists);
|
||||||
}
|
}
|
||||||
@@ -630,12 +626,12 @@ fn spill_entry_params(func: &mut Function, entry: Ebb) {
|
|||||||
/// or calls between writing the stack slots and the call instruction. Writing the slots earlier
|
/// or calls between writing the stack slots and the call instruction. Writing the slots earlier
|
||||||
/// could help reduce register pressure before the call.
|
/// could help reduce register pressure before the call.
|
||||||
fn spill_call_arguments(pos: &mut FuncCursor) -> bool {
|
fn spill_call_arguments(pos: &mut FuncCursor) -> bool {
|
||||||
let inst = pos.current_inst().expect(
|
let inst = pos.current_inst()
|
||||||
"Cursor must point to a call instruction",
|
.expect("Cursor must point to a call instruction");
|
||||||
);
|
let sig_ref = pos.func
|
||||||
let sig_ref = pos.func.dfg.call_signature(inst).expect(
|
.dfg
|
||||||
"Call instruction expected.",
|
.call_signature(inst)
|
||||||
);
|
.expect("Call instruction expected.");
|
||||||
|
|
||||||
// Start by building a list of stack slots and arguments to be replaced.
|
// Start by building a list of stack slots and arguments to be replaced.
|
||||||
// This requires borrowing `pos.func.dfg`, so we can't change anything.
|
// This requires borrowing `pos.func.dfg`, so we can't change anything.
|
||||||
|
|||||||
@@ -51,10 +51,7 @@ pub fn expand_call(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
func.dfg.replace(inst).CallIndirect(
|
func.dfg
|
||||||
ir::Opcode::CallIndirect,
|
.replace(inst)
|
||||||
ptr_ty,
|
.CallIndirect(ir::Opcode::CallIndirect, ptr_ty, sig, new_args);
|
||||||
sig,
|
|
||||||
new_args,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,8 @@ pub fn expand_global_addr(
|
|||||||
/// Expand a `global_addr` instruction for a vmctx global.
|
/// Expand a `global_addr` instruction for a vmctx global.
|
||||||
fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function, offset: i64) {
|
fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function, offset: i64) {
|
||||||
// Get the value representing the `vmctx` argument.
|
// Get the value representing the `vmctx` argument.
|
||||||
let vmctx = func.special_param(ir::ArgumentPurpose::VMContext).expect(
|
let vmctx = func.special_param(ir::ArgumentPurpose::VMContext)
|
||||||
"Missing vmctx parameter",
|
.expect("Missing vmctx parameter");
|
||||||
);
|
|
||||||
|
|
||||||
// Simply replace the `global_addr` instruction with an `iadd_imm`, reusing the result value.
|
// Simply replace the `global_addr` instruction with an `iadd_imm`, reusing the result value.
|
||||||
func.dfg.replace(inst).iadd_imm(vmctx, offset);
|
func.dfg.replace(inst).iadd_imm(vmctx, offset);
|
||||||
|
|||||||
@@ -67,30 +67,21 @@ fn dynamic_addr(
|
|||||||
let oob;
|
let oob;
|
||||||
if size == 1 {
|
if size == 1 {
|
||||||
// `offset > bound - 1` is the same as `offset >= bound`.
|
// `offset > bound - 1` is the same as `offset >= bound`.
|
||||||
oob = pos.ins().icmp(
|
oob = pos.ins()
|
||||||
IntCC::UnsignedGreaterThanOrEqual,
|
.icmp(IntCC::UnsignedGreaterThanOrEqual, offset, bound);
|
||||||
offset,
|
|
||||||
bound,
|
|
||||||
);
|
|
||||||
} else if size <= min_size {
|
} else if size <= min_size {
|
||||||
// We know that bound >= min_size, so here we can compare `offset > bound - size` without
|
// We know that bound >= min_size, so here we can compare `offset > bound - size` without
|
||||||
// wrapping.
|
// wrapping.
|
||||||
let adj_bound = pos.ins().iadd_imm(bound, -size);
|
let adj_bound = pos.ins().iadd_imm(bound, -size);
|
||||||
oob = pos.ins().icmp(
|
oob = pos.ins()
|
||||||
IntCC::UnsignedGreaterThan,
|
.icmp(IntCC::UnsignedGreaterThan, offset, adj_bound);
|
||||||
offset,
|
|
||||||
adj_bound,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// We need an overflow check for the adjusted offset.
|
// We need an overflow check for the adjusted offset.
|
||||||
let size_val = pos.ins().iconst(offset_ty, size);
|
let size_val = pos.ins().iconst(offset_ty, size);
|
||||||
let (adj_offset, overflow) = pos.ins().iadd_cout(offset, size_val);
|
let (adj_offset, overflow) = pos.ins().iadd_cout(offset, size_val);
|
||||||
pos.ins().trapnz(overflow, ir::TrapCode::HeapOutOfBounds);
|
pos.ins().trapnz(overflow, ir::TrapCode::HeapOutOfBounds);
|
||||||
oob = pos.ins().icmp(
|
oob = pos.ins()
|
||||||
IntCC::UnsignedGreaterThan,
|
.icmp(IntCC::UnsignedGreaterThan, adj_offset, bound);
|
||||||
adj_offset,
|
|
||||||
bound,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
||||||
|
|
||||||
@@ -137,17 +128,11 @@ fn static_addr(
|
|||||||
let oob = if limit & 1 == 1 {
|
let oob = if limit & 1 == 1 {
|
||||||
// Prefer testing `offset >= limit - 1` when limit is odd because an even number is
|
// Prefer testing `offset >= limit - 1` when limit is odd because an even number is
|
||||||
// likely to be a convenient constant on ARM and other RISC architectures.
|
// likely to be a convenient constant on ARM and other RISC architectures.
|
||||||
pos.ins().icmp_imm(
|
pos.ins()
|
||||||
IntCC::UnsignedGreaterThanOrEqual,
|
.icmp_imm(IntCC::UnsignedGreaterThanOrEqual, offset, limit - 1)
|
||||||
offset,
|
|
||||||
limit - 1,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
pos.ins().icmp_imm(
|
pos.ins()
|
||||||
IntCC::UnsignedGreaterThan,
|
.icmp_imm(IntCC::UnsignedGreaterThan, offset, limit)
|
||||||
offset,
|
|
||||||
limit,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
//! Expanding instructions as runtime library calls.
|
//! Expanding instructions as runtime library calls.
|
||||||
|
|
||||||
use ir;
|
use ir;
|
||||||
use ir::{InstBuilder, get_libcall_funcref};
|
use ir::{get_libcall_funcref, InstBuilder};
|
||||||
use std::vec::Vec;
|
|
||||||
use isa::TargetIsa;
|
use isa::TargetIsa;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
/// Try to expand `inst` as a library call, returning true is successful.
|
/// Try to expand `inst` as a library call, returning true is successful.
|
||||||
pub fn expand_as_libcall(inst: ir::Inst, func: &mut ir::Function, isa: &TargetIsa) -> bool {
|
pub fn expand_as_libcall(inst: ir::Inst, func: &mut ir::Function, isa: &TargetIsa) -> bool {
|
||||||
// Does the opcode/ctrl_type combo even have a well-known runtime library name.
|
// Does the opcode/ctrl_type combo even have a well-known runtime library name.
|
||||||
let libcall =
|
let libcall = match ir::LibCall::for_inst(func.dfg[inst].opcode(), func.dfg.ctrl_typevar(inst))
|
||||||
match ir::LibCall::for_inst(func.dfg[inst].opcode(), func.dfg.ctrl_typevar(inst)) {
|
{
|
||||||
Some(lc) => lc,
|
Some(lc) => lc,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ mod heap;
|
|||||||
mod libcall;
|
mod libcall;
|
||||||
mod split;
|
mod split;
|
||||||
|
|
||||||
|
use self::call::expand_call;
|
||||||
use self::globalvar::expand_global_addr;
|
use self::globalvar::expand_global_addr;
|
||||||
use self::heap::expand_heap_addr;
|
use self::heap::expand_heap_addr;
|
||||||
use self::call::expand_call;
|
|
||||||
use self::libcall::expand_as_libcall;
|
use self::libcall::expand_as_libcall;
|
||||||
|
|
||||||
/// Legalize `inst` for `isa`. Return true if any changes to the code were
|
/// Legalize `inst` for `isa`. Return true if any changes to the code were
|
||||||
|
|||||||
@@ -134,9 +134,9 @@ fn split_any(
|
|||||||
pos.func.dfg.display_inst(inst, None)
|
pos.func.dfg.display_inst(inst, None)
|
||||||
);
|
);
|
||||||
let fixed_args = branch_opc.constraints().fixed_value_arguments();
|
let fixed_args = branch_opc.constraints().fixed_value_arguments();
|
||||||
let mut args = pos.func.dfg[inst].take_value_list().expect(
|
let mut args = pos.func.dfg[inst]
|
||||||
"Branches must have value lists.",
|
.take_value_list()
|
||||||
);
|
.expect("Branches must have value lists.");
|
||||||
let num_args = args.len(&pos.func.dfg.value_lists);
|
let num_args = args.len(&pos.func.dfg.value_lists);
|
||||||
// Get the old value passed to the EBB argument we're repairing.
|
// Get the old value passed to the EBB argument we're repairing.
|
||||||
let old_arg = args.get(fixed_args + repair.num, &pos.func.dfg.value_lists)
|
let old_arg = args.get(fixed_args + repair.num, &pos.func.dfg.value_lists)
|
||||||
@@ -236,12 +236,9 @@ fn split_value(
|
|||||||
// Note that it is safe to move `pos` here since `reuse` was set above, so we don't
|
// Note that it is safe to move `pos` here since `reuse` was set above, so we don't
|
||||||
// need to insert a split instruction before returning.
|
// need to insert a split instruction before returning.
|
||||||
pos.goto_first_inst(ebb);
|
pos.goto_first_inst(ebb);
|
||||||
pos.ins().with_result(value).Binary(
|
pos.ins()
|
||||||
concat,
|
.with_result(value)
|
||||||
split_type,
|
.Binary(concat, split_type, lo, hi);
|
||||||
lo,
|
|
||||||
hi,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Finally, splitting the EBB parameter is not enough. We also have to repair all
|
// Finally, splitting the EBB parameter is not enough. We also have to repair all
|
||||||
// of the predecessor instructions that branch here.
|
// of the predecessor instructions that branch here.
|
||||||
|
|||||||
@@ -31,17 +31,9 @@
|
|||||||
redundant_field_names,
|
redundant_field_names,
|
||||||
useless_let_if_seq,
|
useless_let_if_seq,
|
||||||
len_without_is_empty))]
|
len_without_is_empty))]
|
||||||
#![cfg_attr(feature="cargo-clippy", warn(
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
float_arithmetic,
|
warn(float_arithmetic, mut_mut, nonminimal_bool, option_map_unwrap_or,
|
||||||
mut_mut,
|
option_map_unwrap_or_else, print_stdout, unicode_not_nfc, use_self))]
|
||||||
nonminimal_bool,
|
|
||||||
option_map_unwrap_or,
|
|
||||||
option_map_unwrap_or_else,
|
|
||||||
print_stdout,
|
|
||||||
unicode_not_nfc,
|
|
||||||
use_self,
|
|
||||||
))]
|
|
||||||
|
|
||||||
// Turns on no_std and alloc features if std is not available.
|
// Turns on no_std and alloc features if std is not available.
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||||
@@ -90,7 +82,6 @@ pub use entity::packed_option;
|
|||||||
|
|
||||||
mod abi;
|
mod abi;
|
||||||
mod bitset;
|
mod bitset;
|
||||||
mod nan_canonicalization;
|
|
||||||
mod constant_hash;
|
mod constant_hash;
|
||||||
mod context;
|
mod context;
|
||||||
mod dce;
|
mod dce;
|
||||||
@@ -99,6 +90,7 @@ mod fx;
|
|||||||
mod iterators;
|
mod iterators;
|
||||||
mod legalizer;
|
mod legalizer;
|
||||||
mod licm;
|
mod licm;
|
||||||
|
mod nan_canonicalization;
|
||||||
mod partition_slice;
|
mod partition_slice;
|
||||||
mod postopt;
|
mod postopt;
|
||||||
mod predicates;
|
mod predicates;
|
||||||
@@ -115,11 +107,11 @@ mod write;
|
|||||||
/// This replaces `std` in builds with `core`.
|
/// This replaces `std` in builds with `core`.
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
mod std {
|
mod std {
|
||||||
|
pub use alloc::{boxed, string, vec};
|
||||||
pub use core::*;
|
pub use core::*;
|
||||||
pub use alloc::{boxed, vec, string};
|
|
||||||
pub mod collections {
|
pub mod collections {
|
||||||
pub use hashmap_core::{HashMap, HashSet};
|
|
||||||
pub use hashmap_core::map as hash_map;
|
|
||||||
pub use alloc::BTreeSet;
|
pub use alloc::BTreeSet;
|
||||||
|
pub use hashmap_core::map as hash_map;
|
||||||
|
pub use hashmap_core::{HashMap, HashSet};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,9 +132,9 @@ fn change_branch_jump_destination(inst: Inst, new_ebb: Ebb, func: &mut Function)
|
|||||||
|
|
||||||
/// Test whether the given opcode is unsafe to even consider for LICM.
|
/// Test whether the given opcode is unsafe to even consider for LICM.
|
||||||
fn trivially_unsafe_for_licm(opcode: Opcode) -> bool {
|
fn trivially_unsafe_for_licm(opcode: Opcode) -> bool {
|
||||||
opcode.can_load() || opcode.can_store() || opcode.is_call() || opcode.is_branch() ||
|
opcode.can_load() || opcode.can_store() || opcode.is_call() || opcode.is_branch()
|
||||||
opcode.is_terminator() || opcode.is_return() ||
|
|| opcode.is_terminator() || opcode.is_return() || opcode.can_trap()
|
||||||
opcode.can_trap() || opcode.other_side_effects() || opcode.writes_cpu_flags()
|
|| opcode.other_side_effects() || opcode.writes_cpu_flags()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given instruction is loop-invariant.
|
/// Test whether the given instruction is loop-invariant.
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
//! that will replace nondeterministic NaN's with a single canonical NaN value.
|
//! that will replace nondeterministic NaN's with a single canonical NaN value.
|
||||||
|
|
||||||
use cursor::{Cursor, FuncCursor};
|
use cursor::{Cursor, FuncCursor};
|
||||||
use ir::{Function, Inst, InstBuilder, InstructionData, Opcode, Value};
|
|
||||||
use ir::condcodes::FloatCC;
|
use ir::condcodes::FloatCC;
|
||||||
use ir::immediates::{Ieee32, Ieee64};
|
use ir::immediates::{Ieee32, Ieee64};
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use ir::types::Type;
|
use ir::types::Type;
|
||||||
|
use ir::{Function, Inst, InstBuilder, InstructionData, Opcode, Value};
|
||||||
use timing;
|
use timing;
|
||||||
|
|
||||||
// Canonical 32-bit and 64-bit NaN values.
|
// Canonical 32-bit and 64-bit NaN values.
|
||||||
@@ -33,13 +33,13 @@ pub fn do_nan_canonicalization(func: &mut Function) {
|
|||||||
fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
|
fn is_fp_arith(pos: &mut FuncCursor, inst: Inst) -> bool {
|
||||||
match pos.func.dfg[inst] {
|
match pos.func.dfg[inst] {
|
||||||
InstructionData::Unary { opcode, .. } => {
|
InstructionData::Unary { opcode, .. } => {
|
||||||
opcode == Opcode::Ceil || opcode == Opcode::Floor || opcode == Opcode::Nearest ||
|
opcode == Opcode::Ceil || opcode == Opcode::Floor || opcode == Opcode::Nearest
|
||||||
opcode == Opcode::Sqrt || opcode == Opcode::Trunc
|
|| opcode == Opcode::Sqrt || opcode == Opcode::Trunc
|
||||||
}
|
}
|
||||||
InstructionData::Binary { opcode, .. } => {
|
InstructionData::Binary { opcode, .. } => {
|
||||||
opcode == Opcode::Fadd || opcode == Opcode::Fdiv || opcode == Opcode::Fmax ||
|
opcode == Opcode::Fadd || opcode == Opcode::Fdiv || opcode == Opcode::Fmax
|
||||||
opcode == Opcode::Fmin || opcode == Opcode::Fmul ||
|
|| opcode == Opcode::Fmin || opcode == Opcode::Fmul
|
||||||
opcode == Opcode::Fsub
|
|| opcode == Opcode::Fsub
|
||||||
}
|
}
|
||||||
InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,
|
InstructionData::Ternary { opcode, .. } => opcode == Opcode::Fma,
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -59,11 +59,9 @@ fn add_nan_canon_seq(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
// the canonical NaN value if `val` is NaN, assign the result to `inst`.
|
// the canonical NaN value if `val` is NaN, assign the result to `inst`.
|
||||||
let is_nan = pos.ins().fcmp(FloatCC::NotEqual, new_res, new_res);
|
let is_nan = pos.ins().fcmp(FloatCC::NotEqual, new_res, new_res);
|
||||||
let canon_nan = insert_nan_const(pos, val_type);
|
let canon_nan = insert_nan_const(pos, val_type);
|
||||||
pos.ins().with_result(val).select(
|
pos.ins()
|
||||||
is_nan,
|
.with_result(val)
|
||||||
canon_nan,
|
.select(is_nan, canon_nan, new_res);
|
||||||
new_res,
|
|
||||||
);
|
|
||||||
|
|
||||||
pos.prev_inst(); // Step backwards so the pass does not skip instructions.
|
pos.prev_inst(); // Step backwards so the pass does not skip instructions.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use ir::condcodes::{CondCode, FloatCC, IntCC};
|
|||||||
use ir::dfg::ValueDef;
|
use ir::dfg::ValueDef;
|
||||||
use ir::immediates::{Imm64, Offset32};
|
use ir::immediates::{Imm64, Offset32};
|
||||||
use ir::instructions::{Opcode, ValueList};
|
use ir::instructions::{Opcode, ValueList};
|
||||||
use ir::{Ebb, Function, Inst, InstBuilder, InstructionData, Value, Type, MemFlags};
|
use ir::{Ebb, Function, Inst, InstBuilder, InstructionData, MemFlags, Type, Value};
|
||||||
use isa::TargetIsa;
|
use isa::TargetIsa;
|
||||||
use timing;
|
use timing;
|
||||||
|
|
||||||
@@ -135,12 +135,10 @@ fn optimize_cpu_flags(
|
|||||||
if info.invert_branch_cond {
|
if info.invert_branch_cond {
|
||||||
cond = cond.inverse();
|
cond = cond.inverse();
|
||||||
}
|
}
|
||||||
pos.func.dfg.replace(info.br_inst).brif(
|
pos.func
|
||||||
cond,
|
.dfg
|
||||||
flags,
|
.replace(info.br_inst)
|
||||||
info.destination,
|
.brif(cond, flags, info.destination, &args);
|
||||||
&args,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
CmpBrKind::IcmpImm { mut cond, imm } => {
|
CmpBrKind::IcmpImm { mut cond, imm } => {
|
||||||
let flags = pos.ins().ifcmp_imm(info.cmp_arg, imm);
|
let flags = pos.ins().ifcmp_imm(info.cmp_arg, imm);
|
||||||
@@ -148,12 +146,10 @@ fn optimize_cpu_flags(
|
|||||||
if info.invert_branch_cond {
|
if info.invert_branch_cond {
|
||||||
cond = cond.inverse();
|
cond = cond.inverse();
|
||||||
}
|
}
|
||||||
pos.func.dfg.replace(info.br_inst).brif(
|
pos.func
|
||||||
cond,
|
.dfg
|
||||||
flags,
|
.replace(info.br_inst)
|
||||||
info.destination,
|
.brif(cond, flags, info.destination, &args);
|
||||||
&args,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
CmpBrKind::Fcmp { mut cond, arg } => {
|
CmpBrKind::Fcmp { mut cond, arg } => {
|
||||||
let flags = pos.ins().ffcmp(info.cmp_arg, arg);
|
let flags = pos.ins().ffcmp(info.cmp_arg, arg);
|
||||||
@@ -161,12 +157,10 @@ fn optimize_cpu_flags(
|
|||||||
if info.invert_branch_cond {
|
if info.invert_branch_cond {
|
||||||
cond = cond.inverse();
|
cond = cond.inverse();
|
||||||
}
|
}
|
||||||
pos.func.dfg.replace(info.br_inst).brff(
|
pos.func
|
||||||
cond,
|
.dfg
|
||||||
flags,
|
.replace(info.br_inst)
|
||||||
info.destination,
|
.brff(cond, flags, info.destination, &args);
|
||||||
&args,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ok = pos.func.update_encoding(info.cmp_inst, isa).is_ok();
|
let ok = pos.func.update_encoding(info.cmp_inst, isa).is_ok();
|
||||||
@@ -175,7 +169,6 @@ fn optimize_cpu_flags(
|
|||||||
debug_assert!(ok);
|
debug_assert!(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct MemOpInfo {
|
struct MemOpInfo {
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
@@ -326,8 +319,6 @@ fn optimize_complex_addresses(pos: &mut EncCursor, inst: Inst, isa: &TargetIsa)
|
|||||||
debug_assert!(ok);
|
debug_assert!(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// The main post-opt pass.
|
// The main post-opt pass.
|
||||||
@@ -343,10 +334,8 @@ pub fn do_postopt(func: &mut Function, isa: &TargetIsa) {
|
|||||||
optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);
|
optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);
|
||||||
|
|
||||||
// Track the most recent seen instruction that clobbers the flags.
|
// Track the most recent seen instruction that clobbers the flags.
|
||||||
if let Some(constraints) =
|
if let Some(constraints) = isa.encoding_info()
|
||||||
isa.encoding_info().operand_constraints(
|
.operand_constraints(pos.func.encodings[inst])
|
||||||
pos.func.encodings[inst],
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if constraints.clobbers_flags {
|
if constraints.clobbers_flags {
|
||||||
last_flags_clobber = Some(inst)
|
last_flags_clobber = Some(inst)
|
||||||
|
|||||||
@@ -137,27 +137,25 @@ fn get_div_info(inst: Inst, dfg: &DataFlowGraph) -> Option<DivRemByConstInfo> {
|
|||||||
/// cannot do any transformation, in which case `inst` is left unchanged.
|
/// cannot do any transformation, in which case `inst` is left unchanged.
|
||||||
fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) {
|
fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCursor, inst: Inst) {
|
||||||
let isRem = match *divrem_info {
|
let isRem = match *divrem_info {
|
||||||
DivRemByConstInfo::DivU32(_, _) |
|
DivRemByConstInfo::DivU32(_, _)
|
||||||
DivRemByConstInfo::DivU64(_, _) |
|
| DivRemByConstInfo::DivU64(_, _)
|
||||||
DivRemByConstInfo::DivS32(_, _) |
|
| DivRemByConstInfo::DivS32(_, _)
|
||||||
DivRemByConstInfo::DivS64(_, _) => false,
|
| DivRemByConstInfo::DivS64(_, _) => false,
|
||||||
DivRemByConstInfo::RemU32(_, _) |
|
DivRemByConstInfo::RemU32(_, _)
|
||||||
DivRemByConstInfo::RemU64(_, _) |
|
| DivRemByConstInfo::RemU64(_, _)
|
||||||
DivRemByConstInfo::RemS32(_, _) |
|
| DivRemByConstInfo::RemS32(_, _)
|
||||||
DivRemByConstInfo::RemS64(_, _) => true,
|
| DivRemByConstInfo::RemS64(_, _) => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
match *divrem_info {
|
match *divrem_info {
|
||||||
// -------------------- U32 --------------------
|
// -------------------- U32 --------------------
|
||||||
|
|
||||||
// U32 div, rem by zero: ignore
|
// U32 div, rem by zero: ignore
|
||||||
DivRemByConstInfo::DivU32(_n1, 0) |
|
DivRemByConstInfo::DivU32(_n1, 0) | DivRemByConstInfo::RemU32(_n1, 0) => {}
|
||||||
DivRemByConstInfo::RemU32(_n1, 0) => {}
|
|
||||||
|
|
||||||
// U32 div by 1: identity
|
// U32 div by 1: identity
|
||||||
// U32 rem by 1: zero
|
// U32 rem by 1: zero
|
||||||
DivRemByConstInfo::DivU32(n1, 1) |
|
DivRemByConstInfo::DivU32(n1, 1) | DivRemByConstInfo::RemU32(n1, 1) => {
|
||||||
DivRemByConstInfo::RemU32(n1, 1) => {
|
|
||||||
if isRem {
|
if isRem {
|
||||||
pos.func.dfg.replace(inst).iconst(I32, 0);
|
pos.func.dfg.replace(inst).iconst(I32, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -166,8 +164,9 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// U32 div, rem by a power-of-2
|
// U32 div, rem by a power-of-2
|
||||||
DivRemByConstInfo::DivU32(n1, d) |
|
DivRemByConstInfo::DivU32(n1, d) | DivRemByConstInfo::RemU32(n1, d)
|
||||||
DivRemByConstInfo::RemU32(n1, d) if d.is_power_of_two() => {
|
if d.is_power_of_two() =>
|
||||||
|
{
|
||||||
debug_assert!(d >= 2);
|
debug_assert!(d >= 2);
|
||||||
// compute k where d == 2^k
|
// compute k where d == 2^k
|
||||||
let k = d.trailing_zeros();
|
let k = d.trailing_zeros();
|
||||||
@@ -181,8 +180,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// U32 div, rem by non-power-of-2
|
// U32 div, rem by non-power-of-2
|
||||||
DivRemByConstInfo::DivU32(n1, d) |
|
DivRemByConstInfo::DivU32(n1, d) | DivRemByConstInfo::RemU32(n1, d) => {
|
||||||
DivRemByConstInfo::RemU32(n1, d) => {
|
|
||||||
debug_assert!(d >= 3);
|
debug_assert!(d >= 3);
|
||||||
let MU32 {
|
let MU32 {
|
||||||
mulBy,
|
mulBy,
|
||||||
@@ -223,13 +221,11 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
// -------------------- U64 --------------------
|
// -------------------- U64 --------------------
|
||||||
|
|
||||||
// U64 div, rem by zero: ignore
|
// U64 div, rem by zero: ignore
|
||||||
DivRemByConstInfo::DivU64(_n1, 0) |
|
DivRemByConstInfo::DivU64(_n1, 0) | DivRemByConstInfo::RemU64(_n1, 0) => {}
|
||||||
DivRemByConstInfo::RemU64(_n1, 0) => {}
|
|
||||||
|
|
||||||
// U64 div by 1: identity
|
// U64 div by 1: identity
|
||||||
// U64 rem by 1: zero
|
// U64 rem by 1: zero
|
||||||
DivRemByConstInfo::DivU64(n1, 1) |
|
DivRemByConstInfo::DivU64(n1, 1) | DivRemByConstInfo::RemU64(n1, 1) => {
|
||||||
DivRemByConstInfo::RemU64(n1, 1) => {
|
|
||||||
if isRem {
|
if isRem {
|
||||||
pos.func.dfg.replace(inst).iconst(I64, 0);
|
pos.func.dfg.replace(inst).iconst(I64, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -238,8 +234,9 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// U64 div, rem by a power-of-2
|
// U64 div, rem by a power-of-2
|
||||||
DivRemByConstInfo::DivU64(n1, d) |
|
DivRemByConstInfo::DivU64(n1, d) | DivRemByConstInfo::RemU64(n1, d)
|
||||||
DivRemByConstInfo::RemU64(n1, d) if d.is_power_of_two() => {
|
if d.is_power_of_two() =>
|
||||||
|
{
|
||||||
debug_assert!(d >= 2);
|
debug_assert!(d >= 2);
|
||||||
// compute k where d == 2^k
|
// compute k where d == 2^k
|
||||||
let k = d.trailing_zeros();
|
let k = d.trailing_zeros();
|
||||||
@@ -253,8 +250,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
|
|
||||||
// U64 div, rem by non-power-of-2
|
// U64 div, rem by non-power-of-2
|
||||||
DivRemByConstInfo::DivU64(n1, d) |
|
DivRemByConstInfo::DivU64(n1, d) | DivRemByConstInfo::RemU64(n1, d) => {
|
||||||
DivRemByConstInfo::RemU64(n1, d) => {
|
|
||||||
debug_assert!(d >= 3);
|
debug_assert!(d >= 3);
|
||||||
let MU64 {
|
let MU64 {
|
||||||
mulBy,
|
mulBy,
|
||||||
@@ -295,15 +291,14 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
// -------------------- S32 --------------------
|
// -------------------- S32 --------------------
|
||||||
|
|
||||||
// S32 div, rem by zero or -1: ignore
|
// S32 div, rem by zero or -1: ignore
|
||||||
DivRemByConstInfo::DivS32(_n1, -1) |
|
DivRemByConstInfo::DivS32(_n1, -1)
|
||||||
DivRemByConstInfo::RemS32(_n1, -1) |
|
| DivRemByConstInfo::RemS32(_n1, -1)
|
||||||
DivRemByConstInfo::DivS32(_n1, 0) |
|
| DivRemByConstInfo::DivS32(_n1, 0)
|
||||||
DivRemByConstInfo::RemS32(_n1, 0) => {}
|
| DivRemByConstInfo::RemS32(_n1, 0) => {}
|
||||||
|
|
||||||
// S32 div by 1: identity
|
// S32 div by 1: identity
|
||||||
// S32 rem by 1: zero
|
// S32 rem by 1: zero
|
||||||
DivRemByConstInfo::DivS32(n1, 1) |
|
DivRemByConstInfo::DivS32(n1, 1) | DivRemByConstInfo::RemS32(n1, 1) => {
|
||||||
DivRemByConstInfo::RemS32(n1, 1) => {
|
|
||||||
if isRem {
|
if isRem {
|
||||||
pos.func.dfg.replace(inst).iconst(I32, 0);
|
pos.func.dfg.replace(inst).iconst(I32, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -311,8 +306,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DivRemByConstInfo::DivS32(n1, d) |
|
DivRemByConstInfo::DivS32(n1, d) | DivRemByConstInfo::RemS32(n1, d) => {
|
||||||
DivRemByConstInfo::RemS32(n1, d) => {
|
|
||||||
if let Some((isNeg, k)) = isPowerOf2_S32(d) {
|
if let Some((isNeg, k)) = isPowerOf2_S32(d) {
|
||||||
// k can be 31 only in the case that d is -2^31.
|
// k can be 31 only in the case that d is -2^31.
|
||||||
debug_assert!(k >= 1 && k <= 31);
|
debug_assert!(k >= 1 && k <= 31);
|
||||||
@@ -372,15 +366,14 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
// -------------------- S64 --------------------
|
// -------------------- S64 --------------------
|
||||||
|
|
||||||
// S64 div, rem by zero or -1: ignore
|
// S64 div, rem by zero or -1: ignore
|
||||||
DivRemByConstInfo::DivS64(_n1, -1) |
|
DivRemByConstInfo::DivS64(_n1, -1)
|
||||||
DivRemByConstInfo::RemS64(_n1, -1) |
|
| DivRemByConstInfo::RemS64(_n1, -1)
|
||||||
DivRemByConstInfo::DivS64(_n1, 0) |
|
| DivRemByConstInfo::DivS64(_n1, 0)
|
||||||
DivRemByConstInfo::RemS64(_n1, 0) => {}
|
| DivRemByConstInfo::RemS64(_n1, 0) => {}
|
||||||
|
|
||||||
// S64 div by 1: identity
|
// S64 div by 1: identity
|
||||||
// S64 rem by 1: zero
|
// S64 rem by 1: zero
|
||||||
DivRemByConstInfo::DivS64(n1, 1) |
|
DivRemByConstInfo::DivS64(n1, 1) | DivRemByConstInfo::RemS64(n1, 1) => {
|
||||||
DivRemByConstInfo::RemS64(n1, 1) => {
|
|
||||||
if isRem {
|
if isRem {
|
||||||
pos.func.dfg.replace(inst).iconst(I64, 0);
|
pos.func.dfg.replace(inst).iconst(I64, 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -388,8 +381,7 @@ fn do_divrem_transformation(divrem_info: &DivRemByConstInfo, pos: &mut FuncCurso
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DivRemByConstInfo::DivS64(n1, d) |
|
DivRemByConstInfo::DivS64(n1, d) | DivRemByConstInfo::RemS64(n1, d) => {
|
||||||
DivRemByConstInfo::RemS64(n1, d) => {
|
|
||||||
if let Some((isNeg, k)) = isPowerOf2_S64(d) {
|
if let Some((isNeg, k)) = isPowerOf2_S64(d) {
|
||||||
// k can be 63 only in the case that d is -2^63.
|
// k can be 63 only in the case that d is -2^63.
|
||||||
debug_assert!(k >= 1 && k <= 63);
|
debug_assert!(k >= 1 && k <= 63);
|
||||||
@@ -483,12 +475,10 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||||
pos.func.dfg.replace(inst).BinaryImm(
|
pos.func
|
||||||
new_opcode,
|
.dfg
|
||||||
ty,
|
.replace(inst)
|
||||||
imm,
|
.BinaryImm(new_opcode, ty, imm, args[0]);
|
||||||
args[0],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if let ValueDef::Result(iconst_inst, _) = pos.func.dfg.value_def(args[0]) {
|
} else if let ValueDef::Result(iconst_inst, _) = pos.func.dfg.value_def(args[0]) {
|
||||||
if let InstructionData::UnaryImm {
|
if let InstructionData::UnaryImm {
|
||||||
@@ -501,12 +491,10 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let ty = pos.func.dfg.ctrl_typevar(inst);
|
let ty = pos.func.dfg.ctrl_typevar(inst);
|
||||||
pos.func.dfg.replace(inst).BinaryImm(
|
pos.func
|
||||||
new_opcode,
|
.dfg
|
||||||
ty,
|
.replace(inst)
|
||||||
imm,
|
.BinaryImm(new_opcode, ty, imm, args[1]);
|
||||||
args[1],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,9 +510,12 @@ fn simplify(pos: &mut FuncCursor, inst: Inst) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstructionData::CondTrap { .. } |
|
InstructionData::CondTrap { .. }
|
||||||
InstructionData::Branch { .. } |
|
| InstructionData::Branch { .. }
|
||||||
InstructionData::Ternary { opcode: Opcode::Select, .. } => {
|
| InstructionData::Ternary {
|
||||||
|
opcode: Opcode::Select,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Fold away a redundant `bint`.
|
// Fold away a redundant `bint`.
|
||||||
let maybe = {
|
let maybe = {
|
||||||
let args = pos.func.dfg.inst_args(inst);
|
let args = pos.func.dfg.inst_args(inst);
|
||||||
|
|||||||
@@ -90,8 +90,7 @@ impl Affinity {
|
|||||||
Affinity::Reg(rc) => {
|
Affinity::Reg(rc) => {
|
||||||
// If the preferred register class is a subclass of the constraint, there's no need
|
// If the preferred register class is a subclass of the constraint, there's no need
|
||||||
// to change anything.
|
// to change anything.
|
||||||
if constraint.kind != ConstraintKind::Stack &&
|
if constraint.kind != ConstraintKind::Stack && !constraint.regclass.has_subclass(rc)
|
||||||
!constraint.regclass.has_subclass(rc)
|
|
||||||
{
|
{
|
||||||
// If the register classes don't overlap, `intersect` returns `Unassigned`, and
|
// If the register classes don't overlap, `intersect` returns `Unassigned`, and
|
||||||
// we just keep our previous affinity.
|
// we just keep our previous affinity.
|
||||||
@@ -120,12 +119,10 @@ impl<'a> fmt::Display for DisplayAffinity<'a> {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
Affinity::Unassigned => write!(f, "unassigned"),
|
Affinity::Unassigned => write!(f, "unassigned"),
|
||||||
Affinity::Stack => write!(f, "stack"),
|
Affinity::Stack => write!(f, "stack"),
|
||||||
Affinity::Reg(rci) => {
|
Affinity::Reg(rci) => match self.1 {
|
||||||
match self.1 {
|
|
||||||
Some(regs) => write!(f, "{}", regs.rc(rci)),
|
Some(regs) => write!(f, "{}", regs.rc(rci)),
|
||||||
None => write!(f, "{}", rci),
|
None => write!(f, "{}", rci),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,8 +196,7 @@ impl<'a> Context<'a> {
|
|||||||
pred_inst,
|
pred_inst,
|
||||||
pred_ebb,
|
pred_ebb,
|
||||||
self.liveness.context(&self.func.layout),
|
self.liveness.context(&self.func.layout),
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
self.isolate_param(ebb, param);
|
self.isolate_param(ebb, param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,8 +218,8 @@ impl<'a> Context<'a> {
|
|||||||
// pre-spilled, and the rest of the virtual register would be forced to spill to the
|
// pre-spilled, and the rest of the virtual register would be forced to spill to the
|
||||||
// `incoming_arg` stack slot too.
|
// `incoming_arg` stack slot too.
|
||||||
if let ir::ValueDef::Param(def_ebb, def_num) = self.func.dfg.value_def(arg) {
|
if let ir::ValueDef::Param(def_ebb, def_num) = self.func.dfg.value_def(arg) {
|
||||||
if Some(def_ebb) == self.func.layout.entry_block() &&
|
if Some(def_ebb) == self.func.layout.entry_block()
|
||||||
self.func.signature.params[def_num].location.is_stack()
|
&& self.func.signature.params[def_num].location.is_stack()
|
||||||
{
|
{
|
||||||
dbg!("-> isolating function stack parameter {}", arg);
|
dbg!("-> isolating function stack parameter {}", arg);
|
||||||
let new_arg = self.isolate_arg(pred_ebb, pred_inst, argnum, arg);
|
let new_arg = self.isolate_arg(pred_ebb, pred_inst, argnum, arg);
|
||||||
@@ -303,16 +302,11 @@ impl<'a> Context<'a> {
|
|||||||
&self.encinfo
|
&self.encinfo
|
||||||
.operand_constraints(pos.func.encodings[inst])
|
.operand_constraints(pos.func.encodings[inst])
|
||||||
.expect("Bad copy encoding")
|
.expect("Bad copy encoding")
|
||||||
.outs
|
.outs[0],
|
||||||
[0],
|
|
||||||
);
|
);
|
||||||
self.liveness.create_dead(new_val, ebb, affinity);
|
self.liveness.create_dead(new_val, ebb, affinity);
|
||||||
self.liveness.extend_locally(
|
self.liveness
|
||||||
new_val,
|
.extend_locally(new_val, ebb, inst, &pos.func.layout);
|
||||||
ebb,
|
|
||||||
inst,
|
|
||||||
&pos.func.layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
new_val
|
new_val
|
||||||
}
|
}
|
||||||
@@ -353,16 +347,11 @@ impl<'a> Context<'a> {
|
|||||||
&self.encinfo
|
&self.encinfo
|
||||||
.operand_constraints(pos.func.encodings[inst])
|
.operand_constraints(pos.func.encodings[inst])
|
||||||
.expect("Bad copy encoding")
|
.expect("Bad copy encoding")
|
||||||
.outs
|
.outs[0],
|
||||||
[0],
|
|
||||||
);
|
);
|
||||||
self.liveness.create_dead(copy, inst, affinity);
|
self.liveness.create_dead(copy, inst, affinity);
|
||||||
self.liveness.extend_locally(
|
self.liveness
|
||||||
copy,
|
.extend_locally(copy, pred_ebb, pred_inst, &pos.func.layout);
|
||||||
pred_ebb,
|
|
||||||
pred_inst,
|
|
||||||
&pos.func.layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy;
|
pos.func.dfg.inst_variable_args_mut(pred_inst)[argnum] = copy;
|
||||||
|
|
||||||
@@ -422,12 +411,9 @@ impl<'a> Context<'a> {
|
|||||||
let node = Node::value(value, 0, self.func);
|
let node = Node::value(value, 0, self.func);
|
||||||
|
|
||||||
// Push this value and get the nearest dominating def back.
|
// Push this value and get the nearest dominating def back.
|
||||||
let parent = match self.forest.push_node(
|
let parent = match self.forest
|
||||||
node,
|
.push_node(node, self.func, self.domtree, self.preorder)
|
||||||
self.func,
|
{
|
||||||
self.domtree,
|
|
||||||
self.preorder,
|
|
||||||
) {
|
|
||||||
None => continue,
|
None => continue,
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
};
|
};
|
||||||
@@ -525,12 +511,8 @@ impl<'a> Context<'a> {
|
|||||||
// Can't merge because of interference. Insert a copy instead.
|
// Can't merge because of interference. Insert a copy instead.
|
||||||
let pred_ebb = self.func.layout.pp_ebb(pred_inst);
|
let pred_ebb = self.func.layout.pp_ebb(pred_inst);
|
||||||
let new_arg = self.isolate_arg(pred_ebb, pred_inst, argnum, arg);
|
let new_arg = self.isolate_arg(pred_ebb, pred_inst, argnum, arg);
|
||||||
self.virtregs.insert_single(
|
self.virtregs
|
||||||
param,
|
.insert_single(param, new_arg, self.func, self.preorder);
|
||||||
new_arg,
|
|
||||||
self.func,
|
|
||||||
self.preorder,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,12 +546,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Restrict the virtual copy nodes we look at and key the `set_id` and `value` properties
|
// Restrict the virtual copy nodes we look at and key the `set_id` and `value` properties
|
||||||
// of the nodes. Set_id 0 will be `param` and set_id 1 will be `arg`.
|
// of the nodes. Set_id 0 will be `param` and set_id 1 will be `arg`.
|
||||||
self.vcopies.set_filter(
|
self.vcopies
|
||||||
[param, arg],
|
.set_filter([param, arg], func, self.virtregs, preorder);
|
||||||
func,
|
|
||||||
self.virtregs,
|
|
||||||
preorder,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now create an ordered sequence of dom-forest nodes from three sources: The two virtual
|
// Now create an ordered sequence of dom-forest nodes from three sources: The two virtual
|
||||||
// registers and the filtered virtual copies.
|
// registers and the filtered virtual copies.
|
||||||
@@ -625,8 +603,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Check if the parent value interferes with the virtual copy.
|
// Check if the parent value interferes with the virtual copy.
|
||||||
let inst = node.def.unwrap_inst();
|
let inst = node.def.unwrap_inst();
|
||||||
if node.set_id != parent.set_id &&
|
if node.set_id != parent.set_id
|
||||||
self.liveness[parent.value].reaches_use(inst, node.ebb, ctx)
|
&& self.liveness[parent.value].reaches_use(inst, node.ebb, ctx)
|
||||||
{
|
{
|
||||||
dbg!(
|
dbg!(
|
||||||
" - interference: {} overlaps vcopy at {}:{}",
|
" - interference: {} overlaps vcopy at {}:{}",
|
||||||
@@ -649,8 +627,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Both node and parent are values, so check for interference.
|
// Both node and parent are values, so check for interference.
|
||||||
debug_assert!(node.is_value() && parent.is_value());
|
debug_assert!(node.is_value() && parent.is_value());
|
||||||
if node.set_id != parent.set_id &&
|
if node.set_id != parent.set_id
|
||||||
self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx)
|
&& self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx)
|
||||||
{
|
{
|
||||||
// The two values are interfering.
|
// The two values are interfering.
|
||||||
dbg!(" - interference: {} overlaps def of {}", parent, node.value);
|
dbg!(" - interference: {} overlaps def of {}", parent, node.value);
|
||||||
@@ -945,9 +923,8 @@ impl VirtualCopies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reorder the predecessor branches as required by the dominator forest.
|
// Reorder the predecessor branches as required by the dominator forest.
|
||||||
self.branches.sort_unstable_by(|&(a, _), &(b, _)| {
|
self.branches
|
||||||
preorder.pre_cmp(a, b, &func.layout)
|
.sort_unstable_by(|&(a, _), &(b, _)| preorder.pre_cmp(a, b, &func.layout));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next unmerged parameter value.
|
/// Get the next unmerged parameter value.
|
||||||
@@ -1097,9 +1074,9 @@ where
|
|||||||
let ord = match (self.a.peek(), self.b.peek()) {
|
let ord = match (self.a.peek(), self.b.peek()) {
|
||||||
(Some(a), Some(b)) => {
|
(Some(a), Some(b)) => {
|
||||||
let layout = self.layout;
|
let layout = self.layout;
|
||||||
self.preorder.pre_cmp_ebb(a.ebb, b.ebb).then_with(|| {
|
self.preorder
|
||||||
layout.cmp(a.def, b.def)
|
.pre_cmp_ebb(a.ebb, b.ebb)
|
||||||
})
|
.then_with(|| layout.cmp(a.def, b.def))
|
||||||
}
|
}
|
||||||
(Some(_), None) => cmp::Ordering::Less,
|
(Some(_), None) => cmp::Ordering::Less,
|
||||||
(None, Some(_)) => cmp::Ordering::Greater,
|
(None, Some(_)) => cmp::Ordering::Greater,
|
||||||
|
|||||||
@@ -51,10 +51,10 @@ use isa::{regs_overlap, RegClass, RegInfo, RegUnit};
|
|||||||
use packed_option::PackedOption;
|
use packed_option::PackedOption;
|
||||||
use regalloc::RegDiversions;
|
use regalloc::RegDiversions;
|
||||||
use regalloc::affinity::Affinity;
|
use regalloc::affinity::Affinity;
|
||||||
use regalloc::register_set::RegisterSet;
|
|
||||||
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
use regalloc::liverange::{LiveRange, LiveRangeContext};
|
use regalloc::liverange::{LiveRange, LiveRangeContext};
|
||||||
|
use regalloc::register_set::RegisterSet;
|
||||||
use regalloc::solver::{Solver, SolverError};
|
use regalloc::solver::{Solver, SolverError};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use timing;
|
use timing;
|
||||||
@@ -142,9 +142,10 @@ impl Coloring {
|
|||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
/// Run the coloring algorithm.
|
/// Run the coloring algorithm.
|
||||||
fn run(&mut self, tracker: &mut LiveValueTracker) {
|
fn run(&mut self, tracker: &mut LiveValueTracker) {
|
||||||
self.cur.func.locations.resize(
|
self.cur
|
||||||
self.cur.func.dfg.num_values(),
|
.func
|
||||||
);
|
.locations
|
||||||
|
.resize(self.cur.func.dfg.num_values());
|
||||||
|
|
||||||
// Visit blocks in reverse post-order. We need to ensure that at least one predecessor has
|
// Visit blocks in reverse post-order. We need to ensure that at least one predecessor has
|
||||||
// been visited before each EBB. That guarantees that the EBB arguments have been colored.
|
// been visited before each EBB. That guarantees that the EBB arguments have been colored.
|
||||||
@@ -372,10 +373,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Update the global register set which has no diversions.
|
// Update the global register set which has no diversions.
|
||||||
if !lv.is_local {
|
if !lv.is_local {
|
||||||
regs.global.free(
|
regs.global
|
||||||
rc,
|
.free(rc, self.cur.func.locations[lv.value].unwrap_reg());
|
||||||
self.cur.func.locations[lv.value].unwrap_reg(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -500,20 +499,14 @@ impl<'a> Context<'a> {
|
|||||||
// already in a register.
|
// already in a register.
|
||||||
let cur_reg = self.divert.reg(value, &self.cur.func.locations);
|
let cur_reg = self.divert.reg(value, &self.cur.func.locations);
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::FixedReg(regunit) |
|
ConstraintKind::FixedReg(regunit) | ConstraintKind::FixedTied(regunit) => {
|
||||||
ConstraintKind::FixedTied(regunit) => {
|
|
||||||
// Add the fixed constraint even if `cur_reg == regunit`.
|
// Add the fixed constraint even if `cur_reg == regunit`.
|
||||||
// It is possible that we will want to convert the value to a variable later,
|
// It is possible that we will want to convert the value to a variable later,
|
||||||
// and this identity assignment prevents that from happening.
|
// and this identity assignment prevents that from happening.
|
||||||
self.solver.reassign_in(
|
self.solver
|
||||||
value,
|
.reassign_in(value, op.regclass, cur_reg, regunit);
|
||||||
op.regclass,
|
|
||||||
cur_reg,
|
|
||||||
regunit,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg | ConstraintKind::Tied(_) => {
|
||||||
ConstraintKind::Tied(_) => {
|
|
||||||
if !op.regclass.contains(cur_reg) {
|
if !op.regclass.contains(cur_reg) {
|
||||||
self.solver.add_var(value, op.regclass, cur_reg);
|
self.solver.add_var(value, op.regclass, cur_reg);
|
||||||
}
|
}
|
||||||
@@ -541,8 +534,7 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
for (op, &value) in constraints.iter().zip(self.cur.func.dfg.inst_args(inst)) {
|
for (op, &value) in constraints.iter().zip(self.cur.func.dfg.inst_args(inst)) {
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg | ConstraintKind::Tied(_) => {
|
||||||
ConstraintKind::Tied(_) => {
|
|
||||||
let cur_reg = self.divert.reg(value, &self.cur.func.locations);
|
let cur_reg = self.divert.reg(value, &self.cur.func.locations);
|
||||||
// This is the opposite condition of `program_input_constraints()`.
|
// This is the opposite condition of `program_input_constraints()`.
|
||||||
if op.regclass.contains(cur_reg) {
|
if op.regclass.contains(cur_reg) {
|
||||||
@@ -556,9 +548,9 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstraintKind::FixedReg(_) |
|
ConstraintKind::FixedReg(_)
|
||||||
ConstraintKind::FixedTied(_) |
|
| ConstraintKind::FixedTied(_)
|
||||||
ConstraintKind::Stack => {}
|
| ConstraintKind::Stack => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,9 +643,9 @@ impl<'a> Context<'a> {
|
|||||||
Pred: FnMut(&LiveRange, LiveRangeContext<Layout>) -> bool,
|
Pred: FnMut(&LiveRange, LiveRangeContext<Layout>) -> bool,
|
||||||
{
|
{
|
||||||
for rdiv in self.divert.all() {
|
for rdiv in self.divert.all() {
|
||||||
let lr = self.liveness.get(rdiv.value).expect(
|
let lr = self.liveness
|
||||||
"Missing live range for diverted register",
|
.get(rdiv.value)
|
||||||
);
|
.expect("Missing live range for diverted register");
|
||||||
if pred(lr, self.liveness.context(&self.cur.func.layout)) {
|
if pred(lr, self.liveness.context(&self.cur.func.layout)) {
|
||||||
if let Affinity::Reg(rci) = lr.affinity {
|
if let Affinity::Reg(rci) = lr.affinity {
|
||||||
let rc = self.reginfo.rc(rci);
|
let rc = self.reginfo.rc(rci);
|
||||||
@@ -703,8 +695,7 @@ impl<'a> Context<'a> {
|
|||||||
) {
|
) {
|
||||||
for (op, lv) in constraints.iter().zip(defs) {
|
for (op, lv) in constraints.iter().zip(defs) {
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::FixedReg(reg) |
|
ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => {
|
||||||
ConstraintKind::FixedTied(reg) => {
|
|
||||||
self.add_fixed_output(lv.value, op.regclass, reg, throughs);
|
self.add_fixed_output(lv.value, op.regclass, reg, throughs);
|
||||||
if !lv.is_local && !global_regs.is_avail(op.regclass, reg) {
|
if !lv.is_local && !global_regs.is_avail(op.regclass, reg) {
|
||||||
dbg!(
|
dbg!(
|
||||||
@@ -716,9 +707,7 @@ impl<'a> Context<'a> {
|
|||||||
*replace_global_defines = true;
|
*replace_global_defines = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg | ConstraintKind::Tied(_) | ConstraintKind::Stack => {}
|
||||||
ConstraintKind::Tied(_) |
|
|
||||||
ConstraintKind::Stack => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -801,9 +790,9 @@ impl<'a> Context<'a> {
|
|||||||
) {
|
) {
|
||||||
for (op, lv) in constraints.iter().zip(defs) {
|
for (op, lv) in constraints.iter().zip(defs) {
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::FixedReg(_) |
|
ConstraintKind::FixedReg(_)
|
||||||
ConstraintKind::FixedTied(_) |
|
| ConstraintKind::FixedTied(_)
|
||||||
ConstraintKind::Stack => continue,
|
| ConstraintKind::Stack => continue,
|
||||||
ConstraintKind::Reg => {
|
ConstraintKind::Reg => {
|
||||||
self.solver.add_def(lv.value, op.regclass, !lv.is_local);
|
self.solver.add_def(lv.value, op.regclass, !lv.is_local);
|
||||||
}
|
}
|
||||||
@@ -816,8 +805,7 @@ impl<'a> Context<'a> {
|
|||||||
op.regclass,
|
op.regclass,
|
||||||
self.divert.reg(arg, &self.cur.func.locations),
|
self.divert.reg(arg, &self.cur.func.locations),
|
||||||
!lv.is_local,
|
!lv.is_local,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
// The value we're tied to has been assigned to a fixed register.
|
// The value we're tied to has been assigned to a fixed register.
|
||||||
// We need to make sure that fixed output register is compatible with the
|
// We need to make sure that fixed output register is compatible with the
|
||||||
// global register set.
|
// global register set.
|
||||||
@@ -881,8 +869,8 @@ impl<'a> Context<'a> {
|
|||||||
// not actually constrained by the instruction. We just want it out of the way.
|
// not actually constrained by the instruction. We just want it out of the way.
|
||||||
let toprc2 = self.reginfo.toprc(rci);
|
let toprc2 = self.reginfo.toprc(rci);
|
||||||
let reg2 = self.divert.reg(lv.value, &self.cur.func.locations);
|
let reg2 = self.divert.reg(lv.value, &self.cur.func.locations);
|
||||||
if rc.contains(reg2) && self.solver.can_add_var(lv.value, toprc2, reg2) &&
|
if rc.contains(reg2) && self.solver.can_add_var(lv.value, toprc2, reg2)
|
||||||
!self.is_live_on_outgoing_edge(lv.value)
|
&& !self.is_live_on_outgoing_edge(lv.value)
|
||||||
{
|
{
|
||||||
self.solver.add_through_var(lv.value, toprc2, reg2);
|
self.solver.add_through_var(lv.value, toprc2, reg2);
|
||||||
return true;
|
return true;
|
||||||
@@ -911,10 +899,10 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
Table(jt) => {
|
Table(jt) => {
|
||||||
let lr = &self.liveness[value];
|
let lr = &self.liveness[value];
|
||||||
!lr.is_local() &&
|
!lr.is_local()
|
||||||
self.cur.func.jump_tables[jt].entries().any(|(_, ebb)| {
|
&& self.cur.func.jump_tables[jt]
|
||||||
lr.is_livein(ebb, ctx)
|
.entries()
|
||||||
})
|
.any(|(_, ebb)| lr.is_livein(ebb, ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,7 +928,9 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
for m in self.solver.moves() {
|
for m in self.solver.moves() {
|
||||||
match *m {
|
match *m {
|
||||||
Reg { value, from, to, .. } => {
|
Reg {
|
||||||
|
value, from, to, ..
|
||||||
|
} => {
|
||||||
self.divert.regmove(value, from, to);
|
self.divert.regmove(value, from, to);
|
||||||
self.cur.ins().regmove(value, from, to);
|
self.cur.ins().regmove(value, from, to);
|
||||||
}
|
}
|
||||||
@@ -951,10 +941,10 @@ impl<'a> Context<'a> {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
debug_assert_eq!(slot[to_slot].expand(), None, "Overwriting slot in use");
|
debug_assert_eq!(slot[to_slot].expand(), None, "Overwriting slot in use");
|
||||||
let ss = self.cur.func.stack_slots.get_emergency_slot(
|
let ss = self.cur
|
||||||
self.cur.func.dfg.value_type(value),
|
.func
|
||||||
&slot[0..spills],
|
.stack_slots
|
||||||
);
|
.get_emergency_slot(self.cur.func.dfg.value_type(value), &slot[0..spills]);
|
||||||
slot[to_slot] = ss.into();
|
slot[to_slot] = ss.into();
|
||||||
self.divert.regspill(value, from, ss);
|
self.divert.regspill(value, from, ss);
|
||||||
self.cur.ins().regspill(value, from, ss);
|
self.cur.ins().regspill(value, from, ss);
|
||||||
@@ -1013,8 +1003,7 @@ impl<'a> Context<'a> {
|
|||||||
if match self.cur.func.dfg.value_def(lv.value) {
|
if match self.cur.func.dfg.value_def(lv.value) {
|
||||||
ValueDef::Result(i, _) => i != inst,
|
ValueDef::Result(i, _) => i != inst,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
} {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if lv.is_local || !lv.affinity.is_reg() {
|
if lv.is_local || !lv.affinity.is_reg() {
|
||||||
@@ -1072,10 +1061,8 @@ impl<'a> Context<'a> {
|
|||||||
};
|
};
|
||||||
regs.input.free(rc, loc.unwrap_reg());
|
regs.input.free(rc, loc.unwrap_reg());
|
||||||
if !lv.is_local {
|
if !lv.is_local {
|
||||||
regs.global.free(
|
regs.global
|
||||||
rc,
|
.free(rc, self.cur.func.locations[lv.value].unwrap_reg());
|
||||||
self.cur.func.locations[lv.value].unwrap_reg(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1096,8 +1083,7 @@ fn program_input_abi(
|
|||||||
) {
|
) {
|
||||||
for (abi, &value) in abi_types.iter().zip(func.dfg.inst_variable_args(inst)) {
|
for (abi, &value) in abi_types.iter().zip(func.dfg.inst_variable_args(inst)) {
|
||||||
if let ArgumentLoc::Reg(reg) = abi.location {
|
if let ArgumentLoc::Reg(reg) = abi.location {
|
||||||
if let Affinity::Reg(rci) =
|
if let Affinity::Reg(rci) = liveness
|
||||||
liveness
|
|
||||||
.get(value)
|
.get(value)
|
||||||
.expect("ABI register must have live range")
|
.expect("ABI register must have live range")
|
||||||
.affinity
|
.affinity
|
||||||
|
|||||||
@@ -140,13 +140,8 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pass: Coloring.
|
// Pass: Coloring.
|
||||||
self.coloring.run(
|
self.coloring
|
||||||
isa,
|
.run(isa, func, domtree, &mut self.liveness, &mut self.tracker);
|
||||||
func,
|
|
||||||
domtree,
|
|
||||||
&mut self.liveness,
|
|
||||||
&mut self.tracker,
|
|
||||||
);
|
|
||||||
|
|
||||||
if isa.flags().enable_verifier() {
|
if isa.flags().enable_verifier() {
|
||||||
verify_context(func, cfg, domtree, isa)?;
|
verify_context(func, cfg, domtree, isa)?;
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ pub struct RegDiversions {
|
|||||||
impl RegDiversions {
|
impl RegDiversions {
|
||||||
/// Create a new empty diversion tracker.
|
/// Create a new empty diversion tracker.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { current: Vec::new() }
|
Self {
|
||||||
|
current: Vec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the tracker, preparing for a new EBB.
|
/// Clear the tracker, preparing for a new EBB.
|
||||||
@@ -152,11 +154,10 @@ impl RegDiversions {
|
|||||||
///
|
///
|
||||||
/// Returns the `to` location of the removed diversion.
|
/// Returns the `to` location of the removed diversion.
|
||||||
pub fn remove(&mut self, value: Value) -> Option<ValueLoc> {
|
pub fn remove(&mut self, value: Value) -> Option<ValueLoc> {
|
||||||
self.current.iter().position(|d| d.value == value).map(
|
self.current
|
||||||
|i| {
|
.iter()
|
||||||
self.current.swap_remove(i).to
|
.position(|d| d.value == value)
|
||||||
},
|
.map(|i| self.current.swap_remove(i).to)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an object that can display the diversions.
|
/// Return an object that can display the diversions.
|
||||||
|
|||||||
@@ -187,15 +187,15 @@ impl LiveValueTracker {
|
|||||||
// If the immediate dominator exits, we must have a stored list for it. This is a
|
// If the immediate dominator exits, we must have a stored list for it. This is a
|
||||||
// requirement to the order EBBs are visited: All dominators must have been processed
|
// requirement to the order EBBs are visited: All dominators must have been processed
|
||||||
// before the current EBB.
|
// before the current EBB.
|
||||||
let idom_live_list = self.idom_sets.get(&idom).expect(
|
let idom_live_list = self.idom_sets
|
||||||
"No stored live set for dominator",
|
.get(&idom)
|
||||||
);
|
.expect("No stored live set for dominator");
|
||||||
let ctx = liveness.context(layout);
|
let ctx = liveness.context(layout);
|
||||||
// Get just the values that are live-in to `ebb`.
|
// Get just the values that are live-in to `ebb`.
|
||||||
for &value in idom_live_list.as_slice(&self.idom_pool) {
|
for &value in idom_live_list.as_slice(&self.idom_pool) {
|
||||||
let lr = liveness.get(value).expect(
|
let lr = liveness
|
||||||
"Immediate dominator value has no live range",
|
.get(value)
|
||||||
);
|
.expect("Immediate dominator value has no live range");
|
||||||
|
|
||||||
// Check if this value is live-in here.
|
// Check if this value is live-in here.
|
||||||
if let Some(endpoint) = lr.livein_local_end(ebb, ctx) {
|
if let Some(endpoint) = lr.livein_local_end(ebb, ctx) {
|
||||||
@@ -217,17 +217,13 @@ impl LiveValueTracker {
|
|||||||
// This is a dead EBB parameter which is not even live into the first
|
// This is a dead EBB parameter which is not even live into the first
|
||||||
// instruction in the EBB.
|
// instruction in the EBB.
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
local_ebb,
|
local_ebb, ebb,
|
||||||
ebb,
|
|
||||||
"EBB parameter live range ends at wrong EBB header"
|
"EBB parameter live range ends at wrong EBB header"
|
||||||
);
|
);
|
||||||
// Give this value a fake endpoint that is the first instruction in the EBB.
|
// Give this value a fake endpoint that is the first instruction in the EBB.
|
||||||
// We expect it to be removed by calling `drop_dead_args()`.
|
// We expect it to be removed by calling `drop_dead_args()`.
|
||||||
self.live.push(
|
self.live
|
||||||
value,
|
.push(value, layout.first_inst(ebb).expect("Empty EBB"), lr);
|
||||||
layout.first_inst(ebb).expect("Empty EBB"),
|
|
||||||
lr,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ use entity::SparseMap;
|
|||||||
use flowgraph::ControlFlowGraph;
|
use flowgraph::ControlFlowGraph;
|
||||||
use ir::dfg::ValueDef;
|
use ir::dfg::ValueDef;
|
||||||
use ir::{Ebb, Function, Inst, Layout, ProgramPoint, Value};
|
use ir::{Ebb, Function, Inst, Layout, ProgramPoint, Value};
|
||||||
use isa::{EncInfo, TargetIsa, OperandConstraint};
|
use isa::{EncInfo, OperandConstraint, TargetIsa};
|
||||||
use regalloc::affinity::Affinity;
|
use regalloc::affinity::Affinity;
|
||||||
use regalloc::liverange::{LiveRange, LiveRangeContext, LiveRangeForest};
|
use regalloc::liverange::{LiveRange, LiveRangeContext, LiveRangeForest};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@@ -217,9 +217,9 @@ fn get_or_create<'a>(
|
|||||||
.map(Affinity::new)
|
.map(Affinity::new)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
// If this is a call, get the return value affinity.
|
// If this is a call, get the return value affinity.
|
||||||
func.dfg.call_signature(inst).map(|sig| {
|
func.dfg
|
||||||
Affinity::abi(&func.dfg.signatures[sig].returns[rnum], isa)
|
.call_signature(inst)
|
||||||
})
|
.map(|sig| Affinity::abi(&func.dfg.signatures[sig].returns[rnum], isa))
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
@@ -336,9 +336,8 @@ impl Liveness {
|
|||||||
where
|
where
|
||||||
PP: Into<ProgramPoint>,
|
PP: Into<ProgramPoint>,
|
||||||
{
|
{
|
||||||
let old = self.ranges.insert(
|
let old = self.ranges
|
||||||
LiveRange::new(value, def.into(), affinity),
|
.insert(LiveRange::new(value, def.into(), affinity));
|
||||||
);
|
|
||||||
debug_assert!(old.is_none(), "{} already has a live range", value);
|
debug_assert!(old.is_none(), "{} already has a live range", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -249,13 +249,12 @@ impl<PO: ProgramOrder> GenLiveRange<PO> {
|
|||||||
//
|
//
|
||||||
// We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't
|
// We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't
|
||||||
// check it without a method for getting `to`'s EBB.
|
// check it without a method for getting `to`'s EBB.
|
||||||
if order.cmp(ebb, self.def_end) != Ordering::Greater &&
|
if order.cmp(ebb, self.def_end) != Ordering::Greater
|
||||||
order.cmp(to, self.def_begin) != Ordering::Less
|
&& order.cmp(to, self.def_begin) != Ordering::Less
|
||||||
{
|
{
|
||||||
let to_pp = to.into();
|
let to_pp = to.into();
|
||||||
debug_assert_ne!(
|
debug_assert_ne!(
|
||||||
to_pp,
|
to_pp, self.def_begin,
|
||||||
self.def_begin,
|
|
||||||
"Can't use value in the defining instruction."
|
"Can't use value in the defining instruction."
|
||||||
);
|
);
|
||||||
if order.cmp(to, self.def_end) == Ordering::Greater {
|
if order.cmp(to, self.def_end) == Ordering::Greater {
|
||||||
@@ -411,8 +410,8 @@ impl<PO: ProgramOrder> GenLiveRange<PO> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for an overlap with the local range.
|
// Check for an overlap with the local range.
|
||||||
if ctx.order.cmp(def, self.def_begin) != Ordering::Less &&
|
if ctx.order.cmp(def, self.def_begin) != Ordering::Less
|
||||||
ctx.order.cmp(def, self.def_end) == Ordering::Less
|
&& ctx.order.cmp(def, self.def_end) == Ordering::Less
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -427,8 +426,8 @@ impl<PO: ProgramOrder> GenLiveRange<PO> {
|
|||||||
/// Check if this live range reaches a use at `user` in `ebb`.
|
/// Check if this live range reaches a use at `user` in `ebb`.
|
||||||
pub fn reaches_use(&self, user: Inst, ebb: Ebb, ctx: LiveRangeContext<PO>) -> bool {
|
pub fn reaches_use(&self, user: Inst, ebb: Ebb, ctx: LiveRangeContext<PO>) -> bool {
|
||||||
// Check for an overlap with the local range.
|
// Check for an overlap with the local range.
|
||||||
if ctx.order.cmp(user, self.def_begin) == Ordering::Greater &&
|
if ctx.order.cmp(user, self.def_begin) == Ordering::Greater
|
||||||
ctx.order.cmp(user, self.def_end) != Ordering::Greater
|
&& ctx.order.cmp(user, self.def_end) != Ordering::Greater
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -535,8 +534,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
self.cmp(lr.def_end, begin) == Ordering::Less ||
|
self.cmp(lr.def_end, begin) == Ordering::Less
|
||||||
self.cmp(lr.def_begin, end) == Ordering::Greater,
|
|| self.cmp(lr.def_begin, end) == Ordering::Greater,
|
||||||
"Interval can't overlap the def EBB"
|
"Interval can't overlap the def EBB"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
//!
|
//!
|
||||||
//! This module contains data structures and algorithms used for register allocation.
|
//! This module contains data structures and algorithms used for register allocation.
|
||||||
|
|
||||||
pub mod register_set;
|
|
||||||
pub mod coloring;
|
pub mod coloring;
|
||||||
pub mod live_value_tracker;
|
pub mod live_value_tracker;
|
||||||
pub mod liveness;
|
pub mod liveness;
|
||||||
pub mod liverange;
|
pub mod liverange;
|
||||||
|
pub mod register_set;
|
||||||
pub mod virtregs;
|
pub mod virtregs;
|
||||||
|
|
||||||
mod affinity;
|
mod affinity;
|
||||||
@@ -18,6 +18,6 @@ mod reload;
|
|||||||
mod solver;
|
mod solver;
|
||||||
mod spilling;
|
mod spilling;
|
||||||
|
|
||||||
pub use self::register_set::RegisterSet;
|
|
||||||
pub use self::context::Context;
|
pub use self::context::Context;
|
||||||
pub use self::diversion::RegDiversions;
|
pub use self::diversion::RegDiversions;
|
||||||
|
pub use self::register_set::RegisterSet;
|
||||||
|
|||||||
@@ -114,9 +114,10 @@ impl Pressure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute per-class limits from `usable`.
|
// Compute per-class limits from `usable`.
|
||||||
for (toprc, rc) in p.toprc.iter_mut().take_while(|t| t.num_toprcs > 0).zip(
|
for (toprc, rc) in p.toprc
|
||||||
reginfo.classes,
|
.iter_mut()
|
||||||
)
|
.take_while(|t| t.num_toprcs > 0)
|
||||||
|
.zip(reginfo.classes)
|
||||||
{
|
{
|
||||||
toprc.limit = usable.iter(rc).len() as u32;
|
toprc.limit = usable.iter(rc).len() as u32;
|
||||||
toprc.width = rc.width;
|
toprc.width = rc.width;
|
||||||
@@ -203,16 +204,16 @@ impl Pressure {
|
|||||||
///
|
///
|
||||||
/// This does not check if there are enough registers available.
|
/// This does not check if there are enough registers available.
|
||||||
pub fn take(&mut self, rc: RegClass) {
|
pub fn take(&mut self, rc: RegClass) {
|
||||||
self.toprc.get_mut(rc.toprc as usize).map(
|
self.toprc
|
||||||
|t| t.base_count += 1,
|
.get_mut(rc.toprc as usize)
|
||||||
);
|
.map(|t| t.base_count += 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Free a register in `rc`.
|
/// Free a register in `rc`.
|
||||||
pub fn free(&mut self, rc: RegClass) {
|
pub fn free(&mut self, rc: RegClass) {
|
||||||
self.toprc.get_mut(rc.toprc as usize).map(
|
self.toprc
|
||||||
|t| t.base_count -= 1,
|
.get_mut(rc.toprc as usize)
|
||||||
);
|
.map(|t| t.base_count -= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset all counts to 0, both base and transient.
|
/// Reset all counts to 0, both base and transient.
|
||||||
@@ -229,9 +230,9 @@ impl Pressure {
|
|||||||
pub fn take_transient(&mut self, rc: RegClass) -> Result<(), RegClassMask> {
|
pub fn take_transient(&mut self, rc: RegClass) -> Result<(), RegClassMask> {
|
||||||
let mask = self.check_avail(rc);
|
let mask = self.check_avail(rc);
|
||||||
if mask == 0 {
|
if mask == 0 {
|
||||||
self.toprc.get_mut(rc.toprc as usize).map(|t| {
|
self.toprc
|
||||||
t.transient_count += 1
|
.get_mut(rc.toprc as usize)
|
||||||
});
|
.map(|t| t.transient_count += 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(mask)
|
Err(mask)
|
||||||
|
|||||||
@@ -104,9 +104,10 @@ impl RegisterSet {
|
|||||||
///
|
///
|
||||||
/// This assumes that unused bits are 1.
|
/// This assumes that unused bits are 1.
|
||||||
pub fn interferes_with(&self, other: &Self) -> bool {
|
pub fn interferes_with(&self, other: &Self) -> bool {
|
||||||
self.avail.iter().zip(&other.avail).any(
|
self.avail
|
||||||
|(&x, &y)| (x | y) != !0,
|
.iter()
|
||||||
)
|
.zip(&other.avail)
|
||||||
|
.any(|(&x, &y)| (x | y) != !0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intersect this set of registers with `other`. This has the effect of removing any register
|
/// Intersect this set of registers with `other`. This has the effect of removing any register
|
||||||
@@ -203,9 +204,10 @@ impl<'a> fmt::Display for DisplayRegisterSet<'a> {
|
|||||||
bank.names
|
bank.names
|
||||||
.get(offset as usize)
|
.get(offset as usize)
|
||||||
.and_then(|name| name.chars().nth(1))
|
.and_then(|name| name.chars().nth(1))
|
||||||
.unwrap_or_else(
|
.unwrap_or_else(|| char::from_digit(
|
||||||
|| char::from_digit(u32::from(offset % 10), 10).unwrap(),
|
u32::from(offset % 10),
|
||||||
)
|
10
|
||||||
|
).unwrap())
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,10 +166,10 @@ impl<'a> Context<'a> {
|
|||||||
if arg.affinity.is_stack() {
|
if arg.affinity.is_stack() {
|
||||||
// An incoming register parameter was spilled. Replace the parameter value
|
// An incoming register parameter was spilled. Replace the parameter value
|
||||||
// with a temporary register value that is immediately spilled.
|
// with a temporary register value that is immediately spilled.
|
||||||
let reg = self.cur.func.dfg.replace_ebb_param(
|
let reg = self.cur
|
||||||
arg.value,
|
.func
|
||||||
abi.value_type,
|
.dfg
|
||||||
);
|
.replace_ebb_param(arg.value, abi.value_type);
|
||||||
let affinity = Affinity::abi(&abi, self.cur.isa);
|
let affinity = Affinity::abi(&abi, self.cur.isa);
|
||||||
self.liveness.create_dead(reg, ebb, affinity);
|
self.liveness.create_dead(reg, ebb, affinity);
|
||||||
self.insert_spill(ebb, arg.value, reg);
|
self.insert_spill(ebb, arg.value, reg);
|
||||||
@@ -199,9 +199,9 @@ impl<'a> Context<'a> {
|
|||||||
self.cur.use_srcloc(inst);
|
self.cur.use_srcloc(inst);
|
||||||
|
|
||||||
// Get the operand constraints for `inst` that we are trying to satisfy.
|
// Get the operand constraints for `inst` that we are trying to satisfy.
|
||||||
let constraints = self.encinfo.operand_constraints(encoding).expect(
|
let constraints = self.encinfo
|
||||||
"Missing instruction encoding",
|
.operand_constraints(encoding)
|
||||||
);
|
.expect("Missing instruction encoding");
|
||||||
|
|
||||||
// Identify reload candidates.
|
// Identify reload candidates.
|
||||||
debug_assert!(self.candidates.is_empty());
|
debug_assert!(self.candidates.is_empty());
|
||||||
@@ -226,12 +226,8 @@ impl<'a> Context<'a> {
|
|||||||
// Create a live range for the new reload.
|
// Create a live range for the new reload.
|
||||||
let affinity = Affinity::Reg(cand.regclass.into());
|
let affinity = Affinity::Reg(cand.regclass.into());
|
||||||
self.liveness.create_dead(reg, fill, affinity);
|
self.liveness.create_dead(reg, fill, affinity);
|
||||||
self.liveness.extend_locally(
|
self.liveness
|
||||||
reg,
|
.extend_locally(reg, ebb, inst, &self.cur.func.layout);
|
||||||
ebb,
|
|
||||||
inst,
|
|
||||||
&self.cur.func.layout,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite instruction arguments.
|
// Rewrite instruction arguments.
|
||||||
@@ -280,19 +276,18 @@ impl<'a> Context<'a> {
|
|||||||
// Same thing for spilled call return values.
|
// Same thing for spilled call return values.
|
||||||
let retvals = &defs[constraints.outs.len()..];
|
let retvals = &defs[constraints.outs.len()..];
|
||||||
if !retvals.is_empty() {
|
if !retvals.is_empty() {
|
||||||
let sig = self.cur.func.dfg.call_signature(inst).expect(
|
let sig = self.cur
|
||||||
"Extra results on non-call instruction",
|
.func
|
||||||
);
|
.dfg
|
||||||
|
.call_signature(inst)
|
||||||
|
.expect("Extra results on non-call instruction");
|
||||||
for (i, lv) in retvals.iter().enumerate() {
|
for (i, lv) in retvals.iter().enumerate() {
|
||||||
let abi = self.cur.func.dfg.signatures[sig].returns[i];
|
let abi = self.cur.func.dfg.signatures[sig].returns[i];
|
||||||
debug_assert!(abi.location.is_reg());
|
debug_assert!(abi.location.is_reg());
|
||||||
if lv.affinity.is_stack() {
|
if lv.affinity.is_stack() {
|
||||||
let reg = self.cur.func.dfg.replace_result(lv.value, abi.value_type);
|
let reg = self.cur.func.dfg.replace_result(lv.value, abi.value_type);
|
||||||
self.liveness.create_dead(
|
self.liveness
|
||||||
reg,
|
.create_dead(reg, inst, Affinity::abi(&abi, self.cur.isa));
|
||||||
inst,
|
|
||||||
Affinity::abi(&abi, self.cur.isa),
|
|
||||||
);
|
|
||||||
self.insert_spill(ebb, lv.value, reg);
|
self.insert_spill(ebb, lv.value, reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,12 +350,8 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Update live ranges.
|
// Update live ranges.
|
||||||
self.liveness.move_def_locally(stack, inst);
|
self.liveness.move_def_locally(stack, inst);
|
||||||
self.liveness.extend_locally(
|
self.liveness
|
||||||
reg,
|
.extend_locally(reg, ebb, inst, &self.cur.func.layout);
|
||||||
ebb,
|
|
||||||
inst,
|
|
||||||
&self.cur.func.layout,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,8 +297,7 @@ impl Move {
|
|||||||
#[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
|
#[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
|
||||||
fn from_reg(&self) -> Option<(RegClass, RegUnit)> {
|
fn from_reg(&self) -> Option<(RegClass, RegUnit)> {
|
||||||
match *self {
|
match *self {
|
||||||
Move::Reg { rc, from, .. } |
|
Move::Reg { rc, from, .. } | Move::Spill { rc, from, .. } => Some((rc, from)),
|
||||||
Move::Spill { rc, from, .. } => Some((rc, from)),
|
|
||||||
Move::Fill { .. } => None,
|
Move::Fill { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,8 +305,7 @@ impl Move {
|
|||||||
/// Get the "to" register and register class, if possible.
|
/// Get the "to" register and register class, if possible.
|
||||||
fn to_reg(&self) -> Option<(RegClass, RegUnit)> {
|
fn to_reg(&self) -> Option<(RegClass, RegUnit)> {
|
||||||
match *self {
|
match *self {
|
||||||
Move::Reg { rc, to, .. } |
|
Move::Reg { rc, to, .. } | Move::Fill { rc, to, .. } => Some((rc, to)),
|
||||||
Move::Fill { rc, to, .. } => Some((rc, to)),
|
|
||||||
Move::Spill { .. } => None,
|
Move::Spill { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,8 +314,7 @@ impl Move {
|
|||||||
fn replace_to_reg(&mut self, new: RegUnit) -> RegUnit {
|
fn replace_to_reg(&mut self, new: RegUnit) -> RegUnit {
|
||||||
mem::replace(
|
mem::replace(
|
||||||
match *self {
|
match *self {
|
||||||
Move::Reg { ref mut to, .. } |
|
Move::Reg { ref mut to, .. } | Move::Fill { ref mut to, .. } => to,
|
||||||
Move::Fill { ref mut to, .. } => to,
|
|
||||||
Move::Spill { .. } => panic!("No to register in a spill {}", self),
|
Move::Spill { .. } => panic!("No to register in a spill {}", self),
|
||||||
},
|
},
|
||||||
new,
|
new,
|
||||||
@@ -348,18 +345,14 @@ impl Move {
|
|||||||
/// Get the value being moved.
|
/// Get the value being moved.
|
||||||
fn value(&self) -> Value {
|
fn value(&self) -> Value {
|
||||||
match *self {
|
match *self {
|
||||||
Move::Reg { value, .. } |
|
Move::Reg { value, .. } | Move::Fill { value, .. } | Move::Spill { value, .. } => value,
|
||||||
Move::Fill { value, .. } |
|
|
||||||
Move::Spill { value, .. } => value,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the associated register class.
|
/// Get the associated register class.
|
||||||
fn rc(&self) -> RegClass {
|
fn rc(&self) -> RegClass {
|
||||||
match *self {
|
match *self {
|
||||||
Move::Reg { rc, .. } |
|
Move::Reg { rc, .. } | Move::Fill { rc, .. } | Move::Spill { rc, .. } => rc,
|
||||||
Move::Fill { rc, .. } |
|
|
||||||
Move::Spill { rc, .. } => rc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,46 +365,40 @@ impl fmt::Display for Move {
|
|||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
rc,
|
rc,
|
||||||
} => {
|
} => write!(
|
||||||
write!(
|
|
||||||
f,
|
f,
|
||||||
"{}:{}({} -> {})",
|
"{}:{}({} -> {})",
|
||||||
value,
|
value,
|
||||||
rc,
|
rc,
|
||||||
rc.info.display_regunit(from),
|
rc.info.display_regunit(from),
|
||||||
rc.info.display_regunit(to)
|
rc.info.display_regunit(to)
|
||||||
)
|
),
|
||||||
}
|
|
||||||
Move::Spill {
|
Move::Spill {
|
||||||
value,
|
value,
|
||||||
from,
|
from,
|
||||||
to_slot,
|
to_slot,
|
||||||
rc,
|
rc,
|
||||||
} => {
|
} => write!(
|
||||||
write!(
|
|
||||||
f,
|
f,
|
||||||
"{}:{}({} -> slot {})",
|
"{}:{}({} -> slot {})",
|
||||||
value,
|
value,
|
||||||
rc,
|
rc,
|
||||||
rc.info.display_regunit(from),
|
rc.info.display_regunit(from),
|
||||||
to_slot
|
to_slot
|
||||||
)
|
),
|
||||||
}
|
|
||||||
Move::Fill {
|
Move::Fill {
|
||||||
value,
|
value,
|
||||||
from_slot,
|
from_slot,
|
||||||
to,
|
to,
|
||||||
rc,
|
rc,
|
||||||
} => {
|
} => write!(
|
||||||
write!(
|
|
||||||
f,
|
f,
|
||||||
"{}:{}(slot {} -> {})",
|
"{}:{}(slot {} -> {})",
|
||||||
value,
|
value,
|
||||||
rc,
|
rc,
|
||||||
from_slot,
|
from_slot,
|
||||||
rc.info.display_regunit(to)
|
rc.info.display_regunit(to)
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,9 +811,8 @@ impl Solver {
|
|||||||
/// This is similar to `add_var`, except the value doesn't have a prior register assignment.
|
/// This is similar to `add_var`, except the value doesn't have a prior register assignment.
|
||||||
pub fn add_def(&mut self, value: Value, constraint: RegClass, is_global: bool) {
|
pub fn add_def(&mut self, value: Value, constraint: RegClass, is_global: bool) {
|
||||||
debug_assert!(self.inputs_done);
|
debug_assert!(self.inputs_done);
|
||||||
self.vars.push(
|
self.vars
|
||||||
Variable::new_def(value, constraint, is_global),
|
.push(Variable::new_def(value, constraint, is_global));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the `is_global` flag on all solver variables.
|
/// Clear the `is_global` flag on all solver variables.
|
||||||
@@ -992,9 +978,8 @@ impl Solver {
|
|||||||
|
|
||||||
// Convert all of the fixed register assignments into moves, but omit the ones that are
|
// Convert all of the fixed register assignments into moves, but omit the ones that are
|
||||||
// already in the right register.
|
// already in the right register.
|
||||||
self.moves.extend(self.assignments.values().filter_map(
|
self.moves
|
||||||
Move::with_assignment,
|
.extend(self.assignments.values().filter_map(Move::with_assignment));
|
||||||
));
|
|
||||||
|
|
||||||
if !(self.moves.is_empty()) {
|
if !(self.moves.is_empty()) {
|
||||||
dbg!("collect_moves: {}", DisplayList(&self.moves));
|
dbg!("collect_moves: {}", DisplayList(&self.moves));
|
||||||
@@ -1029,8 +1014,7 @@ impl Solver {
|
|||||||
if let Some(j) = self.moves[i..].iter().position(|m| match m.to_reg() {
|
if let Some(j) = self.moves[i..].iter().position(|m| match m.to_reg() {
|
||||||
Some((rc, reg)) => avail.is_avail(rc, reg),
|
Some((rc, reg)) => avail.is_avail(rc, reg),
|
||||||
None => true,
|
None => true,
|
||||||
})
|
}) {
|
||||||
{
|
|
||||||
// This move can be executed now.
|
// This move can be executed now.
|
||||||
self.moves.swap(i, i + j);
|
self.moves.swap(i, i + j);
|
||||||
let m = &self.moves[i];
|
let m = &self.moves[i];
|
||||||
@@ -1164,9 +1148,11 @@ mod tests {
|
|||||||
|
|
||||||
// Get a register class by name.
|
// Get a register class by name.
|
||||||
fn rc_by_name(reginfo: &RegInfo, name: &str) -> RegClass {
|
fn rc_by_name(reginfo: &RegInfo, name: &str) -> RegClass {
|
||||||
reginfo.classes.iter().find(|rc| rc.name == name).expect(
|
reginfo
|
||||||
"Can't find named register class.",
|
.classes
|
||||||
)
|
.iter()
|
||||||
|
.find(|rc| rc.name == name)
|
||||||
|
.expect("Can't find named register class.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a register move.
|
// Construct a register move.
|
||||||
|
|||||||
@@ -125,10 +125,8 @@ impl<'a> Context<'a> {
|
|||||||
self.process_spills(tracker);
|
self.process_spills(tracker);
|
||||||
|
|
||||||
while let Some(inst) = self.cur.next_inst() {
|
while let Some(inst) = self.cur.next_inst() {
|
||||||
if let Some(constraints) =
|
if let Some(constraints) = self.encinfo
|
||||||
self.encinfo.operand_constraints(
|
.operand_constraints(self.cur.func.encodings[inst])
|
||||||
self.cur.func.encodings[inst],
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
self.visit_inst(inst, ebb, constraints, tracker);
|
self.visit_inst(inst, ebb, constraints, tracker);
|
||||||
} else {
|
} else {
|
||||||
@@ -283,13 +281,11 @@ impl<'a> Context<'a> {
|
|||||||
dbg!("Need {} reg from {} throughs", op.regclass, throughs.len());
|
dbg!("Need {} reg from {} throughs", op.regclass, throughs.len());
|
||||||
match self.spill_candidate(mask, throughs) {
|
match self.spill_candidate(mask, throughs) {
|
||||||
Some(cand) => self.spill_reg(cand),
|
Some(cand) => self.spill_reg(cand),
|
||||||
None => {
|
None => panic!(
|
||||||
panic!(
|
|
||||||
"Ran out of {} registers for {}",
|
"Ran out of {} registers for {}",
|
||||||
op.regclass,
|
op.regclass,
|
||||||
self.cur.display_inst(inst)
|
self.cur.display_inst(inst)
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,8 +345,7 @@ impl<'a> Context<'a> {
|
|||||||
.constraints()
|
.constraints()
|
||||||
.fixed_value_arguments();
|
.fixed_value_arguments();
|
||||||
let args = self.cur.func.dfg.inst_variable_args(inst);
|
let args = self.cur.func.dfg.inst_variable_args(inst);
|
||||||
for (idx, (abi, &arg)) in
|
for (idx, (abi, &arg)) in self.cur.func.dfg.signatures[sig]
|
||||||
self.cur.func.dfg.signatures[sig]
|
|
||||||
.params
|
.params
|
||||||
.iter()
|
.iter()
|
||||||
.zip(args)
|
.zip(args)
|
||||||
@@ -393,9 +388,9 @@ impl<'a> Context<'a> {
|
|||||||
} else if ru.fixed {
|
} else if ru.fixed {
|
||||||
// This is a fixed register use which doesn't necessarily require a copy.
|
// This is a fixed register use which doesn't necessarily require a copy.
|
||||||
// Make a copy only if this is not the first use of the value.
|
// Make a copy only if this is not the first use of the value.
|
||||||
self.reg_uses.get(i.wrapping_sub(1)).map_or(false, |ru2| {
|
self.reg_uses
|
||||||
ru2.value == ru.value
|
.get(i.wrapping_sub(1))
|
||||||
})
|
.map_or(false, |ru2| ru2.value == ru.value)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
@@ -430,13 +425,11 @@ impl<'a> Context<'a> {
|
|||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
Some(cand) => self.spill_reg(cand),
|
Some(cand) => self.spill_reg(cand),
|
||||||
None => {
|
None => panic!(
|
||||||
panic!(
|
|
||||||
"Ran out of {} registers when inserting copy before {}",
|
"Ran out of {} registers when inserting copy before {}",
|
||||||
rc,
|
rc,
|
||||||
self.cur.display_inst(inst)
|
self.cur.display_inst(inst)
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,9 +494,10 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assign a spill slot for the whole virtual register.
|
// Assign a spill slot for the whole virtual register.
|
||||||
let ss = self.cur.func.stack_slots.make_spill_slot(
|
let ss = self.cur
|
||||||
self.cur.func.dfg.value_type(value),
|
.func
|
||||||
);
|
.stack_slots
|
||||||
|
.make_spill_slot(self.cur.func.dfg.value_type(value));
|
||||||
for &v in self.virtregs.congruence_class(&value) {
|
for &v in self.virtregs.congruence_class(&value) {
|
||||||
self.liveness.spill(v);
|
self.liveness.spill(v);
|
||||||
self.cur.func.locations[v] = ValueLoc::Stack(ss);
|
self.cur.func.locations[v] = ValueLoc::Stack(ss);
|
||||||
|
|||||||
@@ -101,10 +101,8 @@ impl VirtRegs {
|
|||||||
where
|
where
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
self.get(*value).map_or_else(
|
self.get(*value)
|
||||||
|| ref_slice(value),
|
.map_or_else(|| ref_slice(value), |vr| self.values(vr))
|
||||||
|vr| self.values(vr),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if `a` and `b` belong to the same congruence class.
|
/// Check if `a` and `b` belong to the same congruence class.
|
||||||
@@ -153,9 +151,9 @@ impl VirtRegs {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Determine the insertion position for `single`.
|
// Determine the insertion position for `single`.
|
||||||
let index = match self.values(vreg).binary_search_by(
|
let index = match self.values(vreg)
|
||||||
|&v| preorder.pre_cmp_def(v, single, func),
|
.binary_search_by(|&v| preorder.pre_cmp_def(v, single, func))
|
||||||
) {
|
{
|
||||||
Ok(_) => panic!("{} already in {}", single, vreg),
|
Ok(_) => panic!("{} already in {}", single, vreg),
|
||||||
Err(i) => i,
|
Err(i) => i,
|
||||||
};
|
};
|
||||||
@@ -181,9 +179,9 @@ impl VirtRegs {
|
|||||||
|
|
||||||
/// Allocate a new empty virtual register.
|
/// Allocate a new empty virtual register.
|
||||||
fn alloc(&mut self) -> VirtReg {
|
fn alloc(&mut self) -> VirtReg {
|
||||||
self.unused_vregs.pop().unwrap_or_else(|| {
|
self.unused_vregs
|
||||||
self.vregs.push(Default::default())
|
.pop()
|
||||||
})
|
.unwrap_or_else(|| self.vregs.push(Default::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unify `values` into a single virtual register.
|
/// Unify `values` into a single virtual register.
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ pub enum CtonError {
|
|||||||
/// This always represents a bug, either in the code that generated IR for Cretonne, or a bug
|
/// This always represents a bug, either in the code that generated IR for Cretonne, or a bug
|
||||||
/// in Cretonne itself.
|
/// in Cretonne itself.
|
||||||
#[fail(display = "Verifier error: {}", _0)]
|
#[fail(display = "Verifier error: {}", _0)]
|
||||||
Verifier(
|
Verifier(#[cause] verifier::Error),
|
||||||
#[cause]
|
|
||||||
verifier::Error
|
|
||||||
),
|
|
||||||
|
|
||||||
/// An implementation limit was exceeded.
|
/// An implementation limit was exceeded.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
use constant_hash::{probe, simple_hash};
|
use constant_hash::{probe, simple_hash};
|
||||||
use isa::TargetIsa;
|
use isa::TargetIsa;
|
||||||
|
use std::boxed::Box;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::boxed::Box;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
/// A string-based configurator for settings groups.
|
/// A string-based configurator for settings groups.
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use timing;
|
|||||||
|
|
||||||
/// Test whether the given opcode is unsafe to even consider for GVN.
|
/// Test whether the given opcode is unsafe to even consider for GVN.
|
||||||
fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
|
||||||
opcode.is_call() || opcode.is_branch() || opcode.is_terminator() ||
|
opcode.is_call() || opcode.is_branch() || opcode.is_terminator() || opcode.is_return()
|
||||||
opcode.is_return() || opcode.can_trap() || opcode.other_side_effects() ||
|
|| opcode.can_trap() || opcode.other_side_effects() || opcode.can_store()
|
||||||
opcode.can_store() || opcode.can_load() || opcode.writes_cpu_flags()
|
|| opcode.can_load() || opcode.writes_cpu_flags()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform simple GVN on `func`.
|
/// Perform simple GVN on `func`.
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
|
|||||||
.ok_or(CtonError::ImplLimitExceeded)?;
|
.ok_or(CtonError::ImplLimitExceeded)?;
|
||||||
outgoing_max = max(outgoing_max, offset);
|
outgoing_max = max(outgoing_max, offset);
|
||||||
}
|
}
|
||||||
StackSlotKind::SpillSlot |
|
StackSlotKind::SpillSlot
|
||||||
StackSlotKind::ExplicitSlot |
|
| StackSlotKind::ExplicitSlot
|
||||||
StackSlotKind::EmergencySlot => {
|
| StackSlotKind::EmergencySlot => {
|
||||||
// Determine the smallest alignment of any explicit or spill slot.
|
// Determine the smallest alignment of any explicit or spill slot.
|
||||||
min_align = slot.alignment(min_align);
|
min_align = slot.alignment(min_align);
|
||||||
}
|
}
|
||||||
@@ -73,20 +73,19 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
|
|||||||
for slot in frame.values_mut() {
|
for slot in frame.values_mut() {
|
||||||
// Pick out explicit and spill slots with exact alignment `min_align`.
|
// Pick out explicit and spill slots with exact alignment `min_align`.
|
||||||
match slot.kind {
|
match slot.kind {
|
||||||
StackSlotKind::SpillSlot |
|
StackSlotKind::SpillSlot
|
||||||
StackSlotKind::ExplicitSlot |
|
| StackSlotKind::ExplicitSlot
|
||||||
StackSlotKind::EmergencySlot => {
|
| StackSlotKind::EmergencySlot => {
|
||||||
if slot.alignment(alignment) != min_align {
|
if slot.alignment(alignment) != min_align {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StackSlotKind::IncomingArg |
|
StackSlotKind::IncomingArg | StackSlotKind::OutgoingArg => continue,
|
||||||
StackSlotKind::OutgoingArg => continue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = offset.checked_sub(slot.size as StackOffset).ok_or(
|
offset = offset
|
||||||
CtonError::ImplLimitExceeded,
|
.checked_sub(slot.size as StackOffset)
|
||||||
)?;
|
.ok_or(CtonError::ImplLimitExceeded)?;
|
||||||
|
|
||||||
// Aligning the negative offset can never cause overflow. We're only clearing bits.
|
// Aligning the negative offset can never cause overflow. We're only clearing bits.
|
||||||
offset &= -(min_align as StackOffset);
|
offset &= -(min_align as StackOffset);
|
||||||
@@ -98,9 +97,9 @@ pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> Result<Stac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finally, make room for the outgoing arguments.
|
// Finally, make room for the outgoing arguments.
|
||||||
offset = offset.checked_sub(outgoing_max).ok_or(
|
offset = offset
|
||||||
CtonError::ImplLimitExceeded,
|
.checked_sub(outgoing_max)
|
||||||
)?;
|
.ok_or(CtonError::ImplLimitExceeded)?;
|
||||||
offset &= -(alignment as StackOffset);
|
offset &= -(alignment as StackOffset);
|
||||||
|
|
||||||
let frame_size = (offset as StackSize).wrapping_neg();
|
let frame_size = (offset as StackSize).wrapping_neg();
|
||||||
|
|||||||
@@ -209,12 +209,11 @@ mod details {
|
|||||||
|
|
||||||
/// Add `timings` to the accumulated timings for the current thread.
|
/// Add `timings` to the accumulated timings for the current thread.
|
||||||
pub fn add_to_current(times: &PassTimes) {
|
pub fn add_to_current(times: &PassTimes) {
|
||||||
PASS_TIME.with(|rc| for (a, b) in rc.borrow_mut().pass.iter_mut().zip(
|
PASS_TIME.with(|rc| {
|
||||||
×.pass,
|
for (a, b) in rc.borrow_mut().pass.iter_mut().zip(×.pass) {
|
||||||
)
|
|
||||||
{
|
|
||||||
a.total += b.total;
|
a.total += b.total;
|
||||||
a.child += b.child;
|
a.child += b.child;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ impl<'a> CssaVerifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enforce topological ordering of defs in the virtual register.
|
// Enforce topological ordering of defs in the virtual register.
|
||||||
if self.preorder.dominates(def_ebb, prev_ebb) &&
|
if self.preorder.dominates(def_ebb, prev_ebb)
|
||||||
self.domtree.dominates(def, prev_def, &self.func.layout)
|
&& self.domtree.dominates(def, prev_def, &self.func.layout)
|
||||||
{
|
{
|
||||||
return err!(
|
return err!(
|
||||||
val,
|
val,
|
||||||
@@ -112,8 +112,8 @@ impl<'a> CssaVerifier<'a> {
|
|||||||
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
|
let prev_def: ExpandedProgramPoint = self.func.dfg.value_def(prev_val).into();
|
||||||
let prev_ebb = self.func.layout.pp_ebb(prev_def);
|
let prev_ebb = self.func.layout.pp_ebb(prev_def);
|
||||||
|
|
||||||
if self.preorder.dominates(prev_ebb, def_ebb) &&
|
if self.preorder.dominates(prev_ebb, def_ebb)
|
||||||
self.domtree.dominates(prev_def, def, &self.func.layout)
|
&& self.domtree.dominates(prev_def, def, &self.func.layout)
|
||||||
{
|
{
|
||||||
let ctx = self.liveness.context(&self.func.layout);
|
let ctx = self.liveness.context(&self.func.layout);
|
||||||
if self.liveness[prev_val].overlaps_def(def, def_ebb, ctx) {
|
if self.liveness[prev_val].overlaps_def(def, def_ebb, ctx) {
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
let ctx = self.liveness.context(&self.func.layout);
|
let ctx = self.liveness.context(&self.func.layout);
|
||||||
|
|
||||||
// Check if `inst` is in the def range, not including the def itself.
|
// Check if `inst` is in the def range, not including the def itself.
|
||||||
if ctx.order.cmp(lr.def(), inst) == Ordering::Less &&
|
if ctx.order.cmp(lr.def(), inst) == Ordering::Less
|
||||||
ctx.order.cmp(inst, lr.def_local_end()) != Ordering::Greater
|
&& ctx.order.cmp(inst, lr.def_local_end()) != Ordering::Greater
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
enc: isa::Encoding,
|
enc: isa::Encoding,
|
||||||
divert: &RegDiversions,
|
divert: &RegDiversions,
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let constraints = self.encinfo.operand_constraints(enc).expect(
|
let constraints = self.encinfo
|
||||||
"check_enc_constraints requires a legal encoding",
|
.operand_constraints(enc)
|
||||||
);
|
.expect("check_enc_constraints requires a legal encoding");
|
||||||
|
|
||||||
if constraints.satisfied(inst, divert, self.func) {
|
if constraints.satisfied(inst, divert, self.func) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -235,8 +235,8 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
/// Update diversions to reflect the current instruction and check their consistency.
|
/// Update diversions to reflect the current instruction and check their consistency.
|
||||||
fn update_diversions(&self, inst: ir::Inst, divert: &mut RegDiversions) -> Result {
|
fn update_diversions(&self, inst: ir::Inst, divert: &mut RegDiversions) -> Result {
|
||||||
let (arg, src) = match self.func.dfg[inst] {
|
let (arg, src) = match self.func.dfg[inst] {
|
||||||
ir::InstructionData::RegMove { arg, src, .. } |
|
ir::InstructionData::RegMove { arg, src, .. }
|
||||||
ir::InstructionData::RegSpill { arg, src, .. } => (arg, ir::ValueLoc::Reg(src)),
|
| ir::InstructionData::RegSpill { arg, src, .. } => (arg, ir::ValueLoc::Reg(src)),
|
||||||
ir::InstructionData::RegFill { arg, src, .. } => (arg, ir::ValueLoc::Stack(src)),
|
ir::InstructionData::RegFill { arg, src, .. } => (arg, ir::ValueLoc::Stack(src)),
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
@@ -275,12 +275,10 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
let dfg = &self.func.dfg;
|
let dfg = &self.func.dfg;
|
||||||
|
|
||||||
match dfg.analyze_branch(inst) {
|
match dfg.analyze_branch(inst) {
|
||||||
NotABranch => {
|
NotABranch => panic!(
|
||||||
panic!(
|
|
||||||
"No branch information for {}",
|
"No branch information for {}",
|
||||||
dfg.display_inst(inst, self.isa)
|
dfg.display_inst(inst, self.isa)
|
||||||
)
|
),
|
||||||
}
|
|
||||||
SingleDest(ebb, _) => {
|
SingleDest(ebb, _) => {
|
||||||
for d in divert.all() {
|
for d in divert.all() {
|
||||||
let lr = &liveness[d.value];
|
let lr = &liveness[d.value];
|
||||||
|
|||||||
@@ -237,9 +237,8 @@ impl<'a> Verifier<'a> {
|
|||||||
|
|
||||||
let fixed_results = inst_data.opcode().constraints().fixed_results();
|
let fixed_results = inst_data.opcode().constraints().fixed_results();
|
||||||
// var_results is 0 if we aren't a call instruction
|
// var_results is 0 if we aren't a call instruction
|
||||||
let var_results = dfg.call_signature(inst).map_or(0, |sig| {
|
let var_results = dfg.call_signature(inst)
|
||||||
dfg.signatures[sig].returns.len()
|
.map_or(0, |sig| dfg.signatures[sig].returns.len());
|
||||||
});
|
|
||||||
let total_results = fixed_results + var_results;
|
let total_results = fixed_results + var_results;
|
||||||
|
|
||||||
// All result values for multi-valued instructions are created
|
// All result values for multi-valued instructions are created
|
||||||
@@ -281,23 +280,23 @@ impl<'a> Verifier<'a> {
|
|||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
Branch {
|
| Branch {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
BranchInt {
|
| BranchInt {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
BranchFloat {
|
| BranchFloat {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
} |
|
}
|
||||||
BranchIcmp {
|
| BranchIcmp {
|
||||||
destination,
|
destination,
|
||||||
ref args,
|
ref args,
|
||||||
..
|
..
|
||||||
@@ -308,19 +307,22 @@ impl<'a> Verifier<'a> {
|
|||||||
BranchTable { table, .. } => {
|
BranchTable { table, .. } => {
|
||||||
self.verify_jump_table(inst, table)?;
|
self.verify_jump_table(inst, table)?;
|
||||||
}
|
}
|
||||||
Call { func_ref, ref args, .. } => {
|
Call {
|
||||||
|
func_ref, ref args, ..
|
||||||
|
} => {
|
||||||
self.verify_func_ref(inst, func_ref)?;
|
self.verify_func_ref(inst, func_ref)?;
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
CallIndirect { sig_ref, ref args, .. } => {
|
CallIndirect {
|
||||||
|
sig_ref, ref args, ..
|
||||||
|
} => {
|
||||||
self.verify_sig_ref(inst, sig_ref)?;
|
self.verify_sig_ref(inst, sig_ref)?;
|
||||||
self.verify_value_list(inst, args)?;
|
self.verify_value_list(inst, args)?;
|
||||||
}
|
}
|
||||||
FuncAddr { func_ref, .. } => {
|
FuncAddr { func_ref, .. } => {
|
||||||
self.verify_func_ref(inst, func_ref)?;
|
self.verify_func_ref(inst, func_ref)?;
|
||||||
}
|
}
|
||||||
StackLoad { stack_slot, .. } |
|
StackLoad { stack_slot, .. } | StackStore { stack_slot, .. } => {
|
||||||
StackStore { stack_slot, .. } => {
|
|
||||||
self.verify_stack_slot(inst, stack_slot)?;
|
self.verify_stack_slot(inst, stack_slot)?;
|
||||||
}
|
}
|
||||||
UnaryGlobalVar { global_var, .. } => {
|
UnaryGlobalVar { global_var, .. } => {
|
||||||
@@ -343,31 +345,31 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Exhaustive list so we can't forget to add new formats
|
// Exhaustive list so we can't forget to add new formats
|
||||||
Unary { .. } |
|
Unary { .. }
|
||||||
UnaryImm { .. } |
|
| UnaryImm { .. }
|
||||||
UnaryIeee32 { .. } |
|
| UnaryIeee32 { .. }
|
||||||
UnaryIeee64 { .. } |
|
| UnaryIeee64 { .. }
|
||||||
UnaryBool { .. } |
|
| UnaryBool { .. }
|
||||||
Binary { .. } |
|
| Binary { .. }
|
||||||
BinaryImm { .. } |
|
| BinaryImm { .. }
|
||||||
Ternary { .. } |
|
| Ternary { .. }
|
||||||
InsertLane { .. } |
|
| InsertLane { .. }
|
||||||
ExtractLane { .. } |
|
| ExtractLane { .. }
|
||||||
IntCompare { .. } |
|
| IntCompare { .. }
|
||||||
IntCompareImm { .. } |
|
| IntCompareImm { .. }
|
||||||
IntCond { .. } |
|
| IntCond { .. }
|
||||||
FloatCompare { .. } |
|
| FloatCompare { .. }
|
||||||
FloatCond { .. } |
|
| FloatCond { .. }
|
||||||
IntSelect { .. } |
|
| IntSelect { .. }
|
||||||
Load { .. } |
|
| Load { .. }
|
||||||
Store { .. } |
|
| Store { .. }
|
||||||
RegMove { .. } |
|
| RegMove { .. }
|
||||||
CopySpecial { .. } |
|
| CopySpecial { .. }
|
||||||
Trap { .. } |
|
| Trap { .. }
|
||||||
CondTrap { .. } |
|
| CondTrap { .. }
|
||||||
IntCondTrap { .. } |
|
| IntCondTrap { .. }
|
||||||
FloatCondTrap { .. } |
|
| FloatCondTrap { .. }
|
||||||
NullAry { .. } => {}
|
| NullAry { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -480,11 +482,8 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
// Defining instruction dominates the instruction that uses the value.
|
// Defining instruction dominates the instruction that uses the value.
|
||||||
if is_reachable {
|
if is_reachable {
|
||||||
if !self.expected_domtree.dominates(
|
if !self.expected_domtree
|
||||||
def_inst,
|
.dominates(def_inst, loc_inst, &self.func.layout)
|
||||||
loc_inst,
|
|
||||||
&self.func.layout,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
return err!(loc_inst, "uses value from non-dominating {}", def_inst);
|
return err!(loc_inst, "uses value from non-dominating {}", def_inst);
|
||||||
}
|
}
|
||||||
@@ -513,12 +512,9 @@ impl<'a> Verifier<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// The defining EBB dominates the instruction using this value.
|
// The defining EBB dominates the instruction using this value.
|
||||||
if is_reachable &&
|
if is_reachable
|
||||||
!self.expected_domtree.dominates(
|
&& !self.expected_domtree
|
||||||
ebb,
|
.dominates(ebb, loc_inst, &self.func.layout)
|
||||||
loc_inst,
|
|
||||||
&self.func.layout,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
return err!(loc_inst, "uses value arg from non-dominating {}", ebb);
|
return err!(loc_inst, "uses value arg from non-dominating {}", ebb);
|
||||||
}
|
}
|
||||||
@@ -542,13 +538,11 @@ impl<'a> Verifier<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ValueDef::Param(_, _) => {
|
ValueDef::Param(_, _) => err!(
|
||||||
err!(
|
|
||||||
loc_inst,
|
loc_inst,
|
||||||
"instruction result {} is not defined by the instruction",
|
"instruction result {} is not defined by the instruction",
|
||||||
v
|
v
|
||||||
)
|
),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,8 +570,7 @@ impl<'a> Verifier<'a> {
|
|||||||
"incorrect number of Ebbs in postorder traversal"
|
"incorrect number of Ebbs in postorder traversal"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (index, (&test_ebb, &true_ebb)) in
|
for (index, (&test_ebb, &true_ebb)) in domtree
|
||||||
domtree
|
|
||||||
.cfg_postorder()
|
.cfg_postorder()
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.expected_domtree.cfg_postorder().iter())
|
.zip(self.expected_domtree.cfg_postorder().iter())
|
||||||
@@ -595,11 +588,8 @@ impl<'a> Verifier<'a> {
|
|||||||
}
|
}
|
||||||
// We verify rpo_cmp on pairs of adjacent ebbs in the postorder
|
// We verify rpo_cmp on pairs of adjacent ebbs in the postorder
|
||||||
for (&prev_ebb, &next_ebb) in domtree.cfg_postorder().iter().adjacent_pairs() {
|
for (&prev_ebb, &next_ebb) in domtree.cfg_postorder().iter().adjacent_pairs() {
|
||||||
if self.expected_domtree.rpo_cmp(
|
if self.expected_domtree
|
||||||
prev_ebb,
|
.rpo_cmp(prev_ebb, next_ebb, &self.func.layout) != Ordering::Greater
|
||||||
next_ebb,
|
|
||||||
&self.func.layout,
|
|
||||||
) != Ordering::Greater
|
|
||||||
{
|
{
|
||||||
return err!(
|
return err!(
|
||||||
next_ebb,
|
next_ebb,
|
||||||
@@ -737,9 +727,11 @@ impl<'a> Verifier<'a> {
|
|||||||
fn typecheck_variable_args(&self, inst: Inst) -> Result {
|
fn typecheck_variable_args(&self, inst: Inst) -> Result {
|
||||||
match self.func.dfg.analyze_branch(inst) {
|
match self.func.dfg.analyze_branch(inst) {
|
||||||
BranchInfo::SingleDest(ebb, _) => {
|
BranchInfo::SingleDest(ebb, _) => {
|
||||||
let iter = self.func.dfg.ebb_params(ebb).iter().map(|&v| {
|
let iter = self.func
|
||||||
self.func.dfg.value_type(v)
|
.dfg
|
||||||
});
|
.ebb_params(ebb)
|
||||||
|
.iter()
|
||||||
|
.map(|&v| self.func.dfg.value_type(v));
|
||||||
self.typecheck_variable_args_iterator(inst, iter)?;
|
self.typecheck_variable_args_iterator(inst, iter)?;
|
||||||
}
|
}
|
||||||
BranchInfo::Table(table) => {
|
BranchInfo::Table(table) => {
|
||||||
@@ -761,16 +753,18 @@ impl<'a> Verifier<'a> {
|
|||||||
match self.func.dfg[inst].analyze_call(&self.func.dfg.value_lists) {
|
match self.func.dfg[inst].analyze_call(&self.func.dfg.value_lists) {
|
||||||
CallInfo::Direct(func_ref, _) => {
|
CallInfo::Direct(func_ref, _) => {
|
||||||
let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
|
let sig_ref = self.func.dfg.ext_funcs[func_ref].signature;
|
||||||
let arg_types = self.func.dfg.signatures[sig_ref].params.iter().map(|a| {
|
let arg_types = self.func.dfg.signatures[sig_ref]
|
||||||
a.value_type
|
.params
|
||||||
});
|
.iter()
|
||||||
|
.map(|a| a.value_type);
|
||||||
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
||||||
self.check_outgoing_args(inst, sig_ref)?;
|
self.check_outgoing_args(inst, sig_ref)?;
|
||||||
}
|
}
|
||||||
CallInfo::Indirect(sig_ref, _) => {
|
CallInfo::Indirect(sig_ref, _) => {
|
||||||
let arg_types = self.func.dfg.signatures[sig_ref].params.iter().map(|a| {
|
let arg_types = self.func.dfg.signatures[sig_ref]
|
||||||
a.value_type
|
.params
|
||||||
});
|
.iter()
|
||||||
|
.map(|a| a.value_type);
|
||||||
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
self.typecheck_variable_args_iterator(inst, arg_types)?;
|
||||||
self.check_outgoing_args(inst, sig_ref)?;
|
self.check_outgoing_args(inst, sig_ref)?;
|
||||||
}
|
}
|
||||||
@@ -1047,8 +1041,7 @@ impl<'a> Verifier<'a> {
|
|||||||
&self.func,
|
&self.func,
|
||||||
&self.func.dfg[inst],
|
&self.func.dfg[inst],
|
||||||
self.func.dfg.ctrl_typevar(inst),
|
self.func.dfg.ctrl_typevar(inst),
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
if !possible_encodings.is_empty() {
|
if !possible_encodings.is_empty() {
|
||||||
possible_encodings.push_str(", ");
|
possible_encodings.push_str(", ");
|
||||||
multiple_encodings = true;
|
multiple_encodings = true;
|
||||||
@@ -1119,8 +1112,7 @@ impl<'a> Verifier<'a> {
|
|||||||
fn verify_return_at_end(&self) -> Result {
|
fn verify_return_at_end(&self) -> Result {
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
let inst = self.func.layout.last_inst(ebb).unwrap();
|
let inst = self.func.layout.last_inst(ebb).unwrap();
|
||||||
if self.func.dfg[inst].opcode().is_return() &&
|
if self.func.dfg[inst].opcode().is_return() && Some(ebb) != self.func.layout.last_ebb()
|
||||||
Some(ebb) != self.func.layout.last_ebb()
|
|
||||||
{
|
{
|
||||||
return err!(inst, "Internal return not allowed with return_at_end=1");
|
return err!(inst, "Internal return not allowed with return_at_end=1");
|
||||||
}
|
}
|
||||||
@@ -1155,8 +1147,8 @@ impl<'a> Verifier<'a> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::{Error, Verifier};
|
use super::{Error, Verifier};
|
||||||
use entity::EntityList;
|
use entity::EntityList;
|
||||||
use ir::instructions::{InstructionData, Opcode};
|
|
||||||
use ir::Function;
|
use ir::Function;
|
||||||
|
use ir::instructions::{InstructionData, Opcode};
|
||||||
use settings;
|
use settings;
|
||||||
|
|
||||||
macro_rules! assert_err_with_msg {
|
macro_rules! assert_err_with_msg {
|
||||||
|
|||||||
@@ -346,10 +346,12 @@ pub fn write_operands(
|
|||||||
write_ebb_args(w, &args[2..])
|
write_ebb_args(w, &args[2..])
|
||||||
}
|
}
|
||||||
BranchTable { arg, table, .. } => write!(w, " {}, {}", arg, table),
|
BranchTable { arg, table, .. } => write!(w, " {}, {}", arg, table),
|
||||||
Call { func_ref, ref args, .. } => {
|
Call {
|
||||||
write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))
|
func_ref, ref args, ..
|
||||||
}
|
} => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
|
||||||
CallIndirect { sig_ref, ref args, .. } => {
|
CallIndirect {
|
||||||
|
sig_ref, ref args, ..
|
||||||
|
} => {
|
||||||
let args = args.as_slice(pool);
|
let args = args.as_slice(pool);
|
||||||
write!(
|
write!(
|
||||||
w,
|
w,
|
||||||
@@ -360,7 +362,9 @@ pub fn write_operands(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
|
FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
|
||||||
StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset),
|
StackLoad {
|
||||||
|
stack_slot, offset, ..
|
||||||
|
} => write!(w, " {}{}", stack_slot, offset),
|
||||||
StackStore {
|
StackStore {
|
||||||
arg,
|
arg,
|
||||||
stack_slot,
|
stack_slot,
|
||||||
@@ -368,7 +372,9 @@ pub fn write_operands(
|
|||||||
..
|
..
|
||||||
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
|
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
|
||||||
HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
|
HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
|
||||||
Load { flags, arg, offset, .. } => write!(w, "{} {}{}", flags, arg, offset),
|
Load {
|
||||||
|
flags, arg, offset, ..
|
||||||
|
} => write!(w, "{} {}{}", flags, arg, offset),
|
||||||
LoadComplex {
|
LoadComplex {
|
||||||
flags,
|
flags,
|
||||||
ref args,
|
ref args,
|
||||||
@@ -383,7 +389,6 @@ pub fn write_operands(
|
|||||||
DisplayValuesWithDelimiter(&args, '+'),
|
DisplayValuesWithDelimiter(&args, '+'),
|
||||||
offset
|
offset
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
Store {
|
Store {
|
||||||
flags,
|
flags,
|
||||||
@@ -452,8 +457,12 @@ pub fn write_operands(
|
|||||||
}
|
}
|
||||||
Trap { code, .. } => write!(w, " {}", code),
|
Trap { code, .. } => write!(w, " {}", code),
|
||||||
CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
|
CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
|
||||||
IntCondTrap { cond, arg, code, .. } => write!(w, " {} {}, {}", cond, arg, code),
|
IntCondTrap {
|
||||||
FloatCondTrap { cond, arg, code, .. } => write!(w, " {} {}, {}", cond, arg, code),
|
cond, arg, code, ..
|
||||||
|
} => write!(w, " {} {}, {}", cond, arg, code),
|
||||||
|
FloatCondTrap {
|
||||||
|
cond, arg, code, ..
|
||||||
|
} => write!(w, " {} {}, {}", cond, arg, code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,19 +33,10 @@
|
|||||||
#![warn(unused_import_braces)]
|
#![warn(unused_import_braces)]
|
||||||
#![cfg_attr(feature = "std", warn(unstable_features))]
|
#![cfg_attr(feature = "std", warn(unstable_features))]
|
||||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
|
||||||
#![cfg_attr(feature = "cargo-clippy",
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
allow(new_without_default, new_without_default_derive))]
|
warn(float_arithmetic, mut_mut, nonminimal_bool, option_map_unwrap_or,
|
||||||
#![cfg_attr(feature="cargo-clippy", warn(
|
option_map_unwrap_or_else, print_stdout, unicode_not_nfc, use_self))]
|
||||||
float_arithmetic,
|
|
||||||
mut_mut,
|
|
||||||
nonminimal_bool,
|
|
||||||
option_map_unwrap_or,
|
|
||||||
option_map_unwrap_or_else,
|
|
||||||
print_stdout,
|
|
||||||
unicode_not_nfc,
|
|
||||||
use_self,
|
|
||||||
))]
|
|
||||||
|
|
||||||
// Turns on no_std and alloc features if std is not available.
|
// Turns on no_std and alloc features if std is not available.
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
//! Densely numbered entity references as mapping keys.
|
//! Densely numbered entity references as mapping keys.
|
||||||
|
|
||||||
use {EntityRef, Iter, IterMut, Keys};
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use {EntityRef, Iter, IterMut, Keys};
|
||||||
|
|
||||||
/// A mapping `K -> V` for densely indexed entity references.
|
/// A mapping `K -> V` for densely indexed entity references.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ impl<T: ReservedValue> PackedOption<T> {
|
|||||||
|
|
||||||
/// Expand the packed option into a normal `Option`.
|
/// Expand the packed option into a normal `Option`.
|
||||||
pub fn expand(self) -> Option<T> {
|
pub fn expand(self) -> Option<T> {
|
||||||
if self.is_none() { None } else { Some(self.0) }
|
if self.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
|
/// Maps a `PackedOption<T>` to `Option<U>` by applying a function to a contained value.
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! Densely numbered entity references as mapping keys.
|
//! Densely numbered entity references as mapping keys.
|
||||||
use {EntityRef, Iter, IterMut, Keys};
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use {EntityRef, Iter, IterMut, Keys};
|
||||||
|
|
||||||
/// A primary mapping `K -> V` allocating dense entity references.
|
/// A primary mapping `K -> V` allocating dense entity references.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//! Densely numbered entity references as set keys.
|
//! Densely numbered entity references as set keys.
|
||||||
|
|
||||||
use {EntityRef, Keys};
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use {EntityRef, Keys};
|
||||||
|
|
||||||
/// A set of `K` for densely indexed entity references.
|
/// A set of `K` for densely indexed entity references.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
|
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
|
||||||
//! ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
|
//! ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
|
||||||
|
|
||||||
use {EntityMap, EntityRef};
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
use {EntityMap, EntityRef};
|
||||||
|
|
||||||
/// Trait for extracting keys from values stored in a `SparseMap`.
|
/// Trait for extracting keys from values stored in a `SparseMap`.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
//! Defines `FaerieBackend`.
|
//! Defines `FaerieBackend`.
|
||||||
|
|
||||||
use container;
|
use container;
|
||||||
use cretonne_codegen::binemit::{Addend, CodeOffset, Reloc, RelocSink, NullTrapSink};
|
use cretonne_codegen::binemit::{Addend, CodeOffset, NullTrapSink, Reloc, RelocSink};
|
||||||
use cretonne_codegen::isa::TargetIsa;
|
use cretonne_codegen::isa::TargetIsa;
|
||||||
use cretonne_codegen::{self, binemit, ir};
|
use cretonne_codegen::{self, binemit, ir};
|
||||||
use cretonne_module::{Backend, DataContext, Linkage, ModuleNamespace, Init, DataDescription,
|
use cretonne_module::{Backend, DataContext, DataDescription, Init, Linkage, ModuleError,
|
||||||
ModuleError};
|
ModuleNamespace};
|
||||||
use failure::Error;
|
|
||||||
use faerie;
|
use faerie;
|
||||||
|
use failure::Error;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use target;
|
use target;
|
||||||
use traps::{FaerieTrapManifest, FaerieTrapSink};
|
use traps::{FaerieTrapManifest, FaerieTrapSink};
|
||||||
@@ -33,7 +33,6 @@ pub struct FaerieBuilder {
|
|||||||
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl FaerieBuilder {
|
impl FaerieBuilder {
|
||||||
/// Create a new `FaerieBuilder` using the given Cretonne target, that
|
/// Create a new `FaerieBuilder` using the given Cretonne target, that
|
||||||
/// can be passed to
|
/// can be passed to
|
||||||
@@ -89,7 +88,6 @@ impl FaerieBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A `FaerieBackend` implements `Backend` and emits ".o" files using the `faerie` library.
|
/// A `FaerieBackend` implements `Backend` and emits ".o" files using the `faerie` library.
|
||||||
pub struct FaerieBackend {
|
pub struct FaerieBackend {
|
||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
@@ -192,9 +190,9 @@ impl Backend for FaerieBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.artifact.define(name, code).expect(
|
self.artifact
|
||||||
"inconsistent declaration",
|
.define(name, code)
|
||||||
);
|
.expect("inconsistent declaration");
|
||||||
Ok(FaerieCompiledFunction {})
|
Ok(FaerieCompiledFunction {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,8 +237,7 @@ impl Backend for FaerieBackend {
|
|||||||
}
|
}
|
||||||
for &(offset, id, addend) in data_relocs {
|
for &(offset, id, addend) in data_relocs {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
addend,
|
addend, 0,
|
||||||
0,
|
|
||||||
"faerie doesn't support addends in data section relocations yet"
|
"faerie doesn't support addends in data section relocations yet"
|
||||||
);
|
);
|
||||||
let to = &namespace.get_data_decl(&data_decls[id]).name;
|
let to = &namespace.get_data_decl(&data_decls[id]).name;
|
||||||
@@ -253,9 +250,9 @@ impl Backend for FaerieBackend {
|
|||||||
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
|
.map_err(|e| ModuleError::Backend(format!("{}", e)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.artifact.define(name, bytes).expect(
|
self.artifact
|
||||||
"inconsistent declaration",
|
.define(name, bytes)
|
||||||
);
|
.expect("inconsistent declaration");
|
||||||
Ok(FaerieCompiledData {})
|
Ok(FaerieCompiledData {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,25 +343,20 @@ fn translate_function_linkage(linkage: Linkage) -> faerie::Decl {
|
|||||||
fn translate_data_linkage(linkage: Linkage, writable: bool) -> faerie::Decl {
|
fn translate_data_linkage(linkage: Linkage, writable: bool) -> faerie::Decl {
|
||||||
match linkage {
|
match linkage {
|
||||||
Linkage::Import => faerie::Decl::DataImport,
|
Linkage::Import => faerie::Decl::DataImport,
|
||||||
Linkage::Local => {
|
Linkage::Local => faerie::Decl::Data {
|
||||||
faerie::Decl::Data {
|
|
||||||
global: false,
|
global: false,
|
||||||
writeable: writable,
|
writeable: writable,
|
||||||
}
|
},
|
||||||
}
|
Linkage::Export => faerie::Decl::Data {
|
||||||
Linkage::Export => {
|
|
||||||
faerie::Decl::Data {
|
|
||||||
global: true,
|
global: true,
|
||||||
writeable: writable,
|
writeable: writable,
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Linkage::Preemptible => {
|
Linkage::Preemptible => {
|
||||||
unimplemented!("faerie doesn't support preemptible globals yet");
|
unimplemented!("faerie doesn't support preemptible globals yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct FaerieRelocSink<'a> {
|
struct FaerieRelocSink<'a> {
|
||||||
format: container::Format,
|
format: container::Format,
|
||||||
artifact: &'a mut faerie::Artifact,
|
artifact: &'a mut faerie::Artifact,
|
||||||
|
|||||||
@@ -5,18 +5,10 @@
|
|||||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||||
#![warn(unused_import_braces, unstable_features)]
|
#![warn(unused_import_braces, unstable_features)]
|
||||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, new_without_default_derive))]
|
||||||
#![cfg_attr(feature = "cargo-clippy",
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
allow(new_without_default, new_without_default_derive))]
|
warn(float_arithmetic, mut_mut, nonminimal_bool, option_map_unwrap_or,
|
||||||
#![cfg_attr(feature="cargo-clippy", warn(
|
option_map_unwrap_or_else, print_stdout, unicode_not_nfc, use_self))]
|
||||||
float_arithmetic,
|
|
||||||
mut_mut,
|
|
||||||
nonminimal_bool,
|
|
||||||
option_map_unwrap_or,
|
|
||||||
option_map_unwrap_or_else,
|
|
||||||
print_stdout,
|
|
||||||
unicode_not_nfc,
|
|
||||||
use_self,
|
|
||||||
))]
|
|
||||||
|
|
||||||
extern crate cretonne_codegen;
|
extern crate cretonne_codegen;
|
||||||
extern crate cretonne_module;
|
extern crate cretonne_module;
|
||||||
@@ -29,5 +21,5 @@ mod container;
|
|||||||
mod target;
|
mod target;
|
||||||
pub mod traps;
|
pub mod traps;
|
||||||
|
|
||||||
pub use backend::{FaerieBuilder, FaerieBackend, FaerieProduct, FaerieTrapCollection};
|
pub use backend::{FaerieBackend, FaerieBuilder, FaerieProduct, FaerieTrapCollection};
|
||||||
pub use container::Format;
|
pub use container::Format;
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ pub fn translate(isa: &isa::TargetIsa) -> Result<Target, ModuleError> {
|
|||||||
}),
|
}),
|
||||||
"arm32" => Ok(Target::ARMv7),
|
"arm32" => Ok(Target::ARMv7),
|
||||||
"arm64" => Ok(Target::ARM64),
|
"arm64" => Ok(Target::ARM64),
|
||||||
_ => Err(ModuleError::Backend(
|
_ => Err(ModuleError::Backend(format!(
|
||||||
format!("unsupported faerie isa: {}", name),
|
"unsupported faerie isa: {}",
|
||||||
)),
|
name
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Faerie trap manifests record every `TrapCode` that cretonne outputs during code generation,
|
//! Faerie trap manifests record every `TrapCode` that cretonne outputs during code generation,
|
||||||
//! for every function in the module. This data may be useful at runtime.
|
//! for every function in the module. This data may be useful at runtime.
|
||||||
|
|
||||||
use cretonne_codegen::{ir, binemit};
|
use cretonne_codegen::{binemit, ir};
|
||||||
|
|
||||||
/// Record of the arguments cretonne passes to `TrapSink::trap`
|
/// Record of the arguments cretonne passes to `TrapSink::trap`
|
||||||
pub struct FaerieTrapSite {
|
pub struct FaerieTrapSite {
|
||||||
@@ -23,7 +23,6 @@ pub struct FaerieTrapSink {
|
|||||||
pub sites: Vec<FaerieTrapSite>,
|
pub sites: Vec<FaerieTrapSite>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl FaerieTrapSink {
|
impl FaerieTrapSink {
|
||||||
/// Create an empty `FaerieTrapSink`
|
/// Create an empty `FaerieTrapSink`
|
||||||
pub fn new(name: &str, code_size: u32) -> Self {
|
pub fn new(name: &str, code_size: u32) -> Self {
|
||||||
|
|||||||
@@ -47,9 +47,7 @@ impl ConcurrentRunner {
|
|||||||
heartbeat_thread(reply_tx.clone());
|
heartbeat_thread(reply_tx.clone());
|
||||||
|
|
||||||
let handles = (0..num_cpus::get())
|
let handles = (0..num_cpus::get())
|
||||||
.map(|num| {
|
.map(|num| worker_thread(num, request_mutex.clone(), reply_tx.clone()))
|
||||||
worker_thread(num, request_mutex.clone(), reply_tx.clone())
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -101,8 +99,10 @@ impl ConcurrentRunner {
|
|||||||
fn heartbeat_thread(replies: Sender<Reply>) -> thread::JoinHandle<()> {
|
fn heartbeat_thread(replies: Sender<Reply>) -> thread::JoinHandle<()> {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("heartbeat".to_string())
|
.name("heartbeat".to_string())
|
||||||
.spawn(move || while replies.send(Reply::Tick).is_ok() {
|
.spawn(move || {
|
||||||
|
while replies.send(Reply::Tick).is_ok() {
|
||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,9 @@
|
|||||||
type_complexity,
|
type_complexity,
|
||||||
// Rustfmt 0.9.0 is at odds with this lint:
|
// Rustfmt 0.9.0 is at odds with this lint:
|
||||||
block_in_if_condition_stmt))]
|
block_in_if_condition_stmt))]
|
||||||
#![cfg_attr(feature="cargo-clippy", warn(
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
mut_mut,
|
warn(mut_mut, nonminimal_bool, option_map_unwrap_or, option_map_unwrap_or_else,
|
||||||
nonminimal_bool,
|
unicode_not_nfc, use_self))]
|
||||||
option_map_unwrap_or,
|
|
||||||
option_map_unwrap_or_else,
|
|
||||||
unicode_not_nfc,
|
|
||||||
use_self,
|
|
||||||
))]
|
|
||||||
|
|
||||||
#[macro_use(dbg)]
|
#[macro_use(dbg)]
|
||||||
extern crate cretonne_codegen;
|
extern crate cretonne_codegen;
|
||||||
|
|||||||
@@ -40,15 +40,13 @@ impl Display for QueueEntry {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let p = self.path.to_string_lossy();
|
let p = self.path.to_string_lossy();
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Done(Ok(dur)) => {
|
State::Done(Ok(dur)) => write!(
|
||||||
write!(
|
|
||||||
f,
|
f,
|
||||||
"{}.{:03} {}",
|
"{}.{:03} {}",
|
||||||
dur.as_secs(),
|
dur.as_secs(),
|
||||||
dur.subsec_nanos() / 1_000_000,
|
dur.subsec_nanos() / 1_000_000,
|
||||||
p
|
p
|
||||||
)
|
),
|
||||||
}
|
|
||||||
State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
|
State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
|
||||||
_ => write!(f, "{}", p),
|
_ => write!(f, "{}", p),
|
||||||
}
|
}
|
||||||
@@ -180,7 +178,11 @@ impl TestRunner {
|
|||||||
/// Report on the next in-order job, if it's done.
|
/// Report on the next in-order job, if it's done.
|
||||||
fn report_job(&self) -> bool {
|
fn report_job(&self) -> bool {
|
||||||
let jobid = self.reported_tests;
|
let jobid = self.reported_tests;
|
||||||
if let Some(&QueueEntry { state: State::Done(ref result), .. }) = self.tests.get(jobid) {
|
if let Some(&QueueEntry {
|
||||||
|
state: State::Done(ref result),
|
||||||
|
..
|
||||||
|
}) = self.tests.get(jobid)
|
||||||
|
{
|
||||||
if self.verbose || result.is_err() {
|
if self.verbose || result.is_err() {
|
||||||
println!("{}", self.tests[jobid]);
|
println!("{}", self.tests[jobid]);
|
||||||
}
|
}
|
||||||
@@ -283,7 +285,10 @@ impl TestRunner {
|
|||||||
let mut times = self.tests
|
let mut times = self.tests
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|entry| match *entry {
|
.filter_map(|entry| match *entry {
|
||||||
QueueEntry { state: State::Done(Ok(dur)), .. } => Some(dur),
|
QueueEntry {
|
||||||
|
state: State::Done(Ok(dur)),
|
||||||
|
..
|
||||||
|
} => Some(dur),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -312,10 +317,12 @@ impl TestRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for t in self.tests.iter().filter(|entry| match **entry {
|
for t in self.tests.iter().filter(|entry| match **entry {
|
||||||
QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
|
QueueEntry {
|
||||||
|
state: State::Done(Ok(dur)),
|
||||||
|
..
|
||||||
|
} => dur > cut,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
}) {
|
||||||
{
|
|
||||||
println!("slow: {}", t)
|
println!("slow: {}", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,15 +129,11 @@ fn run_one_test<'a>(
|
|||||||
|
|
||||||
// Should we run the verifier before this test?
|
// Should we run the verifier before this test?
|
||||||
if !context.verified && test.needs_verifier() {
|
if !context.verified && test.needs_verifier() {
|
||||||
verify_function(&func, context.flags_or_isa()).map_err(
|
verify_function(&func, context.flags_or_isa())
|
||||||
|e| {
|
.map_err(|e| pretty_verifier_error(&func, isa, &e))?;
|
||||||
pretty_verifier_error(&func, isa, &e)
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
context.verified = true;
|
context.verified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
test.run(func, context).map_err(
|
test.run(func, context)
|
||||||
|e| format!("{}: {}", name, e),
|
.map_err(|e| format!("{}: {}", name, e))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,16 +70,16 @@ pub trait SubTest {
|
|||||||
/// Run filecheck on `text`, using directives extracted from `context`.
|
/// Run filecheck on `text`, using directives extracted from `context`.
|
||||||
pub fn run_filecheck(text: &str, context: &Context) -> Result<()> {
|
pub fn run_filecheck(text: &str, context: &Context) -> Result<()> {
|
||||||
let checker = build_filechecker(context)?;
|
let checker = build_filechecker(context)?;
|
||||||
if checker.check(text, NO_VARIABLES).map_err(|e| {
|
if checker
|
||||||
format!("filecheck: {}", e)
|
.check(text, NO_VARIABLES)
|
||||||
})?
|
.map_err(|e| format!("filecheck: {}", e))?
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// Filecheck mismatch. Emit an explanation as output.
|
// Filecheck mismatch. Emit an explanation as output.
|
||||||
let (_, explain) = checker.explain(text, NO_VARIABLES).map_err(|e| {
|
let (_, explain) = checker
|
||||||
format!("explain: {}", e)
|
.explain(text, NO_VARIABLES)
|
||||||
})?;
|
.map_err(|e| format!("explain: {}", e))?;
|
||||||
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,14 +89,14 @@ pub fn build_filechecker(context: &Context) -> Result<Checker> {
|
|||||||
let mut builder = CheckerBuilder::new();
|
let mut builder = CheckerBuilder::new();
|
||||||
// Preamble comments apply to all functions.
|
// Preamble comments apply to all functions.
|
||||||
for comment in context.preamble_comments {
|
for comment in context.preamble_comments {
|
||||||
builder.directive(comment.text).map_err(|e| {
|
builder
|
||||||
format!("filecheck: {}", e)
|
.directive(comment.text)
|
||||||
})?;
|
.map_err(|e| format!("filecheck: {}", e))?;
|
||||||
}
|
}
|
||||||
for comment in &context.details.comments {
|
for comment in &context.details.comments {
|
||||||
builder.directive(comment.text).map_err(|e| {
|
builder
|
||||||
format!("filecheck: {}", e)
|
.directive(comment.text)
|
||||||
})?;
|
.map_err(|e| format!("filecheck: {}", e))?;
|
||||||
}
|
}
|
||||||
Ok(builder.finish())
|
Ok(builder.finish())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ impl SubTest for TestBinEmit {
|
|||||||
// If not optimizing, just use the first encoding.
|
// If not optimizing, just use the first encoding.
|
||||||
legal_encodings.next()
|
legal_encodings.next()
|
||||||
}
|
}
|
||||||
}
|
} {
|
||||||
{
|
|
||||||
func.encodings[inst] = enc;
|
func.encodings[inst] = enc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,9 +158,8 @@ impl SubTest for TestBinEmit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Relax branches and compute EBB offsets based on the encodings.
|
// Relax branches and compute EBB offsets based on the encodings.
|
||||||
let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
|
let code_size = binemit::relax_branches(&mut func, isa)
|
||||||
pretty_error(&func, context.isa, e)
|
.map_err(|e| pretty_error(&func, context.isa, e))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Collect all of the 'bin:' directives on instructions.
|
// Collect all of the 'bin:' directives on instructions.
|
||||||
let mut bins = HashMap::new();
|
let mut bins = HashMap::new();
|
||||||
@@ -181,8 +179,7 @@ impl SubTest for TestBinEmit {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"'bin:' directive on non-inst {}: {}",
|
"'bin:' directive on non-inst {}: {}",
|
||||||
comment.entity,
|
comment.entity, comment.text
|
||||||
comment.text
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,8 +195,7 @@ impl SubTest for TestBinEmit {
|
|||||||
divert.clear();
|
divert.clear();
|
||||||
// Correct header offsets should have been computed by `relax_branches()`.
|
// Correct header offsets should have been computed by `relax_branches()`.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sink.offset,
|
sink.offset, func.offsets[ebb],
|
||||||
func.offsets[ebb],
|
|
||||||
"Inconsistent {} header offset",
|
"Inconsistent {} header offset",
|
||||||
ebb
|
ebb
|
||||||
);
|
);
|
||||||
@@ -211,9 +207,10 @@ impl SubTest for TestBinEmit {
|
|||||||
// Send legal encodings into the emitter.
|
// Send legal encodings into the emitter.
|
||||||
if enc.is_legal() {
|
if enc.is_legal() {
|
||||||
// Generate a better error message if output locations are not specified.
|
// Generate a better error message if output locations are not specified.
|
||||||
if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
|
if let Some(&v) = func.dfg
|
||||||
!func.locations[v].is_assigned()
|
.inst_results(inst)
|
||||||
})
|
.iter()
|
||||||
|
.find(|&&v| !func.locations[v].is_assigned())
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Missing register/stack slot for {} in {}",
|
"Missing register/stack slot for {} in {}",
|
||||||
@@ -239,9 +236,10 @@ impl SubTest for TestBinEmit {
|
|||||||
if !enc.is_legal() {
|
if !enc.is_legal() {
|
||||||
// A possible cause of an unencoded instruction is a missing location for
|
// A possible cause of an unencoded instruction is a missing location for
|
||||||
// one of the input operands.
|
// one of the input operands.
|
||||||
if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
|
if let Some(&v) = func.dfg
|
||||||
!func.locations[v].is_assigned()
|
.inst_args(inst)
|
||||||
})
|
.iter()
|
||||||
|
.find(|&&v| !func.locations[v].is_assigned())
|
||||||
{
|
{
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Missing register/stack slot for {} in {}",
|
"Missing register/stack slot for {} in {}",
|
||||||
@@ -287,8 +285,7 @@ impl SubTest for TestBinEmit {
|
|||||||
if sink.offset != code_size {
|
if sink.offset != code_size {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Expected code size {}, got {}",
|
"Expected code size {}, got {}",
|
||||||
code_size,
|
code_size, sink.offset
|
||||||
sink.offset
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user