<commit_before>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::ioivec;
import std::ioivec::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

/*
// Imports for "the rest of driver::compile_input"
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn read_whole_file(&str filename) -> str {
    str::unsafe_from_bytes_ivec(ioivec::file_reader(filename).read_whole_stream())
}

fn write_file(&str filename, &str content) {
    ioivec::file_writer(filename, ~[ioivec::create,
                                    ioivec::truncate]).write_str(content);
}

fn file_contains(&str filename, &str needle) -> bool {
    auto contents = read_whole_file(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(&str haystack, &str needle) -> bool { str::find(haystack, needle) != -1 }

fn find_rust_files(&mutable str[] files, str path) {
    if (str::ends_with(path, ".rs")) {
        if (file_contains(path, "xfail-stage1")) {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else {
            files += ~[path];
        }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
        for (str p in fs::list_dir(path)) {
            find_rust_files(files, p);
        }
    }
}

fn safe_to_steal(ast::expr_ e) -> bool {
    alt (e) {
        // pretty-printer precedence issues -- https://github.com/graydon/rust/issues/670
        case (ast::expr_unary(_, _)) { false }
        case (ast::expr_lit(?lit)) {
            alt(lit.node) {
                case(ast::lit_str(_, _)) { true }
                case(ast::lit_char(_)) { true }
                case(ast::lit_int(_)) { false }
                case(ast::lit_uint(_)) { false }
                case(ast::lit_mach_int(_, _)) { false }
                case(ast::lit_float(_)) { false }
                case(ast::lit_mach_float(_, _)) { false }
                case(ast::lit_nil) { true }
                case(ast::lit_bool(_)) { true }
            }
        }
        case (ast::expr_cast(_, _)) { false }
        case (ast::expr_send(_, _)) { false }
        case (ast::expr_recv(_, _)) { false }
        case (ast::expr_assert(_)) { false }
        case (ast::expr_binary(_, _, _)) { false }
        case (ast::expr_assign(_, _)) { false }
        case (ast::expr_assign_op(_, _, _)) { false }

        // https://github.com/graydon/rust/issues/676
        case (ast::expr_ret(option::none)) { false }
        case (ast::expr_put(option::none)) { false }

        case (_) { true }
    }
}

fn steal_exprs(&ast::crate crate) -> ast::expr[] {
    let @mutable ast::expr[] exprs = @mutable ~[];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { if (safe_to_steal(e.node)) { *es += ~[*e]; } else { /* now my indices are wrong :( */ } }
    auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
    alt (e) {
        case (ast::expr_if(_, _, _)) { false }
        case (ast::expr_block(_)) { false }
        case (_) { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
    let @mutable uint j = @mutable 0u;
    fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
      *j_ += 1u;
      if (i_ + 1u == *j_ && safe_to_replace(original)) {
        newexpr_
      } else {
        fold::noop_fold_expr(original, fld)
      }
    }
    auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
    auto af = fold::make_fold(afp);
    let @ast::crate crate2 = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> ioivec::writer { std::ioivec::string_writer().get_writer() }

fn as_str(fn (ioivec::writer) f) -> str { auto w = std::ioivec::string_writer(); f(w.get_writer()); w.get_str() }

/*
fn pp_variants(&ast::crate crate, &codemap::codemap cmap, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                check_roundtrip(crate2, cmap, filename + ".4.rs");
            }
        }
    }
}
*/

fn check_roundtrip(@ast::crate cr1, &codemap::codemap cm1, &str filename, &str str1) {
    auto str2 = as_str(bind pprust::print_crate(cm1, cr1, filename,
                                                ioivec::string_reader(str1), _,
                                                pprust::no_ann()));
    if (true
      && !contains(str2, "][]") // https://github.com/graydon/rust/issues/669
      && !contains(str2, "][mutable]") // https://github.com/graydon/rust/issues/669
      && !contains(str2, "][mutable ]") // https://github.com/graydon/rust/issues/669
      && !contains(str2, "self") // crazy rules enforced by parser rather than typechecker?
      && !contains(str2, "spawn") // more precedence issues
      && !contains(str2, "bind") // more precedence issues?
       ) {
        auto cm2 = codemap::new_codemap();
        auto cr2 = parser::parse_crate_from_source_str(filename, str2, ~[], cm2);
        // should compare crates at this point, but it's easier to compare strings
        auto str3 = as_str(bind pprust::print_crate(cm2, cr2, filename, ioivec::string_reader(str2),
                                                    _, pprust::no_ann()));
        if (!str::is_ascii(str3)) {
          log_err "Non-ASCII in " + filename; // why does non-ASCII work correctly with "rustc --pretty normal" but not here???
        } else if (str2 != str3) {
            write_file("round-trip-a.rs", str2);
            write_file("round-trip-b.rs", str3);
            std::run::run_program("kdiff3", ["round-trip-a.rs", "round-trip-b.rs"]);
            fail "Mismatch";
        }
   }
}

fn main(vec[str] args) {
    if (vec::len(args) != 2u) {
        log_err #fmt("usage: %s <testdir>", args.(0));
        ret;
    }
    auto files = ~[];
    auto root = args.(1);
    find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
    log_err #fmt("%u files", ivec::len(files));

    for (str file in files) {
        log_err "=== " + file + " ===";
        auto cm1 = codemap::new_codemap();
        auto str1 = read_whole_file(file);
        auto cr1 = parser::parse_crate_from_source_str(file, str1, ~[], cm1);
        if (!contains(str1, "#macro") // https://github.com/graydon/rust/issues/671
         && !str::ends_with(file, "block-expr-precedence.rs") // https://github.com/graydon/rust/issues/674
         && !str::ends_with(file, "syntax-extension-fmt.rs") // an issue where -2147483648 gains an extra negative sign each time through, which i can't reproduce using "rustc --pretty normal"???
) {
            check_roundtrip(cr1, cm1, file, str1);
        }
        //pprust::print_crate(cm, crate, file, devnull(), pprust::no_ann());
        // Currently hits https://github.com/graydon/rust/issues/675
        //pp_variants(*crate, cm, file);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Tidy fuzzer a bit.<commit_after>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::ioivec;
import std::ioivec::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

/*
// Imports for "the rest of driver::compile_input"
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn read_whole_file(&str filename) -> str {
    str::unsafe_from_bytes_ivec(ioivec::file_reader(filename).read_whole_stream())
}

fn write_file(&str filename, &str content) {
    ioivec::file_writer(filename, ~[ioivec::create,
                                    ioivec::truncate]).write_str(content);
}

fn file_contains(&str filename, &str needle) -> bool {
    auto contents = read_whole_file(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(&str haystack, &str needle) -> bool { str::find(haystack, needle) != -1 }

fn find_rust_files(&mutable str[] files, str path) {
    if (str::ends_with(path, ".rs")) {
        if (file_contains(path, "xfail-stage1")) {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else {
            files += ~[path];
        }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
        for (str p in fs::list_dir(path)) {
            find_rust_files(files, p);
        }
    }
}

fn safe_to_steal(ast::expr_ e) -> bool {
    alt (e) {
        // pretty-printer precedence issues -- https://github.com/graydon/rust/issues/670
        case (ast::expr_unary(_, _)) { false }
        case (ast::expr_lit(?lit)) {
            alt(lit.node) {
                case(ast::lit_str(_, _)) { true }
                case(ast::lit_char(_)) { true }
                case(ast::lit_int(_)) { false }
                case(ast::lit_uint(_)) { false }
                case(ast::lit_mach_int(_, _)) { false }
                case(ast::lit_float(_)) { false }
                case(ast::lit_mach_float(_, _)) { false }
                case(ast::lit_nil) { true }
                case(ast::lit_bool(_)) { true }
            }
        }
        case (ast::expr_cast(_, _)) { false }
        case (ast::expr_send(_, _)) { false }
        case (ast::expr_recv(_, _)) { false }
        case (ast::expr_assert(_)) { false }
        case (ast::expr_binary(_, _, _)) { false }
        case (ast::expr_assign(_, _)) { false }
        case (ast::expr_assign_op(_, _, _)) { false }

        // https://github.com/graydon/rust/issues/676
        case (ast::expr_ret(option::none)) { false }
        case (ast::expr_put(option::none)) { false }

        case (_) { true }
    }
}

fn steal_exprs(&ast::crate crate) -> ast::expr[] {
    let @mutable ast::expr[] exprs = @mutable ~[];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { if (safe_to_steal(e.node)) { *es += ~[*e]; } else { /* now my indices are wrong :( */ } }
    auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
    alt (e) {
        case (ast::expr_if(_, _, _)) { false }
        case (ast::expr_block(_)) { false }
        case (_) { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
    let @mutable uint j = @mutable 0u;
    fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
      *j_ += 1u;
      if (i_ + 1u == *j_ && safe_to_replace(original)) {
        newexpr_
      } else {
        fold::noop_fold_expr(original, fld)
      }
    }
    auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
    auto af = fold::make_fold(afp);
    let @ast::crate crate2 = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> ioivec::writer { std::ioivec::string_writer().get_writer() }

fn as_str(fn (ioivec::writer) f) -> str {
    auto w = std::ioivec::string_writer();
    f(w.get_writer());
    ret w.get_str();
}

/*
fn pp_variants(&ast::crate crate, &codemap::codemap cmap, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                check_roundtrip(crate2, cmap, filename + ".4.rs");
            }
        }
    }
}
*/

fn parse_and_print(&str code) -> str {
    auto filename = "";
    auto codemap = codemap::new_codemap();
    auto crate = parser::parse_crate_from_source_str(filename, code, ~[], codemap);
    ret as_str(bind pprust::print_crate(codemap, crate, filename,
                                        ioivec::string_reader(code),
                                        _, pprust::no_ann()));
}

fn content_is_confusing(&str code) -> bool {
    auto confusing_patterns = [
        "#macro",      // https://github.com/graydon/rust/issues/671
        "][]",         // https://github.com/graydon/rust/issues/669
        "][mutable]",  // https://github.com/graydon/rust/issues/669
        "][mutable ]", // https://github.com/graydon/rust/issues/669
        "self",        // crazy rules enforced by parser rather than typechecker?
        "spawn",       // more precedence issues
        "bind"         // more precedence issues?
    ];

    for (str p in confusing_patterns) {
        if contains(code, p) {
            ret true;
        }
    }
    ret false;
}

fn file_is_confusing(&str filename) -> bool {
    auto confusing_files = [

        "block-expr-precedence.rs",  // https://github.com/graydon/rust/issues/674

        "syntax-extension-fmt.rs"    // an issue where -2147483648 gains an
                                     // extra negative sign each time through,
                                     // which i can't reproduce using "rustc
                                     // --pretty normal"???
    ];

    for (str f in confusing_files) {
        if contains(filename, f) {
            ret true;
        }
    }

    ret false;
}

fn check_roundtrip_convergence(&str code) {

    auto i = 0;
    auto new = code;
    auto old = code;

    while (i < 10) {
        old = new;
        new = parse_and_print(old);
        i += 1;
        log_err #fmt("cycle %d", i);
    }

    if old != new {
        write_file("round-trip-a.rs", old);
        write_file("round-trip-b.rs", new);
        std::run::run_program("kdiff3", ["round-trip-a.rs", "round-trip-b.rs"]);
        fail "Mismatch";
    }
}

fn main(vec[str] args) {
    if (vec::len(args) != 2u) {
        log_err #fmt("usage: %s <testdir>", args.(0));
        ret;
    }
    auto files = ~[];
    auto root = args.(1);
    find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
    log_err #fmt("%u files", ivec::len(files));

    for (str file in files) {
        log_err "=== " + file + " ===";
        if ! file_is_confusing(file) {
            auto s = read_whole_file(file);
            if ! content_is_confusing(s) {
                check_roundtrip_convergence(s);
            }
        }

        //pprust::print_crate(cm, crate, file, devnull(), pprust::no_ann());
        // Currently hits https://github.com/graydon/rust/issues/675
        //pp_variants(*crate, cm, file);
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use gl;
use gl::types::*;
use gl_context::*;
use shader::*;
use std::cell::RefCell;
use std::mem;
use std::ptr;
use std::rc::Rc;
use vertex;

// TODO: Don't 1-1 vertex array objects with vertex buffers

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case)]
pub fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  name.with_c_str(|ptr| unsafe { gl::GetAttribLocation(shader_program, ptr) })
}

/// Ensures a slice has a given power-of-two alignment, and converts it to a raw pointer.
unsafe fn aligned_slice_to_ptr<T, U>(vs: &[T], alignment: uint) -> *const U {
  let ptr: *const U = mem::transmute(vs.as_ptr());
  assert!(
    (ptr as uint & (alignment - 1) == 0),
    "0x{:x} not {}-aligned",
    ptr as uint,
    alignment
  );
  ptr
}

/// Fixed-size VRAM buffer for individual bytes.
pub struct GLByteBuffer {
  pub gl_id: u32,
  /// number of bytes in the buffer.
  pub length: uint,
  /// maximum number of bytes in the buffer.
  pub capacity: uint,
}

impl GLByteBuffer {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new(capacity: uint) -> GLByteBuffer {
    let mut gl_id = 0;

    unsafe {
      gl::GenBuffers(1, &mut gl_id);
    }

    assert!(gl_id != 0);

    gl::BindBuffer(gl::ARRAY_BUFFER, gl_id);

    unsafe {
      gl::BufferData(
        gl::ARRAY_BUFFER,
        capacity as GLsizeiptr,
        ptr::null(),
        gl::DYNAMIC_DRAW,
      );
    }

    match gl::GetError() {
      gl::NO_ERROR => {},
      gl::OUT_OF_MEMORY => fail!("Out of VRAM"),
      err => fail!("OpenGL error 0x{:x}", err),
    }

    GLByteBuffer {
      gl_id: gl_id,
      length: 0,
      capacity: capacity,
    }
  }

  /// Add more data into this buffer.
  pub unsafe fn push(&mut self, vs: *const u8, count: uint) {
    assert!(
      self.length + count <= self.capacity,
      "GLByteBuffer::push {} into a {}/{} full GLByteBuffer",
      count,
      self.length,
      self.capacity
    );

    self.update_inner(self.length, vs, count);
    self.length += count;
  }

  pub fn swap_remove(&mut self, i: uint, count: uint) {
    assert!(count <= self.length);
    self.length -= count;
    assert!(i <= self.length);

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }

    // In the `i == self.length` case, we don't bother with the swap;
    // decreasing `self.length` is enough.

    if i < self.length {
      assert!(
        i <= self.length - count,
        "GLByteBuffer::swap_remove would cause copy in overlapping regions"
      );

      gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_id);

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }

      gl::CopyBufferSubData(
        gl::ARRAY_BUFFER,
        gl::ARRAY_BUFFER,
        self.length as i64,
        i as i64,
        count as i64,
      );

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }
    }
  }

  pub unsafe fn update(&self, idx: uint, vs: *const u8, count: uint) {
    assert!(idx + count <= self.length);
    self.update_inner(idx, vs, count);
  }

  unsafe fn update_inner(&self, idx: uint, vs: *const u8, count: uint) {
    assert!(idx + count <= self.capacity);

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x} in GLByteBuffer::update", err),
    }

    gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_id);

    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      idx as i64,
      count as i64,
      mem::transmute(vs)
    );

    gl::Finish();

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x} in GLByteBuffer::update_inner", err),
    }
  }
}

#[unsafe_destructor]
impl Drop for GLByteBuffer {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.gl_id);
    }
  }
}

/// Fixed-size typed VRAM buffer, optimized for bulk inserts.
pub struct GLBuffer<T> {
  pub byte_buffer: GLByteBuffer,
  pub length: uint,
}

impl<T> GLBuffer<T> {
  pub fn new(capacity: uint) -> GLBuffer<T> {
    GLBuffer {
      byte_buffer: GLByteBuffer::new(capacity * mem::size_of::<T>()),
      length: 0,
    }
  }

  pub fn push(&mut self, vs: &[T]) {
    unsafe {
      self.byte_buffer.push(
        aligned_slice_to_ptr(vs, 4),
        mem::size_of::<T>() * vs.len()
      );
    }
    self.length += vs.len();
  }

  pub fn update(&mut self, idx: uint, vs: &[T]) {
    unsafe {
      self.byte_buffer.update(
        mem::size_of::<T>() * idx,
        aligned_slice_to_ptr(vs, 4),
        mem::size_of::<T>() * vs.len(),
      );
    }
  }

  pub fn swap_remove(&mut self, idx: uint, count: uint) {
    self.byte_buffer.swap_remove(
      mem::size_of::<T>() * idx,
      mem::size_of::<T>() * count,
    );
    self.length -= count;
  }
}

pub enum DrawMode {
  Lines,
  Triangles,
  Points,
}

impl DrawMode {
  fn to_enum(&self) -> GLenum {
    match self {
      &Lines     => gl::LINES,
      &Triangles => gl::TRIANGLES,
      &Points    => gl::POINTS,
    }
  }
}

/// A fixed-capacity array of bytes passed to OpenGL.
pub struct GLArray<T> {
  pub buffer: GLBuffer<T>,
  pub gl_id: u32,
  /// How to draw this buffer. Ex: gl::LINES, gl::TRIANGLES, etc.
  pub mode: GLenum,
  /// size of T in vertices
  pub attrib_span: uint,
  /// length in vertices
  pub length: uint,
}

impl<T> GLArray<T> {
  #[inline]
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new(
    _gl: &GLContext,
    shader_program: Rc<RefCell<Shader>>,
    attribs: &[vertex::AttribData],
    mode: DrawMode,
    buffer: GLBuffer<T>,
  ) -> GLArray<T> {
    let mut gl_id = 0;

    // TODO(cgaebel): Error checking?

    unsafe {
      gl::GenVertexArrays(1, &mut gl_id);
    }

    gl::BindVertexArray(gl_id);

    let mut offset = 0;
    let attrib_span = {
      let mut attrib_span = 0;
      for attrib in attribs.iter() {
        attrib_span += attrib.size * attrib.unit.size();
      }
      attrib_span
    };
    for attrib in attribs.iter() {
      let shader_attrib = glGetAttribLocation(shader_program.deref().borrow().id, attrib.name) as GLuint;
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);

      gl::EnableVertexAttribArray(shader_attrib);
      unsafe {
        if attrib.unit.is_integral() {
          gl::VertexAttribIPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        } else {
          gl::VertexAttribPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            gl::FALSE as GLboolean,
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        }
      }
      offset += (attrib.size * attrib.unit.size()) as int;
    }

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x}", err),
    }

    assert!(mem::size_of::<T>() % attrib_span == 0);

    GLArray {
      buffer: buffer,
      gl_id: gl_id,
      mode: mode.to_enum(),
      attrib_span: mem::size_of::<T>() / attrib_span,
      length: 0,
    }
  }

  pub fn push(&mut self, vs: &[T]) {
    self.buffer.push(vs);
    self.length += vs.len() * self.attrib_span;
  }

  pub fn swap_remove(&mut self, idx: uint, count: uint) {
    self.buffer.swap_remove(idx, count);
    self.length -= count * self.attrib_span;
  }

  #[inline]
  /// Draws all the queued triangles to the screen.
  pub fn draw(&self, gl: &GLContext) {
    self.draw_slice(gl, 0, self.buffer.length);
  }

  /// Draw some subset of the triangle array.
  pub fn draw_slice(&self, _gl: &GLContext, start: uint, len: uint) {
    assert!(start + len <= self.length);

    gl::BindVertexArray(self.gl_id);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer.byte_buffer.gl_id);

    gl::DrawArrays(self.mode, (start * self.attrib_span) as i32, (len * self.attrib_span) as i32);

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x}", err),
    }
  }
}

#[unsafe_destructor]
impl<T> Drop for GLArray<T> {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteVertexArrays(1, &self.gl_id);
    }
  }
}
<commit_msg>stop GLByteBuffer from checking pointer alignment<commit_after>use gl;
use gl::types::*;
use gl_context::*;
use shader::*;
use std::cell::RefCell;
use std::mem;
use std::ptr;
use std::rc::Rc;
use vertex;

// TODO: Don't 1-1 vertex array objects with vertex buffers

/// Gets the id number for a given input of the shader program.
#[allow(non_snake_case)]
pub fn glGetAttribLocation(shader_program: GLuint, name: &str) -> GLint {
  name.with_c_str(|ptr| unsafe { gl::GetAttribLocation(shader_program, ptr) })
}

/// Fixed-size VRAM buffer for individual bytes.
pub struct GLByteBuffer {
  pub gl_id: u32,
  /// number of bytes in the buffer.
  pub length: uint,
  /// maximum number of bytes in the buffer.
  pub capacity: uint,
}

impl GLByteBuffer {
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new(capacity: uint) -> GLByteBuffer {
    let mut gl_id = 0;

    unsafe {
      gl::GenBuffers(1, &mut gl_id);
    }

    assert!(gl_id != 0);

    gl::BindBuffer(gl::ARRAY_BUFFER, gl_id);

    unsafe {
      gl::BufferData(
        gl::ARRAY_BUFFER,
        capacity as GLsizeiptr,
        ptr::null(),
        gl::DYNAMIC_DRAW,
      );
    }

    match gl::GetError() {
      gl::NO_ERROR => {},
      gl::OUT_OF_MEMORY => fail!("Out of VRAM"),
      err => fail!("OpenGL error 0x{:x}", err),
    }

    GLByteBuffer {
      gl_id: gl_id,
      length: 0,
      capacity: capacity,
    }
  }

  /// Add more data into this buffer.
  pub unsafe fn push(&mut self, vs: *const u8, count: uint) {
    assert!(
      self.length + count <= self.capacity,
      "GLByteBuffer::push {} into a {}/{} full GLByteBuffer",
      count,
      self.length,
      self.capacity
    );

    self.update_inner(self.length, vs, count);
    self.length += count;
  }

  pub fn swap_remove(&mut self, i: uint, count: uint) {
    assert!(count <= self.length);
    self.length -= count;
    assert!(i <= self.length);

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }

    // In the `i == self.length` case, we don't bother with the swap;
    // decreasing `self.length` is enough.

    if i < self.length {
      assert!(
        i <= self.length - count,
        "GLByteBuffer::swap_remove would cause copy in overlapping regions"
      );

      gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_id);

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }

      gl::CopyBufferSubData(
        gl::ARRAY_BUFFER,
        gl::ARRAY_BUFFER,
        self.length as i64,
        i as i64,
        count as i64,
      );

      match gl::GetError() {
        gl::NO_ERROR => {},
        err => fail!("OpenGL error 0x{:x} in update", err),
      }
    }
  }

  pub unsafe fn update(&self, idx: uint, vs: *const u8, count: uint) {
    assert!(idx + count <= self.length);
    self.update_inner(idx, vs, count);
  }

  unsafe fn update_inner(&self, idx: uint, vs: *const u8, count: uint) {
    assert!(idx + count <= self.capacity);

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x} in GLByteBuffer::update", err),
    }

    gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_id);

    gl::BufferSubData(
      gl::ARRAY_BUFFER,
      idx as i64,
      count as i64,
      mem::transmute(vs)
    );

    gl::Finish();

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x} in GLByteBuffer::update_inner", err),
    }
  }
}

#[unsafe_destructor]
impl Drop for GLByteBuffer {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.gl_id);
    }
  }
}

/// Fixed-size typed VRAM buffer, optimized for bulk inserts.
pub struct GLBuffer<T> {
  pub byte_buffer: GLByteBuffer,
  pub length: uint,
}

impl<T> GLBuffer<T> {
  pub fn new(capacity: uint) -> GLBuffer<T> {
    GLBuffer {
      byte_buffer: GLByteBuffer::new(capacity * mem::size_of::<T>()),
      length: 0,
    }
  }

  pub fn push(&mut self, vs: &[T]) {
    unsafe {
      self.byte_buffer.push(
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len()
      );
    }
    self.length += vs.len();
  }

  pub fn update(&mut self, idx: uint, vs: &[T]) {
    unsafe {
      self.byte_buffer.update(
        mem::size_of::<T>() * idx,
        mem::transmute(vs.as_ptr()),
        mem::size_of::<T>() * vs.len(),
      );
    }
  }

  pub fn swap_remove(&mut self, idx: uint, count: uint) {
    self.byte_buffer.swap_remove(
      mem::size_of::<T>() * idx,
      mem::size_of::<T>() * count,
    );
    self.length -= count;
  }
}

pub enum DrawMode {
  Lines,
  Triangles,
  Points,
}

impl DrawMode {
  fn to_enum(&self) -> GLenum {
    match self {
      &Lines     => gl::LINES,
      &Triangles => gl::TRIANGLES,
      &Points    => gl::POINTS,
    }
  }
}

/// A fixed-capacity array of bytes passed to OpenGL.
pub struct GLArray<T> {
  pub buffer: GLBuffer<T>,
  pub gl_id: u32,
  /// How to draw this buffer. Ex: gl::LINES, gl::TRIANGLES, etc.
  pub mode: GLenum,
  /// size of T in vertices
  pub attrib_span: uint,
  /// length in vertices
  pub length: uint,
}

impl<T> GLArray<T> {
  #[inline]
  /// Creates a new array of objects on the GPU.
  /// capacity is provided in units of size slice_span.
  pub fn new(
    _gl: &GLContext,
    shader_program: Rc<RefCell<Shader>>,
    attribs: &[vertex::AttribData],
    mode: DrawMode,
    buffer: GLBuffer<T>,
  ) -> GLArray<T> {
    let mut gl_id = 0;

    // TODO(cgaebel): Error checking?

    unsafe {
      gl::GenVertexArrays(1, &mut gl_id);
    }

    gl::BindVertexArray(gl_id);

    let mut offset = 0;
    let attrib_span = {
      let mut attrib_span = 0;
      for attrib in attribs.iter() {
        attrib_span += attrib.size * attrib.unit.size();
      }
      attrib_span
    };
    for attrib in attribs.iter() {
      let shader_attrib = glGetAttribLocation(shader_program.deref().borrow().id, attrib.name) as GLuint;
      assert!(shader_attrib != -1, "shader attribute \"{}\" not found", attrib.name);

      gl::EnableVertexAttribArray(shader_attrib);
      unsafe {
        if attrib.unit.is_integral() {
          gl::VertexAttribIPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        } else {
          gl::VertexAttribPointer(
            shader_attrib,
            attrib.size as i32,
            attrib.unit.gl_enum(),
            gl::FALSE as GLboolean,
            attrib_span as i32,
            ptr::null().offset(offset),
          );
        }
      }
      offset += (attrib.size * attrib.unit.size()) as int;
    }

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x}", err),
    }

    assert!(mem::size_of::<T>() % attrib_span == 0);

    GLArray {
      buffer: buffer,
      gl_id: gl_id,
      mode: mode.to_enum(),
      attrib_span: mem::size_of::<T>() / attrib_span,
      length: 0,
    }
  }

  pub fn push(&mut self, vs: &[T]) {
    self.buffer.push(vs);
    self.length += vs.len() * self.attrib_span;
  }

  pub fn swap_remove(&mut self, idx: uint, count: uint) {
    self.buffer.swap_remove(idx, count);
    self.length -= count * self.attrib_span;
  }

  #[inline]
  /// Draws all the queued triangles to the screen.
  pub fn draw(&self, gl: &GLContext) {
    self.draw_slice(gl, 0, self.buffer.length);
  }

  /// Draw some subset of the triangle array.
  pub fn draw_slice(&self, _gl: &GLContext, start: uint, len: uint) {
    assert!(start + len <= self.length);

    gl::BindVertexArray(self.gl_id);
    gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer.byte_buffer.gl_id);

    gl::DrawArrays(self.mode, (start * self.attrib_span) as i32, (len * self.attrib_span) as i32);

    match gl::GetError() {
      gl::NO_ERROR => {},
      err => fail!("OpenGL error 0x{:x}", err),
    }
  }
}

#[unsafe_destructor]
impl<T> Drop for GLArray<T> {
  #[inline]
  fn drop(&mut self) {
    unsafe {
      gl::DeleteVertexArrays(1, &self.gl_id);
    }
  }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Happy_numbers

extern crate collections;
use collections::treemap::TreeSet;
use std::iter::count;

fn digits(mut n: uint) -> Vec<uint> {
    let mut ds = vec![];
    if n == 0 {
        return vec![0];
    }
    while n > 0 {
        ds.push(n % 10);
        n /= 10;
    }
    ds.reverse();
    ds
}

fn is_happy(mut x: uint) -> bool {
    let mut past = TreeSet::new();
    while x != 1 {
        // Take the sum of the squares of the digits of x
        x = digits(x).iter().fold(0, |a, &b| a + b * b);

        // The number is not happy if there is an endless loop
        if past.contains(&x) {
            return false
        }

        past.insert(d);
    }
    true
}

#[cfg(not(test))]
fn main() {
    // Print the first 8 happy numbers
    let v: Vec<uint> = count(1u, 1)
        .filter(|&x| is_happy(*x))
        .take(8)
        .collect();
    println!("{}", v)
}

#[test]
fn test_digits() {
    assert_eq!(digits(0), vec![0]);
    assert_eq!(digits(1), vec![1]);
    assert_eq!(digits(2), vec![2]);
    assert_eq!(digits(10), vec![1, 0]);
    assert_eq!(digits(11), vec![1, 1]);
    assert_eq!(digits(101), vec![1, 0, 1]);
    assert_eq!(digits(1000), vec![1, 0, 0, 0]);
}

#[test]
fn test_is_happy() {
    let happys = [1u, 7, 10, 13, 19, 23, 28, 31, 1607, 1663];
    let unhappys = [0u, 2, 3, 4, 5, 6, 8, 9, 29, 1662];

    assert!(happys.iter().all(|&n| is_happy(n)));
    assert_eq!(unhappys.iter().all(|&n| !is_happy(n)), false);
}
<commit_msg>fix happy_numbers<commit_after>// Implements http://rosettacode.org/wiki/Happy_numbers

extern crate collections;
use collections::treemap::TreeSet;
#[cfg(not(test))]
use std::iter::count;

fn digits(mut n: uint) -> Vec<uint> {
    let mut ds = vec![];
    if n == 0 {
        return vec![0];
    }
    while n > 0 {
        ds.push(n % 10);
        n /= 10;
    }
    ds.reverse();
    ds
}

fn is_happy(mut x: uint) -> bool {
    let mut past = TreeSet::new();
    while x != 1 {
        // Take the sum of the squares of the digits of x
        x = digits(x).iter().fold(0, |a, &b| a + b * b);

        // The number is not happy if there is an endless loop
        if past.contains(&x) {
            return false
        }

        past.insert(x);
    }
    true
}

#[cfg(not(test))]
fn main() {
    // Print the first 8 happy numbers
    let v: Vec<uint> = count(1u, 1)
        .filter(|x| is_happy(*x))
        .take(8)
        .collect();
    println!("{}", v)
}

#[test]
fn test_digits() {
    assert_eq!(digits(0), vec![0]);
    assert_eq!(digits(1), vec![1]);
    assert_eq!(digits(2), vec![2]);
    assert_eq!(digits(10), vec![1, 0]);
    assert_eq!(digits(11), vec![1, 1]);
    assert_eq!(digits(101), vec![1, 0, 1]);
    assert_eq!(digits(1000), vec![1, 0, 0, 0]);
}

#[test]
fn test_is_happy() {
    let happys = [1u, 7, 10, 13, 19, 23, 28, 31, 1607, 1663];
    let unhappys = [0u, 2, 3, 4, 5, 6, 8, 9, 29, 1662];

    assert!(happys.iter().all(|&n| is_happy(n)));
    assert!(unhappys.iter().all(|&n| !is_happy(n)));
}
<|endoftext|>


<commit_before>use ra_db::SourceDatabase;
use ra_syntax::{
    AstNode, ast,
    algo::find_token_at_offset
};

use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};

pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let file = db.parse(position.file_id);

    let node = find_token_at_offset(file.syntax(), position.offset).find_map(|token| {
        token
            .parent()
            .ancestors()
            .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
    })?;

    let analyzer = hir::SourceAnalyzer::new(db, position.file_id, node, None);

    let ty: hir::Ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e))
    {
        ty
    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
        ty
    } else {
        return None;
    };

    let (adt_def, _) = ty.as_adt()?;
    let nav = NavigationTarget::from_adt_def(db, adt_def);

    Some(RangeInfo::new(node.range(), vec![nav]))
}

#[cfg(test)]
mod tests {
    use crate::mock_analysis::analysis_and_position;

    fn check_goto(fixture: &str, expected: &str) {
        let (analysis, pos) = analysis_and_position(fixture);

        let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        nav.assert_match(expected);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check_goto(
            "
            //- /lib.rs
            struct Foo;
            fn foo() {
                let f: Foo;
                f<|>
            }
            ",
            "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
        );
    }
}
<commit_msg>See through references<commit_after>use ra_db::SourceDatabase;
use ra_syntax::{
    AstNode, ast,
    algo::find_token_at_offset
};

use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};

pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let file = db.parse(position.file_id);

    let node = find_token_at_offset(file.syntax(), position.offset).find_map(|token| {
        token
            .parent()
            .ancestors()
            .find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
    })?;

    let analyzer = hir::SourceAnalyzer::new(db, position.file_id, node, None);

    let ty: hir::Ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e))
    {
        ty
    } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
        ty
    } else {
        return None;
    };

    let adt_def = ty.autoderef(db).find_map(|ty| match ty.as_adt() {
        Some((adt_def, _)) => Some(adt_def),
        None => None,
    })?;

    let nav = NavigationTarget::from_adt_def(db, adt_def);
    Some(RangeInfo::new(node.range(), vec![nav]))
}

#[cfg(test)]
mod tests {
    use crate::mock_analysis::analysis_and_position;

    fn check_goto(fixture: &str, expected: &str) {
        let (analysis, pos) = analysis_and_position(fixture);

        let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        nav.assert_match(expected);
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check_goto(
            "
            //- /lib.rs
            struct Foo;
            fn foo() {
                let f: Foo;
                f<|>
            }
            ",
            "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
        );
    }

    #[test]
    fn goto_type_definition_works_simple_ref() {
        check_goto(
            "
            //- /lib.rs
            struct Foo;
            fn foo() {
                let f: &Foo;
                f<|>
            }
            ",
            "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
        );
    }
}
<|endoftext|>


<commit_before>use super::btree::BPlusTree;
use super::sstable::*;
use super::*;
use client::AsyncClient;
use parking_lot::RwLock;
use std::sync::Arc;
use std::{mem, ptr};

const LEVEL_PAGES_MULTIPLIER: usize = 1000;
const LEVEL_DIFF_MULTIPLIER: usize = 10;
const LEVEL_M: usize = super::btree::NUM_KEYS;
const LEVEL_1: usize = LEVEL_M * LEVEL_DIFF_MULTIPLIER;
const LEVEL_2: usize = LEVEL_1 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_3: usize = LEVEL_2 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_4: usize = LEVEL_3 * LEVEL_DIFF_MULTIPLIER;
// TODO: debug assert the last one will not overflow MAX_SEGMENT_SIZE

type LevelTrees = Vec<Box<Tree>>;

macro_rules! with_levels {
    ($($sym:ident, $level:ident;)*) => {
        $(
            type $sym = [EntryKey; $level];
            impl_sspage_slice!($sym, EntryKey, $level);
        )*

        fn init_lsm_level_trees(neb_client: &Arc<AsyncClient>) -> LevelTrees {
            let mut trees = LevelTrees::new();
            $(
                trees.push(box LevelTree::<$sym>::new(neb_client));
            )*
            return trees;
        }
    };
}

pub struct LSMTree {
    level_m: BPlusTree,
    trees: LevelTrees // use Vec here for convenience
}

impl LSMTree {
    pub fn new(neb_client: &Arc<AsyncClient>) -> Self {
        LSMTree {
            level_m: BPlusTree::new(neb_client),
            trees: init_lsm_level_trees(neb_client)
        }
    }
}

with_levels!{
    L1, LEVEL_1;
    L2, LEVEL_2;
    L3, LEVEL_3;
    L4, LEVEL_4;
}
<commit_msg>tree sizes<commit_after>use super::btree::BPlusTree;
use super::sstable::*;
use super::*;
use client::AsyncClient;
use parking_lot::RwLock;
use std::sync::Arc;
use std::{mem, ptr};

const LEVEL_PAGES_MULTIPLIER: usize = 1000;
const LEVEL_DIFF_MULTIPLIER: usize = 10;
const LEVEL_M: usize = super::btree::NUM_KEYS;
const LEVEL_1: usize = LEVEL_M * LEVEL_DIFF_MULTIPLIER;
const LEVEL_2: usize = LEVEL_1 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_3: usize = LEVEL_2 * LEVEL_DIFF_MULTIPLIER;
const LEVEL_4: usize = LEVEL_3 * LEVEL_DIFF_MULTIPLIER;
// TODO: debug assert the last one will not overflow MAX_SEGMENT_SIZE

type LevelTrees = Vec<Box<Tree>>;

macro_rules! with_levels {
    ($($sym:ident, $level:ident;)*) => {
        $(
            type $sym = [EntryKey; $level];
            impl_sspage_slice!($sym, EntryKey, $level);
        )*

        fn init_lsm_level_trees(neb_client: &Arc<AsyncClient>) -> (LevelTrees, Vec<usize>) {
            let mut trees = LevelTrees::new();
            let mut sizes = Vec::new();
            $(
                trees.push(box LevelTree::<$sym>::new(neb_client));
                sizes.push($level);
            )*
            return (trees, sizes);
        }
    };
}

with_levels!{
    L1, LEVEL_1;
    L2, LEVEL_2;
    L3, LEVEL_3;
    L4, LEVEL_4;
}

pub struct LSMTree {
    level_m: BPlusTree,
    trees: LevelTrees,
    // use Vec here for convenience
    sizes: Vec<usize>
}

impl LSMTree {
    pub fn new(neb_client: &Arc<AsyncClient>) -> Self {
        let (trees, sizes) = init_lsm_level_trees(neb_client);
        LSMTree {
            level_m: BPlusTree::new(neb_client),
            trees,
            sizes
        }
    }
}
<|endoftext|>


<commit_before>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

// We have to do this because of an rpath problem with crates linking to
// foreign libraries
pub extern mod grustna {
}

#[test]
fn simple() {
    grust::init();
    let f = &gio::File::new_for_path("/dev/null") as &gio::File;
    assert!(f.get_path().to_str() == ~"/dev/null");
}

// Crashes due to https://github.com/mozilla/rust/issues/5882
#[test]
#[ignore]
fn off_stack() {
    grust::init();
    let f = ~gio::File::new_for_path("/dev/null") as ~gio::File;
    do task::try {
        let p = f.get_path();
        assert!(p.to_str() == ~"/dev/null");
    };
}
<commit_msg>Added a couple more tests that don't crash<commit_after>/* This file is part of Grust, GObject introspection bindings for Rust
 *
 * Copyright (C) 2013  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

extern mod grust (name="grust", vers="0.1");
extern mod gio (name="grust-Gio", vers="2.0");

// We have to do this because of an rpath problem with crates linking to
// foreign libraries
pub extern mod grustna {
}

#[test]
fn simple() {
    grust::init();
    let f = &gio::File::new_for_path("/dev/null") as &gio::File;
    assert!(f.get_path().to_str() == ~"/dev/null");
}

#[test]
fn clone() {
    grust::init();
    let f = &gio::File::new_for_path("/dev/null").clone() as &gio::File;
    assert!(f.get_path().to_str() == ~"/dev/null");
}

// Crashes due to https://github.com/mozilla/rust/issues/5882
#[test]
#[ignore]
fn off_stack() {
    grust::init();
    let f = ~gio::File::new_for_path("/dev/null") as ~gio::File;
    do spawn {
        let p = f.get_path();
        assert!(p.to_str() == ~"/dev/null");
    }
}

#[test]
fn off_stack_borrow() {
    grust::init();
    let f = ~gio::File::new_for_path("/dev/null");
    do spawn {
        let g = &*f as &gio::File;
        let p = g.get_path();
        assert!(p.to_str() == ~"/dev/null");
    }
}
<|endoftext|>


<commit_before>use std::io::Cursor;
use std::ops::Deref;

use crate::{
    crypto::{self, double_sha256, Digest, KeyPair},
    serializer::*,
    tx::*,
};

#[derive(Clone, Debug, PartialEq)]
pub struct Block {
    pub previous_hash: Digest,
    pub height: u64,
    pub timestamp: u64,
    pub tx_merkle_root: Digest,
    pub transactions: Vec<TxVariant>,
}

impl Block {
    pub fn sign(self, key_pair: &KeyPair) -> SignedBlock {
        let buf = self.calc_hash();
        SignedBlock {
            base: self,
            sig_pair: key_pair.sign(buf.as_ref()),
        }
    }

    pub fn serialize_with_tx(&self, buf: &mut Vec<u8>) {
        self.serialize_header(buf);

        buf.push_u32(self.transactions.len() as u32);
        for tx in &self.transactions {
            tx.serialize(buf)
        }
    }

    pub fn deserialize_with_tx(cur: &mut Cursor<&[u8]>) -> Option<Self> {
        let previous_hash = Digest::from_slice(&cur.take_bytes().ok()?)?;
        let height = cur.take_u64().ok()?;
        let timestamp = cur.take_u64().ok()?;
        let tx_merkle_root = Digest::from_slice(&cur.take_bytes().ok()?)?;

        let len = cur.take_u32().ok()?;
        let mut transactions = Vec::<TxVariant>::with_capacity(len as usize);
        for _ in 0..len {
            transactions.push(TxVariant::deserialize(cur)?);
        }

        Some(Self {
            previous_hash,
            height,
            timestamp,
            tx_merkle_root,
            transactions,
        })
    }

    fn serialize_header(&self, vec: &mut Vec<u8>) {
        vec.push_bytes(self.previous_hash.as_ref());
        vec.push_u64(self.height);
        vec.push_u64(self.timestamp);
        vec.push_bytes(self.tx_merkle_root.as_ref());
    }

    pub fn verify_tx_merkle_root(&self) -> bool {
        let digest = Self::calc_tx_merkle_root(&self.transactions);
        self.tx_merkle_root == digest
    }

    pub fn calc_hash(&self) -> Digest {
        let mut buf = Vec::with_capacity(1024);
        self.serialize_header(&mut buf);
        double_sha256(&buf)
    }

    pub fn verify_previous_hash(&self, prev_block: &Block) -> bool {
        self.previous_hash == prev_block.calc_hash()
    }

    pub fn calc_tx_merkle_root(txs: &[TxVariant]) -> Digest {
        let mut buf = Vec::with_capacity(4096 * txs.len());
        for tx in txs {
            tx.serialize(&mut buf)
        }
        double_sha256(&buf)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct SignedBlock {
    pub base: Block,
    pub sig_pair: crypto::SigPair,
}

impl SignedBlock {
    pub fn new_child(&self, txs: Vec<TxVariant>) -> Block {
        let previous_hash = {
            let mut buf = Vec::with_capacity(1024);
            self.base.serialize_header(&mut buf);
            double_sha256(&buf)
        };
        let height = self.height + 1;
        let tx_merkle_root = Block::calc_tx_merkle_root(&txs);
        let timestamp = crate::get_epoch_ms();
        Block {
            previous_hash,
            height,
            timestamp,
            tx_merkle_root,
            transactions: txs,
        }
    }

    pub fn serialize_with_tx(&self, vec: &mut Vec<u8>) {
        self.base.serialize_with_tx(vec);
        vec.push_sig_pair(&self.sig_pair);
    }

    pub fn deserialize_with_tx(cur: &mut Cursor<&[u8]>) -> Option<Self> {
        let block = Block::deserialize_with_tx(cur)?;
        let sig_pair = cur.take_sig_pair().ok()?;
        Some(Self {
            base: block,
            sig_pair,
        })
    }
}

impl Deref for SignedBlock {
    type Target = Block;

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{asset::Asset, crypto::KeyPair};

    #[test]
    fn serialize_block() {
        let keys = KeyPair::gen();
        let transactions = {
            let mut vec = Vec::new();
            vec.push(TxVariant::RewardTx(RewardTx {
                base: Tx {
                    fee: Asset::default(),
                    timestamp: 1234567890,
                    signature_pairs: Vec::new(),
                },
                to: keys.0.clone().into(),
                rewards: Asset::default(),
            }));
            vec
        };
        let tx_merkle_root = {
            let mut buf = Vec::new();
            for tx in &transactions {
                tx.serialize(&mut buf)
            }
            double_sha256(&buf)
        };
        let block = (Block {
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            height: 123,
            timestamp: 1532992800,
            tx_merkle_root,
            transactions,
        })
        .sign(&keys);

        let mut buf = Vec::new();
        block.serialize_with_tx(&mut buf);

        let mut cur = Cursor::<&[u8]>::new(&buf);
        let dec = SignedBlock::deserialize_with_tx(&mut cur).unwrap();

        assert_eq!(block.previous_hash, dec.previous_hash);
        assert_eq!(block.height, dec.height);
        assert_eq!(block.timestamp, dec.timestamp);
        assert_eq!(block.tx_merkle_root, dec.tx_merkle_root);
        assert_eq!(block.transactions.len(), dec.transactions.len());
        assert_eq!(block.sig_pair, dec.sig_pair);

        assert_eq!(block, dec);
    }

    #[test]
    fn merkle_root() {
        let mut block = Block {
            previous_hash: Digest::from_slice(&[0; 32]).unwrap(),
            height: 0,
            timestamp: 0,
            tx_merkle_root: double_sha256(&[0; 0]),
            transactions: vec![],
        };
        assert!(block.verify_tx_merkle_root());

        block.tx_merkle_root = Digest::from_slice(&[0; 32]).unwrap();
        assert!(!block.verify_tx_merkle_root());
    }
}
<commit_msg>Move merkle calculation function out of Block<commit_after>use std::io::Cursor;
use std::ops::Deref;

use crate::{
    crypto::{self, double_sha256, Digest, KeyPair},
    serializer::*,
    tx::*,
};

#[derive(Clone, Debug, PartialEq)]
pub struct Block {
    pub previous_hash: Digest,
    pub height: u64,
    pub timestamp: u64,
    pub tx_merkle_root: Digest,
    pub transactions: Vec<TxVariant>,
}

impl Block {
    pub fn sign(self, key_pair: &KeyPair) -> SignedBlock {
        let buf = self.calc_hash();
        SignedBlock {
            base: self,
            sig_pair: key_pair.sign(buf.as_ref()),
        }
    }

    pub fn serialize_with_tx(&self, buf: &mut Vec<u8>) {
        self.serialize_header(buf);

        buf.push_u32(self.transactions.len() as u32);
        for tx in &self.transactions {
            tx.serialize(buf)
        }
    }

    pub fn deserialize_with_tx(cur: &mut Cursor<&[u8]>) -> Option<Self> {
        let previous_hash = Digest::from_slice(&cur.take_bytes().ok()?)?;
        let height = cur.take_u64().ok()?;
        let timestamp = cur.take_u64().ok()?;
        let tx_merkle_root = Digest::from_slice(&cur.take_bytes().ok()?)?;

        let len = cur.take_u32().ok()?;
        let mut transactions = Vec::<TxVariant>::with_capacity(len as usize);
        for _ in 0..len {
            transactions.push(TxVariant::deserialize(cur)?);
        }

        Some(Self {
            previous_hash,
            height,
            timestamp,
            tx_merkle_root,
            transactions,
        })
    }

    fn serialize_header(&self, vec: &mut Vec<u8>) {
        vec.push_bytes(self.previous_hash.as_ref());
        vec.push_u64(self.height);
        vec.push_u64(self.timestamp);
        vec.push_bytes(self.tx_merkle_root.as_ref());
    }

    pub fn verify_tx_merkle_root(&self) -> bool {
        let digest = calc_tx_merkle_root(&self.transactions);
        self.tx_merkle_root == digest
    }

    pub fn calc_hash(&self) -> Digest {
        let mut buf = Vec::with_capacity(1024);
        self.serialize_header(&mut buf);
        double_sha256(&buf)
    }

    pub fn verify_previous_hash(&self, prev_block: &Block) -> bool {
        self.previous_hash == prev_block.calc_hash()
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct SignedBlock {
    pub base: Block,
    pub sig_pair: crypto::SigPair,
}

impl SignedBlock {
    pub fn new_child(&self, txs: Vec<TxVariant>) -> Block {
        let previous_hash = {
            let mut buf = Vec::with_capacity(1024);
            self.base.serialize_header(&mut buf);
            double_sha256(&buf)
        };
        let height = self.height + 1;
        let tx_merkle_root = calc_tx_merkle_root(&txs);
        let timestamp = crate::get_epoch_ms();
        Block {
            previous_hash,
            height,
            timestamp,
            tx_merkle_root,
            transactions: txs,
        }
    }

    pub fn serialize_with_tx(&self, vec: &mut Vec<u8>) {
        self.base.serialize_with_tx(vec);
        vec.push_sig_pair(&self.sig_pair);
    }

    pub fn deserialize_with_tx(cur: &mut Cursor<&[u8]>) -> Option<Self> {
        let block = Block::deserialize_with_tx(cur)?;
        let sig_pair = cur.take_sig_pair().ok()?;
        Some(Self {
            base: block,
            sig_pair,
        })
    }
}

impl Deref for SignedBlock {
    type Target = Block;

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

pub fn calc_tx_merkle_root(txs: &[TxVariant]) -> Digest {
    let mut buf = Vec::with_capacity(4096 * txs.len());
    for tx in txs {
        tx.serialize(&mut buf)
    }
    double_sha256(&buf)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{asset::Asset, crypto::KeyPair};

    #[test]
    fn serialize_block() {
        let keys = KeyPair::gen();
        let transactions = {
            let mut vec = Vec::new();
            vec.push(TxVariant::RewardTx(RewardTx {
                base: Tx {
                    fee: Asset::default(),
                    timestamp: 1234567890,
                    signature_pairs: Vec::new(),
                },
                to: keys.0.clone().into(),
                rewards: Asset::default(),
            }));
            vec
        };
        let tx_merkle_root = {
            let mut buf = Vec::new();
            for tx in &transactions {
                tx.serialize(&mut buf)
            }
            double_sha256(&buf)
        };
        let block = (Block {
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            height: 123,
            timestamp: 1532992800,
            tx_merkle_root,
            transactions,
        })
        .sign(&keys);

        let mut buf = Vec::new();
        block.serialize_with_tx(&mut buf);

        let mut cur = Cursor::<&[u8]>::new(&buf);
        let dec = SignedBlock::deserialize_with_tx(&mut cur).unwrap();

        assert_eq!(block.previous_hash, dec.previous_hash);
        assert_eq!(block.height, dec.height);
        assert_eq!(block.timestamp, dec.timestamp);
        assert_eq!(block.tx_merkle_root, dec.tx_merkle_root);
        assert_eq!(block.transactions.len(), dec.transactions.len());
        assert_eq!(block.sig_pair, dec.sig_pair);

        assert_eq!(block, dec);
    }

    #[test]
    fn merkle_root() {
        let mut block = Block {
            previous_hash: Digest::from_slice(&[0; 32]).unwrap(),
            height: 0,
            timestamp: 0,
            tx_merkle_root: double_sha256(&[0; 0]),
            transactions: vec![],
        };
        assert!(block.verify_tx_merkle_root());

        block.tx_merkle_root = Digest::from_slice(&[0; 32]).unwrap();
        assert!(!block.verify_tx_merkle_root());
    }
}
<|endoftext|>


<commit_before>//! A post-legalization rewriting pass.

#![allow(non_snake_case)]

use cursor::{Cursor, EncCursor};
use ir::condcodes::{CondCode, FloatCC, IntCC};
use ir::dfg::ValueDef;
use ir::immediates::{Imm64, Offset32};
use ir::instructions::{Opcode, ValueList};
use ir::{Ebb, Function, Inst, InstBuilder, InstructionData, Value, Type, MemFlags};
use isa::TargetIsa;
use timing;

/// Information collected about a compare+branch sequence.
struct CmpBrInfo {
    /// The branch instruction.
    br_inst: Inst,
    /// The icmp, icmp_imm, or fcmp instruction.
    cmp_inst: Inst,
    /// The destination of the branch.
    destination: Ebb,
    /// The arguments of the branch.
    args: ValueList,
    /// The first argument to the comparison. The second is in the `kind` field.
    cmp_arg: Value,
    /// If the branch is `brz` rather than `brnz`, we need to invert the condition
    /// before the branch.
    invert_branch_cond: bool,
    /// The kind of comparison, and the second argument.
    kind: CmpBrKind,
}

enum CmpBrKind {
    Icmp { cond: IntCC, arg: Value },
    IcmpImm { cond: IntCC, imm: Imm64 },
    Fcmp { cond: FloatCC, arg: Value },
}

/// Optimize comparisons to use flags values, to avoid materializing conditions
/// in integer registers.
///
/// For example, optimize icmp/fcmp brz/brnz sequences into ifcmp/ffcmp brif/brff
/// sequences.
fn optimize_cpu_flags(
    pos: &mut EncCursor,
    inst: Inst,
    last_flags_clobber: Option<Inst>,
    isa: &TargetIsa,
) {
    // Look for compare and branch patterns.
    // This code could be considerably simplified with non-lexical lifetimes.
    let info = match pos.func.dfg[inst] {
        InstructionData::Branch {
            opcode,
            destination,
            ref args,
        } => {
            let first_arg = args.first(&pos.func.dfg.value_lists).unwrap();
            let invert_branch_cond = match opcode {
                Opcode::Brz => true,
                Opcode::Brnz => false,
                _ => panic!(),
            };
            if let ValueDef::Result(cond_inst, _) = pos.func.dfg.value_def(first_arg) {
                match pos.func.dfg[cond_inst] {
                    InstructionData::IntCompare {
                        cond,
                        args: cmp_args,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg: cmp_args[0],
                        invert_branch_cond,
                        kind: CmpBrKind::Icmp {
                            cond,
                            arg: cmp_args[1],
                        },
                    },
                    InstructionData::IntCompareImm {
                        cond,
                        arg: cmp_arg,
                        imm: cmp_imm,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg,
                        invert_branch_cond,
                        kind: CmpBrKind::IcmpImm { cond, imm: cmp_imm },
                    },
                    InstructionData::FloatCompare {
                        cond,
                        args: cmp_args,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg: cmp_args[0],
                        invert_branch_cond,
                        kind: CmpBrKind::Fcmp {
                            cond,
                            arg: cmp_args[1],
                        },
                    },
                    _ => return,
                }
            } else {
                return;
            }
        }
        // TODO: trapif, trueif, selectif, and their ff counterparts.
        _ => return,
    };

    // If any instructions clobber the flags between the comparison and the branch,
    // don't optimize them.
    if last_flags_clobber != Some(info.cmp_inst) {
        return;
    }

    // We found a compare+branch pattern. Transform it to use flags.
    let args = info.args.as_slice(&pos.func.dfg.value_lists)[1..].to_vec();
    pos.goto_inst(info.cmp_inst);
    match info.kind {
        CmpBrKind::Icmp { mut cond, arg } => {
            let flags = pos.ins().ifcmp(info.cmp_arg, arg);
            pos.func.dfg.replace(info.cmp_inst).trueif(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brif(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
        CmpBrKind::IcmpImm { mut cond, imm } => {
            let flags = pos.ins().ifcmp_imm(info.cmp_arg, imm);
            pos.func.dfg.replace(info.cmp_inst).trueif(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brif(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
        CmpBrKind::Fcmp { mut cond, arg } => {
            let flags = pos.ins().ffcmp(info.cmp_arg, arg);
            pos.func.dfg.replace(info.cmp_inst).trueff(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brff(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
    }
    pos.func.update_encoding(info.cmp_inst, isa).is_ok();
    pos.func.update_encoding(info.br_inst, isa).is_ok();
}

struct MemOpInfo {
    opcode: Opcode,
    inst: Inst,
    itype: Type,
    arg: Value,
    st_arg: Option<Value>,
    flags: MemFlags,
    offset: Offset32,
    add_args: Option<[Value; 2]>,
}

fn optimize_complex_addresses(pos: &mut EncCursor, inst: Inst, isa: &TargetIsa) {
    let mut info = match pos.func.dfg[inst] {
        InstructionData::Load {
            opcode,
            arg,
            flags,
            offset,
        } => MemOpInfo {
            opcode: opcode,
            inst: inst,
            itype: pos.func.dfg.ctrl_typevar(inst),
            arg: arg,
            st_arg: None,
            flags: flags,
            offset: offset,
            add_args: None,
        },
        InstructionData::Store {
            opcode,
            args,
            flags,
            offset,
        } => MemOpInfo {
            opcode: opcode,
            inst: inst,
            itype: pos.func.dfg.ctrl_typevar(inst),
            arg: args[1],
            st_arg: Some(args[0]),
            flags: flags,
            offset: offset,
            add_args: None,
        },
        _ => return,
    };

    if let ValueDef::Result(result_inst, _) = pos.func.dfg.value_def(info.arg) {
        match pos.func.dfg[result_inst] {
            InstructionData::Binary { opcode, args } if opcode == Opcode::Iadd => {
                info.add_args = Some(args.clone());
            }
            _ => return,
        }
    } else {
        return;
    }

    match info.opcode {
        Opcode::Load => {
            pos.func.dfg.replace(info.inst).load_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload8 => {
            pos.func.dfg.replace(info.inst).uload8_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload8 => {
            pos.func.dfg.replace(info.inst).sload8_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload16 => {
            pos.func.dfg.replace(info.inst).uload16_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload16 => {
            pos.func.dfg.replace(info.inst).sload16_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload32 => {
            pos.func.dfg.replace(info.inst).uload32_complex(
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload32 => {
            pos.func.dfg.replace(info.inst).sload32_complex(
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Store => {
            pos.func.dfg.replace(info.inst).store_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore8 => {
            pos.func.dfg.replace(info.inst).istore8_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore16 => {
            pos.func.dfg.replace(info.inst).istore16_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore32 => {
            pos.func.dfg.replace(info.inst).istore32_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        _ => return,
    }
    pos.func.update_encoding(info.inst, isa).is_ok();
}

//----------------------------------------------------------------------
//
// The main post-opt pass.

pub fn do_postopt(func: &mut Function, isa: &TargetIsa) {
    let _tt = timing::postopt();
    let mut pos = EncCursor::new(func, isa);
    while let Some(_ebb) = pos.next_ebb() {
        let mut last_flags_clobber = None;
        while let Some(inst) = pos.next_inst() {
            if isa.uses_cpu_flags() {
                // Optimize instructions to make use of flags.
                optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);

                // Track the most recent seen instruction that clobbers the flags.
                if let Some(constraints) =
                    isa.encoding_info().operand_constraints(
                        pos.func.encodings[inst],
                    )
                {
                    if constraints.clobbers_flags {
                        last_flags_clobber = Some(inst)
                    }
                }
            }

            if isa.uses_complex_addresses() {
                optimize_complex_addresses(&mut pos, inst, isa);
            }
        }
    }
}
<commit_msg>Add assertions to check that postopt encoding succeeds.<commit_after>//! A post-legalization rewriting pass.

#![allow(non_snake_case)]

use cursor::{Cursor, EncCursor};
use ir::condcodes::{CondCode, FloatCC, IntCC};
use ir::dfg::ValueDef;
use ir::immediates::{Imm64, Offset32};
use ir::instructions::{Opcode, ValueList};
use ir::{Ebb, Function, Inst, InstBuilder, InstructionData, Value, Type, MemFlags};
use isa::TargetIsa;
use timing;

/// Information collected about a compare+branch sequence.
struct CmpBrInfo {
    /// The branch instruction.
    br_inst: Inst,
    /// The icmp, icmp_imm, or fcmp instruction.
    cmp_inst: Inst,
    /// The destination of the branch.
    destination: Ebb,
    /// The arguments of the branch.
    args: ValueList,
    /// The first argument to the comparison. The second is in the `kind` field.
    cmp_arg: Value,
    /// If the branch is `brz` rather than `brnz`, we need to invert the condition
    /// before the branch.
    invert_branch_cond: bool,
    /// The kind of comparison, and the second argument.
    kind: CmpBrKind,
}

enum CmpBrKind {
    Icmp { cond: IntCC, arg: Value },
    IcmpImm { cond: IntCC, imm: Imm64 },
    Fcmp { cond: FloatCC, arg: Value },
}

/// Optimize comparisons to use flags values, to avoid materializing conditions
/// in integer registers.
///
/// For example, optimize icmp/fcmp brz/brnz sequences into ifcmp/ffcmp brif/brff
/// sequences.
fn optimize_cpu_flags(
    pos: &mut EncCursor,
    inst: Inst,
    last_flags_clobber: Option<Inst>,
    isa: &TargetIsa,
) {
    // Look for compare and branch patterns.
    // This code could be considerably simplified with non-lexical lifetimes.
    let info = match pos.func.dfg[inst] {
        InstructionData::Branch {
            opcode,
            destination,
            ref args,
        } => {
            let first_arg = args.first(&pos.func.dfg.value_lists).unwrap();
            let invert_branch_cond = match opcode {
                Opcode::Brz => true,
                Opcode::Brnz => false,
                _ => panic!(),
            };
            if let ValueDef::Result(cond_inst, _) = pos.func.dfg.value_def(first_arg) {
                match pos.func.dfg[cond_inst] {
                    InstructionData::IntCompare {
                        cond,
                        args: cmp_args,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg: cmp_args[0],
                        invert_branch_cond,
                        kind: CmpBrKind::Icmp {
                            cond,
                            arg: cmp_args[1],
                        },
                    },
                    InstructionData::IntCompareImm {
                        cond,
                        arg: cmp_arg,
                        imm: cmp_imm,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg,
                        invert_branch_cond,
                        kind: CmpBrKind::IcmpImm { cond, imm: cmp_imm },
                    },
                    InstructionData::FloatCompare {
                        cond,
                        args: cmp_args,
                        ..
                    } => CmpBrInfo {
                        br_inst: inst,
                        cmp_inst: cond_inst,
                        destination,
                        args: args.clone(),
                        cmp_arg: cmp_args[0],
                        invert_branch_cond,
                        kind: CmpBrKind::Fcmp {
                            cond,
                            arg: cmp_args[1],
                        },
                    },
                    _ => return,
                }
            } else {
                return;
            }
        }
        // TODO: trapif, trueif, selectif, and their ff counterparts.
        _ => return,
    };

    // If any instructions clobber the flags between the comparison and the branch,
    // don't optimize them.
    if last_flags_clobber != Some(info.cmp_inst) {
        return;
    }

    // We found a compare+branch pattern. Transform it to use flags.
    let args = info.args.as_slice(&pos.func.dfg.value_lists)[1..].to_vec();
    pos.goto_inst(info.cmp_inst);
    match info.kind {
        CmpBrKind::Icmp { mut cond, arg } => {
            let flags = pos.ins().ifcmp(info.cmp_arg, arg);
            pos.func.dfg.replace(info.cmp_inst).trueif(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brif(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
        CmpBrKind::IcmpImm { mut cond, imm } => {
            let flags = pos.ins().ifcmp_imm(info.cmp_arg, imm);
            pos.func.dfg.replace(info.cmp_inst).trueif(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brif(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
        CmpBrKind::Fcmp { mut cond, arg } => {
            let flags = pos.ins().ffcmp(info.cmp_arg, arg);
            pos.func.dfg.replace(info.cmp_inst).trueff(cond, flags);
            if info.invert_branch_cond {
                cond = cond.inverse();
            }
            pos.func.dfg.replace(info.br_inst).brff(
                cond,
                flags,
                info.destination,
                &args,
            );
        }
    }
    let ok = pos.func.update_encoding(info.cmp_inst, isa).is_ok();
    debug_assert!(ok);
    let ok = pos.func.update_encoding(info.br_inst, isa).is_ok();
    debug_assert!(ok);
}

struct MemOpInfo {
    opcode: Opcode,
    inst: Inst,
    itype: Type,
    arg: Value,
    st_arg: Option<Value>,
    flags: MemFlags,
    offset: Offset32,
    add_args: Option<[Value; 2]>,
}

fn optimize_complex_addresses(pos: &mut EncCursor, inst: Inst, isa: &TargetIsa) {
    let mut info = match pos.func.dfg[inst] {
        InstructionData::Load {
            opcode,
            arg,
            flags,
            offset,
        } => MemOpInfo {
            opcode: opcode,
            inst: inst,
            itype: pos.func.dfg.ctrl_typevar(inst),
            arg: arg,
            st_arg: None,
            flags: flags,
            offset: offset,
            add_args: None,
        },
        InstructionData::Store {
            opcode,
            args,
            flags,
            offset,
        } => MemOpInfo {
            opcode: opcode,
            inst: inst,
            itype: pos.func.dfg.ctrl_typevar(inst),
            arg: args[1],
            st_arg: Some(args[0]),
            flags: flags,
            offset: offset,
            add_args: None,
        },
        _ => return,
    };

    if let ValueDef::Result(result_inst, _) = pos.func.dfg.value_def(info.arg) {
        match pos.func.dfg[result_inst] {
            InstructionData::Binary { opcode, args } if opcode == Opcode::Iadd => {
                info.add_args = Some(args.clone());
            }
            _ => return,
        }
    } else {
        return;
    }

    match info.opcode {
        Opcode::Load => {
            pos.func.dfg.replace(info.inst).load_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload8 => {
            pos.func.dfg.replace(info.inst).uload8_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload8 => {
            pos.func.dfg.replace(info.inst).sload8_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload16 => {
            pos.func.dfg.replace(info.inst).uload16_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload16 => {
            pos.func.dfg.replace(info.inst).sload16_complex(
                info.itype,
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Uload32 => {
            pos.func.dfg.replace(info.inst).uload32_complex(
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Sload32 => {
            pos.func.dfg.replace(info.inst).sload32_complex(
                info.flags,
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Store => {
            pos.func.dfg.replace(info.inst).store_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore8 => {
            pos.func.dfg.replace(info.inst).istore8_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore16 => {
            pos.func.dfg.replace(info.inst).istore16_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        Opcode::Istore32 => {
            pos.func.dfg.replace(info.inst).istore32_complex(
                info.flags,
                info.st_arg.unwrap(),
                &info.add_args.unwrap(),
                info.offset,
            );
        }
        _ => return,
    }
    let ok = pos.func.update_encoding(info.inst, isa).is_ok();
    debug_assert!(ok);
}

//----------------------------------------------------------------------
//
// The main post-opt pass.

pub fn do_postopt(func: &mut Function, isa: &TargetIsa) {
    let _tt = timing::postopt();
    let mut pos = EncCursor::new(func, isa);
    while let Some(_ebb) = pos.next_ebb() {
        let mut last_flags_clobber = None;
        while let Some(inst) = pos.next_inst() {
            if isa.uses_cpu_flags() {
                // Optimize instructions to make use of flags.
                optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);

                // Track the most recent seen instruction that clobbers the flags.
                if let Some(constraints) =
                    isa.encoding_info().operand_constraints(
                        pos.func.encodings[inst],
                    )
                {
                    if constraints.clobbers_flags {
                        last_flags_clobber = Some(inst)
                    }
                }
            }

            if isa.uses_complex_addresses() {
                optimize_complex_addresses(&mut pos, inst, isa);
            }
        }
    }
}
<|endoftext|>