<commit_before>use std::env;
use std::fs;
use std::io::prelude::*;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use filetime::FileTime;

static CARGO_INTEGRATION_TEST_DIR : &'static str = "cit";
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
thread_local!(static TASK_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst));

pub fn root() -> PathBuf {
    env::current_exe().unwrap()
                  .parent().unwrap() // chop off exe name
                  .parent().unwrap() // chop off 'debug'
                  .parent().unwrap() // chop off target
                  .join(CARGO_INTEGRATION_TEST_DIR)
                  .join(&TASK_ID.with(|my_id| format!("t{}", my_id)))
}

pub fn home() -> PathBuf {
    root().join("home")
}

pub trait CargoPathExt {
    fn rm_rf(&self) -> io::Result<()>;
    fn mkdir_p(&self) -> io::Result<()>;
    fn move_into_the_past(&self) -> io::Result<()>;

    // cargo versions of the standard PathExt trait
    fn c_exists(&self) -> bool;
    fn c_is_file(&self) -> bool;
    fn c_is_dir(&self) -> bool;
    fn c_metadata(&self) -> io::Result<fs::Metadata>;
}

impl CargoPathExt for Path {
    /* Technically there is a potential race condition, but we don't
     * care all that much for our tests
     */
    fn rm_rf(&self) -> io::Result<()> {
        if self.c_exists() {
            for file in fs::read_dir(self).unwrap() {
                let file = try!(file).path();

                if file.c_is_dir() {
                    try!(file.rm_rf());
                } else {
                    // On windows we can't remove a readonly file, and git will
                    // often clone files as readonly. As a result, we have some
                    // special logic to remove readonly files on windows.
                    match fs::remove_file(&file) {
                        Ok(()) => {}
                        Err(ref e) if cfg!(windows) &&
                                      e.kind() == ErrorKind::PermissionDenied => {
                            let mut p = file.c_metadata().unwrap().permissions();
                            p.set_readonly(false);
                            fs::set_permissions(&file, p).unwrap();
                            try!(fs::remove_file(&file));
                        }
                        Err(e) => return Err(e)
                    }
                }
            }
            fs::remove_dir(self)
        } else {
            Ok(())
        }
    }

    fn mkdir_p(&self) -> io::Result<()> {
        fs::create_dir_all(self)
    }

    fn move_into_the_past(&self) -> io::Result<()> {
        if self.c_is_file() {
            try!(time_travel(self));
        } else {
            try!(recurse(self, &self.join("target")));
        }
        return Ok(());

        fn recurse(p: &Path, bad: &Path) -> io::Result<()> {
            if p.c_is_file() {
                time_travel(p)
            } else if p.starts_with(bad) {
                Ok(())
            } else {
                for f in try!(fs::read_dir(p)) {
                    let f = try!(f).path();
                    try!(recurse(&f, bad));
                }
                Ok(())
            }
        }

        fn time_travel(path: &Path) -> io::Result<()> {
            let stat = try!(path.c_metadata());

            let mtime = FileTime::from_last_modification_time(&stat);
            let newtime = mtime.seconds() - 3600;

            // Sadly change_file_times has a failure mode where a readonly file
            // cannot have its times changed on windows.
            match set_file_times(path, newtime, newtime) {
                Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {}
                e => return e,
            }
            let mut perms = stat.permissions();
            perms.set_readonly(false);
            try!(fs::set_permissions(path, perms));
            set_file_times(path, newtime, newtime)
        }

        #[cfg(unix)]
        fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
            use std::os::unix::prelude::*;
            use std::ffi::CString;
            use libc::{timeval, time_t, c_char, c_int};

            let p = try!(CString::new(p.as_os_str().as_bytes()));
            let atime = timeval { tv_sec: atime as time_t, tv_usec: 0, };
            let mtime = timeval { tv_sec: mtime as time_t, tv_usec: 0, };
            let times = [atime, mtime];
            extern {
                fn utimes(name: *const c_char, times: *const timeval) -> c_int;
            }
            unsafe {
                if utimes(p.as_ptr(), times.as_ptr()) == 0 {
                    Ok(())
                } else {
                    Err(io::Error::last_os_error())
                }
            }
        }

        #[cfg(windows)]
        fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
            use std::fs::OpenOptions;
            use std::os::windows::prelude::*;
            use winapi::{FILETIME, DWORD};
            use kernel32;

            let f = try!(OpenOptions::new().write(true).open(p));
            let atime = to_filetime(atime);
            let mtime = to_filetime(mtime);
            return unsafe {
                let ret = kernel32::SetFileTime(f.as_raw_handle() as *mut _,
                                                0 as *const _,
                                                &atime, &mtime);
                if ret != 0 {
                    Ok(())
                } else {
                    Err(io::Error::last_os_error())
                }
            };

            fn to_filetime(seconds: u64) -> FILETIME {
                // FILETIME is a count of 100ns intervals, and there are 10^7 of
                // these in a second
                let seconds = seconds * 10_000_000;
                FILETIME {
                    dwLowDateTime: seconds as DWORD,
                    dwHighDateTime: (seconds >> 32) as DWORD,
                }
            }
        }
    }

    fn c_exists(&self) -> bool {
        fs::metadata(self).is_ok()
    }

    fn c_is_file(&self) -> bool {
        fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
    }

    fn c_is_dir(&self) -> bool {
        fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
    }

    fn c_metadata(&self) -> io::Result<fs::Metadata> {
        fs::metadata(self)
    }
}

/// Ensure required test directories exist and are empty
pub fn setup() {
    debug!("path setup; root={}; home={}", root().display(), home().display());
    static INIT: Once = ONCE_INIT;
    INIT.call_once(|| {
        root().parent().unwrap().mkdir_p().unwrap();
    });
    root().rm_rf().unwrap();
    home().mkdir_p().unwrap();
}
<commit_msg>Fix running `cargo test` from the project root<commit_after>use std::env;
use std::fs;
use std::io::prelude::*;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use filetime::FileTime;

static CARGO_INTEGRATION_TEST_DIR : &'static str = "cit";
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
thread_local!(static TASK_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst));

pub fn root() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop(); // chop off exe name
    path.pop(); // chop off 'debug'

    // If `cargo test` is run manually then our path looks like
    // `target/debug/foo`, in which case our `path` is already pointing at
    // `target`. If, however, `cargo test --target $target` is used then the
    // output is `target/$target/debug/foo`, so our path is pointing at
    // `target/$target`. Here we conditionally pop the `$target` name.
    if path.file_name().and_then(|s| s.to_str()) != Some("target") {
        path.pop();
    }

    path.join(CARGO_INTEGRATION_TEST_DIR)
        .join(&TASK_ID.with(|my_id| format!("t{}", my_id)))
}

pub fn home() -> PathBuf {
    root().join("home")
}

pub trait CargoPathExt {
    fn rm_rf(&self) -> io::Result<()>;
    fn mkdir_p(&self) -> io::Result<()>;
    fn move_into_the_past(&self) -> io::Result<()>;

    // cargo versions of the standard PathExt trait
    fn c_exists(&self) -> bool;
    fn c_is_file(&self) -> bool;
    fn c_is_dir(&self) -> bool;
    fn c_metadata(&self) -> io::Result<fs::Metadata>;
}

impl CargoPathExt for Path {
    /* Technically there is a potential race condition, but we don't
     * care all that much for our tests
     */
    fn rm_rf(&self) -> io::Result<()> {
        if self.c_exists() {
            for file in fs::read_dir(self).unwrap() {
                let file = try!(file).path();

                if file.c_is_dir() {
                    try!(file.rm_rf());
                } else {
                    // On windows we can't remove a readonly file, and git will
                    // often clone files as readonly. As a result, we have some
                    // special logic to remove readonly files on windows.
                    match fs::remove_file(&file) {
                        Ok(()) => {}
                        Err(ref e) if cfg!(windows) &&
                                      e.kind() == ErrorKind::PermissionDenied => {
                            let mut p = file.c_metadata().unwrap().permissions();
                            p.set_readonly(false);
                            fs::set_permissions(&file, p).unwrap();
                            try!(fs::remove_file(&file));
                        }
                        Err(e) => return Err(e)
                    }
                }
            }
            fs::remove_dir(self)
        } else {
            Ok(())
        }
    }

    fn mkdir_p(&self) -> io::Result<()> {
        fs::create_dir_all(self)
    }

    fn move_into_the_past(&self) -> io::Result<()> {
        if self.c_is_file() {
            try!(time_travel(self));
        } else {
            try!(recurse(self, &self.join("target")));
        }
        return Ok(());

        fn recurse(p: &Path, bad: &Path) -> io::Result<()> {
            if p.c_is_file() {
                time_travel(p)
            } else if p.starts_with(bad) {
                Ok(())
            } else {
                for f in try!(fs::read_dir(p)) {
                    let f = try!(f).path();
                    try!(recurse(&f, bad));
                }
                Ok(())
            }
        }

        fn time_travel(path: &Path) -> io::Result<()> {
            let stat = try!(path.c_metadata());

            let mtime = FileTime::from_last_modification_time(&stat);
            let newtime = mtime.seconds() - 3600;

            // Sadly change_file_times has a failure mode where a readonly file
            // cannot have its times changed on windows.
            match set_file_times(path, newtime, newtime) {
                Err(ref e) if e.kind() == io::ErrorKind::PermissionDenied => {}
                e => return e,
            }
            let mut perms = stat.permissions();
            perms.set_readonly(false);
            try!(fs::set_permissions(path, perms));
            set_file_times(path, newtime, newtime)
        }

        #[cfg(unix)]
        fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
            use std::os::unix::prelude::*;
            use std::ffi::CString;
            use libc::{timeval, time_t, c_char, c_int};

            let p = try!(CString::new(p.as_os_str().as_bytes()));
            let atime = timeval { tv_sec: atime as time_t, tv_usec: 0, };
            let mtime = timeval { tv_sec: mtime as time_t, tv_usec: 0, };
            let times = [atime, mtime];
            extern {
                fn utimes(name: *const c_char, times: *const timeval) -> c_int;
            }
            unsafe {
                if utimes(p.as_ptr(), times.as_ptr()) == 0 {
                    Ok(())
                } else {
                    Err(io::Error::last_os_error())
                }
            }
        }

        #[cfg(windows)]
        fn set_file_times(p: &Path, atime: u64, mtime: u64) -> io::Result<()> {
            use std::fs::OpenOptions;
            use std::os::windows::prelude::*;
            use winapi::{FILETIME, DWORD};
            use kernel32;

            let f = try!(OpenOptions::new().write(true).open(p));
            let atime = to_filetime(atime);
            let mtime = to_filetime(mtime);
            return unsafe {
                let ret = kernel32::SetFileTime(f.as_raw_handle() as *mut _,
                                                0 as *const _,
                                                &atime, &mtime);
                if ret != 0 {
                    Ok(())
                } else {
                    Err(io::Error::last_os_error())
                }
            };

            fn to_filetime(seconds: u64) -> FILETIME {
                // FILETIME is a count of 100ns intervals, and there are 10^7 of
                // these in a second
                let seconds = seconds * 10_000_000;
                FILETIME {
                    dwLowDateTime: seconds as DWORD,
                    dwHighDateTime: (seconds >> 32) as DWORD,
                }
            }
        }
    }

    fn c_exists(&self) -> bool {
        fs::metadata(self).is_ok()
    }

    fn c_is_file(&self) -> bool {
        fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
    }

    fn c_is_dir(&self) -> bool {
        fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
    }

    fn c_metadata(&self) -> io::Result<fs::Metadata> {
        fs::metadata(self)
    }
}

/// Ensure required test directories exist and are empty
pub fn setup() {
    debug!("path setup; root={}; home={}", root().display(), home().display());
    static INIT: Once = ONCE_INIT;
    INIT.call_once(|| {
        root().parent().unwrap().mkdir_p().unwrap();
    });
    root().rm_rf().unwrap();
    home().mkdir_p().unwrap();
}
<|endoftext|>


<commit_before>mod attestation_producer;
mod block_producer;
mod config;
mod duties;
pub mod error;
mod service;
mod signer;

use crate::config::Config as ValidatorClientConfig;
use crate::service::Service as ValidatorService;
use clap::{App, Arg};
use eth2_config::{get_data_dir, read_from_file, write_to_file, Eth2Config};
use protos::services_grpc::ValidatorServiceClient;
use slog::{crit, error, info, o, Drain};
use std::path::PathBuf;
use types::{Keypair, MainnetEthSpec, MinimalEthSpec};

pub const DEFAULT_SPEC: &str = "minimal";
pub const DEFAULT_DATA_DIR: &str = ".lighthouse-validator";
pub const CLIENT_CONFIG_FILENAME: &str = "validator-client.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";

fn main() {
    // Logging
    let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::CompactFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();
    let log = slog::Logger::root(drain, o!());

    // CLI
    let matches = App::new("Lighthouse Validator Client")
        .version("0.0.1")
        .author("Sigma Prime <contact@sigmaprime.io>")
        .about("Eth 2.0 Validator Client")
        .arg(
            Arg::with_name("datadir")
                .long("datadir")
                .value_name("DIR")
                .help("Data directory for keys and databases.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("eth2-spec")
                .long("eth2-spec")
                .short("e")
                .value_name("TOML_FILE")
                .help("Path to Ethereum 2.0 specifications file.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("server")
                .long("server")
                .value_name("server")
                .help("Address to connect to BeaconNode.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("spec-constants")
                .long("spec-constants")
                .value_name("TITLE")
                .short("s")
                .help("The title of the spec constants for chain config.")
                .takes_value(true)
                .possible_values(&["mainnet", "minimal"])
                .default_value("minimal"),
        )
        .get_matches();

    let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) {
        Ok(dir) => dir,
        Err(e) => {
            crit!(log, "Failed to initialize data dir"; "error" => format!("{:?}", e));
            return;
        }
    };

    let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME);

    // Attempt to lead the `ClientConfig` from disk.
    //
    // If file doesn't exist, create a new, default one.
    let mut client_config = match read_from_file::<ValidatorClientConfig>(
        client_config_path.clone(),
    ) {
        Ok(Some(c)) => c,
        Ok(None) => {
            let default = ValidatorClientConfig::default();
            if let Err(e) = write_to_file(client_config_path.clone(), &default) {
                crit!(log, "Failed to write default ClientConfig to file"; "error" => format!("{:?}", e));
                return;
            }
            default
        }
        Err(e) => {
            crit!(log, "Failed to load a ChainConfig file"; "error" => format!("{:?}", e));
            return;
        }
    };

    // Ensure the `data_dir` in the config matches that supplied to the CLI.
    client_config.data_dir = data_dir.clone();

    // Update the client config with any CLI args.
    match client_config.apply_cli_args(&matches) {
        Ok(()) => (),
        Err(s) => {
            crit!(log, "Failed to parse ClientConfig CLI arguments"; "error" => s);
            return;
        }
    };

    let eth2_config_path: PathBuf = matches
        .value_of("eth2-spec")
        .and_then(|s| Some(PathBuf::from(s)))
        .unwrap_or_else(|| data_dir.join(ETH2_CONFIG_FILENAME));

    // Attempt to load the `Eth2Config` from file.
    //
    // If the file doesn't exist, create a default one depending on the CLI flags.
    let mut eth2_config = match read_from_file::<Eth2Config>(eth2_config_path.clone()) {
        Ok(Some(c)) => c,
        Ok(None) => {
            let default = match matches.value_of("spec-constants") {
                Some("mainnet") => Eth2Config::mainnet(),
                Some("minimal") => Eth2Config::minimal(),
                _ => unreachable!(), // Guarded by slog.
            };
            if let Err(e) = write_to_file(eth2_config_path, &default) {
                crit!(log, "Failed to write default Eth2Config to file"; "error" => format!("{:?}", e));
                return;
            }
            default
        }
        Err(e) => {
            crit!(log, "Failed to instantiate an Eth2Config"; "error" => format!("{:?}", e));
            return;
        }
    };

    // Update the eth2 config with any CLI flags.
    match eth2_config.apply_cli_args(&matches) {
        Ok(()) => (),
        Err(s) => {
            crit!(log, "Failed to parse Eth2Config CLI arguments"; "error" => s);
            return;
        }
    };

    info!(
        log,
        "Starting validator client";
        "datadir" => client_config.data_dir.to_str(),
        "spec_constants" => &eth2_config.spec_constants,
    );

    let result = match eth2_config.spec_constants.as_str() {
        "mainnet" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MainnetEthSpec>(
            client_config,
            eth2_config,
            log.clone(),
        ),
        "minimal" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MinimalEthSpec>(
            client_config,
            eth2_config,
            log.clone(),
        ),
        other => {
            crit!(log, "Unknown spec constants"; "title" => other);
            return;
        }
    };

    // start the validator service.
    // this specifies the GRPC and signer type to use as the duty manager beacon node.
    match result {
        Ok(_) => info!(log, "Validator client shutdown successfully."),
        Err(e) => crit!(log, "Validator client exited with error"; "error" => e.to_string()),
    }
}
<commit_msg>Small CLI update.  - Added the 'd' short version of the 'datadir' flag for the validator client.<commit_after>mod attestation_producer;
mod block_producer;
mod config;
mod duties;
pub mod error;
mod service;
mod signer;

use crate::config::Config as ValidatorClientConfig;
use crate::service::Service as ValidatorService;
use clap::{App, Arg};
use eth2_config::{get_data_dir, read_from_file, write_to_file, Eth2Config};
use protos::services_grpc::ValidatorServiceClient;
use slog::{crit, error, info, o, Drain};
use std::path::PathBuf;
use types::{Keypair, MainnetEthSpec, MinimalEthSpec};

pub const DEFAULT_SPEC: &str = "minimal";
pub const DEFAULT_DATA_DIR: &str = ".lighthouse-validator";
pub const CLIENT_CONFIG_FILENAME: &str = "validator-client.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";

fn main() {
    // Logging
    let decorator = slog_term::TermDecorator::new().build();
    let drain = slog_term::CompactFormat::new(decorator).build().fuse();
    let drain = slog_async::Async::new(drain).build().fuse();
    let log = slog::Logger::root(drain, o!());

    // CLI
    let matches = App::new("Lighthouse Validator Client")
        .version("0.0.1")
        .author("Sigma Prime <contact@sigmaprime.io>")
        .about("Eth 2.0 Validator Client")
        .arg(
            Arg::with_name("datadir")
                .long("datadir")
                .short("d")
                .value_name("DIR")
                .help("Data directory for keys and databases.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("eth2-spec")
                .long("eth2-spec")
                .short("e")
                .value_name("TOML_FILE")
                .help("Path to Ethereum 2.0 specifications file.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("server")
                .long("server")
                .value_name("server")
                .help("Address to connect to BeaconNode.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("spec-constants")
                .long("spec-constants")
                .value_name("TITLE")
                .short("s")
                .help("The title of the spec constants for chain config.")
                .takes_value(true)
                .possible_values(&["mainnet", "minimal"])
                .default_value("minimal"),
        )
        .get_matches();

    let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) {
        Ok(dir) => dir,
        Err(e) => {
            crit!(log, "Failed to initialize data dir"; "error" => format!("{:?}", e));
            return;
        }
    };

    let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME);

    // Attempt to lead the `ClientConfig` from disk.
    //
    // If file doesn't exist, create a new, default one.
    let mut client_config = match read_from_file::<ValidatorClientConfig>(
        client_config_path.clone(),
    ) {
        Ok(Some(c)) => c,
        Ok(None) => {
            let default = ValidatorClientConfig::default();
            if let Err(e) = write_to_file(client_config_path.clone(), &default) {
                crit!(log, "Failed to write default ClientConfig to file"; "error" => format!("{:?}", e));
                return;
            }
            default
        }
        Err(e) => {
            crit!(log, "Failed to load a ChainConfig file"; "error" => format!("{:?}", e));
            return;
        }
    };

    // Ensure the `data_dir` in the config matches that supplied to the CLI.
    client_config.data_dir = data_dir.clone();

    // Update the client config with any CLI args.
    match client_config.apply_cli_args(&matches) {
        Ok(()) => (),
        Err(s) => {
            crit!(log, "Failed to parse ClientConfig CLI arguments"; "error" => s);
            return;
        }
    };

    let eth2_config_path: PathBuf = matches
        .value_of("eth2-spec")
        .and_then(|s| Some(PathBuf::from(s)))
        .unwrap_or_else(|| data_dir.join(ETH2_CONFIG_FILENAME));

    // Attempt to load the `Eth2Config` from file.
    //
    // If the file doesn't exist, create a default one depending on the CLI flags.
    let mut eth2_config = match read_from_file::<Eth2Config>(eth2_config_path.clone()) {
        Ok(Some(c)) => c,
        Ok(None) => {
            let default = match matches.value_of("spec-constants") {
                Some("mainnet") => Eth2Config::mainnet(),
                Some("minimal") => Eth2Config::minimal(),
                _ => unreachable!(), // Guarded by slog.
            };
            if let Err(e) = write_to_file(eth2_config_path, &default) {
                crit!(log, "Failed to write default Eth2Config to file"; "error" => format!("{:?}", e));
                return;
            }
            default
        }
        Err(e) => {
            crit!(log, "Failed to instantiate an Eth2Config"; "error" => format!("{:?}", e));
            return;
        }
    };

    // Update the eth2 config with any CLI flags.
    match eth2_config.apply_cli_args(&matches) {
        Ok(()) => (),
        Err(s) => {
            crit!(log, "Failed to parse Eth2Config CLI arguments"; "error" => s);
            return;
        }
    };

    info!(
        log,
        "Starting validator client";
        "datadir" => client_config.data_dir.to_str(),
        "spec_constants" => &eth2_config.spec_constants,
    );

    let result = match eth2_config.spec_constants.as_str() {
        "mainnet" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MainnetEthSpec>(
            client_config,
            eth2_config,
            log.clone(),
        ),
        "minimal" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MinimalEthSpec>(
            client_config,
            eth2_config,
            log.clone(),
        ),
        other => {
            crit!(log, "Unknown spec constants"; "title" => other);
            return;
        }
    };

    // start the validator service.
    // this specifies the GRPC and signer type to use as the duty manager beacon node.
    match result {
        Ok(_) => info!(log, "Validator client shutdown successfully."),
        Err(e) => crit!(log, "Validator client exited with error"; "error" => e.to_string()),
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Borders, padding, and margins.

use std::num::Zero;
use geom::side_offsets::SideOffsets2D;
use gfx::geometry::Au;
use newcss::complete::CompleteStyle;
use newcss::units::{Em, Pt, Px};
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin, CSSFontSize, CSSFontSizeLength};
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
use newcss::values::{CSSHeight, CSSHeightLength, CSSHeightPercentage, CSSHeightAuto};
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
pub struct BoxModel {
    border: SideOffsets2D<Au>,
    padding: SideOffsets2D<Au>,
    margin: SideOffsets2D<Au>,
    /// The width of the content box.
    content_box_width: Au,
}

/// Useful helper data type when computing values for blocks and positioned elements.
pub enum MaybeAuto {
    Auto,
    Specified(Au),
}

impl MaybeAuto {
    pub fn from_margin(margin: CSSMargin, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
        match margin {
            CSSMarginAuto => Auto,
            CSSMarginPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
            CSSMarginLength(Px(v)) => Specified(Au::from_frac_px(v)),
            CSSMarginLength(Pt(v)) => Specified(Au::from_pt(v)),
            CSSMarginLength(Em(em)) => {
                match font_size {
                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
                    _ => fail!(~"expected non-relative font size"),
                }
            }
        }
    }

    pub fn from_width(width: CSSWidth, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
        match width {
            CSSWidthAuto => Auto,
            CSSWidthPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
            CSSWidthLength(Px(v)) => Specified(Au::from_frac_px(v)),
            CSSWidthLength(Pt(v)) => Specified(Au::from_pt(v)),
            CSSWidthLength(Em(em)) => {
                match font_size {
                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
                    _ => fail!(~"expected non-relative font size"),
                }
            }
        }
    }

    pub fn from_height(height: CSSHeight, cb_height: Au, font_size: CSSFontSize) -> MaybeAuto {
        match height {
            CSSHeightAuto => Auto,
            CSSHeightPercentage(percent) => Specified(cb_height.scale_by(percent/100.0)),
            CSSHeightLength(Px(v)) => Specified(Au::from_frac_px(v)),
            CSSHeightLength(Pt(v)) => Specified(Au::from_pt(v)),
            CSSHeightLength(Em(em)) => {
                match font_size {
                    CSSFontSizeLength(Px(v)) => Specified(Au::from_frac_px(em * v)),
                    CSSFontSizeLength(Pt(v)) => Specified(Au::from_pt(em * v)),
                    _ => fail!(~"expected non-relative font size"),
                }
            }

        }
    }

    pub fn specified_or_default(&self, default: Au) -> Au {
        match *self {
            Auto => default,
            Specified(value) => value,
        }
    }

    pub fn specified_or_zero(&self) -> Au {
        self.specified_or_default(Au(0))
    }
}

impl Zero for BoxModel {
    fn zero() -> BoxModel {
        BoxModel {
            border: Zero::zero(),
            padding: Zero::zero(),
            margin: Zero::zero(),
            content_box_width: Zero::zero(),
        }
    }

    fn is_zero(&self) -> bool {
        self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
    }
}

impl BoxModel {
    /// Populates the box model parameters from the given computed style.
    pub fn compute_borders(&mut self, style: CompleteStyle) {
        // Compute the borders.
        self.border.top = self.compute_border_width(style.border_top_width(), style.font_size());
        self.border.right = self.compute_border_width(style.border_right_width(), style.font_size());
        self.border.bottom = self.compute_border_width(style.border_bottom_width(), style.font_size());
        self.border.left = self.compute_border_width(style.border_left_width(), style.font_size());
    }

    pub fn compute_padding(&mut self, style: CompleteStyle, containing_width: Au) {
        self.padding.top = self.compute_padding_length(style.padding_top(), containing_width, style.font_size());
        self.padding.right = self.compute_padding_length(style.padding_right(), containing_width, style.font_size());
        self.padding.bottom = self.compute_padding_length(style.padding_bottom(), containing_width, style.font_size());
        self.padding.left = self.compute_padding_length(style.padding_left(), containing_width, style.font_size());
    }

    pub fn noncontent_width(&self) -> Au {
        let left = self.margin.left + self.border.left + self.padding.left;
        let right = self.margin.right + self.border.right + self.padding.right;
        left + right
    }

    pub fn offset(&self) -> Au {
        self.margin.left + self.border.left + self.padding.left
    }

    /// Helper function to compute the border width in app units from the CSS border width.
    pub fn compute_border_width(&self, width: CSSBorderWidth, font_size: CSSFontSize) -> Au {
        match width {
            CSSBorderWidthLength(Px(v)) => Au::from_frac_px(v),
            CSSBorderWidthLength(Pt(v)) => Au::from_pt(v),
            CSSBorderWidthLength(Em(em)) => {
                match font_size {
                    CSSFontSizeLength(Px(v)) => Au::from_frac_px(em * v),
                    CSSFontSizeLength(Pt(v)) => Au::from_pt(em * v),
                    _ => fail!(~"expected non-relative font size"),
                }
            },
            CSSBorderWidthThin => Au::from_px(1),
            CSSBorderWidthMedium => Au::from_px(5),
            CSSBorderWidthThick => Au::from_px(10),
        }
    }

    pub fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au, font_size: CSSFontSize) -> Au {
        match padding {
            CSSPaddingLength(Px(v)) => Au::from_frac_px(v),
            CSSPaddingLength(Pt(v)) => Au::from_pt(v),
            CSSPaddingLength(Em(em)) => {
                match font_size {
                    CSSFontSizeLength(Px(v)) => Au::from_frac_px(em * v),
                    CSSFontSizeLength(Pt(v)) => Au::from_pt(em * v),
                    _ => fail!(~"expected non-relative font size"),
                }
            },
            CSSPaddingPercentage(p) => content_box_width.scale_by(p/100.0)
        }
    }
}
<commit_msg>auto merge of #663 : sanxiyn/servo/from-length, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Borders, padding, and margins.

use std::num::Zero;
use geom::side_offsets::SideOffsets2D;
use gfx::geometry::Au;
use newcss::complete::CompleteStyle;
use newcss::units::{Length, Em, Pt, Px};
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin, CSSFontSize, CSSFontSizeLength};
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
use newcss::values::{CSSHeight, CSSHeightLength, CSSHeightPercentage, CSSHeightAuto};
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
pub struct BoxModel {
    border: SideOffsets2D<Au>,
    padding: SideOffsets2D<Au>,
    margin: SideOffsets2D<Au>,
    /// The width of the content box.
    content_box_width: Au,
}

fn from_length(length: Length, font_size: CSSFontSize) -> Au {
    match length {
        Px(v) => Au::from_frac_px(v),
        Pt(v) => Au::from_pt(v),
        Em(em) => {
            match font_size {
                CSSFontSizeLength(Px(v)) => Au::from_frac_px(em * v),
                CSSFontSizeLength(Pt(v)) => Au::from_pt(em * v),
                _ => fail!("expected non-relative font size")
            }
        }
    }
}

/// Useful helper data type when computing values for blocks and positioned elements.
pub enum MaybeAuto {
    Auto,
    Specified(Au),
}

impl MaybeAuto {
    pub fn from_margin(margin: CSSMargin, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
        match margin {
            CSSMarginAuto => Auto,
            CSSMarginPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
            CSSMarginLength(length) => Specified(from_length(length, font_size))
        }
    }

    pub fn from_width(width: CSSWidth, containing_width: Au, font_size: CSSFontSize) -> MaybeAuto {
        match width {
            CSSWidthAuto => Auto,
            CSSWidthPercentage(percent) => Specified(containing_width.scale_by(percent/100.0)),
            CSSWidthLength(length) => Specified(from_length(length, font_size))
        }
    }

    pub fn from_height(height: CSSHeight, cb_height: Au, font_size: CSSFontSize) -> MaybeAuto {
        match height {
            CSSHeightAuto => Auto,
            CSSHeightPercentage(percent) => Specified(cb_height.scale_by(percent/100.0)),
            CSSHeightLength(length) => Specified(from_length(length, font_size))
        }
    }

    pub fn specified_or_default(&self, default: Au) -> Au {
        match *self {
            Auto => default,
            Specified(value) => value,
        }
    }

    pub fn specified_or_zero(&self) -> Au {
        self.specified_or_default(Au(0))
    }
}

impl Zero for BoxModel {
    fn zero() -> BoxModel {
        BoxModel {
            border: Zero::zero(),
            padding: Zero::zero(),
            margin: Zero::zero(),
            content_box_width: Zero::zero(),
        }
    }

    fn is_zero(&self) -> bool {
        self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
    }
}

impl BoxModel {
    /// Populates the box model parameters from the given computed style.
    pub fn compute_borders(&mut self, style: CompleteStyle) {
        // Compute the borders.
        self.border.top = self.compute_border_width(style.border_top_width(), style.font_size());
        self.border.right = self.compute_border_width(style.border_right_width(), style.font_size());
        self.border.bottom = self.compute_border_width(style.border_bottom_width(), style.font_size());
        self.border.left = self.compute_border_width(style.border_left_width(), style.font_size());
    }

    pub fn compute_padding(&mut self, style: CompleteStyle, containing_width: Au) {
        self.padding.top = self.compute_padding_length(style.padding_top(), containing_width, style.font_size());
        self.padding.right = self.compute_padding_length(style.padding_right(), containing_width, style.font_size());
        self.padding.bottom = self.compute_padding_length(style.padding_bottom(), containing_width, style.font_size());
        self.padding.left = self.compute_padding_length(style.padding_left(), containing_width, style.font_size());
    }

    pub fn noncontent_width(&self) -> Au {
        let left = self.margin.left + self.border.left + self.padding.left;
        let right = self.margin.right + self.border.right + self.padding.right;
        left + right
    }

    pub fn offset(&self) -> Au {
        self.margin.left + self.border.left + self.padding.left
    }

    /// Helper function to compute the border width in app units from the CSS border width.
    pub fn compute_border_width(&self, width: CSSBorderWidth, font_size: CSSFontSize) -> Au {
        match width {
            CSSBorderWidthLength(length) => from_length(length, font_size),
            CSSBorderWidthThin => Au::from_px(1),
            CSSBorderWidthMedium => Au::from_px(5),
            CSSBorderWidthThick => Au::from_px(10),
        }
    }

    pub fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au, font_size: CSSFontSize) -> Au {
        match padding {
            CSSPaddingLength(length) => from_length(length, font_size),
            CSSPaddingPercentage(p) => content_box_width.scale_by(p/100.0)
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

/// Test the functionality of device mapper devices.

extern crate devicemapper;
extern crate libstratis;
extern crate tempdir;
extern crate uuid;

use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;

use self::tempdir::TempDir;
use self::uuid::Uuid;

use self::devicemapper::DM;
use self::devicemapper::LinearDev;
use self::devicemapper::Segment;
use self::devicemapper::{DataBlocks, Sectors};
use self::devicemapper::{ThinDev, ThinDevId, ThinPoolDev};

use libstratis::engine::strat_engine::blockdevmgr::{initialize, resolve_devices};
use libstratis::engine::strat_engine::device::{blkdev_size, wipe_sectors};
use libstratis::engine::strat_engine::filesystem::{create_fs, mount_fs, unmount_fs};
use libstratis::engine::strat_engine::metadata::MIN_MDA_SECTORS;

/// Verify that the sum of the lengths of the available range of the
/// blockdevs in the linear device equals the size of the linear device.
pub fn test_linear_device(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();
    let initialized = initialize(&Uuid::new_v4(),
                                 unique_devices.clone(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();
    let total_blockdev_size = initialized
        .iter()
        .fold(Sectors(0), |s, i| s + i.avail_range().length);

    let segments = initialized
        .iter()
        .map(|block_dev| block_dev.avail_range())
        .collect::<Vec<Segment>>();

    let dm = DM::new().unwrap();
    let lineardev = LinearDev::new("stratis_testing_linear", &dm, segments).unwrap();
    let lineardev_size = blkdev_size(&OpenOptions::new()
                                          .read(true)
                                          .open(lineardev.devnode().unwrap())
                                          .unwrap())
            .unwrap();
    assert!(total_blockdev_size.bytes() == lineardev_size);
    lineardev.teardown(&dm).unwrap();
}

/// Verify no errors when writing files to a mounted filesystem on a thin
/// device.
pub fn test_thinpool_device(paths: &[&Path]) -> () {
    let initialized = initialize(&Uuid::new_v4(),
                                 resolve_devices(&paths).unwrap(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();

    let (metadata_blockdev, data_blockdev) = (initialized.first().unwrap(),
                                              initialized.last().unwrap());

    let dm = DM::new().unwrap();
    let metadata_dev = LinearDev::new("stratis_testing_thinpool_metadata",
                                      &dm,
                                      vec![metadata_blockdev.avail_range()])
            .unwrap();

    // Clear the meta data device.  If the first block is not all zeros - the
    // stale data will cause the device to appear as an existing meta rather
    // than a new one.  Clear the entire device to be safe.  Stratis implements
    // the same approach when constructing a thin pool.
    wipe_sectors(&metadata_dev.devnode().unwrap(),
                 Sectors(0),
                 metadata_dev.size().unwrap())
            .unwrap();

    let data_dev = LinearDev::new("stratis_testing_thinpool_datadev",
                                  &dm,
                                  vec![data_blockdev.avail_range()])
            .unwrap();
    let thinpool_dev = ThinPoolDev::new("stratis_testing_thinpool",
                                        &dm,
                                        data_dev.size().unwrap(),
                                        Sectors(1024),
                                        DataBlocks(256000),
                                        metadata_dev,
                                        data_dev)
            .unwrap();
    let thin_dev = ThinDev::new("stratis_testing_thindev",
                                &dm,
                                &thinpool_dev,
                                ThinDevId::new_u64(7).expect("7 is small enough"),
                                Sectors(300000))
            .unwrap();

    create_fs(&thin_dev.devnode().unwrap()).unwrap();

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    mount_fs(&thin_dev.devnode().unwrap(), tmp_dir.path()).unwrap();
    for i in 0..100 {
        let file_path = tmp_dir.path().join(format!("stratis_test{}.txt", i));
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(file_path)
                      .unwrap(),
                 "data")
                .unwrap();
    }
    // The -d (detach-loop) is passed for both loopback and real devs,
    // it helps with loopback devs and does no harm for real devs.
    unmount_fs(tmp_dir.path(), &["-d"]).unwrap();
    thin_dev.teardown(&dm).unwrap();
    thinpool_dev.teardown(&dm).unwrap();
}
<commit_msg>Use sum() instead of more general fold<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

/// Test the functionality of device mapper devices.

extern crate devicemapper;
extern crate libstratis;
extern crate tempdir;
extern crate uuid;

use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;

use self::tempdir::TempDir;
use self::uuid::Uuid;

use self::devicemapper::DM;
use self::devicemapper::LinearDev;
use self::devicemapper::Segment;
use self::devicemapper::{DataBlocks, Sectors};
use self::devicemapper::{ThinDev, ThinDevId, ThinPoolDev};

use libstratis::engine::strat_engine::blockdevmgr::{initialize, resolve_devices};
use libstratis::engine::strat_engine::device::{blkdev_size, wipe_sectors};
use libstratis::engine::strat_engine::filesystem::{create_fs, mount_fs, unmount_fs};
use libstratis::engine::strat_engine::metadata::MIN_MDA_SECTORS;

/// Verify that the sum of the lengths of the available range of the
/// blockdevs in the linear device equals the size of the linear device.
pub fn test_linear_device(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();
    let initialized = initialize(&Uuid::new_v4(),
                                 unique_devices.clone(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();
    let total_blockdev_size: Sectors = initialized.iter().map(|i| i.avail_range().length).sum();

    let segments = initialized
        .iter()
        .map(|block_dev| block_dev.avail_range())
        .collect::<Vec<Segment>>();

    let dm = DM::new().unwrap();
    let lineardev = LinearDev::new("stratis_testing_linear", &dm, segments).unwrap();
    let lineardev_size = blkdev_size(&OpenOptions::new()
                                          .read(true)
                                          .open(lineardev.devnode().unwrap())
                                          .unwrap())
            .unwrap();
    assert!(total_blockdev_size.bytes() == lineardev_size);
    lineardev.teardown(&dm).unwrap();
}

/// Verify no errors when writing files to a mounted filesystem on a thin
/// device.
pub fn test_thinpool_device(paths: &[&Path]) -> () {
    let initialized = initialize(&Uuid::new_v4(),
                                 resolve_devices(&paths).unwrap(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();

    let (metadata_blockdev, data_blockdev) = (initialized.first().unwrap(),
                                              initialized.last().unwrap());

    let dm = DM::new().unwrap();
    let metadata_dev = LinearDev::new("stratis_testing_thinpool_metadata",
                                      &dm,
                                      vec![metadata_blockdev.avail_range()])
            .unwrap();

    // Clear the meta data device.  If the first block is not all zeros - the
    // stale data will cause the device to appear as an existing meta rather
    // than a new one.  Clear the entire device to be safe.  Stratis implements
    // the same approach when constructing a thin pool.
    wipe_sectors(&metadata_dev.devnode().unwrap(),
                 Sectors(0),
                 metadata_dev.size().unwrap())
            .unwrap();

    let data_dev = LinearDev::new("stratis_testing_thinpool_datadev",
                                  &dm,
                                  vec![data_blockdev.avail_range()])
            .unwrap();
    let thinpool_dev = ThinPoolDev::new("stratis_testing_thinpool",
                                        &dm,
                                        data_dev.size().unwrap(),
                                        Sectors(1024),
                                        DataBlocks(256000),
                                        metadata_dev,
                                        data_dev)
            .unwrap();
    let thin_dev = ThinDev::new("stratis_testing_thindev",
                                &dm,
                                &thinpool_dev,
                                ThinDevId::new_u64(7).expect("7 is small enough"),
                                Sectors(300000))
            .unwrap();

    create_fs(&thin_dev.devnode().unwrap()).unwrap();

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    mount_fs(&thin_dev.devnode().unwrap(), tmp_dir.path()).unwrap();
    for i in 0..100 {
        let file_path = tmp_dir.path().join(format!("stratis_test{}.txt", i));
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(file_path)
                      .unwrap(),
                 "data")
                .unwrap();
    }
    // The -d (detach-loop) is passed for both loopback and real devs,
    // it helps with loopback devs and does no harm for real devs.
    unmount_fs(tmp_dir.path(), &["-d"]).unwrap();
    thin_dev.teardown(&dm).unwrap();
    thinpool_dev.teardown(&dm).unwrap();
}
<|endoftext|>


<commit_before>//! Shared definitions for the Cranelift intermediate language.

mod entities;
pub mod formats;
pub mod immediates;
pub mod instructions;
pub mod legalize;
pub mod settings;
pub mod types;

use crate::cdsl::formats::{FormatStructure, InstructionFormat};
use crate::cdsl::instructions::{AllInstructions, InstructionGroup};
use crate::cdsl::settings::SettingGroup;
use crate::cdsl::xform::TransformGroups;

use crate::shared::entities::EntityRefs;
use crate::shared::formats::Formats;
use crate::shared::immediates::Immediates;

use std::collections::HashMap;
use std::iter::FromIterator;
use std::rc::Rc;

pub(crate) struct Definitions {
    pub settings: SettingGroup,
    pub all_instructions: AllInstructions,
    pub instructions: InstructionGroup,
    pub imm: Immediates,
    pub formats: Formats,
    pub transform_groups: TransformGroups,
}

pub(crate) fn define() -> Definitions {
    let mut all_instructions = AllInstructions::new();

    let immediates = Immediates::new();
    let entities = EntityRefs::new();
    let formats = Formats::new(&immediates, &entities);
    let instructions =
        instructions::define(&mut all_instructions, &formats, &immediates, &entities);
    let transform_groups = legalize::define(&instructions, &immediates);

    Definitions {
        settings: settings::define(),
        all_instructions,
        instructions,
        imm: immediates,
        formats,
        transform_groups,
    }
}

impl Definitions {
    /// Verifies certain properties of formats.
    ///
    /// - Formats must be uniquely named: if two formats have the same name, they must refer to the
    /// same data. Otherwise, two format variants in the codegen crate would have the same name.
    /// - Formats must be structurally different from each other. Otherwise, this would lead to
    /// code duplicate in the codegen crate.
    ///
    /// Returns a list of all the instruction formats effectively used.
    pub fn verify_instruction_formats(&self) -> Vec<&InstructionFormat> {
        let mut format_names: HashMap<&'static str, &Rc<InstructionFormat>> = HashMap::new();

        // A structure is: number of input value operands / whether there's varargs or not / names
        // of immediate fields.
        let mut format_structures: HashMap<FormatStructure, &InstructionFormat> = HashMap::new();

        for inst in self.all_instructions.values() {
            // Check name.
            if let Some(existing_format) = format_names.get(&inst.format.name) {
                assert!(
                    Rc::ptr_eq(&existing_format, &inst.format),
                    "formats must uniquely named; there's a\
                     conflict on the name '{}', please make sure it is used only once.",
                    existing_format.name
                );
            } else {
                format_names.insert(inst.format.name, &inst.format);
            }

            // Check structure.
            let key = inst.format.structure();
            if let Some(existing_format) = format_structures.get(&key) {
                assert_eq!(
                    existing_format.name, inst.format.name,
                    "duplicate instruction formats {} and {}; please remove one.",
                    existing_format.name, inst.format.name
                );
            } else {
                format_structures.insert(key, &inst.format);
            }
        }

        Vec::from_iter(format_structures.into_iter().map(|(_, v)| v))
    }
}
<commit_msg>Sort the list of instruction formats by name<commit_after>//! Shared definitions for the Cranelift intermediate language.

mod entities;
pub mod formats;
pub mod immediates;
pub mod instructions;
pub mod legalize;
pub mod settings;
pub mod types;

use crate::cdsl::formats::{FormatStructure, InstructionFormat};
use crate::cdsl::instructions::{AllInstructions, InstructionGroup};
use crate::cdsl::settings::SettingGroup;
use crate::cdsl::xform::TransformGroups;

use crate::shared::entities::EntityRefs;
use crate::shared::formats::Formats;
use crate::shared::immediates::Immediates;

use std::collections::HashMap;
use std::iter::FromIterator;
use std::rc::Rc;

pub(crate) struct Definitions {
    pub settings: SettingGroup,
    pub all_instructions: AllInstructions,
    pub instructions: InstructionGroup,
    pub imm: Immediates,
    pub formats: Formats,
    pub transform_groups: TransformGroups,
}

pub(crate) fn define() -> Definitions {
    let mut all_instructions = AllInstructions::new();

    let immediates = Immediates::new();
    let entities = EntityRefs::new();
    let formats = Formats::new(&immediates, &entities);
    let instructions =
        instructions::define(&mut all_instructions, &formats, &immediates, &entities);
    let transform_groups = legalize::define(&instructions, &immediates);

    Definitions {
        settings: settings::define(),
        all_instructions,
        instructions,
        imm: immediates,
        formats,
        transform_groups,
    }
}

impl Definitions {
    /// Verifies certain properties of formats.
    ///
    /// - Formats must be uniquely named: if two formats have the same name, they must refer to the
    /// same data. Otherwise, two format variants in the codegen crate would have the same name.
    /// - Formats must be structurally different from each other. Otherwise, this would lead to
    /// code duplicate in the codegen crate.
    ///
    /// Returns a list of all the instruction formats effectively used.
    pub fn verify_instruction_formats(&self) -> Vec<&InstructionFormat> {
        let mut format_names: HashMap<&'static str, &Rc<InstructionFormat>> = HashMap::new();

        // A structure is: number of input value operands / whether there's varargs or not / names
        // of immediate fields.
        let mut format_structures: HashMap<FormatStructure, &InstructionFormat> = HashMap::new();

        for inst in self.all_instructions.values() {
            // Check name.
            if let Some(existing_format) = format_names.get(&inst.format.name) {
                assert!(
                    Rc::ptr_eq(&existing_format, &inst.format),
                    "formats must uniquely named; there's a\
                     conflict on the name '{}', please make sure it is used only once.",
                    existing_format.name
                );
            } else {
                format_names.insert(inst.format.name, &inst.format);
            }

            // Check structure.
            let key = inst.format.structure();
            if let Some(existing_format) = format_structures.get(&key) {
                assert_eq!(
                    existing_format.name, inst.format.name,
                    "duplicate instruction formats {} and {}; please remove one.",
                    existing_format.name, inst.format.name
                );
            } else {
                format_structures.insert(key, &inst.format);
            }
        }

        let mut result = Vec::from_iter(format_structures.into_iter().map(|(_, v)| v));
        result.sort_by_key(|format| format.name);
        result
    }
}
<|endoftext|>


<commit_before>//! Value splitting.
//!
//! Some value types are too large to fit in registers, so they need to be split into smaller parts
//! that the ISA can operate on. There's two dimensions of splitting, represented by two
//! complementary instruction pairs:
//!
//! - `isplit` and `iconcat` for splitting integer types into smaller integers.
//! - `vsplit` and `vconcat` for splitting vector types into smaller vector types with the same
//!   lane types.
//!
//! There is no floating point splitting. If an ISA doesn't support `f64` values, they probably
//! have to be bit-cast to `i64` and possibly split into two `i32` values that fit in registers.
//! This breakdown is handled by the ABI lowering.
//!
//! When legalizing a single instruction, it is wrapped in splits and concatenations:
//!
//! ```clif
//!     v1 = bxor.i64 v2, v3
//! ```
//!
//! becomes:
//!
//! ```clif
//!     v20, v21 = isplit v2
//!     v30, v31 = isplit v3
//!     v10 = bxor.i32 v20, v30
//!     v11 = bxor.i32 v21, v31
//!     v1 = iconcat v10, v11
//! ```
//!
//! This local expansion approach still leaves the original `i64` values in the code as operands on
//! the `split` and `concat` instructions. It also creates a lot of redundant code to clean up as
//! values are constantly split and concatenated.
//!
//! # Optimized splitting
//!
//! We can eliminate a lot of the splitting code quite easily. Whenever we need to split a value,
//! first check if the value is defined by the corresponding concatenation. If so, then just use
//! the two concatenation inputs directly:
//!
//! ```clif
//!     v4 = iadd_imm.i64 v1, 1
//! ```
//!
//! becomes, using the expanded code from above:
//!
//! ```clif
//!     v40, v5 = iadd_imm_cout.i32 v10, 1
//!     v6 = bint.i32
//!     v41 = iadd.i32 v11, v6
//!     v4 = iconcat v40, v41
//! ```
//!
//! This means that the `iconcat` instructions defining `v1` and `v4` end up with no uses, so they
//! can be trivially deleted by a dead code elimination pass.
//!
//! # EBB arguments
//!
//! If all instructions that produce an `i64` value are legalized as above, we will eventually end
//! up with no `i64` values anywhere, except for EBB arguments. We can work around this by
//! iteratively splitting EBB arguments too. That should leave us with no illegal value types
//! anywhere.
//!
//! It is possible to have circular dependencies of EBB arguments that are never used by any real
//! instructions. These loops will remain in the program.

use crate::cursor::{Cursor, CursorPosition, FuncCursor};
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::ir::{self, Ebb, Inst, InstBuilder, InstructionData, Opcode, Type, Value, ValueDef};
use core::iter;
use std::vec::Vec;

/// Split `value` into two values using the `isplit` semantics. Do this by reusing existing values
/// if possible.
pub fn isplit(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
) -> (Value, Value) {
    split_any(func, cfg, pos, srcloc, value, Opcode::Iconcat)
}

/// Split `value` into halves using the `vsplit` semantics. Do this by reusing existing values if
/// possible.
pub fn vsplit(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
) -> (Value, Value) {
    split_any(func, cfg, pos, srcloc, value, Opcode::Vconcat)
}

/// After splitting an EBB argument, we need to go back and fix up all of the predecessor
/// instructions. This is potentially a recursive operation, but we don't implement it recursively
/// since that could use up too muck stack.
///
/// Instead, the repairs are deferred and placed on a work list in stack form.
struct Repair {
    concat: Opcode,
    // The argument type after splitting.
    split_type: Type,
    // The destination EBB whose arguments have been split.
    ebb: Ebb,
    // Number of the original EBB argument which has been replaced by the low part.
    num: usize,
    // Number of the new EBB argument which represents the high part after the split.
    hi_num: usize,
}

/// Generic version of `isplit` and `vsplit` controlled by the `concat` opcode.
fn split_any(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
    concat: Opcode,
) -> (Value, Value) {
    let mut repairs = Vec::new();
    let pos = &mut FuncCursor::new(func).at_position(pos).with_srcloc(srcloc);
    let result = split_value(pos, value, concat, &mut repairs);

    perform_repairs(pos, cfg, repairs);

    result
}

pub fn split_ebb_params(func: &mut ir::Function, cfg: &ControlFlowGraph, ebb: Ebb) {
    let mut repairs = Vec::new();
    let pos = &mut FuncCursor::new(func).at_top(ebb);

    for (num, ebb_param) in pos
        .func
        .dfg
        .ebb_params(ebb)
        .to_vec()
        .into_iter()
        .enumerate()
    {
        let ty = pos.func.dfg.value_type(ebb_param);
        if ty != ir::types::I128 {
            continue;
        }

        split_ebb_param(pos, ebb, num, ebb_param, Opcode::Iconcat, &mut repairs);
    }

    perform_repairs(pos, cfg, repairs);
}

fn perform_repairs(pos: &mut FuncCursor, cfg: &ControlFlowGraph, mut repairs: Vec<Repair>) {
    // We have split the value requested, and now we may need to fix some EBB predecessors.
    while let Some(repair) = repairs.pop() {
        for BasicBlock { inst, .. } in cfg.pred_iter(repair.ebb) {
            let branch_opc = pos.func.dfg[inst].opcode();
            debug_assert!(
                branch_opc.is_branch(),
                "Predecessor not a branch: {}",
                pos.func.dfg.display_inst(inst, None)
            );
            let num_fixed_args = branch_opc.constraints().num_fixed_value_arguments();
            let mut args = pos.func.dfg[inst]
                .take_value_list()
                .expect("Branches must have value lists.");
            let num_args = args.len(&pos.func.dfg.value_lists);
            // Get the old value passed to the EBB argument we're repairing.
            let old_arg = args
                .get(num_fixed_args + repair.num, &pos.func.dfg.value_lists)
                .expect("Too few branch arguments");

            // It's possible that the CFG's predecessor list has duplicates. Detect them here.
            if pos.func.dfg.value_type(old_arg) == repair.split_type {
                pos.func.dfg[inst].put_value_list(args);
                continue;
            }

            // Split the old argument, possibly causing more repairs to be scheduled.
            pos.goto_inst(inst);
            let (lo, hi) = split_value(pos, old_arg, repair.concat, &mut repairs);

            // The `lo` part replaces the original argument.
            *args
                .get_mut(num_fixed_args + repair.num, &mut pos.func.dfg.value_lists)
                .unwrap() = lo;

            // The `hi` part goes at the end. Since multiple repairs may have been scheduled to the
            // same EBB, there could be multiple arguments missing.
            if num_args > num_fixed_args + repair.hi_num {
                *args
                    .get_mut(
                        num_fixed_args + repair.hi_num,
                        &mut pos.func.dfg.value_lists,
                    )
                    .unwrap() = hi;
            } else {
                // We need to append one or more arguments. If we're adding more than one argument,
                // there must be pending repairs on the stack that will fill in the correct values
                // instead of `hi`.
                args.extend(
                    iter::repeat(hi).take(1 + num_fixed_args + repair.hi_num - num_args),
                    &mut pos.func.dfg.value_lists,
                );
            }

            // Put the value list back after manipulating it.
            pos.func.dfg[inst].put_value_list(args);
        }
    }
}

/// Split a single value using the integer or vector semantics given by the `concat` opcode.
///
/// If the value is defined by a `concat` instruction, just reuse the operand values of that
/// instruction.
///
/// Return the two new values representing the parts of `value`.
fn split_value(
    pos: &mut FuncCursor,
    value: Value,
    concat: Opcode,
    repairs: &mut Vec<Repair>,
) -> (Value, Value) {
    let value = pos.func.dfg.resolve_aliases(value);
    let mut reuse = None;

    match pos.func.dfg.value_def(value) {
        ValueDef::Result(inst, num) => {
            // This is an instruction result. See if the value was created by a `concat`
            // instruction.
            if let InstructionData::Binary { opcode, args, .. } = pos.func.dfg[inst] {
                debug_assert_eq!(num, 0);
                if opcode == concat {
                    reuse = Some((args[0], args[1]));
                }
            }
        }
        ValueDef::Param(ebb, num) => {
            // This is an EBB parameter. We can split the parameter value unless this is the entry
            // block.
            if pos.func.layout.entry_block() != Some(ebb) {
                reuse = Some(split_ebb_param(pos, ebb, num, value, concat, repairs));
            }
        }
    }

    // Did the code above succeed in finding values we can reuse?
    if let Some(pair) = reuse {
        pair
    } else {
        // No, we'll just have to insert the requested split instruction at `pos`. Note that `pos`
        // has not been moved by the EBB argument code above when `reuse` is `None`.
        match concat {
            Opcode::Iconcat => pos.ins().isplit(value),
            Opcode::Vconcat => pos.ins().vsplit(value),
            _ => panic!("Unhandled concat opcode: {}", concat),
        }
    }
}

fn split_ebb_param(
    pos: &mut FuncCursor,
    ebb: Ebb,
    param_num: usize,
    value: Value,
    concat: Opcode,
    repairs: &mut Vec<Repair>,
) -> (Value, Value) {
    // We are going to replace the parameter at `num` with two new arguments.
    // Determine the new value types.
    let ty = pos.func.dfg.value_type(value);
    let split_type = match concat {
        Opcode::Iconcat => ty.half_width().expect("Invalid type for isplit"),
        Opcode::Vconcat => ty.half_vector().expect("Invalid type for vsplit"),
        _ => panic!("Unhandled concat opcode: {}", concat),
    };

    // Since the `repairs` stack potentially contains other parameter numbers for
    // `ebb`, avoid shifting and renumbering EBB parameters. It could invalidate other
    // `repairs` entries.
    //
    // Replace the original `value` with the low part, and append the high part at the
    // end of the argument list.
    let lo = pos.func.dfg.replace_ebb_param(value, split_type);
    let hi_num = pos.func.dfg.num_ebb_params(ebb);
    let hi = pos.func.dfg.append_ebb_param(ebb, split_type);

    // Now the original value is dangling. Insert a concatenation instruction that can
    // compute it from the two new parameters. This also serves as a record of what we
    // did so a future call to this function doesn't have to redo the work.
    //
    // 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.
    pos.goto_first_inst(ebb);
    pos.ins()
        .with_result(value)
        .Binary(concat, split_type, lo, hi);

    // Finally, splitting the EBB parameter is not enough. We also have to repair all
    // of the predecessor instructions that branch here.
    add_repair(concat, split_type, ebb, param_num, hi_num, repairs);

    (lo, hi)
}

// Add a repair entry to the work list.
fn add_repair(
    concat: Opcode,
    split_type: Type,
    ebb: Ebb,
    num: usize,
    hi_num: usize,
    repairs: &mut Vec<Repair>,
) {
    repairs.push(Repair {
        concat,
        split_type,
        ebb,
        num,
        hi_num,
    });
}

/// Strip concat-split chains. Return a simpler way of computing the same value.
///
/// Given this input:
///
/// ```clif
///     v10 = iconcat v1, v2
///     v11, v12 = isplit v10
/// ```
///
/// This function resolves `v11` to `v1` and `v12` to `v2`.
fn resolve_splits(dfg: &ir::DataFlowGraph, value: Value) -> Value {
    let value = dfg.resolve_aliases(value);

    // Deconstruct a split instruction.
    let split_res;
    let concat_opc;
    let split_arg;
    if let ValueDef::Result(inst, num) = dfg.value_def(value) {
        split_res = num;
        concat_opc = match dfg[inst].opcode() {
            Opcode::Isplit => Opcode::Iconcat,
            Opcode::Vsplit => Opcode::Vconcat,
            _ => return value,
        };
        split_arg = dfg.inst_args(inst)[0];
    } else {
        return value;
    }

    // See if split_arg is defined by a concatenation instruction.
    if let ValueDef::Result(inst, _) = dfg.value_def(split_arg) {
        if dfg[inst].opcode() == concat_opc {
            return dfg.inst_args(inst)[split_res];
        }
    }

    value
}

/// Simplify the arguments to a branch *after* the instructions leading up to the branch have been
/// legalized.
///
/// The branch argument repairs performed by `split_any()` above may be performed on branches that
/// have not yet been legalized. The repaired arguments can be defined by actual split
/// instructions in that case.
///
/// After legalizing the instructions computing the value that was split, it is likely that we can
/// avoid depending on the split instruction. Its input probably comes from a concatenation.
pub fn simplify_branch_arguments(dfg: &mut ir::DataFlowGraph, branch: Inst) {
    let mut new_args = Vec::new();

    for &arg in dfg.inst_args(branch) {
        let new_arg = resolve_splits(dfg, arg);
        new_args.push(new_arg);
    }

    dfg.inst_args_mut(branch).copy_from_slice(&new_args);
}
<commit_msg>legalizer/split.rs: simplify_branch_arguments: use SmallVec instead of Vec<commit_after>//! Value splitting.
//!
//! Some value types are too large to fit in registers, so they need to be split into smaller parts
//! that the ISA can operate on. There's two dimensions of splitting, represented by two
//! complementary instruction pairs:
//!
//! - `isplit` and `iconcat` for splitting integer types into smaller integers.
//! - `vsplit` and `vconcat` for splitting vector types into smaller vector types with the same
//!   lane types.
//!
//! There is no floating point splitting. If an ISA doesn't support `f64` values, they probably
//! have to be bit-cast to `i64` and possibly split into two `i32` values that fit in registers.
//! This breakdown is handled by the ABI lowering.
//!
//! When legalizing a single instruction, it is wrapped in splits and concatenations:
//!
//! ```clif
//!     v1 = bxor.i64 v2, v3
//! ```
//!
//! becomes:
//!
//! ```clif
//!     v20, v21 = isplit v2
//!     v30, v31 = isplit v3
//!     v10 = bxor.i32 v20, v30
//!     v11 = bxor.i32 v21, v31
//!     v1 = iconcat v10, v11
//! ```
//!
//! This local expansion approach still leaves the original `i64` values in the code as operands on
//! the `split` and `concat` instructions. It also creates a lot of redundant code to clean up as
//! values are constantly split and concatenated.
//!
//! # Optimized splitting
//!
//! We can eliminate a lot of the splitting code quite easily. Whenever we need to split a value,
//! first check if the value is defined by the corresponding concatenation. If so, then just use
//! the two concatenation inputs directly:
//!
//! ```clif
//!     v4 = iadd_imm.i64 v1, 1
//! ```
//!
//! becomes, using the expanded code from above:
//!
//! ```clif
//!     v40, v5 = iadd_imm_cout.i32 v10, 1
//!     v6 = bint.i32
//!     v41 = iadd.i32 v11, v6
//!     v4 = iconcat v40, v41
//! ```
//!
//! This means that the `iconcat` instructions defining `v1` and `v4` end up with no uses, so they
//! can be trivially deleted by a dead code elimination pass.
//!
//! # EBB arguments
//!
//! If all instructions that produce an `i64` value are legalized as above, we will eventually end
//! up with no `i64` values anywhere, except for EBB arguments. We can work around this by
//! iteratively splitting EBB arguments too. That should leave us with no illegal value types
//! anywhere.
//!
//! It is possible to have circular dependencies of EBB arguments that are never used by any real
//! instructions. These loops will remain in the program.

use crate::cursor::{Cursor, CursorPosition, FuncCursor};
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::ir::{self, Ebb, Inst, InstBuilder, InstructionData, Opcode, Type, Value, ValueDef};
use core::iter;
use smallvec::SmallVec;
use std::vec::Vec;

/// Split `value` into two values using the `isplit` semantics. Do this by reusing existing values
/// if possible.
pub fn isplit(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
) -> (Value, Value) {
    split_any(func, cfg, pos, srcloc, value, Opcode::Iconcat)
}

/// Split `value` into halves using the `vsplit` semantics. Do this by reusing existing values if
/// possible.
pub fn vsplit(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
) -> (Value, Value) {
    split_any(func, cfg, pos, srcloc, value, Opcode::Vconcat)
}

/// After splitting an EBB argument, we need to go back and fix up all of the predecessor
/// instructions. This is potentially a recursive operation, but we don't implement it recursively
/// since that could use up too muck stack.
///
/// Instead, the repairs are deferred and placed on a work list in stack form.
struct Repair {
    concat: Opcode,
    // The argument type after splitting.
    split_type: Type,
    // The destination EBB whose arguments have been split.
    ebb: Ebb,
    // Number of the original EBB argument which has been replaced by the low part.
    num: usize,
    // Number of the new EBB argument which represents the high part after the split.
    hi_num: usize,
}

/// Generic version of `isplit` and `vsplit` controlled by the `concat` opcode.
fn split_any(
    func: &mut ir::Function,
    cfg: &ControlFlowGraph,
    pos: CursorPosition,
    srcloc: ir::SourceLoc,
    value: Value,
    concat: Opcode,
) -> (Value, Value) {
    let mut repairs = Vec::new();
    let pos = &mut FuncCursor::new(func).at_position(pos).with_srcloc(srcloc);
    let result = split_value(pos, value, concat, &mut repairs);

    perform_repairs(pos, cfg, repairs);

    result
}

pub fn split_ebb_params(func: &mut ir::Function, cfg: &ControlFlowGraph, ebb: Ebb) {
    let mut repairs = Vec::new();
    let pos = &mut FuncCursor::new(func).at_top(ebb);

    for (num, ebb_param) in pos
        .func
        .dfg
        .ebb_params(ebb)
        .to_vec()
        .into_iter()
        .enumerate()
    {
        let ty = pos.func.dfg.value_type(ebb_param);
        if ty != ir::types::I128 {
            continue;
        }

        split_ebb_param(pos, ebb, num, ebb_param, Opcode::Iconcat, &mut repairs);
    }

    perform_repairs(pos, cfg, repairs);
}

fn perform_repairs(pos: &mut FuncCursor, cfg: &ControlFlowGraph, mut repairs: Vec<Repair>) {
    // We have split the value requested, and now we may need to fix some EBB predecessors.
    while let Some(repair) = repairs.pop() {
        for BasicBlock { inst, .. } in cfg.pred_iter(repair.ebb) {
            let branch_opc = pos.func.dfg[inst].opcode();
            debug_assert!(
                branch_opc.is_branch(),
                "Predecessor not a branch: {}",
                pos.func.dfg.display_inst(inst, None)
            );
            let num_fixed_args = branch_opc.constraints().num_fixed_value_arguments();
            let mut args = pos.func.dfg[inst]
                .take_value_list()
                .expect("Branches must have value lists.");
            let num_args = args.len(&pos.func.dfg.value_lists);
            // Get the old value passed to the EBB argument we're repairing.
            let old_arg = args
                .get(num_fixed_args + repair.num, &pos.func.dfg.value_lists)
                .expect("Too few branch arguments");

            // It's possible that the CFG's predecessor list has duplicates. Detect them here.
            if pos.func.dfg.value_type(old_arg) == repair.split_type {
                pos.func.dfg[inst].put_value_list(args);
                continue;
            }

            // Split the old argument, possibly causing more repairs to be scheduled.
            pos.goto_inst(inst);
            let (lo, hi) = split_value(pos, old_arg, repair.concat, &mut repairs);

            // The `lo` part replaces the original argument.
            *args
                .get_mut(num_fixed_args + repair.num, &mut pos.func.dfg.value_lists)
                .unwrap() = lo;

            // The `hi` part goes at the end. Since multiple repairs may have been scheduled to the
            // same EBB, there could be multiple arguments missing.
            if num_args > num_fixed_args + repair.hi_num {
                *args
                    .get_mut(
                        num_fixed_args + repair.hi_num,
                        &mut pos.func.dfg.value_lists,
                    )
                    .unwrap() = hi;
            } else {
                // We need to append one or more arguments. If we're adding more than one argument,
                // there must be pending repairs on the stack that will fill in the correct values
                // instead of `hi`.
                args.extend(
                    iter::repeat(hi).take(1 + num_fixed_args + repair.hi_num - num_args),
                    &mut pos.func.dfg.value_lists,
                );
            }

            // Put the value list back after manipulating it.
            pos.func.dfg[inst].put_value_list(args);
        }
    }
}

/// Split a single value using the integer or vector semantics given by the `concat` opcode.
///
/// If the value is defined by a `concat` instruction, just reuse the operand values of that
/// instruction.
///
/// Return the two new values representing the parts of `value`.
fn split_value(
    pos: &mut FuncCursor,
    value: Value,
    concat: Opcode,
    repairs: &mut Vec<Repair>,
) -> (Value, Value) {
    let value = pos.func.dfg.resolve_aliases(value);
    let mut reuse = None;

    match pos.func.dfg.value_def(value) {
        ValueDef::Result(inst, num) => {
            // This is an instruction result. See if the value was created by a `concat`
            // instruction.
            if let InstructionData::Binary { opcode, args, .. } = pos.func.dfg[inst] {
                debug_assert_eq!(num, 0);
                if opcode == concat {
                    reuse = Some((args[0], args[1]));
                }
            }
        }
        ValueDef::Param(ebb, num) => {
            // This is an EBB parameter. We can split the parameter value unless this is the entry
            // block.
            if pos.func.layout.entry_block() != Some(ebb) {
                reuse = Some(split_ebb_param(pos, ebb, num, value, concat, repairs));
            }
        }
    }

    // Did the code above succeed in finding values we can reuse?
    if let Some(pair) = reuse {
        pair
    } else {
        // No, we'll just have to insert the requested split instruction at `pos`. Note that `pos`
        // has not been moved by the EBB argument code above when `reuse` is `None`.
        match concat {
            Opcode::Iconcat => pos.ins().isplit(value),
            Opcode::Vconcat => pos.ins().vsplit(value),
            _ => panic!("Unhandled concat opcode: {}", concat),
        }
    }
}

fn split_ebb_param(
    pos: &mut FuncCursor,
    ebb: Ebb,
    param_num: usize,
    value: Value,
    concat: Opcode,
    repairs: &mut Vec<Repair>,
) -> (Value, Value) {
    // We are going to replace the parameter at `num` with two new arguments.
    // Determine the new value types.
    let ty = pos.func.dfg.value_type(value);
    let split_type = match concat {
        Opcode::Iconcat => ty.half_width().expect("Invalid type for isplit"),
        Opcode::Vconcat => ty.half_vector().expect("Invalid type for vsplit"),
        _ => panic!("Unhandled concat opcode: {}", concat),
    };

    // Since the `repairs` stack potentially contains other parameter numbers for
    // `ebb`, avoid shifting and renumbering EBB parameters. It could invalidate other
    // `repairs` entries.
    //
    // Replace the original `value` with the low part, and append the high part at the
    // end of the argument list.
    let lo = pos.func.dfg.replace_ebb_param(value, split_type);
    let hi_num = pos.func.dfg.num_ebb_params(ebb);
    let hi = pos.func.dfg.append_ebb_param(ebb, split_type);

    // Now the original value is dangling. Insert a concatenation instruction that can
    // compute it from the two new parameters. This also serves as a record of what we
    // did so a future call to this function doesn't have to redo the work.
    //
    // 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.
    pos.goto_first_inst(ebb);
    pos.ins()
        .with_result(value)
        .Binary(concat, split_type, lo, hi);

    // Finally, splitting the EBB parameter is not enough. We also have to repair all
    // of the predecessor instructions that branch here.
    add_repair(concat, split_type, ebb, param_num, hi_num, repairs);

    (lo, hi)
}

// Add a repair entry to the work list.
fn add_repair(
    concat: Opcode,
    split_type: Type,
    ebb: Ebb,
    num: usize,
    hi_num: usize,
    repairs: &mut Vec<Repair>,
) {
    repairs.push(Repair {
        concat,
        split_type,
        ebb,
        num,
        hi_num,
    });
}

/// Strip concat-split chains. Return a simpler way of computing the same value.
///
/// Given this input:
///
/// ```clif
///     v10 = iconcat v1, v2
///     v11, v12 = isplit v10
/// ```
///
/// This function resolves `v11` to `v1` and `v12` to `v2`.
fn resolve_splits(dfg: &ir::DataFlowGraph, value: Value) -> Value {
    let value = dfg.resolve_aliases(value);

    // Deconstruct a split instruction.
    let split_res;
    let concat_opc;
    let split_arg;
    if let ValueDef::Result(inst, num) = dfg.value_def(value) {
        split_res = num;
        concat_opc = match dfg[inst].opcode() {
            Opcode::Isplit => Opcode::Iconcat,
            Opcode::Vsplit => Opcode::Vconcat,
            _ => return value,
        };
        split_arg = dfg.inst_args(inst)[0];
    } else {
        return value;
    }

    // See if split_arg is defined by a concatenation instruction.
    if let ValueDef::Result(inst, _) = dfg.value_def(split_arg) {
        if dfg[inst].opcode() == concat_opc {
            return dfg.inst_args(inst)[split_res];
        }
    }

    value
}

/// Simplify the arguments to a branch *after* the instructions leading up to the branch have been
/// legalized.
///
/// The branch argument repairs performed by `split_any()` above may be performed on branches that
/// have not yet been legalized. The repaired arguments can be defined by actual split
/// instructions in that case.
///
/// After legalizing the instructions computing the value that was split, it is likely that we can
/// avoid depending on the split instruction. Its input probably comes from a concatenation.
pub fn simplify_branch_arguments(dfg: &mut ir::DataFlowGraph, branch: Inst) {
    let mut new_args = SmallVec::<[Value; 32]>::new();

    for &arg in dfg.inst_args(branch) {
        let new_arg = resolve_splits(dfg, arg);
        new_args.push(new_arg);
    }

    dfg.inst_args_mut(branch).copy_from_slice(&new_args);
}
<|endoftext|>


<commit_before>//! Conversion to the `Stream` type from iterators.

use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

use progress::frontier::Antichain;
use progress::{Operate, Timestamp};
use progress::nested::subgraph::Source;
use progress::count_map::CountMap;

use timely_communication::Allocate;

use Data;
use dataflow::channels::Content;
use dataflow::channels::pushers::{Tee, Counter};
use dataflow::channels::pushers::buffer::Buffer;

use dataflow::{Stream, Scope};

pub trait ToStream<D: Data> {
    /// Converts an iterator to a timely `Stream`, with records at the default time.
    ///
    /// #Examples
    /// ```
    /// use timely::dataflow::operators::{ToStream, Inspect};
    ///
    /// timely::example(|scope| {
    ///     (0..10).to_stream(scope)
    ///            .inspect(|x| println!("seen: {:?}", x));
    /// });
    /// ```
    fn to_stream<S: Scope>(self, scope: &mut S) -> Stream<S, D>;
}

impl<D: Data, I: Iterator<Item=D>+'static> ToStream<D> for I {
    fn to_stream<S: Scope>(self, scope: &mut S) -> Stream<S, D> {

        let (output, registrar) = Tee::<S::Timestamp, D>::new();
        let copies = scope.peers();

        let index = scope.add_operator(Operator {
            iterator: Some(self),
            copies: copies,
            output: Buffer::new(Counter::new(output, Rc::new(RefCell::new(CountMap::new())))),
        });

        Stream::new(Source { index: index, port: 0 }, registrar, scope.clone())
    }
}

struct Operator<T:Timestamp, D: Data, I: Iterator<Item=D>> {
    iterator: Option<I>,
    copies: usize,
    output: Buffer<T, D, Counter<T, D, Tee<T, D>>>,
}

impl<T:Timestamp, D: Data, I: Iterator<Item=D>> Operate<T> for Operator<T, D, I> {
    fn name(&self) -> String { "ToStream".to_owned() }
    fn inputs(&self) -> usize { 0 }
    fn outputs(&self) -> usize { 1 }

    fn get_internal_summary(&mut self) -> (Vec<Vec<Antichain<T::Summary>>>, Vec<CountMap<T>>) {
        let mut map = CountMap::new();
        map.update(&Default::default(), self.copies as i64);
        (Vec::new(), vec![map])
    }

    fn pull_internal_progress(&mut self,_messages_consumed: &mut [CountMap<T>],
                                         frontier_progress: &mut [CountMap<T>],
                                         messages_produced: &mut [CountMap<T>]) -> bool
    {
        if self.iterator.is_some() {

            // if we find that there is a next element, send a bunch.
            if let Some(element) = self.iterator.as_mut().unwrap().next() {

                // send 4096 messages, or whatever the buffer size is.
                let mut session = self.output.session(&Default::default());
                session.give(element);
                for element in self.iterator.as_mut().unwrap().take((256 * Content::<D>::default_length()) - 1) {
                    session.give(element);
                }
            }
            else {

                self.output.cease();
                self.output.inner().pull_progress(&mut messages_produced[0]);
                frontier_progress[0].update(&Default::default(), -1);
                self.iterator = None;
            }
        }

        false
    }

    fn notify_me(&self) -> bool { false }
}
<commit_msg>to_stream uses IntoIterator, and is bound to a timestamp type<commit_after>//! Conversion to the `Stream` type from iterators.

use std::rc::Rc;
use std::cell::RefCell;
use std::default::Default;

use progress::frontier::Antichain;
use progress::{Operate, Timestamp};
use progress::nested::subgraph::Source;
use progress::count_map::CountMap;

use timely_communication::Allocate;

use Data;
use dataflow::channels::Content;
use dataflow::channels::pushers::{Tee, Counter};
use dataflow::channels::pushers::buffer::Buffer;

use dataflow::{Stream, Scope};

pub trait ToStream<T: Timestamp, D: Data> {
    /// Converts to a timely `Stream`.
    ///
    /// #Examples
    ///
    /// `ToStream` is implemented by any implementor of `IntoIterator` whose
    /// associated type `Item` implements `Data`. This means that iterators,
    /// vectors, and other collections may be directly converted to timely
    /// streams.
    ///
    /// ```
    /// use timely::dataflow::operators::{ToStream, Capture};
    /// use timely::dataflow::operators::capture::Extract;
    ///
    /// let (data1, data2) = timely::example(|scope| {
    ///     let data1 = (0..3).to_stream(scope).capture();
    ///     let data2 = vec![0,1,2].to_stream(scope).capture();
    ///     (data1, data2)
    /// });
    ///
    /// assert_eq!(data1.extract(), data2.extract());
    /// ```
    fn to_stream<S: Scope<Timestamp=T>>(self, scope: &mut S) -> Stream<S, D>;
}

impl<T: Timestamp, I: IntoIterator+'static> ToStream<T, I::Item> for I where I::Item: Data {
    fn to_stream<S: Scope<Timestamp=T>>(self, scope: &mut S) -> Stream<S, I::Item> {

        let (output, registrar) = Tee::<S::Timestamp, I::Item>::new();
        let copies = scope.peers();

        let index = scope.add_operator(Operator {
            iterator: Some(self.into_iter()),
            copies: copies,
            output: Buffer::new(Counter::new(output, Rc::new(RefCell::new(CountMap::new())))),
        });

        Stream::new(Source { index: index, port: 0 }, registrar, scope.clone())
    }
}

struct Operator<T:Timestamp, D: Data, I: Iterator<Item=D>> {
    iterator: Option<I>,
    copies: usize,
    output: Buffer<T, D, Counter<T, D, Tee<T, D>>>,
}

impl<T:Timestamp, D: Data, I: Iterator<Item=D>> Operate<T> for Operator<T, D, I> {
    fn name(&self) -> String { "ToStream".to_owned() }
    fn inputs(&self) -> usize { 0 }
    fn outputs(&self) -> usize { 1 }

    fn get_internal_summary(&mut self) -> (Vec<Vec<Antichain<T::Summary>>>, Vec<CountMap<T>>) {
        let mut map = CountMap::new();
        map.update(&Default::default(), self.copies as i64);
        (Vec::new(), vec![map])
    }

    fn pull_internal_progress(&mut self,_messages_consumed: &mut [CountMap<T>],
                                         frontier_progress: &mut [CountMap<T>],
                                         messages_produced: &mut [CountMap<T>]) -> bool
    {
        if self.iterator.is_some() {

            // if we find that there is a next element, send a bunch.
            if let Some(element) = self.iterator.as_mut().unwrap().next() {

                // send 4096 messages, or whatever the buffer size is.
                let mut session = self.output.session(&Default::default());
                session.give(element);
                for element in self.iterator.as_mut().unwrap().take((256 * Content::<D>::default_length()) - 1) {
                    session.give(element);
                }
            }
            else {

                self.output.cease();
                self.output.inner().pull_progress(&mut messages_produced[0]);
                frontier_progress[0].update(&Default::default(), -1);
                self.iterator = None;
            }
        }

        false
    }

    fn notify_me(&self) -> bool { false }
}
<|endoftext|>