<commit_before>use utils::data::hash_map::HashMap;
use utils::data::pair::{Pair, PairTWHash};
use utils::data::uid_remap::{UidRemap, FastKey};
use geometry::query::Proximity;
use narrow_phase::{ContactDispatcher, ContactAlgorithm, ContactSignal,   ContactGenerator,
                   ProximityDispatcher, ProximityAlgorithm, ProximitySignal, ProximityDetector,
                   NarrowPhase, ContactPairs, ProximityPairs};
use world::{CollisionObject, GeometricQueryType};
use math::Point;

// FIXME: move this to the `narrow_phase` module.
/// Collision detector dispatcher for collision objects.
pub struct DefaultNarrowPhase<P, M> {
    contact_dispatcher: Box<ContactDispatcher<P, M> + 'static>,
    contact_generators:  HashMap<Pair, ContactAlgorithm<P, M>, PairTWHash>,

    proximity_dispatcher: Box<ProximityDispatcher<P, M> + 'static>,
    proximity_detectors:  HashMap<Pair, ProximityAlgorithm<P, M>, PairTWHash>,
}

impl<P: Point, M: 'static> DefaultNarrowPhase<P, M> {
    /// Creates a new `DefaultNarrowPhase`.
    pub fn new(contact_dispatcher: Box<ContactDispatcher<P, M> + 'static>,
               proximity_dispatcher: Box<ProximityDispatcher<P, M> + 'static>)
               -> DefaultNarrowPhase<P, M> {
        DefaultNarrowPhase {
            contact_dispatcher: contact_dispatcher,
            contact_generators:  HashMap::new(PairTWHash::new()),

            proximity_dispatcher: proximity_dispatcher,
            proximity_detectors:  HashMap::new(PairTWHash::new())
        }
    }
}

impl<P: Point, M: 'static, T> NarrowPhase<P, M, T> for DefaultNarrowPhase<P, M> {
    fn update(&mut self,
              objects:          &UidRemap<CollisionObject<P, M, T>>,
              contact_signal:   &mut ContactSignal<P, M, T>,
              proximity_signal: &mut ProximitySignal<P, M, T>,
              timestamp:        usize) {
        for e in self.contact_generators.elements_mut().iter_mut() {
            let co1 = &objects[e.key.first];
            let co2 = &objects[e.key.second];

            if co1.timestamp == timestamp || co2.timestamp == timestamp {
                let had_contacts = e.value.num_contacts() != 0;

                e.value.update(&*self.contact_dispatcher,
                               &co1.position, co1.shape.as_ref(),
                               &co2.position, co2.shape.as_ref(),
                               co1.query_type.query_limit() + co2.query_type.query_limit());

                if e.value.num_contacts() == 0 {
                    if had_contacts {
                        contact_signal.trigger_contact_stopped_signal(&co1, &co2);
                    }
                }
                else {
                    if !had_contacts {
                        contact_signal.trigger_contact_started_signal(&co1, &co2, &e.value)
                    }
                }
            }
        }

        for e in self.proximity_detectors.elements_mut().iter_mut() {
            let co1 = &objects[e.key.first];
            let co2 = &objects[e.key.second];

            if co1.timestamp == timestamp || co2.timestamp == timestamp {
                let prev_prox = e.value.proximity();

                e.value.update(&*self.proximity_dispatcher,
                               &co1.position, co1.shape.as_ref(),
                               &co2.position, co2.shape.as_ref(),
                               co1.query_type.query_limit() + co2.query_type.query_limit());

                let new_prox = e.value.proximity();

                if new_prox != prev_prox {
                    proximity_signal.trigger_proximity_signal(&co1, &co2, prev_prox, new_prox);
                }
            }
        }
    }

    fn handle_interaction(&mut self,
                          contact_signal:   &mut ContactSignal<P, M, T>,
                          proximity_signal: &mut ProximitySignal<P, M, T>,
                          objects:          &UidRemap<CollisionObject<P, M, T>>,
                          fk1:              &FastKey,
                          fk2:              &FastKey,
                          started:          bool) {
        let key = Pair::new(*fk1, *fk2);
        let co1 = &objects[*fk1];
        let co2 = &objects[*fk2];

        match (co1.query_type, co2.query_type) {
            (GeometricQueryType::Contacts(_), GeometricQueryType::Contacts(_)) => {
                if started {
                    let cd = self.contact_dispatcher.get_contact_algorithm(co1.shape.as_ref(), co2.shape.as_ref());

                    if let Some(cd) = cd {
                        let _ = self.contact_generators.insert(key, cd);
                    }
                }
                else {
                    // Proximity stopped.
                    match self.contact_generators.get_and_remove(&key) {
                        Some(detector) => {
                            // Trigger the collision lost signal if there was a contact.
                            if detector.value.num_contacts() != 0 {
                                contact_signal.trigger_contact_stopped_signal(&co1, &co2);
                            }
                        },
                        None => { }
                    }
                }
            },
            (_, GeometricQueryType::Proximity(_)) | (GeometricQueryType::Proximity(_), _) => {
                if started {
                    let cd = self.proximity_dispatcher.get_proximity_algorithm(co1.shape.as_ref(), co2.shape.as_ref());

                    if let Some(cd) = cd {
                        let _ = self.proximity_detectors.insert(key, cd);
                    }
                }
                else {
                    // Proximity stopped.
                    match self.proximity_detectors.get_and_remove(&key) {
                        Some(detector) => {
                            // Trigger the proximity lost signal if they were not disjoint.
                            let prev_prox = detector.value.proximity();
                            if prev_prox != Proximity::Disjoint {
                                proximity_signal.trigger_proximity_signal(&co1, &co2, prev_prox, Proximity::Disjoint);
                            }
                        },
                        None => { }
                    }
                }
            }
        }

    }

    fn contact_pairs<'a>(&'a self, objects: &'a UidRemap<CollisionObject<P, M, T>>)
                         -> ContactPairs<'a, P, M, T> {
        ContactPairs::new(objects, self.contact_generators.elements().iter())
    }

    fn proximity_pairs<'a>(&'a self, objects: &'a UidRemap<CollisionObject<P, M, T>>)
                           -> ProximityPairs<'a, P, M, T> {
        ProximityPairs::new(objects, self.proximity_detectors.elements().iter())
    }
}
<commit_msg>default narrow phase: do not re-instanciate algorithms if they are already present.<commit_after>use utils::data::hash_map::HashMap;
use utils::data::pair::{Pair, PairTWHash};
use utils::data::uid_remap::{UidRemap, FastKey};
use geometry::query::Proximity;
use narrow_phase::{ContactDispatcher, ContactAlgorithm, ContactSignal,   ContactGenerator,
                   ProximityDispatcher, ProximityAlgorithm, ProximitySignal, ProximityDetector,
                   NarrowPhase, ContactPairs, ProximityPairs};
use world::{CollisionObject, GeometricQueryType};
use math::Point;

// FIXME: move this to the `narrow_phase` module.
/// Collision detector dispatcher for collision objects.
pub struct DefaultNarrowPhase<P, M> {
    contact_dispatcher: Box<ContactDispatcher<P, M> + 'static>,
    contact_generators:  HashMap<Pair, ContactAlgorithm<P, M>, PairTWHash>,

    proximity_dispatcher: Box<ProximityDispatcher<P, M> + 'static>,
    proximity_detectors:  HashMap<Pair, ProximityAlgorithm<P, M>, PairTWHash>,
}

impl<P: Point, M: 'static> DefaultNarrowPhase<P, M> {
    /// Creates a new `DefaultNarrowPhase`.
    pub fn new(contact_dispatcher: Box<ContactDispatcher<P, M> + 'static>,
               proximity_dispatcher: Box<ProximityDispatcher<P, M> + 'static>)
               -> DefaultNarrowPhase<P, M> {
        DefaultNarrowPhase {
            contact_dispatcher: contact_dispatcher,
            contact_generators:  HashMap::new(PairTWHash::new()),

            proximity_dispatcher: proximity_dispatcher,
            proximity_detectors:  HashMap::new(PairTWHash::new())
        }
    }
}

impl<P: Point, M: 'static, T> NarrowPhase<P, M, T> for DefaultNarrowPhase<P, M> {
    fn update(&mut self,
              objects:          &UidRemap<CollisionObject<P, M, T>>,
              contact_signal:   &mut ContactSignal<P, M, T>,
              proximity_signal: &mut ProximitySignal<P, M, T>,
              timestamp:        usize) {
        for e in self.contact_generators.elements_mut().iter_mut() {
            let co1 = &objects[e.key.first];
            let co2 = &objects[e.key.second];

            if co1.timestamp == timestamp || co2.timestamp == timestamp {
                let had_contacts = e.value.num_contacts() != 0;

                e.value.update(&*self.contact_dispatcher,
                               &co1.position, co1.shape.as_ref(),
                               &co2.position, co2.shape.as_ref(),
                               co1.query_type.query_limit() + co2.query_type.query_limit());

                if e.value.num_contacts() == 0 {
                    if had_contacts {
                        contact_signal.trigger_contact_stopped_signal(&co1, &co2);
                    }
                }
                else {
                    if !had_contacts {
                        contact_signal.trigger_contact_started_signal(&co1, &co2, &e.value)
                    }
                }
            }
        }

        for e in self.proximity_detectors.elements_mut().iter_mut() {
            let co1 = &objects[e.key.first];
            let co2 = &objects[e.key.second];

            if co1.timestamp == timestamp || co2.timestamp == timestamp {
                let prev_prox = e.value.proximity();

                e.value.update(&*self.proximity_dispatcher,
                               &co1.position, co1.shape.as_ref(),
                               &co2.position, co2.shape.as_ref(),
                               co1.query_type.query_limit() + co2.query_type.query_limit());

                let new_prox = e.value.proximity();

                if new_prox != prev_prox {
                    proximity_signal.trigger_proximity_signal(&co1, &co2, prev_prox, new_prox);
                }
            }
        }
    }

    fn handle_interaction(&mut self,
                          contact_signal:   &mut ContactSignal<P, M, T>,
                          proximity_signal: &mut ProximitySignal<P, M, T>,
                          objects:          &UidRemap<CollisionObject<P, M, T>>,
                          fk1:              &FastKey,
                          fk2:              &FastKey,
                          started:          bool) {
        let key = Pair::new(*fk1, *fk2);
        let co1 = &objects[*fk1];
        let co2 = &objects[*fk2];

        match (co1.query_type, co2.query_type) {
            (GeometricQueryType::Contacts(_), GeometricQueryType::Contacts(_)) => {
                if started {
                    let dispatcher = &self.contact_dispatcher;

                    let _ = self.contact_generators.find_or_insert_lazy(key, || {
                        dispatcher.get_contact_algorithm(co1.shape.as_ref(), co2.shape.as_ref())
                    });
                }
                else {
                    // Proximity stopped.
                    match self.contact_generators.get_and_remove(&key) {
                        Some(detector) => {
                            // Trigger the collision lost signal if there was a contact.
                            if detector.value.num_contacts() != 0 {
                                contact_signal.trigger_contact_stopped_signal(&co1, &co2);
                            }
                        },
                        None => { }
                    }
                }
            },
            (_, GeometricQueryType::Proximity(_)) | (GeometricQueryType::Proximity(_), _) => {
                if started {
                    let dispatcher = &self.proximity_dispatcher;

                    let _ = self.proximity_detectors.find_or_insert_lazy(key, || {
                        dispatcher.get_proximity_algorithm(co1.shape.as_ref(), co2.shape.as_ref())
                    });
                }
                else {
                    // Proximity stopped.
                    match self.proximity_detectors.get_and_remove(&key) {
                        Some(detector) => {
                            // Trigger the proximity lost signal if they were not disjoint.
                            let prev_prox = detector.value.proximity();
                            if prev_prox != Proximity::Disjoint {
                                proximity_signal.trigger_proximity_signal(&co1, &co2, prev_prox, Proximity::Disjoint);
                            }
                        },
                        None => { }
                    }
                }
            }
        }

    }

    fn contact_pairs<'a>(&'a self, objects: &'a UidRemap<CollisionObject<P, M, T>>)
                         -> ContactPairs<'a, P, M, T> {
        ContactPairs::new(objects, self.contact_generators.elements().iter())
    }

    fn proximity_pairs<'a>(&'a self, objects: &'a UidRemap<CollisionObject<P, M, T>>)
                           -> ProximityPairs<'a, P, M, T> {
        ProximityPairs::new(objects, self.proximity_detectors.elements().iter())
    }
}
<|endoftext|>


<commit_before>extern crate collenchyma;
extern crate collenchyma_nn;

use self::collenchyma::framework::IFramework;
use self::collenchyma::backend::{Backend, BackendConfig};
use self::collenchyma::frameworks::Native;
use self::collenchyma::hardware::IHardware;
use self::collenchyma::device::IDevice;
use self::collenchyma::binary::IBinary;
use self::collenchyma::memory::IMemory;
use self::collenchyma::Error;
use self::collenchyma::tensor::SharedTensor;
use self::collenchyma_nn::*;

pub struct Ctrnn {
    pub backend: Backend<Native>
}

impl Ctrnn {
    pub fn new() -> Ctrnn{
        let framework = Native::new();
        let hardwares = &framework.hardwares().to_vec();
        let backend_config = BackendConfig::new(framework, hardwares);
        let backend = Backend::new(backend_config);
        Ctrnn {
            backend: backend.unwrap()
        }
    }

    pub fn activate(&self, steps: usize, gamma: &Vec<f64>, delta_t: f64, tau: &Vec<f64>, wij: &Vec<Vec<f64>>, theta: &Vec<f64>, wik: &Vec<Vec<f64>>, i: &Vec<f64>) -> Vec<f64> {
        return vec![0.0, 0.0];
    }
}

#[cfg(test)]
mod tests {
    use neat::*;

    #[test]
    fn neural_network_activation_should_return_correct_values(){
        let gamma =         vec![0.0, 0.0, 0.0];
        let delta_t =       13.436;
        let tau =           vec![61.694, 10.149, 16.851];
        let wij =           vec![vec![-2.94737, 2.70665, -0.57046],
                            vec![-3.27553,   3.67193, 1.83218],
                            vec![2.32476,   0.24739, 0.58587]];
        let theta =         vec![-0.695126, -0.677891, -0.072129];
        let wik =           vec![vec![-0.10097, -3.04457],
                            vec![-4.86594, 1.79273],
                            vec![-3.45899, -1.27388]];
        let i =             vec![0.98856, 0.31540];

        let ctrnn = Ctrnn::new();

        assert_eq!( ctrnn.activate(1, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-0.86998, -6.11057, -2.14179]);
        assert_eq!( ctrnn.activate(10, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7698, -4.9550, -3.1953]);
        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7869, -4.9408, -3.2092]);

        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            ctrnn.activate(100, &gamma, delta_t, &tau, &wij, &theta, &wik, &i));
    }
}
<commit_msg>First sigmoid with tensors and collenchyma<commit_after>extern crate collenchyma;
extern crate collenchyma_nn;

use self::collenchyma::framework::IFramework;
use self::collenchyma::backend::{Backend, BackendConfig};
use self::collenchyma::frameworks::Native;
use self::collenchyma::Error;
use self::collenchyma::tensor::SharedTensor;
use self::collenchyma::DeviceType;
use self::collenchyma::MemoryType;
use self::collenchyma_nn::*;
use self::collenchyma::tensor::IntoTensorDesc;

pub struct Ctrnn {
    pub backend: Backend<Native>, //shuld replace with general backend
    pub cpu: DeviceType
}

impl Ctrnn {
    pub fn new() -> Ctrnn{
        let framework = Native::new();
        let hardwares = &framework.hardwares().to_vec();
        let backend_config = BackendConfig::new(framework, hardwares);
        let backend = Backend::new(backend_config);
        let native = Native::new();
        Ctrnn {
            backend: backend.unwrap(),
            cpu: native.new_device(native.hardwares()).unwrap()
        }
    }

    pub fn activate(&self, steps: usize, gamma_v: &Vec<f64>, delta_t: f64, tau_v: &Vec<f64>, wij_v: &Vec<Vec<f64>>, theta_v: &Vec<f64>, wik_v: &Vec<Vec<f64>>, i_v: &Vec<f64>) -> Vec<f64> {
        let mut gamma = self.vector_to_tensor(gamma_v);
        let mut tau = self.vector_to_tensor(tau_v);
        let mut wij = self.matrix_to_tensor(wij_v);
        let mut theta = self.vector_to_tensor(theta_v);
        let mut wik = self.matrix_to_tensor(wik_v);
        let mut i = self.vector_to_tensor(i_v);
        let mut result = SharedTensor::<f64>::new(self.backend.device(), wij.desc()).unwrap();
        &self.backend.sigmoid(&mut wij, &mut result).unwrap();
        //result.add_device(&cpu).unwrap();
        result.sync(&self.cpu).unwrap();
        return vec![0.0, 0.0];
    }

    fn matrix_to_tensor(&self, matrix: &Vec<Vec<f64>>) -> SharedTensor<f64> {
        let mut tensor = SharedTensor::<f64>::new(self.backend.device(), &(matrix.len(), matrix[0].len())).unwrap();
        //tensor.add_device(&self.cpu).unwrap();
        tensor.sync(&self.cpu).unwrap();
        self.write_to_memory(tensor.get_mut(&self.cpu).unwrap(), &matrix.iter().flat_map(|s| s.iter()).collect::<Vec<&f64>>());
        tensor
    }

    fn vector_to_tensor(&self, vector: &Vec<f64>) -> SharedTensor<f64> {
        self.generic_to_tensor(&vec!(vector.len()), vector)
    }

    fn generic_to_tensor(&self, desc: &Vec<usize>, data: &[f64]) -> SharedTensor<f64> {
        let mut tensor = SharedTensor::<f64>::new(self.backend.device(), desc).unwrap();
        //tensor.add_device(&self.cpu).unwrap();
        tensor.sync(&self.cpu).unwrap();
        self.write_to_memory(tensor.get_mut(&self.cpu).unwrap(), data);
        tensor
    }

    fn write_to_memory<T: Copy>(&self, mem: &mut MemoryType, data: &[T]) {
        let &mut MemoryType::Native(ref mut mem) = mem;
        let mut mem_buffer = mem.as_mut_slice::<T>();
        for (index, datum) in data.iter().enumerate() {
            mem_buffer[index] = *datum;
        }
    }
}

#[cfg(test)]
mod tests {
    use neat::*;

    #[test]
    fn neural_network_activation_should_return_correct_values(){
        let gamma =         vec![0.0, 0.0, 0.0];
        let delta_t =       13.436;
        let tau =           vec![61.694, 10.149, 16.851];
        let wij =           vec![vec![-2.94737, 2.70665, -0.57046],
                            vec![-3.27553,   3.67193, 1.83218],
                            vec![2.32476,   0.24739, 0.58587]];
        let theta =         vec![-0.695126, -0.677891, -0.072129];
        let wik =           vec![vec![-0.10097, -3.04457],
                            vec![-4.86594, 1.79273],
                            vec![-3.45899, -1.27388]];
        let i =             vec![0.98856, 0.31540];

        let ctrnn = Ctrnn::new();

        assert_eq!( ctrnn.activate(1, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-0.86998, -6.11057, -2.14179]);
        assert_eq!( ctrnn.activate(10, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7698, -4.9550, -3.1953]);
        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            vec![-1.7869, -4.9408, -3.2092]);

        assert_eq!( ctrnn.activate(30, &gamma, delta_t, &tau, &wij, &theta, &wik, &i),
            ctrnn.activate(100, &gamma, delta_t, &tau, &wij, &theta, &wik, &i));
    }
}
<|endoftext|>


<commit_before>use backend::Backend;
use query_builder::{AstPass, QueryFragment};
use result::QueryResult;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoLockingClause;

impl<DB: Backend> QueryFragment<DB> for NoLockingClause {
    fn walk_ast(&self, _: AstPass<DB>) -> QueryResult<()> {
        Ok(())
    }
}

#[derive(Debug, Clone, Copy, QueryId)]
pub struct LockingClause<LockMode = ForUpdate, Modifier = NoModifier> {
    pub(crate) lock_mode: LockMode,
    modifier: Modifier,
}

impl<LockMode, Modifier> LockingClause<LockMode, Modifier> {
    pub(crate) fn new(lock_mode: LockMode, modifier: Modifier) -> Self {
        LockingClause {
            lock_mode,
            modifier,
        }
    }
}

impl<DB: Backend, L: QueryFragment<DB>, M: QueryFragment<DB>> QueryFragment<DB>
    for LockingClause<L, M>
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        self.lock_mode.walk_ast(out.reborrow())?;
        self.modifier.walk_ast(out.reborrow())
    }
}

/// LockMode parameters
/// All the different types of row locks that can be acquired.
#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForUpdate;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForNoKeyUpdate;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForShare;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForKeyShare;

/// Modifiers
/// To be used in conjunction with a lock mode.
#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoModifier;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct SkipLocked;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoWait;
<commit_msg>Fix formatting<commit_after>use backend::Backend;
use query_builder::{AstPass, QueryFragment};
use result::QueryResult;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoLockingClause;

impl<DB: Backend> QueryFragment<DB> for NoLockingClause {
    fn walk_ast(&self, _: AstPass<DB>) -> QueryResult<()> {
        Ok(())
    }
}

#[derive(Debug, Clone, Copy, QueryId)]
pub struct LockingClause<LockMode = ForUpdate, Modifier = NoModifier> {
    pub(crate) lock_mode: LockMode,
    modifier: Modifier,
}

impl<LockMode, Modifier> LockingClause<LockMode, Modifier> {
    pub(crate) fn new(lock_mode: LockMode, modifier: Modifier) -> Self {
        LockingClause {
            lock_mode,
            modifier,
        }
    }
}

impl<DB: Backend, L: QueryFragment<DB>, M: QueryFragment<DB>> QueryFragment<DB>
    for LockingClause<L, M>
{
    fn walk_ast(&self, mut out: AstPass<DB>) -> QueryResult<()> {
        self.lock_mode.walk_ast(out.reborrow())?;
        self.modifier.walk_ast(out.reborrow())
    }
}

// `LockMode` parameters
// All the different types of row locks that can be acquired.
#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForUpdate;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForNoKeyUpdate;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForShare;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct ForKeyShare;

// Modifiers
// To be used in conjunction with a lock mode.
#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoModifier;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct SkipLocked;

#[derive(Debug, Clone, Copy, QueryId)]
pub struct NoWait;
<|endoftext|>


<commit_before>use super::{pointer::Pointer, Heap};
use crate::{
    builtin_functions::BuiltinFunction,
    compiler::{
        hir::Id,
        hir_to_lir::HirToLir,
        lir::{Instruction, Lir},
    },
    database::Database,
    module::Module,
    vm::ids::ChannelId,
};
use itertools::Itertools;
use num_bigint::BigInt;
use std::{
    collections::{hash_map::DefaultHasher, HashMap},
    hash::{Hash, Hasher},
    ops::Deref,
};

#[derive(Clone)]
pub struct Object {
    pub reference_count: usize,
    pub data: Data,
}
#[derive(Clone)]
pub enum Data {
    Int(Int),
    Text(Text),
    Symbol(Symbol),
    List(List),
    Struct(Struct),
    Closure(Closure),
    Builtin(Builtin),
    SendPort(SendPort),
    ReceivePort(ReceivePort),
}

#[derive(Clone)]
pub struct Int {
    pub value: BigInt,
}

#[derive(Clone)]
pub struct Text {
    pub value: String,
}

#[derive(Clone)]
pub struct Symbol {
    // TODO: Choose a more efficient representation.
    pub value: String,
}

#[derive(Default, Clone)]
pub struct List {
    pub items: Vec<Pointer>,
}

#[derive(Default, Clone)]
pub struct Struct {
    pub fields: Vec<(u64, Pointer, Pointer)>, // list of hash, key, and value
}

#[derive(Clone)]
pub struct Closure {
    pub id: Id,
    pub captured: Vec<Pointer>,
    pub num_args: usize,
    pub body: Vec<Instruction>,
    pub responsible: Option<Id>,
}

#[derive(Clone)]
pub struct Builtin {
    pub function: BuiltinFunction,
}

impl List {
    fn equals(&self, heap: &Heap, other: &List) -> bool {
        if self.items.len() != other.items.len() {
            return false;
        }

        self.items
            .iter()
            .zip_eq(other.items.iter())
            .all(|(a, &b)| a.equals(heap, b))
    }
}
impl Struct {
    pub fn from_fields(heap: &Heap, fields: HashMap<Pointer, Pointer>) -> Self {
        let mut s = Self::default();
        for (key, value) in fields {
            s.insert(heap, key, value);
        }
        s
    }
    /// If the struct contains the key, returns the index of its field.
    /// Otherwise, returns the index of where the key would be inserted.
    fn index_of_key(&self, heap: &Heap, key: Pointer, key_hash: u64) -> Result<usize, usize> {
        let index_of_first_hash_occurrence = self
            .fields
            .partition_point(|(existing_hash, _, _)| *existing_hash < key_hash);
        let fields_with_same_hash = self.fields[index_of_first_hash_occurrence..]
            .iter()
            .enumerate()
            .take_while(|(_, (existing_hash, _, _))| *existing_hash == key_hash)
            .map(|(i, (_, key, _))| (index_of_first_hash_occurrence + i, key));

        for (index, existing_key) in fields_with_same_hash {
            if existing_key.equals(heap, key) {
                return Ok(index);
            }
        }
        Err(index_of_first_hash_occurrence)
    }
    fn insert(&mut self, heap: &Heap, key: Pointer, value: Pointer) {
        let hash = key.hash(heap);
        let field = (hash, key, value);
        match self.index_of_key(heap, key, hash) {
            Ok(index) => self.fields[index] = field,
            Err(index) => self.fields.insert(index, field),
        }
    }
    pub fn get(&self, heap: &Heap, key: Pointer) -> Option<Pointer> {
        let index = self.index_of_key(heap, key, key.hash(heap)).ok()?;
        Some(self.fields[index].2)
    }
    fn len(&self) -> usize {
        self.fields.len()
    }
    pub fn iter(&self) -> impl Iterator<Item = (Pointer, Pointer)> {
        self.fields
            .clone()
            .into_iter()
            .map(|(_, key, value)| (key, value))
    }
    fn equals(&self, heap: &Heap, other: &Struct) -> bool {
        if self.len() != other.len() {
            return false;
        }

        self.iter()
            .zip_eq(other.iter())
            .all(|((key_a, value_a), (key_b, value_b))| {
                key_a.equals(heap, key_b) && value_a.equals(heap, value_b)
            })
    }
}

impl Closure {
    pub fn of_module_lir(module: Module, lir: Lir) -> Self {
        Closure {
            id: Id::new(module.clone(), vec![]),
            captured: vec![],
            num_args: 0,
            body: vec![
                Instruction::TraceModuleStarts {
                    module: module.clone(),
                },
                Instruction::CreateClosure {
                    id: Id::new(module, vec![]),
                    captured: vec![],
                    num_args: 0,
                    body: lir.instructions,
                    is_curly: true,
                },
                Instruction::Call { num_args: 0 },
                Instruction::TraceModuleEnds,
                Instruction::Return,
            ],
            responsible: None,
        }
    }
    pub fn of_module(db: &Database, module: Module) -> Option<Self> {
        let lir = db.lir(module.clone())?;
        Some(Self::of_module_lir(module, (*lir).clone()))
    }
}

#[derive(Clone)]
pub struct SendPort {
    pub channel: ChannelId,
}
#[derive(Clone)]
pub struct ReceivePort {
    pub channel: ChannelId,
}

impl SendPort {
    pub fn new(channel: ChannelId) -> Self {
        Self { channel }
    }
}
impl ReceivePort {
    pub fn new(channel: ChannelId) -> Self {
        Self { channel }
    }
}

impl Data {
    fn hash(&self, heap: &Heap) -> u64 {
        let mut state = DefaultHasher::new();
        self.hash_with_state(heap, &mut state);
        state.finish()
    }

    fn hash_with_state<H: Hasher>(&self, heap: &Heap, state: &mut H) {
        match self {
            Data::Int(int) => int.value.hash(state),
            Data::Text(text) => text.value.hash(state),
            Data::Symbol(symbol) => symbol.value.hash(state),
            Data::List(List { items }) => {
                let mut s = 0;
                for item in items {
                    s ^= item.hash(heap);
                }
                s.hash(state)
            }
            Data::Struct(struct_) => {
                let mut s = 0;
                for (key, value) in struct_.iter() {
                    s ^= key.hash(heap);
                    s ^= value.hash(heap);
                }
                s.hash(state)
            }
            Data::Closure(closure) => {
                for captured in &closure.captured {
                    captured.hash_with_state(heap, state);
                }
                closure.num_args.hash(state);
                closure.body.hash(state);
            }
            Data::Builtin(builtin) => builtin.function.hash(state),
            Data::SendPort(port) => port.channel.hash(state),
            Data::ReceivePort(port) => port.channel.hash(state),
        }
    }

    pub fn equals(&self, heap: &Heap, other: &Self) -> bool {
        match (self, other) {
            (Data::Int(a), Data::Int(b)) => a.value == b.value,
            (Data::Text(a), Data::Text(b)) => a.value == b.value,
            (Data::Symbol(a), Data::Symbol(b)) => a.value == b.value,
            (Data::List(a), Data::List(b)) => a.equals(heap, b),
            (Data::Struct(a), Data::Struct(b)) => a.equals(heap, b),
            (Data::Closure(_), Data::Closure(_)) => false,
            (Data::Builtin(a), Data::Builtin(b)) => a.function == b.function,
            (Data::SendPort(a), Data::SendPort(b)) => a.channel == b.channel,
            (Data::ReceivePort(a), Data::ReceivePort(b)) => a.channel == b.channel,
            _ => false,
        }
    }

    pub fn format(&self, heap: &Heap) -> String {
        match self {
            Data::Int(int) => format!("{}", int.value),
            Data::Text(text) => format!("\"{}\"", text.value),
            Data::Symbol(symbol) => symbol.value.to_string(),
            Data::List(List { items }) => format!(
                "({})",
                items.iter().map(|item| item.format(heap)).join(", ")
            ),
            Data::Struct(struct_) => format!(
                "[{}]",
                struct_
                    .iter()
                    .map(|(key, value)| (key.format(heap), value.format(heap)))
                    .sorted_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b))
                    .map(|(key, value)| format!("{}: {}", key, value))
                    .join(", ")
            ),
            Data::Closure(_) => "{…}".to_string(),
            Data::Builtin(builtin) => format!("builtin{:?}", builtin.function),
            Data::SendPort(port) => format!("sendPort {:?}", port.channel),
            Data::ReceivePort(port) => format!("receivePort {:?}", port.channel),
        }
    }

    pub fn children(&self) -> Vec<Pointer> {
        match self {
            Data::Int(_)
            | Data::Text(_)
            | Data::Symbol(_)
            | Data::Builtin(_)
            | Data::SendPort(_)
            | Data::ReceivePort(_) => vec![],
            Data::List(List { items }) => items.clone(),
            Data::Struct(struct_) => struct_
                .iter()
                .flat_map(|(a, b)| vec![a, b].into_iter())
                .collect_vec(),
            Data::Closure(closure) => closure.captured.clone(),
        }
    }

    pub fn channel(&self) -> Option<ChannelId> {
        if let Data::SendPort(SendPort { channel }) | Data::ReceivePort(ReceivePort { channel }) =
            self
        {
            Some(*channel)
        } else {
            None
        }
    }
}

impl Deref for Object {
    type Target = Data;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl Pointer {
    fn hash(&self, heap: &Heap) -> u64 {
        heap.get(*self).hash(heap)
    }
    fn hash_with_state<H: Hasher>(&self, heap: &Heap, state: &mut H) {
        heap.get(*self).hash_with_state(heap, state)
    }
    pub fn equals(&self, heap: &Heap, other: Self) -> bool {
        if *self == other {
            return true;
        }
        heap.get(*self).equals(heap, heap.get(other))
    }
    pub fn format(&self, heap: &Heap) -> String {
        heap.get(*self).format(heap)
    }
}
<commit_msg>Fix empty list Display formatting in heap<commit_after>use super::{pointer::Pointer, Heap};
use crate::{
    builtin_functions::BuiltinFunction,
    compiler::{
        hir::Id,
        hir_to_lir::HirToLir,
        lir::{Instruction, Lir},
    },
    database::Database,
    module::Module,
    vm::ids::ChannelId,
};
use itertools::Itertools;
use num_bigint::BigInt;
use std::{
    collections::{hash_map::DefaultHasher, HashMap},
    hash::{Hash, Hasher},
    ops::Deref,
};

#[derive(Clone)]
pub struct Object {
    pub reference_count: usize,
    pub data: Data,
}
#[derive(Clone)]
pub enum Data {
    Int(Int),
    Text(Text),
    Symbol(Symbol),
    List(List),
    Struct(Struct),
    Closure(Closure),
    Builtin(Builtin),
    SendPort(SendPort),
    ReceivePort(ReceivePort),
}

#[derive(Clone)]
pub struct Int {
    pub value: BigInt,
}

#[derive(Clone)]
pub struct Text {
    pub value: String,
}

#[derive(Clone)]
pub struct Symbol {
    // TODO: Choose a more efficient representation.
    pub value: String,
}

#[derive(Default, Clone)]
pub struct List {
    pub items: Vec<Pointer>,
}

#[derive(Default, Clone)]
pub struct Struct {
    pub fields: Vec<(u64, Pointer, Pointer)>, // list of hash, key, and value
}

#[derive(Clone)]
pub struct Closure {
    pub id: Id,
    pub captured: Vec<Pointer>,
    pub num_args: usize,
    pub body: Vec<Instruction>,
    pub responsible: Option<Id>,
}

#[derive(Clone)]
pub struct Builtin {
    pub function: BuiltinFunction,
}

impl List {
    fn equals(&self, heap: &Heap, other: &List) -> bool {
        if self.items.len() != other.items.len() {
            return false;
        }

        self.items
            .iter()
            .zip_eq(other.items.iter())
            .all(|(a, &b)| a.equals(heap, b))
    }
}
impl Struct {
    pub fn from_fields(heap: &Heap, fields: HashMap<Pointer, Pointer>) -> Self {
        let mut s = Self::default();
        for (key, value) in fields {
            s.insert(heap, key, value);
        }
        s
    }
    /// If the struct contains the key, returns the index of its field.
    /// Otherwise, returns the index of where the key would be inserted.
    fn index_of_key(&self, heap: &Heap, key: Pointer, key_hash: u64) -> Result<usize, usize> {
        let index_of_first_hash_occurrence = self
            .fields
            .partition_point(|(existing_hash, _, _)| *existing_hash < key_hash);
        let fields_with_same_hash = self.fields[index_of_first_hash_occurrence..]
            .iter()
            .enumerate()
            .take_while(|(_, (existing_hash, _, _))| *existing_hash == key_hash)
            .map(|(i, (_, key, _))| (index_of_first_hash_occurrence + i, key));

        for (index, existing_key) in fields_with_same_hash {
            if existing_key.equals(heap, key) {
                return Ok(index);
            }
        }
        Err(index_of_first_hash_occurrence)
    }
    fn insert(&mut self, heap: &Heap, key: Pointer, value: Pointer) {
        let hash = key.hash(heap);
        let field = (hash, key, value);
        match self.index_of_key(heap, key, hash) {
            Ok(index) => self.fields[index] = field,
            Err(index) => self.fields.insert(index, field),
        }
    }
    pub fn get(&self, heap: &Heap, key: Pointer) -> Option<Pointer> {
        let index = self.index_of_key(heap, key, key.hash(heap)).ok()?;
        Some(self.fields[index].2)
    }
    fn len(&self) -> usize {
        self.fields.len()
    }
    pub fn iter(&self) -> impl Iterator<Item = (Pointer, Pointer)> {
        self.fields
            .clone()
            .into_iter()
            .map(|(_, key, value)| (key, value))
    }
    fn equals(&self, heap: &Heap, other: &Struct) -> bool {
        if self.len() != other.len() {
            return false;
        }

        self.iter()
            .zip_eq(other.iter())
            .all(|((key_a, value_a), (key_b, value_b))| {
                key_a.equals(heap, key_b) && value_a.equals(heap, value_b)
            })
    }
}

impl Closure {
    pub fn of_module_lir(module: Module, lir: Lir) -> Self {
        Closure {
            id: Id::new(module.clone(), vec![]),
            captured: vec![],
            num_args: 0,
            body: vec![
                Instruction::TraceModuleStarts {
                    module: module.clone(),
                },
                Instruction::CreateClosure {
                    id: Id::new(module, vec![]),
                    captured: vec![],
                    num_args: 0,
                    body: lir.instructions,
                    is_curly: true,
                },
                Instruction::Call { num_args: 0 },
                Instruction::TraceModuleEnds,
                Instruction::Return,
            ],
            responsible: None,
        }
    }
    pub fn of_module(db: &Database, module: Module) -> Option<Self> {
        let lir = db.lir(module.clone())?;
        Some(Self::of_module_lir(module, (*lir).clone()))
    }
}

#[derive(Clone)]
pub struct SendPort {
    pub channel: ChannelId,
}
#[derive(Clone)]
pub struct ReceivePort {
    pub channel: ChannelId,
}

impl SendPort {
    pub fn new(channel: ChannelId) -> Self {
        Self { channel }
    }
}
impl ReceivePort {
    pub fn new(channel: ChannelId) -> Self {
        Self { channel }
    }
}

impl Data {
    fn hash(&self, heap: &Heap) -> u64 {
        let mut state = DefaultHasher::new();
        self.hash_with_state(heap, &mut state);
        state.finish()
    }

    fn hash_with_state<H: Hasher>(&self, heap: &Heap, state: &mut H) {
        match self {
            Data::Int(int) => int.value.hash(state),
            Data::Text(text) => text.value.hash(state),
            Data::Symbol(symbol) => symbol.value.hash(state),
            Data::List(List { items }) => {
                let mut s = 0;
                for item in items {
                    s ^= item.hash(heap);
                }
                s.hash(state)
            }
            Data::Struct(struct_) => {
                let mut s = 0;
                for (key, value) in struct_.iter() {
                    s ^= key.hash(heap);
                    s ^= value.hash(heap);
                }
                s.hash(state)
            }
            Data::Closure(closure) => {
                for captured in &closure.captured {
                    captured.hash_with_state(heap, state);
                }
                closure.num_args.hash(state);
                closure.body.hash(state);
            }
            Data::Builtin(builtin) => builtin.function.hash(state),
            Data::SendPort(port) => port.channel.hash(state),
            Data::ReceivePort(port) => port.channel.hash(state),
        }
    }

    pub fn equals(&self, heap: &Heap, other: &Self) -> bool {
        match (self, other) {
            (Data::Int(a), Data::Int(b)) => a.value == b.value,
            (Data::Text(a), Data::Text(b)) => a.value == b.value,
            (Data::Symbol(a), Data::Symbol(b)) => a.value == b.value,
            (Data::List(a), Data::List(b)) => a.equals(heap, b),
            (Data::Struct(a), Data::Struct(b)) => a.equals(heap, b),
            (Data::Closure(_), Data::Closure(_)) => false,
            (Data::Builtin(a), Data::Builtin(b)) => a.function == b.function,
            (Data::SendPort(a), Data::SendPort(b)) => a.channel == b.channel,
            (Data::ReceivePort(a), Data::ReceivePort(b)) => a.channel == b.channel,
            _ => false,
        }
    }

    pub fn format(&self, heap: &Heap) -> String {
        match self {
            Data::Int(int) => format!("{}", int.value),
            Data::Text(text) => format!("\"{}\"", text.value),
            Data::Symbol(symbol) => symbol.value.to_string(),
            Data::List(List { items }) => format!(
                "({})",
                if items.is_empty() {
                    ",".to_owned()
                } else {
                    items.iter().map(|item| item.format(heap)).join(", ")
                },
            ),
            Data::Struct(struct_) => format!(
                "[{}]",
                struct_
                    .iter()
                    .map(|(key, value)| (key.format(heap), value.format(heap)))
                    .sorted_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b))
                    .map(|(key, value)| format!("{}: {}", key, value))
                    .join(", ")
            ),
            Data::Closure(_) => "{…}".to_string(),
            Data::Builtin(builtin) => format!("builtin{:?}", builtin.function),
            Data::SendPort(port) => format!("sendPort {:?}", port.channel),
            Data::ReceivePort(port) => format!("receivePort {:?}", port.channel),
        }
    }

    pub fn children(&self) -> Vec<Pointer> {
        match self {
            Data::Int(_)
            | Data::Text(_)
            | Data::Symbol(_)
            | Data::Builtin(_)
            | Data::SendPort(_)
            | Data::ReceivePort(_) => vec![],
            Data::List(List { items }) => items.clone(),
            Data::Struct(struct_) => struct_
                .iter()
                .flat_map(|(a, b)| vec![a, b].into_iter())
                .collect_vec(),
            Data::Closure(closure) => closure.captured.clone(),
        }
    }

    pub fn channel(&self) -> Option<ChannelId> {
        if let Data::SendPort(SendPort { channel }) | Data::ReceivePort(ReceivePort { channel }) =
            self
        {
            Some(*channel)
        } else {
            None
        }
    }
}

impl Deref for Object {
    type Target = Data;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl Pointer {
    fn hash(&self, heap: &Heap) -> u64 {
        heap.get(*self).hash(heap)
    }
    fn hash_with_state<H: Hasher>(&self, heap: &Heap, state: &mut H) {
        heap.get(*self).hash_with_state(heap, state)
    }
    pub fn equals(&self, heap: &Heap, other: Self) -> bool {
        if *self == other {
            return true;
        }
        heap.get(*self).equals(heap, heap.get(other))
    }
    pub fn format(&self, heap: &Heap) -> String {
        heap.get(*self).format(heap)
    }
}
<|endoftext|>


<commit_before>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use failure::{err_msg, Error, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn command_output(cmd: &mut Command) -> Result<process::Output, Error> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<(), Error> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    command_output(&mut cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
    #[serde(default = "default_true")]
    nll: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
            nll: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    nll: bool,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.sysroot.command("cargo");
        cmd
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize))
            .current_dir(cwd)
            .arg(subcommand)
            .arg("--manifest-path")
            .arg(&self.manifest_path);
        cmd
    }

    fn nll(mut self, nll: bool) -> Self {
        self.nll = nll;
        self
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn run(self, cwd: &Path, mode: CargoMode) -> Result<Vec<Stat>, Error> {
        let subcommand = match mode {
            CargoMode::Build => "rustc",
            CargoMode::Clean => "clean",
        };
        let mut cmd = self.base_command(cwd, subcommand);
        {
            let mut pkgid_cmd = self.base_command(cwd, "pkgid");
            let out = command_output(&mut pkgid_cmd)
                .unwrap_or_else(|e| {
                    panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
                })
                .stdout;
            let package_id = str::from_utf8(&out).unwrap();
            cmd.arg("-p").arg(package_id.trim());
        }
        if mode == CargoMode::Build && self.options.check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        if mode == CargoMode::Build {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            if self.nll {
                cmd.arg("-Znll");
            }
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }

        debug!("running cargo: {:?}", cmd);

        match mode {
            CargoMode::Build => loop {
                touch_all(&cwd)?;
                let output = command_output(&mut cmd)?;
                match process_output(output) {
                    Ok(stats) => return Ok(stats),
                    Err(DeserializeStatError::NoOutput(output)) => {
                        warn!(
                            "failed to deserialize stats, retrying; output: {:?}",
                            output
                        );
                    }
                    Err(e @ DeserializeStatError::ParseError { .. }) => {
                        panic!("process_output failed: {:?}", e);
                    }
                }
            },
            CargoMode::Clean => {
                let _output = command_output(&mut cmd)?;
                return Ok(Vec::new());
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum CargoMode {
    Build,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self, Error> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .with_context(|_| format!("failed to open {:?}", config_path))?)
                .with_context(|_| {
                format!("failed to parse {:?}", config_path)
            })?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir, Error> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        command_output(&mut cmd).with_context(|_| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn mk_cargo_process<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            nll: false,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark, Error> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut nll_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                .run(base_build.path(), CargoMode::Build)?;
            clean_stats.push(clean);
            self.mk_cargo_process(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), CargoMode::Clean)?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;
                if self.config.nll {
                    let nll = self.mk_cargo_process(sysroot, Incremental(false), opt)
                        .nll(true)
                        .run(base_build.path(), CargoMode::Build)?;
                    nll_stats.push(nll);
                }
                let incr = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;
                let incr_clean = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;

                clean_stats.push(clean);
                incr_stats.push(incr);
                incr_clean_stats.push(incr_clean);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path()).map_err(|s| err_msg(s))?;
                    let out = self.mk_cargo_process(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), CargoMode::Build)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            if self.config.nll {
                ret.runs
                    .push(process_stats(opt, BenchmarkState::Nll, nll_stats));
            }
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

#[derive(Fail, PartialEq, Eq, Debug)]
enum DeserializeStatError {
    #[fail(display = "could not deserialize empty output to stats, output: {:?}", _0)]
    NoOutput(process::Output),
    #[fail(display = "could not parse `{}` as a float", _0)]
    ParseError(String, #[fail(cause)] ::std::num::ParseFloatError),
}

fn process_output(output: process::Output) -> Result<Vec<Stat>, DeserializeStatError> {
    let stdout = String::from_utf8(output.stdout.clone()).expect("utf8 output");
    let mut stats = Vec::new();

    for line in stdout.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e: expr) => {
                match $e {
                    Some(s) => s,
                    None => {
                        warn!("unhandled line: {}", line);
                        continue;
                    }
                }
            };
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .map_err(|e| DeserializeStatError::ParseError(cnt.to_string(), e))?,
        });
    }

    if stats.is_empty() {
        return Err(DeserializeStatError::NoOutput(output));
    }

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<commit_msg>Move pkgid getting into its own function.<commit_after>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use failure::{err_msg, Error, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn command_output(cmd: &mut Command) -> Result<process::Output, Error> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<(), Error> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    command_output(&mut cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
    #[serde(default = "default_true")]
    nll: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
            nll: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    nll: bool,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base_command(&self, cwd: &Path, subcommand: &str) -> Command {
        let mut cmd = self.sysroot.command("cargo");
        cmd
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize))
            .current_dir(cwd)
            .arg(subcommand)
            .arg("--manifest-path")
            .arg(&self.manifest_path);
        cmd
    }

    fn nll(mut self, nll: bool) -> Self {
        self.nll = nll;
        self
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn get_pkgid(&self, cwd: &Path) -> String {
        let mut pkgid_cmd = self.base_command(cwd, "pkgid");
        let out = command_output(&mut pkgid_cmd)
            .unwrap_or_else(|e| {
                panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
            })
            .stdout;
        let package_id = str::from_utf8(&out).unwrap();
        package_id.trim().to_string()
    }

    fn run(self, cwd: &Path, mode: CargoMode) -> Result<Vec<Stat>, Error> {
        let subcommand = match mode {
            CargoMode::Build => "rustc",
            CargoMode::Clean => "clean",
        };
        let mut cmd = self.base_command(cwd, subcommand);
        cmd.arg("-p").arg(self.get_pkgid(cwd));
        if mode == CargoMode::Build && self.options.check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        if mode == CargoMode::Build {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            if self.nll {
                cmd.arg("-Znll");
            }
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }

        debug!("running cargo: {:?}", cmd);

        match mode {
            CargoMode::Build => loop {
                touch_all(&cwd)?;
                let output = command_output(&mut cmd)?;
                match process_output(output) {
                    Ok(stats) => return Ok(stats),
                    Err(DeserializeStatError::NoOutput(output)) => {
                        warn!(
                            "failed to deserialize stats, retrying; output: {:?}",
                            output
                        );
                    }
                    Err(e @ DeserializeStatError::ParseError { .. }) => {
                        panic!("process_output failed: {:?}", e);
                    }
                }
            },
            CargoMode::Clean => {
                let _output = command_output(&mut cmd)?;
                return Ok(Vec::new());
            }
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum CargoMode {
    Build,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self, Error> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path)
                .with_context(|_| format!("failed to open {:?}", config_path))?)
                .with_context(|_| {
                format!("failed to parse {:?}", config_path)
            })?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir, Error> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        command_output(&mut cmd).with_context(|_| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn mk_cargo_process<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            nll: false,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark, Error> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut nll_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                .run(base_build.path(), CargoMode::Build)?;
            clean_stats.push(clean);
            self.mk_cargo_process(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), CargoMode::Clean)?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.mk_cargo_process(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;
                if self.config.nll {
                    let nll = self.mk_cargo_process(sysroot, Incremental(false), opt)
                        .nll(true)
                        .run(base_build.path(), CargoMode::Build)?;
                    nll_stats.push(nll);
                }
                let incr = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;
                let incr_clean = self.mk_cargo_process(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), CargoMode::Build)?;

                clean_stats.push(clean);
                incr_stats.push(incr);
                incr_clean_stats.push(incr_clean);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path()).map_err(|s| err_msg(s))?;
                    let out = self.mk_cargo_process(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), CargoMode::Build)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            if self.config.nll {
                ret.runs
                    .push(process_stats(opt, BenchmarkState::Nll, nll_stats));
            }
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

#[derive(Fail, PartialEq, Eq, Debug)]
enum DeserializeStatError {
    #[fail(display = "could not deserialize empty output to stats, output: {:?}", _0)]
    NoOutput(process::Output),
    #[fail(display = "could not parse `{}` as a float", _0)]
    ParseError(String, #[fail(cause)] ::std::num::ParseFloatError),
}

fn process_output(output: process::Output) -> Result<Vec<Stat>, DeserializeStatError> {
    let stdout = String::from_utf8(output.stdout.clone()).expect("utf8 output");
    let mut stats = Vec::new();

    for line in stdout.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e: expr) => {
                match $e {
                    Some(s) => s,
                    None => {
                        warn!("unhandled line: {}", line);
                        continue;
                    }
                }
            };
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .map_err(|e| DeserializeStatError::ParseError(cnt.to_string(), e))?,
        });
    }

    if stats.is_empty() {
        return Err(DeserializeStatError::NoOutput(output));
    }

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<|endoftext|>


<commit_before>use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::query::Providers;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::symbol::Symbol;

// When adding features to the below lists
// check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's
// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted

const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("aclass", Some(sym::arm_target_feature)),
    ("mclass", Some(sym::arm_target_feature)),
    ("rclass", Some(sym::arm_target_feature)),
    ("dsp", Some(sym::arm_target_feature)),
    ("neon", Some(sym::arm_target_feature)),
    ("crc", Some(sym::arm_target_feature)),
    ("crypto", Some(sym::arm_target_feature)),
    ("aes", Some(sym::arm_target_feature)),
    ("sha2", Some(sym::arm_target_feature)),
    ("v5te", Some(sym::arm_target_feature)),
    ("v6", Some(sym::arm_target_feature)),
    ("v6k", Some(sym::arm_target_feature)),
    ("v6t2", Some(sym::arm_target_feature)),
    ("v7", Some(sym::arm_target_feature)),
    ("v8", Some(sym::arm_target_feature)),
    ("vfp2", Some(sym::arm_target_feature)),
    ("vfp3", Some(sym::arm_target_feature)),
    ("vfp4", Some(sym::arm_target_feature)),
    ("fp-armv8", Some(sym::arm_target_feature)),
    // This is needed for inline assembly, but shouldn't be stabilized as-is
    // since it should be enabled per-function using #[instruction_set], not
    // #[target_feature].
    ("thumb-mode", Some(sym::arm_target_feature)),
];

// Commented features are not available in LLVM 10.0, or have since been renamed
const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    // FEAT_AdvSimd
    ("neon", Some(sym::aarch64_target_feature)),
    // FEAT_FP
    ("fp", Some(sym::aarch64_target_feature)),
    // FEAT_FP16
    ("fp16", Some(sym::aarch64_target_feature)),
    // FEAT_SVE
    ("sve", Some(sym::aarch64_target_feature)),
    // FEAT_CRC
    ("crc", Some(sym::aarch64_target_feature)),
    // FEAT_RAS
    ("ras", Some(sym::aarch64_target_feature)),
    // FEAT_LSE
    ("lse", Some(sym::aarch64_target_feature)),
    // FEAT_RDM
    ("rdm", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC
    ("rcpc", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC2
    ("rcpc2", Some(sym::aarch64_target_feature)),
    // FEAT_DotProd
    ("dotprod", Some(sym::aarch64_target_feature)),
    // FEAT_TME
    ("tme", Some(sym::aarch64_target_feature)),
    // FEAT_FHM
    ("fhm", Some(sym::aarch64_target_feature)),
    // FEAT_DIT
    ("dit", Some(sym::aarch64_target_feature)),
    // FEAT_FLAGM
    // ("flagm", Some(sym::aarch64_target_feature)),
    // FEAT_SSBS
    ("ssbs", Some(sym::aarch64_target_feature)),
    // FEAT_SB
    ("sb", Some(sym::aarch64_target_feature)),
    // FEAT_PAUTH
    // ("pauth", Some(sym::aarch64_target_feature)),
    // FEAT_DPB
    ("dpb", Some(sym::aarch64_target_feature)),
    // FEAT_DPB2
    ("dpb2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2
    ("sve2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_AES
    ("sve2-aes", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SM4
    ("sve2-sm4", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SHA3
    ("sve2-sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_BitPerm
    ("sve2-bitperm", Some(sym::aarch64_target_feature)),
    // FEAT_FRINTTS
    ("frintts", Some(sym::aarch64_target_feature)),
    // FEAT_I8MM
    ("i8mm", Some(sym::aarch64_target_feature)),
    // FEAT_F32MM
    // ("f32mm", Some(sym::aarch64_target_feature)),
    // FEAT_F64MM
    // ("f64mm", Some(sym::aarch64_target_feature)),
    // FEAT_BF16
    // ("bf16", Some(sym::aarch64_target_feature)),
    // FEAT_RAND
    ("rand", Some(sym::aarch64_target_feature)),
    // FEAT_BTI
    ("bti", Some(sym::aarch64_target_feature)),
    // FEAT_MTE
    ("mte", Some(sym::aarch64_target_feature)),
    // FEAT_JSCVT
    ("jsconv", Some(sym::aarch64_target_feature)),
    // FEAT_FCMA
    ("fcma", Some(sym::aarch64_target_feature)),
    // FEAT_AES
    ("aes", Some(sym::aarch64_target_feature)),
    // FEAT_SHA1 & FEAT_SHA256
    ("sha2", Some(sym::aarch64_target_feature)),
    // FEAT_SHA512 & FEAT_SHA3
    ("sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SM3 & FEAT_SM4
    ("sm4", Some(sym::aarch64_target_feature)),
    ("v8.1a", Some(sym::aarch64_target_feature)),
    ("v8.2a", Some(sym::aarch64_target_feature)),
    ("v8.3a", Some(sym::aarch64_target_feature)),
    ("v8.4a", Some(sym::aarch64_target_feature)),
    ("v8.5a", Some(sym::aarch64_target_feature)),
    // ("v8.6a", Some(sym::aarch64_target_feature)),
    // ("v8.7a", Some(sym::aarch64_target_feature)),
];

const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("adx", Some(sym::adx_target_feature)),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bf16", Some(sym::avx512_target_feature)),
    ("avx512bitalg", Some(sym::avx512_target_feature)),
    ("avx512bw", Some(sym::avx512_target_feature)),
    ("avx512cd", Some(sym::avx512_target_feature)),
    ("avx512dq", Some(sym::avx512_target_feature)),
    ("avx512er", Some(sym::avx512_target_feature)),
    ("avx512f", Some(sym::avx512_target_feature)),
    ("avx512gfni", Some(sym::avx512_target_feature)),
    ("avx512ifma", Some(sym::avx512_target_feature)),
    ("avx512pf", Some(sym::avx512_target_feature)),
    ("avx512vaes", Some(sym::avx512_target_feature)),
    ("avx512vbmi", Some(sym::avx512_target_feature)),
    ("avx512vbmi2", Some(sym::avx512_target_feature)),
    ("avx512vl", Some(sym::avx512_target_feature)),
    ("avx512vnni", Some(sym::avx512_target_feature)),
    ("avx512vp2intersect", Some(sym::avx512_target_feature)),
    ("avx512vpclmulqdq", Some(sym::avx512_target_feature)),
    ("avx512vpopcntdq", Some(sym::avx512_target_feature)),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)),
    ("ermsb", Some(sym::ermsb_target_feature)),
    ("f16c", Some(sym::f16c_target_feature)),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("movbe", Some(sym::movbe_target_feature)),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("rtm", Some(sym::rtm_target_feature)),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some(sym::sse4a_target_feature)),
    ("ssse3", None),
    ("tbm", Some(sym::tbm_target_feature)),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("hvx", Some(sym::hexagon_target_feature)),
    ("hvx-length128b", Some(sym::hexagon_target_feature)),
];

const POWERPC_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("altivec", Some(sym::powerpc_target_feature)),
    ("power8-altivec", Some(sym::powerpc_target_feature)),
    ("power9-altivec", Some(sym::powerpc_target_feature)),
    ("power8-vector", Some(sym::powerpc_target_feature)),
    ("power9-vector", Some(sym::powerpc_target_feature)),
    ("vsx", Some(sym::powerpc_target_feature)),
];

const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] =
    &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))];

const RISCV_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("m", Some(sym::riscv_target_feature)),
    ("a", Some(sym::riscv_target_feature)),
    ("c", Some(sym::riscv_target_feature)),
    ("f", Some(sym::riscv_target_feature)),
    ("d", Some(sym::riscv_target_feature)),
    ("e", Some(sym::riscv_target_feature)),
];

const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("simd128", None),
    ("atomics", Some(sym::wasm_target_feature)),
    ("nontrapping-fptoint", Some(sym::wasm_target_feature)),
];

const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol>)> {
    std::iter::empty()
        .chain(ARM_ALLOWED_FEATURES.iter())
        .chain(AARCH64_ALLOWED_FEATURES.iter())
        .chain(X86_ALLOWED_FEATURES.iter())
        .chain(HEXAGON_ALLOWED_FEATURES.iter())
        .chain(POWERPC_ALLOWED_FEATURES.iter())
        .chain(MIPS_ALLOWED_FEATURES.iter())
        .chain(RISCV_ALLOWED_FEATURES.iter())
        .chain(WASM_ALLOWED_FEATURES.iter())
        .chain(BPF_ALLOWED_FEATURES.iter())
        .cloned()
}

pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option<Symbol>)] {
    match &*sess.target.arch {
        "arm" => ARM_ALLOWED_FEATURES,
        "aarch64" => AARCH64_ALLOWED_FEATURES,
        "x86" | "x86_64" => X86_ALLOWED_FEATURES,
        "hexagon" => HEXAGON_ALLOWED_FEATURES,
        "mips" | "mips64" => MIPS_ALLOWED_FEATURES,
        "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES,
        "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES,
        "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES,
        "bpf" => BPF_ALLOWED_FEATURES,
        _ => &[],
    }
}

pub(crate) fn provide(providers: &mut Providers) {
    providers.supported_target_features = |tcx, cnum| {
        assert_eq!(cnum, LOCAL_CRATE);
        if tcx.sess.opts.actually_rustdoc {
            // rustdoc needs to be able to document functions that use all the features, so
            // whitelist them all
            all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
        } else {
            supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
        }
    };
}
<commit_msg>enable `i8mm` on arm<commit_after>use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::query::Providers;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::symbol::Symbol;

// When adding features to the below lists
// check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's
// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted

const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("aclass", Some(sym::arm_target_feature)),
    ("mclass", Some(sym::arm_target_feature)),
    ("rclass", Some(sym::arm_target_feature)),
    ("dsp", Some(sym::arm_target_feature)),
    ("neon", Some(sym::arm_target_feature)),
    ("crc", Some(sym::arm_target_feature)),
    ("crypto", Some(sym::arm_target_feature)),
    ("aes", Some(sym::arm_target_feature)),
    ("sha2", Some(sym::arm_target_feature)),
    ("i8mm", Some(sym::arm_target_feature)),
    ("v5te", Some(sym::arm_target_feature)),
    ("v6", Some(sym::arm_target_feature)),
    ("v6k", Some(sym::arm_target_feature)),
    ("v6t2", Some(sym::arm_target_feature)),
    ("v7", Some(sym::arm_target_feature)),
    ("v8", Some(sym::arm_target_feature)),
    ("vfp2", Some(sym::arm_target_feature)),
    ("vfp3", Some(sym::arm_target_feature)),
    ("vfp4", Some(sym::arm_target_feature)),
    ("fp-armv8", Some(sym::arm_target_feature)),
    // This is needed for inline assembly, but shouldn't be stabilized as-is
    // since it should be enabled per-function using #[instruction_set], not
    // #[target_feature].
    ("thumb-mode", Some(sym::arm_target_feature)),
];

// Commented features are not available in LLVM 10.0, or have since been renamed
const AARCH64_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    // FEAT_AdvSimd
    ("neon", Some(sym::aarch64_target_feature)),
    // FEAT_FP
    ("fp", Some(sym::aarch64_target_feature)),
    // FEAT_FP16
    ("fp16", Some(sym::aarch64_target_feature)),
    // FEAT_SVE
    ("sve", Some(sym::aarch64_target_feature)),
    // FEAT_CRC
    ("crc", Some(sym::aarch64_target_feature)),
    // FEAT_RAS
    ("ras", Some(sym::aarch64_target_feature)),
    // FEAT_LSE
    ("lse", Some(sym::aarch64_target_feature)),
    // FEAT_RDM
    ("rdm", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC
    ("rcpc", Some(sym::aarch64_target_feature)),
    // FEAT_RCPC2
    ("rcpc2", Some(sym::aarch64_target_feature)),
    // FEAT_DotProd
    ("dotprod", Some(sym::aarch64_target_feature)),
    // FEAT_TME
    ("tme", Some(sym::aarch64_target_feature)),
    // FEAT_FHM
    ("fhm", Some(sym::aarch64_target_feature)),
    // FEAT_DIT
    ("dit", Some(sym::aarch64_target_feature)),
    // FEAT_FLAGM
    // ("flagm", Some(sym::aarch64_target_feature)),
    // FEAT_SSBS
    ("ssbs", Some(sym::aarch64_target_feature)),
    // FEAT_SB
    ("sb", Some(sym::aarch64_target_feature)),
    // FEAT_PAUTH
    // ("pauth", Some(sym::aarch64_target_feature)),
    // FEAT_DPB
    ("dpb", Some(sym::aarch64_target_feature)),
    // FEAT_DPB2
    ("dpb2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2
    ("sve2", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_AES
    ("sve2-aes", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SM4
    ("sve2-sm4", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_SHA3
    ("sve2-sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SVE2_BitPerm
    ("sve2-bitperm", Some(sym::aarch64_target_feature)),
    // FEAT_FRINTTS
    ("frintts", Some(sym::aarch64_target_feature)),
    // FEAT_I8MM
    ("i8mm", Some(sym::aarch64_target_feature)),
    // FEAT_F32MM
    // ("f32mm", Some(sym::aarch64_target_feature)),
    // FEAT_F64MM
    // ("f64mm", Some(sym::aarch64_target_feature)),
    // FEAT_BF16
    // ("bf16", Some(sym::aarch64_target_feature)),
    // FEAT_RAND
    ("rand", Some(sym::aarch64_target_feature)),
    // FEAT_BTI
    ("bti", Some(sym::aarch64_target_feature)),
    // FEAT_MTE
    ("mte", Some(sym::aarch64_target_feature)),
    // FEAT_JSCVT
    ("jsconv", Some(sym::aarch64_target_feature)),
    // FEAT_FCMA
    ("fcma", Some(sym::aarch64_target_feature)),
    // FEAT_AES
    ("aes", Some(sym::aarch64_target_feature)),
    // FEAT_SHA1 & FEAT_SHA256
    ("sha2", Some(sym::aarch64_target_feature)),
    // FEAT_SHA512 & FEAT_SHA3
    ("sha3", Some(sym::aarch64_target_feature)),
    // FEAT_SM3 & FEAT_SM4
    ("sm4", Some(sym::aarch64_target_feature)),
    ("v8.1a", Some(sym::aarch64_target_feature)),
    ("v8.2a", Some(sym::aarch64_target_feature)),
    ("v8.3a", Some(sym::aarch64_target_feature)),
    ("v8.4a", Some(sym::aarch64_target_feature)),
    ("v8.5a", Some(sym::aarch64_target_feature)),
    // ("v8.6a", Some(sym::aarch64_target_feature)),
    // ("v8.7a", Some(sym::aarch64_target_feature)),
];

const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("adx", Some(sym::adx_target_feature)),
    ("aes", None),
    ("avx", None),
    ("avx2", None),
    ("avx512bf16", Some(sym::avx512_target_feature)),
    ("avx512bitalg", Some(sym::avx512_target_feature)),
    ("avx512bw", Some(sym::avx512_target_feature)),
    ("avx512cd", Some(sym::avx512_target_feature)),
    ("avx512dq", Some(sym::avx512_target_feature)),
    ("avx512er", Some(sym::avx512_target_feature)),
    ("avx512f", Some(sym::avx512_target_feature)),
    ("avx512gfni", Some(sym::avx512_target_feature)),
    ("avx512ifma", Some(sym::avx512_target_feature)),
    ("avx512pf", Some(sym::avx512_target_feature)),
    ("avx512vaes", Some(sym::avx512_target_feature)),
    ("avx512vbmi", Some(sym::avx512_target_feature)),
    ("avx512vbmi2", Some(sym::avx512_target_feature)),
    ("avx512vl", Some(sym::avx512_target_feature)),
    ("avx512vnni", Some(sym::avx512_target_feature)),
    ("avx512vp2intersect", Some(sym::avx512_target_feature)),
    ("avx512vpclmulqdq", Some(sym::avx512_target_feature)),
    ("avx512vpopcntdq", Some(sym::avx512_target_feature)),
    ("bmi1", None),
    ("bmi2", None),
    ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)),
    ("ermsb", Some(sym::ermsb_target_feature)),
    ("f16c", Some(sym::f16c_target_feature)),
    ("fma", None),
    ("fxsr", None),
    ("lzcnt", None),
    ("movbe", Some(sym::movbe_target_feature)),
    ("pclmulqdq", None),
    ("popcnt", None),
    ("rdrand", None),
    ("rdseed", None),
    ("rtm", Some(sym::rtm_target_feature)),
    ("sha", None),
    ("sse", None),
    ("sse2", None),
    ("sse3", None),
    ("sse4.1", None),
    ("sse4.2", None),
    ("sse4a", Some(sym::sse4a_target_feature)),
    ("ssse3", None),
    ("tbm", Some(sym::tbm_target_feature)),
    ("xsave", None),
    ("xsavec", None),
    ("xsaveopt", None),
    ("xsaves", None),
];

const HEXAGON_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("hvx", Some(sym::hexagon_target_feature)),
    ("hvx-length128b", Some(sym::hexagon_target_feature)),
];

const POWERPC_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("altivec", Some(sym::powerpc_target_feature)),
    ("power8-altivec", Some(sym::powerpc_target_feature)),
    ("power9-altivec", Some(sym::powerpc_target_feature)),
    ("power8-vector", Some(sym::powerpc_target_feature)),
    ("power9-vector", Some(sym::powerpc_target_feature)),
    ("vsx", Some(sym::powerpc_target_feature)),
];

const MIPS_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] =
    &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))];

const RISCV_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("m", Some(sym::riscv_target_feature)),
    ("a", Some(sym::riscv_target_feature)),
    ("c", Some(sym::riscv_target_feature)),
    ("f", Some(sym::riscv_target_feature)),
    ("d", Some(sym::riscv_target_feature)),
    ("e", Some(sym::riscv_target_feature)),
];

const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
    ("simd128", None),
    ("atomics", Some(sym::wasm_target_feature)),
    ("nontrapping-fptoint", Some(sym::wasm_target_feature)),
];

const BPF_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[("alu32", Some(sym::bpf_target_feature))];

/// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented.
///
/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator!
pub fn all_known_features() -> impl Iterator<Item = (&'static str, Option<Symbol>)> {
    std::iter::empty()
        .chain(ARM_ALLOWED_FEATURES.iter())
        .chain(AARCH64_ALLOWED_FEATURES.iter())
        .chain(X86_ALLOWED_FEATURES.iter())
        .chain(HEXAGON_ALLOWED_FEATURES.iter())
        .chain(POWERPC_ALLOWED_FEATURES.iter())
        .chain(MIPS_ALLOWED_FEATURES.iter())
        .chain(RISCV_ALLOWED_FEATURES.iter())
        .chain(WASM_ALLOWED_FEATURES.iter())
        .chain(BPF_ALLOWED_FEATURES.iter())
        .cloned()
}

pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option<Symbol>)] {
    match &*sess.target.arch {
        "arm" => ARM_ALLOWED_FEATURES,
        "aarch64" => AARCH64_ALLOWED_FEATURES,
        "x86" | "x86_64" => X86_ALLOWED_FEATURES,
        "hexagon" => HEXAGON_ALLOWED_FEATURES,
        "mips" | "mips64" => MIPS_ALLOWED_FEATURES,
        "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES,
        "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES,
        "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES,
        "bpf" => BPF_ALLOWED_FEATURES,
        _ => &[],
    }
}

pub(crate) fn provide(providers: &mut Providers) {
    providers.supported_target_features = |tcx, cnum| {
        assert_eq!(cnum, LOCAL_CRATE);
        if tcx.sess.opts.actually_rustdoc {
            // rustdoc needs to be able to document functions that use all the features, so
            // whitelist them all
            all_known_features().map(|(a, b)| (a.to_string(), b)).collect()
        } else {
            supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect()
        }
    };
}
<|endoftext|>