<commit_before>#![allow(unused_results)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

pub(crate) fn clock() -> f64 {
    if cfg!(feature = "no_metrics") {
        0.
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut _aux = 0;
            core::arch::x86_64::__rdtscp(&mut _aux) as f64
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) as f64 + f64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: f64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline(always)]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline(always)]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

/// Measure the time spent on calling a given function in a given `Histogram`.
#[cfg_attr(not(feature = "no_inline"), inline)]
pub(crate) fn measure<F: FnOnce() -> R, R>(_histo: &Histogram, f: F) -> R {
    #[cfg(not(feature = "no_metrics"))]
    let _measure = Measure::new(_histo);
    f()
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub advance_snapshot: Histogram,
    pub tree_set: Histogram,
    pub tree_get: Histogram,
    pub tree_del: Histogram,
    pub tree_cas: Histogram,
    pub tree_scan: Histogram,
    pub tree_reverse_scan: Histogram,
    pub tree_merge: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub get_page: Histogram,
    pub rewrite_page: Histogram,
    pub replace_page: Histogram,
    pub link_page: Histogram,
    pub merge_page: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub serialize: Histogram,
    pub deserialize: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub make_stable: Histogram,
    pub assign_offset: Histogram,
    pub assign_spinloop: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub reserve_current_condvar_wait: Histogram,
    pub reserve_written_condvar_wait: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    pub read: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub accountant_lock: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_next: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_bump_tip: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "pagecache profile:\n\
            {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("start", &self.tree_start),
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        println!("tree contention loops: {}", self.tree_loops.load(Acquire));
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("snapshot", &self.advance_snapshot),
            lat("get", &self.get_page),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("merge", &self.merge_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            lat("compress", &self.compress),
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("assign spinloop", &self.assign_spinloop),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
            lat("res cvar r", &self.reserve_current_condvar_wait),
            lat("res cvar w", &self.reserve_written_condvar_wait),
        ]);
        println!("log reservations: {}", self.log_reservations.load(Acquire));
        println!(
            "log res attempts: {}",
            self.log_reservation_attempts.load(Acquire)
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub fn log_reservation_attempted(&self) {}

    pub fn log_reservation_success(&self) {}

    pub fn tree_child_split_attempt(&self) {}

    pub fn tree_child_split_success(&self) {}

    pub fn tree_parent_split_attempt(&self) {}

    pub fn tree_parent_split_success(&self) {}

    pub fn tree_root_split_attempt(&self) {}

    pub fn tree_root_split_success(&self) {}

    pub fn tree_looped(&self) {}

    pub fn print_profile(&self) {}
}
<commit_msg>fix conditional compilation<commit_after>#![allow(unused_results)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use std::time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

pub(crate) fn clock() -> f64 {
    if cfg!(feature = "no_metrics") {
        0.
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut _aux = 0;
            core::arch::x86_64::__rdtscp(&mut _aux) as f64
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) as f64 + f64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: f64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline(always)]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline(always)]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

/// Measure the time spent on calling a given function in a given `Histogram`.
#[cfg_attr(not(feature = "no_inline"), inline)]
pub(crate) fn measure<F: FnOnce() -> R, R>(_histo: &Histogram, f: F) -> R {
    #[cfg(not(feature = "no_metrics"))]
    let _measure = Measure::new(_histo);
    f()
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub advance_snapshot: Histogram,
    pub tree_set: Histogram,
    pub tree_get: Histogram,
    pub tree_del: Histogram,
    pub tree_cas: Histogram,
    pub tree_scan: Histogram,
    pub tree_reverse_scan: Histogram,
    pub tree_merge: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub get_page: Histogram,
    pub rewrite_page: Histogram,
    pub replace_page: Histogram,
    pub link_page: Histogram,
    pub merge_page: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub serialize: Histogram,
    pub deserialize: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub make_stable: Histogram,
    pub assign_offset: Histogram,
    pub assign_spinloop: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub reserve_current_condvar_wait: Histogram,
    pub reserve_written_condvar_wait: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    pub read: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub accountant_lock: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_next: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_bump_tip: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "pagecache profile:\n\
            {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("start", &self.tree_start),
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        println!("tree contention loops: {}", self.tree_loops.load(Acquire));
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("snapshot", &self.advance_snapshot),
            lat("get", &self.get_page),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("merge", &self.merge_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            lat("compress", &self.compress),
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("assign spinloop", &self.assign_spinloop),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
            lat("res cvar r", &self.reserve_current_condvar_wait),
            lat("res cvar w", &self.reserve_written_condvar_wait),
        ]);
        println!("log reservations: {}", self.log_reservations.load(Acquire));
        println!(
            "log res attempts: {}",
            self.log_reservation_attempts.load(Acquire)
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub fn log_reservation_attempted(&self) {}

    pub fn log_reservation_success(&self) {}

    pub fn tree_child_split_attempt(&self) {}

    pub fn tree_child_split_success(&self) {}

    pub fn tree_parent_split_attempt(&self) {}

    pub fn tree_parent_split_success(&self) {}

    pub fn tree_root_split_attempt(&self) {}

    pub fn tree_root_split_success(&self) {}

    pub fn tree_looped(&self) {}

    pub fn print_profile(&self) {}
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::ptr;

use libc;

use baseline::fct::JitFct;
use class::TypeParams;
use ctxt::{Fct, FctParent, FctSrc, IdentType, SemContext, VarId};
use opt::fct::JitOptFct;
use ty::{BuiltinType, MachineMode};

use dora_parser::ast::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;
use dora_parser::lexer::token::{FloatSuffix, IntSuffix};

use llvm::prelude::*;
use llvm::analysis::*;
use llvm::core::*;
use llvm::orc::*;

pub mod util;
pub mod fct;

pub fn generate_fct<'ast>(
    ctxt: &SemContext<'ast>,
    fct: &Fct<'ast>,
    src: &mut FctSrc,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let name = fct.full_name(ctxt);

    let jit_fct = {
        let mut cg = CodeGen {
            ctxt: ctxt,
            fct: fct,
            ast: fct.ast,
            src: src,
            cls_type_params,
            fct_type_params,

            fct_name: CString::new(name).unwrap(),

            context: ptr::null_mut(),
            module: ptr::null_mut(),
            shared_module: ptr::null_mut(),
            builder: ptr::null_mut(),
            function: ptr::null_mut(),

            map_vars: HashMap::new(),
        };

        cg.generate()?
    };

    let fct_start = jit_fct.fct_ptr();

    {
        let mut specials = src.specializations.write().unwrap();
        let key = (cls_type_params.clone(), fct_type_params.clone());

        let jit_fct_id = ctxt.jit_fcts.len().into();
        ctxt.jit_fcts.push(JitFct::Opt(jit_fct));
        specials.insert(key, jit_fct_id);
    }

    Ok(fct_start)
}

type EmitResult<T> = Result<T, ()>;

fn fail<T>() -> EmitResult<T> {
    Err(())
}

fn ok<T>(value: T) -> EmitResult<T> {
    Ok(value)
}

struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    fct: &'a Fct<'ast>,
    ast: &'ast Function,
    src: &'a mut FctSrc,
    cls_type_params: &'a TypeParams,
    fct_type_params: &'a TypeParams,

    fct_name: CString,

    context: LLVMContextRef,
    module: LLVMModuleRef,
    shared_module: LLVMSharedModuleRef,
    builder: LLVMBuilderRef,
    function: LLVMValueRef,

    map_vars: HashMap<VarId, LLVMValueRef>,
}

impl<'a, 'ast> CodeGen<'a, 'ast>
where
    'ast: 'a,
{
    fn generate(&mut self) -> EmitResult<JitOptFct> {
        self.init();
        self.create_function()?;
        self.add_entry_bb();
        self.add_params();

        let block = self.ast.block.as_ref().unwrap();
        self.emit_stmt(block)?;

        unsafe {
            LLVMDisposeBuilder(self.builder);
        }

        self.verify()?;

        if self.ctxt.args.flag_emit_llvm {
            unsafe {
                LLVMDumpModule(self.module);
            }
        }

        let orc = self.ctxt.llvm_jit.orc;
        let mut ptr = 0;

        unsafe {
            // add eagerly compiled IR
            let mut handle = 0;
            let res = LLVMOrcAddEagerlyCompiledIR(
                orc,
                &mut handle,
                self.shared_module,
                resolver,
                ptr::null_mut(),
            );
            assert!(res == LLVMOrcErrorCode::LLVMOrcErrSuccess);

            if LLVMOrcGetSymbolAddress(orc, &mut ptr, self.fct_name.as_ptr()) ==
                LLVMOrcErrorCode::LLVMOrcErrSuccess
            {
                assert!(ptr != 0);

                let jit_fct = JitOptFct {
                    fct_id: self.fct.id,
                    fct_start: ptr as *const _,
                };

                ok(jit_fct)
            } else {
                fail()
            }
        }
    }

    fn init(&mut self) {
        unsafe {
            self.context = LLVMContextCreate();
            self.module =
                LLVMModuleCreateWithNameInContext(b"\0".as_ptr() as *const _, self.context);
            self.shared_module = LLVMOrcMakeSharedModule(self.module);
            self.builder = LLVMCreateBuilderInContext(self.context);
        }
    }

    fn create_function(&mut self) -> EmitResult<()> {
        let mut params = Vec::with_capacity(self.ast.params.len());

        for param in &self.ast.params {
            let varid = *self.src.map_vars.get(param.id).unwrap();
            let ty = self.ty_var(varid);
            params.push(self.llvm_ty(ty));
        }

        let return_type = self.specialize_type(self.fct.return_type);
        let llvm_return_type = self.llvm_ty(return_type);

        unsafe {
            let function_type = LLVMFunctionType(
                llvm_return_type,
                params.as_mut_ptr(),
                params.len() as u32,
                0,
            );

            self.function = LLVMAddFunction(self.module, self.fct_name.as_ptr(), function_type);
        }

        ok(())
    }

    fn add_entry_bb(&mut self) {
        unsafe {
            let bb = LLVMAppendBasicBlockInContext(
                self.context,
                self.function,
                b"entry\0".as_ptr() as *const _,
            );

            LLVMPositionBuilderAtEnd(self.builder, bb);
        }
    }

    fn add_params(&mut self) {
        for (ind, param) in self.ast.params.iter().enumerate() {
            let varid = *self.src.map_vars.get(param.id).unwrap();
            let ty = self.ty_var(varid);
            let ty = self.llvm_ty(ty);

            unsafe {
                let value = LLVMGetParam(self.function, ind as u32);
                let ptr = LLVMBuildAlloca(self.builder, ty, b"\0".as_ptr() as *const _);

                LLVMBuildStore(self.builder, value, ptr);
                self.map_vars.insert(varid, ptr);
            }
        }
    }

    fn verify(&mut self) -> EmitResult<()> {
        unsafe {
            if LLVMVerifyFunction(
                self.function,
                LLVMVerifierFailureAction::LLVMPrintMessageAction,
            ) == 1
            {
                println!("invalid llvm function!");
                fail()
            } else {
                ok(())
            }
        }
    }

    fn emit_stmt(&mut self, s: &'ast Stmt) -> EmitResult<()> {
        match *s {
            StmtExpr(ref stmt) => {
                self.emit_expr(&stmt.expr)?;

                Ok(())
            }

            StmtIf(_) => fail(),
            StmtLoop(_) => fail(),
            StmtWhile(_) => fail(),
            StmtFor(_) => fail(),
            StmtReturn(ref stmt) => self.emit_return(stmt),
            StmtBreak(_) => fail(),
            StmtContinue(_) => fail(),
            StmtBlock(ref stmt) => self.emit_block(stmt),
            StmtVar(ref stmt) => self.emit_var(stmt),
            StmtThrow(_) => fail(),
            StmtDefer(_) => fail(),
            StmtDo(_) => fail(),
            StmtSpawn(_) => fail(),
        }
    }

    fn emit_return(&mut self, s: &'ast StmtReturnType) -> EmitResult<()> {
        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;

            unsafe {
                LLVMBuildRet(self.builder, value);
            }

        } else {
            unsafe {
                LLVMBuildRetVoid(self.builder);
            }
        }

        ok(())
    }

    fn emit_block(&mut self, s: &'ast StmtBlockType) -> EmitResult<()> {
        for stmt in &s.stmts {
            self.emit_stmt(stmt)?;
        }

        ok(())
    }

    fn emit_var(&mut self, s: &'ast StmtVarType) -> EmitResult<()> {
        let var = *self.src.map_vars.get(s.id).unwrap();
        let ty = self.ty_var(var);
        let ty = self.llvm_ty(ty);

        let ptr = unsafe {
            LLVMBuildAlloca(self.builder, ty, ptr::null())
        };

        self.map_vars.insert(var, ptr);

        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;
            unsafe {
                LLVMBuildStore(self.builder, value, ptr);
            }
        }

        ok(())
    }

    fn emit_expr(&mut self, e: &'ast Expr) -> EmitResult<LLVMValueRef> {
        match *e {
            ExprLitChar(ref lit) => self.emit_lit_char(lit),
            ExprLitInt(ref lit) => self.emit_lit_int(lit),
            ExprLitFloat(ref lit) => self.emit_lit_float(lit),
            ExprLitBool(ref lit) => self.emit_lit_bool(lit),
            ExprLitStr(_) => fail(),
            ExprLitStruct(_) => fail(),
            ExprUn(_) => fail(),
            ExprIdent(ref ident) => self.emit_ident(ident),
            ExprAssign(ref expr) => self.emit_assign(expr),
            ExprBin(_) => fail(),
            ExprCall(_) => fail(),
            ExprDelegation(_) => fail(),
            ExprField(_) => fail(),
            ExprSelf(_) => fail(),
            ExprSuper(_) => fail(),
            ExprNil(_) => fail(),
            ExprArray(_) => fail(),
            ExprConv(_) => fail(),
            ExprTry(_) => fail(),
            ExprLambda(_) => fail(),
        }
    }

    fn emit_lit_char(&mut self, e: &'ast ExprLitCharType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = LLVMInt32TypeInContext(self.context);
            let value = LLVMConstInt(ty, e.value as u64, 0);
            ok(value)
        }
    }

    fn emit_lit_int(&mut self, e: &'ast ExprLitIntType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = match e.suffix {
                IntSuffix::Byte => LLVMInt8TypeInContext(self.context),
                IntSuffix::Int => LLVMInt32TypeInContext(self.context),
                IntSuffix::Long => LLVMInt64TypeInContext(self.context),
            };

            let value = LLVMConstInt(ty, e.value, 0);
            ok(value)
        }
    }

    fn emit_lit_float(&mut self, e: &'ast ExprLitFloatType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = match e.suffix {
                FloatSuffix::Float => LLVMFloatTypeInContext(self.context),
                FloatSuffix::Double => LLVMDoubleTypeInContext(self.context),
            };

            let value = LLVMConstReal(ty, e.value);
            ok(value)
        }
    }

    fn emit_lit_bool(&mut self, e: &'ast ExprLitBoolType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = LLVMInt8TypeInContext(self.context);
            let value = if e.value { 1 } else { 0 };
            let value = LLVMConstInt(ty, value, 0);

            ok(value)
        }
    }

    fn emit_ident(&mut self, e: &'ast ExprIdentType) -> EmitResult<LLVMValueRef> {
        let &ident = self.src.map_idents.get(e.id).unwrap();

        match ident {
            IdentType::Var(varid) => {
                let ptr = self.map_vars[&varid];
                let value = unsafe {
                    LLVMBuildLoad(self.builder, ptr, b"\0".as_ptr() as *const _)
                };

                ok(value)
            }

            _ => fail()
        }
    }

    fn emit_assign(&mut self, e: &'ast ExprAssignType) -> EmitResult<LLVMValueRef> {
        if e.lhs.is_array() {
            return fail();
        }

        let &ident_type = self.src.map_idents.get(e.lhs.id()).unwrap();

        match ident_type {
            IdentType::Var(varid) => {
                let ptr = self.map_vars[&varid];
                let value = self.emit_expr(&e.rhs)?;

                unsafe {
                    LLVMBuildStore(self.builder, value, ptr);
                }

                ok(ptr::null_mut())
            }

            _ => fail()
        }
    }

    fn ty(&self, id: NodeId) -> BuiltinType {
        let ty = self.src.ty(id);
        self.specialize_type(ty)
    }

    fn ty_var(&self, id: VarId) -> BuiltinType {
        let ty = self.src.vars[id].ty;
        self.specialize_type(ty)
    }

    fn specialize_type(&self, ty: BuiltinType) -> BuiltinType {
        match ty {
            BuiltinType::ClassTypeParam(cls_id, id) => {
                assert!(self.fct.parent == FctParent::Class(cls_id));
                self.cls_type_params[id.idx()]
            }

            BuiltinType::FctTypeParam(fct_id, id) => {
                assert!(self.fct.id == fct_id);
                self.fct_type_params[id.idx()]
            }

            BuiltinType::Class(cls_id, list_id) => {
                let params = self.ctxt.lists.borrow().get(list_id);

                let params: Vec<_> = params.iter().map(|t| self.specialize_type(t)).collect();

                let list_id = self.ctxt.lists.borrow_mut().insert(params.into());

                BuiltinType::Class(cls_id, list_id)
            }

            BuiltinType::Lambda(_) => unimplemented!(),

            _ => ty,
        }
    }

    fn llvm_ty(&self, ty: BuiltinType) -> LLVMTypeRef {
        let mode = ty.mode();

        unsafe {
            match mode {
                MachineMode::Int64 => LLVMInt64TypeInContext(self.context),
                MachineMode::Int32 => LLVMInt32TypeInContext(self.context),
                MachineMode::Int8 => LLVMInt8TypeInContext(self.context),
                MachineMode::Float32 => LLVMFloatTypeInContext(self.context),
                MachineMode::Float64 => LLVMDoubleTypeInContext(self.context),
                MachineMode::Ptr => {
                    let int8 = LLVMInt8TypeInContext(self.context);
                    LLVMPointerType(int8, 1)
                }
            }
        }
    }
}

extern "C" fn resolver(name: *const i8, _: *mut libc::c_void) -> u64 {
    let name = unsafe { CStr::from_ptr(name) };
    panic!("resolver unimplemented: {:?}", name);
}
<commit_msg>opt: start invoking SROA pass<commit_after>use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::ptr;

use libc;

use baseline::fct::JitFct;
use class::TypeParams;
use ctxt::{Fct, FctParent, FctSrc, IdentType, SemContext, VarId};
use opt::fct::JitOptFct;
use ty::{BuiltinType, MachineMode};

use dora_parser::ast::*;
use dora_parser::ast::Expr::*;
use dora_parser::ast::Stmt::*;
use dora_parser::lexer::token::{FloatSuffix, IntSuffix};

use llvm::prelude::*;
use llvm::analysis::*;
use llvm::core::*;
use llvm::orc::*;
use llvm::transforms::scalar::*;

pub mod util;
pub mod fct;

pub fn generate_fct<'ast>(
    ctxt: &SemContext<'ast>,
    fct: &Fct<'ast>,
    src: &mut FctSrc,
    cls_type_params: &TypeParams,
    fct_type_params: &TypeParams,
) -> Result<*const u8, ()> {
    let name = fct.full_name(ctxt);

    let jit_fct = {
        let mut cg = CodeGen {
            ctxt: ctxt,
            fct: fct,
            ast: fct.ast,
            src: src,
            cls_type_params,
            fct_type_params,

            fct_name: CString::new(name).unwrap(),

            context: ptr::null_mut(),
            module: ptr::null_mut(),
            shared_module: ptr::null_mut(),
            builder: ptr::null_mut(),
            function: ptr::null_mut(),

            map_vars: HashMap::new(),
        };

        cg.generate()?
    };

    let fct_start = jit_fct.fct_ptr();

    {
        let mut specials = src.specializations.write().unwrap();
        let key = (cls_type_params.clone(), fct_type_params.clone());

        let jit_fct_id = ctxt.jit_fcts.len().into();
        ctxt.jit_fcts.push(JitFct::Opt(jit_fct));
        specials.insert(key, jit_fct_id);
    }

    Ok(fct_start)
}

type EmitResult<T> = Result<T, ()>;

fn fail<T>() -> EmitResult<T> {
    Err(())
}

fn ok<T>(value: T) -> EmitResult<T> {
    Ok(value)
}

struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    fct: &'a Fct<'ast>,
    ast: &'ast Function,
    src: &'a mut FctSrc,
    cls_type_params: &'a TypeParams,
    fct_type_params: &'a TypeParams,

    fct_name: CString,

    context: LLVMContextRef,
    module: LLVMModuleRef,
    shared_module: LLVMSharedModuleRef,
    builder: LLVMBuilderRef,
    function: LLVMValueRef,

    map_vars: HashMap<VarId, LLVMValueRef>,
}

impl<'a, 'ast> CodeGen<'a, 'ast>
where
    'ast: 'a,
{
    fn generate(&mut self) -> EmitResult<JitOptFct> {
        self.init();
        self.create_function()?;
        self.add_entry_bb();
        self.add_params();

        let block = self.ast.block.as_ref().unwrap();
        self.emit_stmt(block)?;

        unsafe {
            LLVMDisposeBuilder(self.builder);
        }

        self.verify()?;

        if self.ctxt.args.flag_emit_llvm {
            unsafe {
                LLVMDumpModule(self.module);
            }
        }

        let orc = self.ctxt.llvm_jit.orc;
        let mut ptr = 0;

        unsafe {
            // add eagerly compiled IR
            let mut handle = 0;
            let res = LLVMOrcAddEagerlyCompiledIR(
                orc,
                &mut handle,
                self.shared_module,
                resolver,
                ptr::null_mut(),
            );
            assert!(res == LLVMOrcErrorCode::LLVMOrcErrSuccess);

            let pm = LLVMCreateFunctionPassManagerForModule(self.module);
            LLVMAddScalarReplAggregatesPassSSA(pm);
            LLVMInitializeFunctionPassManager(pm);
            LLVMRunFunctionPassManager(pm, self.function);

            if self.ctxt.args.flag_emit_llvm {
                LLVMDumpModule(self.module);
            }

            if LLVMOrcGetSymbolAddress(orc, &mut ptr, self.fct_name.as_ptr()) ==
                LLVMOrcErrorCode::LLVMOrcErrSuccess
            {
                assert!(ptr != 0);

                let jit_fct = JitOptFct {
                    fct_id: self.fct.id,
                    fct_start: ptr as *const _,
                };

                ok(jit_fct)
            } else {
                fail()
            }
        }
    }

    fn init(&mut self) {
        unsafe {
            self.context = LLVMContextCreate();
            self.module =
                LLVMModuleCreateWithNameInContext(b"\0".as_ptr() as *const _, self.context);
            self.shared_module = LLVMOrcMakeSharedModule(self.module);
            self.builder = LLVMCreateBuilderInContext(self.context);
        }
    }

    fn create_function(&mut self) -> EmitResult<()> {
        let mut params = Vec::with_capacity(self.ast.params.len());

        for param in &self.ast.params {
            let varid = *self.src.map_vars.get(param.id).unwrap();
            let ty = self.ty_var(varid);
            params.push(self.llvm_ty(ty));
        }

        let return_type = self.specialize_type(self.fct.return_type);
        let llvm_return_type = self.llvm_ty(return_type);

        unsafe {
            let function_type = LLVMFunctionType(
                llvm_return_type,
                params.as_mut_ptr(),
                params.len() as u32,
                0,
            );

            self.function = LLVMAddFunction(self.module, self.fct_name.as_ptr(), function_type);
        }

        ok(())
    }

    fn add_entry_bb(&mut self) {
        unsafe {
            let bb = LLVMAppendBasicBlockInContext(
                self.context,
                self.function,
                b"entry\0".as_ptr() as *const _,
            );

            LLVMPositionBuilderAtEnd(self.builder, bb);
        }
    }

    fn add_params(&mut self) {
        for (ind, param) in self.ast.params.iter().enumerate() {
            let varid = *self.src.map_vars.get(param.id).unwrap();
            let ty = self.ty_var(varid);
            let ty = self.llvm_ty(ty);

            unsafe {
                let value = LLVMGetParam(self.function, ind as u32);
                let ptr = LLVMBuildAlloca(self.builder, ty, b"\0".as_ptr() as *const _);

                LLVMBuildStore(self.builder, value, ptr);
                self.map_vars.insert(varid, ptr);
            }
        }
    }

    fn verify(&mut self) -> EmitResult<()> {
        unsafe {
            if LLVMVerifyFunction(
                self.function,
                LLVMVerifierFailureAction::LLVMPrintMessageAction,
            ) == 1
            {
                println!("invalid llvm function!");
                fail()
            } else {
                ok(())
            }
        }
    }

    fn emit_stmt(&mut self, s: &'ast Stmt) -> EmitResult<()> {
        match *s {
            StmtExpr(ref stmt) => {
                self.emit_expr(&stmt.expr)?;

                Ok(())
            }

            StmtIf(_) => fail(),
            StmtLoop(_) => fail(),
            StmtWhile(_) => fail(),
            StmtFor(_) => fail(),
            StmtReturn(ref stmt) => self.emit_return(stmt),
            StmtBreak(_) => fail(),
            StmtContinue(_) => fail(),
            StmtBlock(ref stmt) => self.emit_block(stmt),
            StmtVar(ref stmt) => self.emit_var(stmt),
            StmtThrow(_) => fail(),
            StmtDefer(_) => fail(),
            StmtDo(_) => fail(),
            StmtSpawn(_) => fail(),
        }
    }

    fn emit_return(&mut self, s: &'ast StmtReturnType) -> EmitResult<()> {
        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;

            unsafe {
                LLVMBuildRet(self.builder, value);
            }

        } else {
            unsafe {
                LLVMBuildRetVoid(self.builder);
            }
        }

        ok(())
    }

    fn emit_block(&mut self, s: &'ast StmtBlockType) -> EmitResult<()> {
        for stmt in &s.stmts {
            self.emit_stmt(stmt)?;
        }

        ok(())
    }

    fn emit_var(&mut self, s: &'ast StmtVarType) -> EmitResult<()> {
        let var = *self.src.map_vars.get(s.id).unwrap();
        let ty = self.ty_var(var);
        let ty = self.llvm_ty(ty);

        let ptr = unsafe {
            LLVMBuildAlloca(self.builder, ty, ptr::null())
        };

        self.map_vars.insert(var, ptr);

        if let Some(ref expr) = s.expr {
            let value = self.emit_expr(expr)?;
            unsafe {
                LLVMBuildStore(self.builder, value, ptr);
            }
        }

        ok(())
    }

    fn emit_expr(&mut self, e: &'ast Expr) -> EmitResult<LLVMValueRef> {
        match *e {
            ExprLitChar(ref lit) => self.emit_lit_char(lit),
            ExprLitInt(ref lit) => self.emit_lit_int(lit),
            ExprLitFloat(ref lit) => self.emit_lit_float(lit),
            ExprLitBool(ref lit) => self.emit_lit_bool(lit),
            ExprLitStr(_) => fail(),
            ExprLitStruct(_) => fail(),
            ExprUn(_) => fail(),
            ExprIdent(ref ident) => self.emit_ident(ident),
            ExprAssign(ref expr) => self.emit_assign(expr),
            ExprBin(_) => fail(),
            ExprCall(_) => fail(),
            ExprDelegation(_) => fail(),
            ExprField(_) => fail(),
            ExprSelf(_) => fail(),
            ExprSuper(_) => fail(),
            ExprNil(_) => fail(),
            ExprArray(_) => fail(),
            ExprConv(_) => fail(),
            ExprTry(_) => fail(),
            ExprLambda(_) => fail(),
        }
    }

    fn emit_lit_char(&mut self, e: &'ast ExprLitCharType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = LLVMInt32TypeInContext(self.context);
            let value = LLVMConstInt(ty, e.value as u64, 0);
            ok(value)
        }
    }

    fn emit_lit_int(&mut self, e: &'ast ExprLitIntType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = match e.suffix {
                IntSuffix::Byte => LLVMInt8TypeInContext(self.context),
                IntSuffix::Int => LLVMInt32TypeInContext(self.context),
                IntSuffix::Long => LLVMInt64TypeInContext(self.context),
            };

            let value = LLVMConstInt(ty, e.value, 0);
            ok(value)
        }
    }

    fn emit_lit_float(&mut self, e: &'ast ExprLitFloatType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = match e.suffix {
                FloatSuffix::Float => LLVMFloatTypeInContext(self.context),
                FloatSuffix::Double => LLVMDoubleTypeInContext(self.context),
            };

            let value = LLVMConstReal(ty, e.value);
            ok(value)
        }
    }

    fn emit_lit_bool(&mut self, e: &'ast ExprLitBoolType) -> EmitResult<LLVMValueRef> {
        unsafe {
            let ty = LLVMInt8TypeInContext(self.context);
            let value = if e.value { 1 } else { 0 };
            let value = LLVMConstInt(ty, value, 0);

            ok(value)
        }
    }

    fn emit_ident(&mut self, e: &'ast ExprIdentType) -> EmitResult<LLVMValueRef> {
        let &ident = self.src.map_idents.get(e.id).unwrap();

        match ident {
            IdentType::Var(varid) => {
                let ptr = self.map_vars[&varid];
                let value = unsafe {
                    LLVMBuildLoad(self.builder, ptr, b"\0".as_ptr() as *const _)
                };

                ok(value)
            }

            _ => fail()
        }
    }

    fn emit_assign(&mut self, e: &'ast ExprAssignType) -> EmitResult<LLVMValueRef> {
        if e.lhs.is_array() {
            return fail();
        }

        let &ident_type = self.src.map_idents.get(e.lhs.id()).unwrap();

        match ident_type {
            IdentType::Var(varid) => {
                let ptr = self.map_vars[&varid];
                let value = self.emit_expr(&e.rhs)?;

                unsafe {
                    LLVMBuildStore(self.builder, value, ptr);
                }

                ok(ptr::null_mut())
            }

            _ => fail()
        }
    }

    fn ty(&self, id: NodeId) -> BuiltinType {
        let ty = self.src.ty(id);
        self.specialize_type(ty)
    }

    fn ty_var(&self, id: VarId) -> BuiltinType {
        let ty = self.src.vars[id].ty;
        self.specialize_type(ty)
    }

    fn specialize_type(&self, ty: BuiltinType) -> BuiltinType {
        match ty {
            BuiltinType::ClassTypeParam(cls_id, id) => {
                assert!(self.fct.parent == FctParent::Class(cls_id));
                self.cls_type_params[id.idx()]
            }

            BuiltinType::FctTypeParam(fct_id, id) => {
                assert!(self.fct.id == fct_id);
                self.fct_type_params[id.idx()]
            }

            BuiltinType::Class(cls_id, list_id) => {
                let params = self.ctxt.lists.borrow().get(list_id);

                let params: Vec<_> = params.iter().map(|t| self.specialize_type(t)).collect();

                let list_id = self.ctxt.lists.borrow_mut().insert(params.into());

                BuiltinType::Class(cls_id, list_id)
            }

            BuiltinType::Lambda(_) => unimplemented!(),

            _ => ty,
        }
    }

    fn llvm_ty(&self, ty: BuiltinType) -> LLVMTypeRef {
        let mode = ty.mode();

        unsafe {
            match mode {
                MachineMode::Int64 => LLVMInt64TypeInContext(self.context),
                MachineMode::Int32 => LLVMInt32TypeInContext(self.context),
                MachineMode::Int8 => LLVMInt8TypeInContext(self.context),
                MachineMode::Float32 => LLVMFloatTypeInContext(self.context),
                MachineMode::Float64 => LLVMDoubleTypeInContext(self.context),
                MachineMode::Ptr => {
                    let int8 = LLVMInt8TypeInContext(self.context);
                    LLVMPointerType(int8, 1)
                }
            }
        }
    }
}

extern "C" fn resolver(name: *const i8, _: *mut libc::c_void) -> u64 {
    let name = unsafe { CStr::from_ptr(name) };
    panic!("resolver unimplemented: {:?}", name);
}
<|endoftext|>


<commit_before>extern crate uuid;

use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::ops::AddAssign;

use variables::LpExpression::*;
use variables::*;

use self::uuid::Uuid;

/// Enum helping to specify the objective function of the linear problem.
///
/// # Examples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// ```
#[derive(Debug, PartialEq)]
pub enum LpObjective {
    Minimize,
    Maximize,
}

pub trait Problem {
    fn add_objective_expression(&mut self, expr: &LpExpression);
    fn add_constraints(&mut self, expr: &LpConstraint);
}

/// Structure used for creating the model and solving a linear problem.
///
/// # Examples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, Problem, LpProblem};
/// use lp_modeler::operations::{LpOperations};
/// use lp_modeler::variables::LpInteger;
/// use lp_modeler::solvers::{SolverTrait, CbcSolver};
///
/// let ref a = LpInteger::new("a");
/// let ref b = LpInteger::new("b");
/// let ref c = LpInteger::new("c");
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// problem += 10.0 * a + 20.0 * b;
///
/// problem += (500*a + 1200*b + 1500*c).le(10000);
/// problem += (a + b*2 + c).le(10);
/// problem += (a).le(b);
///
/// let solver = CbcSolver::new();
///
/// match solver.run(&problem) {
/// Ok((status, res)) => {
///     println!("Status {:?}", status);
///         for (name, value) in res.iter() {
///             println!("value of {} = {}", name, value);
///         }
///     },
///     Err(msg) => println!("{}", msg),
/// }
/// ```
#[derive(Debug)]
pub struct LpProblem {
    pub name: &'static str,
    pub unique_name: String,
    objective_type: LpObjective,
    obj_expr: Option<LpExpression>,
    constraints: Vec<LpConstraint>,
}

impl LpProblem {
    /// Create a new problem
    pub fn new(name: &'static str, objective: LpObjective) -> LpProblem {
        let unique_name = format!("{}_{}", name, Uuid::new_v4());
        LpProblem {
            name,
            unique_name,
            objective_type: objective,
            obj_expr: None,
            constraints: Vec::new(),
        }
    }

    // TODO: Call once and pass into parameter
    // TODO: Check variables on the objective function
    fn variables(&self) -> HashMap<String, &LpExpression> {
        fn var<'a>(expr: &'a LpExpression, lst: &mut Vec<(String, &'a LpExpression)>) {
            match expr {
                &ConsBin(LpBinary { ref name, .. })
                | &ConsInt(LpInteger { ref name, .. })
                | &ConsCont(LpContinuous { ref name, .. }) => {
                    lst.push((name.clone(), expr));
                }

                &MulExpr(_, ref e) => {
                    var(&*e, lst);
                }
                &AddExpr(ref e1, ref e2) | &SubExpr(ref e1, ref e2) => {
                    var(&*e1, lst);
                    var(&*e2, lst);
                }
                _ => (),
            }
        }

        let mut lst: Vec<_> = Vec::new();
        for e in &self.constraints {
            var(&e.0, &mut lst);
        }
        lst.iter()
            .map(|&(ref n, ref x)| (n.clone(), *x))
            .collect::<HashMap<String, &LpExpression>>()
    }
}

pub trait LpFileFormat {
    fn to_lp_file_format(&self) -> String;
    fn write_lp(&self, file_model: &str) -> std::io::Result<()> {
        let mut buffer = File::create(file_model)?;
        buffer.write(self.to_lp_file_format().as_bytes())?;
        Ok(())
    }
}

fn objective_string(prob: &LpProblem) -> String {
    match prob.obj_expr {
        Some(ref expr) => format!("obj: {}", expr.to_lp_file_format()),
        _ => String::new()
    }
}
fn constraints_string(prob: &LpProblem) -> String {
    let mut res = String::new();
    let mut constraints = prob.constraints.iter();
    let mut index = 1;
    while let Some(ref constraint) = constraints.next() {
        res.push_str(&format!("  c{}: {}\n", index.to_string(), &constraint.to_lp_file_format()));
        index += 1;
    }
    res
}
impl LpFileFormat for LpProblem {

    fn to_lp_file_format(&self) -> String {

        let bounds_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsInt(LpInteger {
                        ref name,
                        lower_bound,
                        upper_bound,
                    })
                    | &ConsCont(LpContinuous {
                        ref name,
                        lower_bound,
                        upper_bound,
                    }) => {
                        if let Some(l) = lower_bound {
                            res.push_str("  ");
                            res.push_str(&l.to_string());
                            res.push_str(" <= ");
                            res.push_str(&name);
                            if let Some(u) = upper_bound {
                                res.push_str(" <= ");
                                res.push_str(&u.to_string());
                            }
                            res.push_str("\n");
                        } else if let Some(u) = upper_bound {
                            res.push_str("  ");
                            res.push_str(&name);
                            res.push_str(" <= ");
                            res.push_str(&u.to_string());
                            res.push_str("\n");
                        } else {
                            match v {
                                &ConsCont(LpContinuous { .. }) => {
                                    res.push_str("  ");
                                    res.push_str(&name);
                                    res.push_str(" free\n");
                                } // TODO: IntegerVar => -INF to INF
                                _ => (),
                            }
                        }
                    }
                    _ => (),
                }
            }
            res
        };

        let integers_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsInt(LpInteger { ref name, .. }) => {
                        res.push_str(name);
                        res.push_str(" ");
                    }
                    _ => (),
                }
            }
            res
        };

        let binaries_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsBin(LpBinary { ref name }) => {
                        res.push_str(name);
                        res.push_str(" ");
                    }
                    _ => (),
                }
            }
            res
        };

        let mut buffer = String::new();

        buffer.push_str("\\ ");
        buffer.push_str(&self.name);
        buffer.push_str("\n\n");

        // Write objectives
        match self.objective_type {
            LpObjective::Maximize => {
                buffer.push_str("Maximize\n  ");
            }
            LpObjective::Minimize => {
                buffer.push_str("Minimize\n  ");
            }
        }
        let obj_str = objective_string(self);
        buffer.push_str(&obj_str);

        // Write constraints
        let cstr_str = constraints_string(self);
        if cstr_str.len() > 0 {
            buffer.push_str("\n\nSubject To\n");
            buffer.push_str(&cstr_str);
        }

        // Write bounds for Integer and Continuous
        let bound_str = bounds_string();
        if bound_str.len() > 0 {
            buffer.push_str("\nBounds\n");
            buffer.push_str(&bound_str);
        }

        // Write Integer vars
        let generals_str = integers_string();
        if generals_str.len() > 0 {
            buffer.push_str("\nGenerals\n  ");
            buffer.push_str(&generals_str);
            buffer.push_str("\n");
        }

        // Write Binaries vars
        let binaries_str = binaries_string();
        if binaries_str.len() > 0 {
            buffer.push_str("\nBinary\n  ");
            buffer.push_str(&binaries_str);
            buffer.push_str("\n");
        }

        buffer.push_str("\nEnd");

        buffer
    }
}

impl Problem for LpProblem {
    fn add_objective_expression(&mut self, expr: &LpExpression) {
        if let Some(e) = self.obj_expr.clone() {
            let (_, simpl_expr) = split_constant_and_expr(&simplify(&AddExpr(
                Box::new(expr.clone()),
                Box::new(e.clone()),
            )));
            self.obj_expr = Some(simpl_expr);
        } else {
            let (_, simpl_expr) = split_constant_and_expr(&simplify(expr));
            self.obj_expr = Some(simpl_expr);
        }
    }

    fn add_constraints(&mut self, expr: &LpConstraint) {
        self.constraints.push(expr.clone());
    }
}

macro_rules! impl_addassign_for_generic_problem {
    ($problem: ty) => {
        /// Add constraints
        impl AddAssign<LpConstraint> for $problem {
            fn add_assign(&mut self, _rhs: LpConstraint) {
                self.add_constraints(&_rhs);
            }
        }
        /// Add an expression as an objective function
        impl<T> AddAssign<T> for $problem
        where
            T: Into<LpExpression>,
        {
            fn add_assign(&mut self, _rhs: T) {
                self.add_objective_expression(&_rhs.into());
            }
        }
    };
}
impl_addassign_for_generic_problem!(LpProblem);
<commit_msg>Reduce bounds_string method size<commit_after>extern crate uuid;

use std;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::ops::AddAssign;

use variables::LpExpression::*;
use variables::*;

use self::uuid::Uuid;

/// Enum helping to specify the objective function of the linear problem.
///
/// # Examples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// ```
#[derive(Debug, PartialEq)]
pub enum LpObjective {
    Minimize,
    Maximize,
}

pub trait Problem {
    fn add_objective_expression(&mut self, expr: &LpExpression);
    fn add_constraints(&mut self, expr: &LpConstraint);
}

/// Structure used for creating the model and solving a linear problem.
///
/// # Examples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, Problem, LpProblem};
/// use lp_modeler::operations::{LpOperations};
/// use lp_modeler::variables::LpInteger;
/// use lp_modeler::solvers::{SolverTrait, CbcSolver};
///
/// let ref a = LpInteger::new("a");
/// let ref b = LpInteger::new("b");
/// let ref c = LpInteger::new("c");
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// problem += 10.0 * a + 20.0 * b;
///
/// problem += (500*a + 1200*b + 1500*c).le(10000);
/// problem += (a + b*2 + c).le(10);
/// problem += (a).le(b);
///
/// let solver = CbcSolver::new();
///
/// match solver.run(&problem) {
/// Ok((status, res)) => {
///     println!("Status {:?}", status);
///         for (name, value) in res.iter() {
///             println!("value of {} = {}", name, value);
///         }
///     },
///     Err(msg) => println!("{}", msg),
/// }
/// ```
#[derive(Debug)]
pub struct LpProblem {
    pub name: &'static str,
    pub unique_name: String,
    objective_type: LpObjective,
    obj_expr: Option<LpExpression>,
    constraints: Vec<LpConstraint>,
}

impl LpProblem {
    /// Create a new problem
    pub fn new(name: &'static str, objective: LpObjective) -> LpProblem {
        let unique_name = format!("{}_{}", name, Uuid::new_v4());
        LpProblem {
            name,
            unique_name,
            objective_type: objective,
            obj_expr: None,
            constraints: Vec::new(),
        }
    }

    // TODO: Call once and pass into parameter
    // TODO: Check variables on the objective function
    fn variables(&self) -> HashMap<String, &LpExpression> {
        fn var<'a>(expr: &'a LpExpression, lst: &mut Vec<(String, &'a LpExpression)>) {
            match expr {
                &ConsBin(LpBinary { ref name, .. })
                | &ConsInt(LpInteger { ref name, .. })
                | &ConsCont(LpContinuous { ref name, .. }) => {
                    lst.push((name.clone(), expr));
                }

                &MulExpr(_, ref e) => {
                    var(&*e, lst);
                }
                &AddExpr(ref e1, ref e2) | &SubExpr(ref e1, ref e2) => {
                    var(&*e1, lst);
                    var(&*e2, lst);
                }
                _ => (),
            }
        }

        let mut lst: Vec<_> = Vec::new();
        for e in &self.constraints {
            var(&e.0, &mut lst);
        }
        lst.iter()
            .map(|&(ref n, ref x)| (n.clone(), *x))
            .collect::<HashMap<String, &LpExpression>>()
    }
}

pub trait LpFileFormat {
    fn to_lp_file_format(&self) -> String;
    fn write_lp(&self, file_model: &str) -> std::io::Result<()> {
        let mut buffer = File::create(file_model)?;
        buffer.write(self.to_lp_file_format().as_bytes())?;
        Ok(())
    }
}

fn objective_string(prob: &LpProblem) -> String {
    match prob.obj_expr {
        Some(ref expr) => format!("obj: {}", expr.to_lp_file_format()),
        _ => String::new()
    }
}
fn constraints_string(prob: &LpProblem) -> String {
    let mut res = String::new();
    let mut constraints = prob.constraints.iter();
    let mut index = 1;
    while let Some(ref constraint) = constraints.next() {
        res.push_str(&format!("  c{}: {}\n", index.to_string(), &constraint.to_lp_file_format()));
        index += 1;
    }
    res
}

impl LpFileFormat for LpProblem {

    fn to_lp_file_format(&self) -> String {
        let bounds_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsInt(LpInteger {
                        ref name,
                        lower_bound,
                        upper_bound,
                    })
                    | &ConsCont(LpContinuous {
                        ref name,
                        lower_bound,
                        upper_bound,
                    }) => {
                        if let Some(l) = lower_bound {
                            res.push_str(&format!("  {} <= {}", &l.to_string(), &name));
                            if let Some(u) = upper_bound {
                                res.push_str(&format!(" <= {}", &u.to_string()));
                            }
                            res.push_str("\n");
                        } else if let Some(u) = upper_bound {
                            res.push_str(&format!("  {} <= {}\n", &name, &u.to_string()));
                        } else {
                            match v {
                                &ConsCont(LpContinuous { .. }) => {
                                    res.push_str(&format!("  {} free\n", &name));
                                } // TODO: IntegerVar => -INF to INF
                                _ => (),
                            }
                        }
                    }
                    _ => (),
                }
            }
            res
        };

        let integers_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsInt(LpInteger { ref name, .. }) => {
                        res.push_str(name);
                        res.push_str(" ");
                    }
                    _ => (),
                }
            }
            res
        };

        let binaries_string = || {
            let mut res = String::new();
            for (_, v) in self.variables() {
                match v {
                    &ConsBin(LpBinary { ref name }) => {
                        res.push_str(name);
                        res.push_str(" ");
                    }
                    _ => (),
                }
            }
            res
        };

        let mut buffer = String::new();

        buffer.push_str("\\ ");
        buffer.push_str(&self.name);
        buffer.push_str("\n\n");

        // Write objectives
        match self.objective_type {
            LpObjective::Maximize => {
                buffer.push_str("Maximize\n  ");
            }
            LpObjective::Minimize => {
                buffer.push_str("Minimize\n  ");
            }
        }
        let obj_str = objective_string(self);
        buffer.push_str(&obj_str);

        // Write constraints
        let cstr_str = constraints_string(self);
        if cstr_str.len() > 0 {
            buffer.push_str("\n\nSubject To\n");
            buffer.push_str(&cstr_str);
        }

        // Write bounds for Integer and Continuous
        let bound_str = bounds_string();
        if bound_str.len() > 0 {
            buffer.push_str("\nBounds\n");
            buffer.push_str(&bound_str);
        }

        // Write Integer vars
        let generals_str = integers_string();
        if generals_str.len() > 0 {
            buffer.push_str("\nGenerals\n  ");
            buffer.push_str(&generals_str);
            buffer.push_str("\n");
        }

        // Write Binaries vars
        let binaries_str = binaries_string();
        if binaries_str.len() > 0 {
            buffer.push_str("\nBinary\n  ");
            buffer.push_str(&binaries_str);
            buffer.push_str("\n");
        }

        buffer.push_str("\nEnd");

        buffer
    }
}

impl Problem for LpProblem {
    fn add_objective_expression(&mut self, expr: &LpExpression) {
        if let Some(e) = self.obj_expr.clone() {
            let (_, simpl_expr) = split_constant_and_expr(&simplify(&AddExpr(
                Box::new(expr.clone()),
                Box::new(e.clone()),
            )));
            self.obj_expr = Some(simpl_expr);
        } else {
            let (_, simpl_expr) = split_constant_and_expr(&simplify(expr));
            self.obj_expr = Some(simpl_expr);
        }
    }

    fn add_constraints(&mut self, expr: &LpConstraint) {
        self.constraints.push(expr.clone());
    }
}

macro_rules! impl_addassign_for_generic_problem {
    ($problem: ty) => {
        /// Add constraints
        impl AddAssign<LpConstraint> for $problem {
            fn add_assign(&mut self, _rhs: LpConstraint) {
                self.add_constraints(&_rhs);
            }
        }
        /// Add an expression as an objective function
        impl<T> AddAssign<T> for $problem
        where
            T: Into<LpExpression>,
        {
            fn add_assign(&mut self, _rhs: T) {
                self.add_objective_expression(&_rhs.into());
            }
        }
    };
}
impl_addassign_for_generic_problem!(LpProblem);
<|endoftext|>


<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => {
                            let jump_report = self.start_jump_forward(vm, index);
                            index = jump_report.0;
                            self.status = jump_report.1;
                        }
                        Command::JumpBackward => index = self.jump_backward(&vm, index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }
                self.instruction_pointer = Some(index);
            }

            (Some(index), ProgramStatus::Seeking(goal_depth)) => {
                let (new_index, new_status) = self.seek_forward(index, goal_depth);
                self.instruction_pointer = Some(new_index);
                self.status = new_status;
                self.execute(vm);
            }

            _ => {}
        }
    }

    fn jump_backward(&mut self, vm: &VM, starting_index: usize) -> usize {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                return starting_index + 1;
            }
            _ => {
                let goal_depth = self.current_depth;
                for index in (0..starting_index).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                return index + 1;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize, goal_depth: u64) -> (usize, ProgramStatus) {
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth += 1,
                Command::JumpBackward => self.current_depth -= 1,
                _ => {}
            }
            if self.current_depth == goal_depth {
                return (index + 1, ProgramStatus::Normal);
            }
        }
        return (self.instructions.len(), ProgramStatus::Seeking(goal_depth));
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> (usize, ProgramStatus) {
        match vm.cells[vm.data_pointer] {
            0 => {
                let goal_depth = self.current_depth;
                return self.seek_forward(index, goal_depth);
            }
            _ => {
                self.current_depth += 1;
                return (index + 1, ProgramStatus::Normal);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: Some(0),
            current_depth: 0,
            status: ProgramStatus::Normal,
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let actual = program.seek_forward(0, 0);
        let expected = (4, ProgramStatus::Normal);
        assert_eq!(actual, expected);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut program = Program {
            instruction_pointer: Some(0),
            current_depth: 0,
            status: ProgramStatus::Normal,
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        let actual = program.seek_forward(0, 0);
        let expected = (3, ProgramStatus::Seeking(0));
        assert_eq!(actual, expected);
    }
}
<commit_msg>Change function names to better demonstrate intent<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions: Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new() -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() {
            self.instruction_pointer = Some(0);
        }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => {
                            let jump_report = self.handle_jump_forward(vm, index);
                            index = jump_report.0;
                            self.status = jump_report.1;
                        }
                        Command::JumpBackward => index = self.handle_jump_backward(&vm, index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                }
                self.instruction_pointer = Some(index);
            }

            (Some(index), ProgramStatus::Seeking(goal_depth)) => {
                let (new_index, new_status) = self.seek_forward(index, goal_depth);
                self.instruction_pointer = Some(new_index);
                self.status = new_status;
                self.execute(vm);
            }

            _ => {}
        }
    }

    fn handle_jump_backward(&mut self, vm: &VM, starting_index: usize) -> usize {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth -= 1;
                return starting_index + 1;
            }
            _ => {
                let goal_depth = self.current_depth;
                for index in (0..starting_index).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth {
                                return index + 1;
                            }
                            self.current_depth -= 1;
                        }
                        _ => {}
                    }
                }
                panic!("No starting brace found!");
            }
        }
    }

    fn seek_forward(&mut self, starting_index: usize, goal_depth: u64) -> (usize, ProgramStatus) {
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth += 1,
                Command::JumpBackward => self.current_depth -= 1,
                _ => {}
            }
            if self.current_depth == goal_depth {
                return (index + 1, ProgramStatus::Normal);
            }
        }
        return (self.instructions.len(), ProgramStatus::Seeking(goal_depth));
    }

    fn handle_jump_forward(&mut self, vm: &VM, index: usize) -> (usize, ProgramStatus) {
        match vm.cells[vm.data_pointer] {
            0 => {
                let goal_depth = self.current_depth;
                return self.seek_forward(index, goal_depth);
            }
            _ => {
                self.current_depth += 1;
                return (index + 1, ProgramStatus::Normal);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    pub fn test_seek_forward_normal() {
        let mut program = Program {
            instruction_pointer: Some(0),
            current_depth: 0,
            status: ProgramStatus::Normal,
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
                Command::JumpBackward,
            ],
        };
        let actual = program.seek_forward(0, 0);
        let expected = (4, ProgramStatus::Normal);
        assert_eq!(actual, expected);
    }

    #[test]
    pub fn test_seek_forward_seeking() {
        let mut program = Program {
            instruction_pointer: Some(0),
            current_depth: 0,
            status: ProgramStatus::Normal,
            instructions: vec![
                Command::JumpForward,
                Command::JumpForward,
                Command::JumpBackward,
            ],
        };
        let actual = program.seek_forward(0, 0);
        let expected = (3, ProgramStatus::Seeking(0));
        assert_eq!(actual, expected);
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::ffi::CString;
use super::gl;
use super::gl_lib::types::*;
use super::GLError;
use super::Shader;

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub unsafe fn from_id(id: GLuint) -> Program {
        Program { gl_id: id }
    }

    pub fn attach_shader(&mut self, shader: &Shader) {
        unsafe {
            gl::AttachShader(self.gl_id, shader.gl_id());
        }
    }

    pub fn link(&self) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(self.gl_id);
            let mut link_status : GLint = mem::uninitialized();
            gl::GetProgramiv(self.gl_id,
                             gl::LINK_STATUS,
                             &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            unsafe {
                let mut info_length : GLint = mem::uninitialized();
                gl::GetProgramiv(self.gl_id,
                                 gl::INFO_LOG_LENGTH,
                                 &mut info_length as *mut GLint);

                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(self.gl_id,
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                bytes.set_len((info_length - 1) as usize);

                let msg = String::from_utf8(bytes)
                                 .unwrap_or(String::from("<Unknown error>"));

                Err(GLError { message: msg })
            }
        }
    }

    pub fn get_attrib_location(&self, name: &str) -> Result<ProgramAttrib, ()> {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(self.gl_id, str_ptr);
            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}
<commit_msg>Add `Program.use_program`<commit_after>use std::mem;
use std::ptr;
use std::ffi::CString;
use super::gl;
use super::gl_lib::types::*;
use super::GLError;
use super::Shader;

pub struct Program {
    gl_id: GLuint
}

impl Program {
    pub unsafe fn from_id(id: GLuint) -> Program {
        Program { gl_id: id }
    }

    pub fn attach_shader(&mut self, shader: &Shader) {
        unsafe {
            gl::AttachShader(self.gl_id, shader.gl_id());
        }
    }

    pub fn link(&self) -> Result<(), GLError> {
        let success = unsafe {
            gl::LinkProgram(self.gl_id);
            let mut link_status : GLint = mem::uninitialized();
            gl::GetProgramiv(self.gl_id,
                             gl::LINK_STATUS,
                             &mut link_status as *mut GLint);

            link_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            unsafe {
                let mut info_length : GLint = mem::uninitialized();
                gl::GetProgramiv(self.gl_id,
                                 gl::INFO_LOG_LENGTH,
                                 &mut info_length as *mut GLint);

                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetProgramInfoLog(self.gl_id,
                                      info_length,
                                      ptr::null_mut(),
                                      bytes.as_mut_ptr() as *mut GLchar);
                bytes.set_len((info_length - 1) as usize);

                let msg = String::from_utf8(bytes)
                                 .unwrap_or(String::from("<Unknown error>"));

                Err(GLError { message: msg })
            }
        }
    }

    pub fn get_attrib_location(&self, name: &str) -> Result<ProgramAttrib, ()> {
        let c_str = try!(CString::new(name).or(Err(())));
        let str_ptr = c_str.as_ptr() as *const GLchar;
        unsafe {
            let index = gl::GetAttribLocation(self.gl_id, str_ptr);
            if index >= 0 {
                Ok(ProgramAttrib { gl_index: index as GLuint })
            }
            else {
                Err(())
            }
        }
    }

    pub fn use_program(&self) {
        unsafe {
            gl::UseProgram(self.gl_id)
        }
    }
}

impl Drop for Program {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteProgram(self.gl_id);
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ProgramAttrib {
    pub gl_index: GLuint
}
<|endoftext|>