<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use arena::TypedArena;
use llvm::{self, ValueRef, get_params};
use rustc::hir::def_id::DefId;
use abi::{Abi, FnType};
use attributes;
use base::*;
use callee::{self, Callee};
use common::*;
use debuginfo::{DebugLoc};
use declare;
use monomorphize::{Instance};
use value::Value;
use rustc::ty::{self, Ty, TyCtxt};

use rustc::hir;

fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           closure_id: DefId,
                           fn_ty: Ty<'tcx>)
                           -> Ty<'tcx> {
    match tcx.closure_kind(closure_id) {
        ty::ClosureKind::Fn => {
            tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), fn_ty)
        }
        ty::ClosureKind::FnMut => {
            tcx.mk_mut_ref(tcx.mk_region(ty::ReErased), fn_ty)
        }
        ty::ClosureKind::FnOnce => fn_ty,
    }
}

/// Returns the LLVM function declaration for a closure, creating it if
/// necessary. If the ID does not correspond to a closure ID, returns None.
fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                               closure_id: DefId,
                                               substs: ty::ClosureSubsts<'tcx>)
                                               -> ValueRef {
    // Normalize type so differences in regions and typedefs don't cause
    // duplicate declarations
    let tcx = ccx.tcx();
    let substs = tcx.erase_regions(&substs);
    let instance = Instance::new(closure_id, substs.func_substs);

    if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
        debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
               instance, Value(llfn));
        return llfn;
    }

    let symbol = instance.symbol_name(ccx.shared());

    // Compute the rust-call form of the closure call method.
    let sig = &tcx.closure_type(closure_id, substs).sig;
    let sig = tcx.erase_late_bound_regions(sig);
    let sig = tcx.normalize_associated_type(&sig);
    let closure_type = tcx.mk_closure_from_closure_substs(closure_id, substs);
    let function_type = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: hir::Unsafety::Normal,
        abi: Abi::RustCall,
        sig: ty::Binder(ty::FnSig {
            inputs: Some(get_self_type(tcx, closure_id, closure_type))
                        .into_iter().chain(sig.inputs).collect(),
            output: sig.output,
            variadic: false
        })
    }));
    let llfn = declare::declare_fn(ccx, &symbol, function_type);

    attributes::set_frame_pointer_elimination(ccx, llfn);

    debug!("get_or_create_declaration_if_closure(): inserting new \
            closure {:?}: {:?}",
           instance, Value(llfn));

    // NOTE: We do *not* store llfn in the ccx.instances() map here,
    //       that is only done, when the closures body is translated.

    llfn
}

pub fn trans_closure_body_via_mir<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                            closure_def_id: DefId,
                                            closure_substs: ty::ClosureSubsts<'tcx>) {
    // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
    // defid of the closure in its original crate, whereas `id` will be the id of the local
    // inlined copy.
    debug!("trans_closure_body_via_mir(closure_def_id={:?}, closure_substs={:?})",
           closure_def_id, closure_substs);

    let tcx = ccx.tcx();
    let _icx = push_ctxt("closure::trans_closure_expr");

    let param_substs = closure_substs.func_substs;
    let instance = Instance::new(closure_def_id, param_substs);

    // If we have not done so yet, translate this closure's body
    if  !ccx.instances().borrow().contains_key(&instance) {
        let llfn = get_or_create_closure_declaration(ccx, closure_def_id, closure_substs);

        unsafe {
            if ccx.sess().target.target.options.allows_weak_linkage {
                llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::WeakODRLinkage);
                llvm::SetUniqueComdat(ccx.llmod(), llfn);
            } else {
                llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage);
            }
        }

        // set an inline hint for all closures
        attributes::inline(llfn, attributes::InlineAttr::Hint);

        // Get the type of this closure. Use the current `param_substs` as
        // the closure substitutions. This makes sense because the closure
        // takes the same set of type arguments as the enclosing fn, and
        // this function (`trans_closure`) is invoked at the point
        // of the closure expression.

        let sig = &tcx.closure_type(closure_def_id, closure_substs).sig;
        let sig = tcx.erase_late_bound_regions(sig);
        let sig = tcx.normalize_associated_type(&sig);

        let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
                                                              closure_substs);
        let sig = ty::FnSig {
            inputs: Some(get_self_type(tcx, closure_def_id, closure_type))
                        .into_iter().chain(sig.inputs).collect(),
            output: sig.output,
            variadic: false
        };

        trans_closure(ccx,
                      llfn,
                      Instance::new(closure_def_id, param_substs),
                      &sig,
                      Abi::RustCall);

        ccx.instances().borrow_mut().insert(instance, llfn);
    }
}

pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
                                      closure_def_id: DefId,
                                      substs: ty::ClosureSubsts<'tcx>,
                                      method_instance: Instance<'tcx>,
                                      trait_closure_kind: ty::ClosureKind)
                                      -> ValueRef
{
    // If this is a closure, redirect to it.
    let llfn = get_or_create_closure_declaration(ccx, closure_def_id, substs);

    // If weak linkage is not allowed, we have to make sure that a local,
    // private copy of the closure is available in this codegen unit
    if !ccx.sess().target.target.options.allows_weak_linkage &&
       !ccx.sess().opts.single_codegen_unit() {

        trans_closure_body_via_mir(ccx, closure_def_id, substs);
    }

    // If the closure is a Fn closure, but a FnOnce is needed (etc),
    // then adapt the self type
    let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);

    let _icx = push_ctxt("trans_closure_adapter_shim");

    debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
           trait_closure_kind={:?}, llfn={:?})",
           llfn_closure_kind, trait_closure_kind, Value(llfn));

    match (llfn_closure_kind, trait_closure_kind) {
        (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
        (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
            // No adapter needed.
            llfn
        }
        (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
            // The closure fn `llfn` is a `fn(&self, ...)`.  We want a
            // `fn(&mut self, ...)`. In fact, at trans time, these are
            // basically the same thing, so we can just return llfn.
            llfn
        }
        (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
            // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
            // self, ...)`.  We want a `fn(self, ...)`. We can produce
            // this by doing something like:
            //
            //     fn call_once(self, ...) { call_mut(&self, ...) }
            //     fn call_once(mut self, ...) { call_mut(&mut self, ...) }
            //
            // These are both the same at trans time.
            trans_fn_once_adapter_shim(ccx, closure_def_id, substs, method_instance, llfn)
        }
        _ => {
            bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
                 llfn_closure_kind,
                 trait_closure_kind);
        }
    }
}

fn trans_fn_once_adapter_shim<'a, 'tcx>(
    ccx: &'a CrateContext<'a, 'tcx>,
    closure_def_id: DefId,
    substs: ty::ClosureSubsts<'tcx>,
    method_instance: Instance<'tcx>,
    llreffn: ValueRef)
    -> ValueRef
{
    debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})",
           closure_def_id, substs, Value(llreffn));

    let tcx = ccx.tcx();

    // Find a version of the closure type. Substitute static for the
    // region since it doesn't really matter.
    let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, substs);
    let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);

    // Make a version with the type of by-ref closure.
    let ty::ClosureTy { unsafety, abi, mut sig } =
        tcx.closure_type(closure_def_id, substs);
    sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
    let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: unsafety,
        abi: abi,
        sig: sig.clone()
    }));
    debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
           llref_fn_ty);

    // Make a version of the closure type with the same arguments, but
    // with argument #0 being by value.
    assert_eq!(abi, Abi::RustCall);
    sig.0.inputs[0] = closure_ty;

    let sig = tcx.erase_late_bound_regions(&sig);
    let sig = tcx.normalize_associated_type(&sig);
    let fn_ty = FnType::new(ccx, abi, &sig, &[]);

    let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: unsafety,
        abi: abi,
        sig: ty::Binder(sig)
    }));

    // Create the by-value helper.
    let function_name = method_instance.symbol_name(ccx.shared());
    let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
    attributes::set_frame_pointer_elimination(ccx, lloncefn);

    let (block_arena, fcx): (TypedArena<_>, FunctionContext);
    block_arena = TypedArena::new();
    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
    let mut bcx = fcx.init(false);

    // the first argument (`self`) will be the (by value) closure env.

    let mut llargs = get_params(fcx.llfn);
    let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
    let env_arg = &fcx.fn_ty.args[0];
    let llenv = if env_arg.is_indirect() {
        llargs[self_idx]
    } else {
        let scratch = alloc_ty(bcx, closure_ty, "self");
        let mut llarg_idx = self_idx;
        env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
        scratch
    };

    debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
    // Adjust llargs such that llargs[self_idx..] has the call arguments.
    // For zero-sized closures that means sneaking in a new argument.
    if env_arg.is_ignore() {
        if self_idx > 0 {
            self_idx -= 1;
            llargs[self_idx] = llenv;
        } else {
            llargs.insert(0, llenv);
        }
    } else {
        llargs[self_idx] = llenv;
    }

    let dest = fcx.llretslotptr.get();

    let callee = Callee {
        data: callee::Fn(llreffn),
        ty: llref_fn_ty
    };

    // Call the by-ref closure body with `self` in a cleanup scope,
    // to drop `self` when the body returns, or in case it unwinds.
    let self_scope = fcx.push_custom_cleanup_scope();
    fcx.schedule_drop_mem(self_scope, llenv, closure_ty);

    bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;

    fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);

    fcx.finish(bcx, DebugLoc::None);

    lloncefn
}
<commit_msg>trans: Make sure that each FnOnce shim is only translated once.<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use arena::TypedArena;
use llvm::{self, ValueRef, get_params};
use rustc::hir::def_id::DefId;
use abi::{Abi, FnType};
use attributes;
use base::*;
use callee::{self, Callee};
use common::*;
use debuginfo::{DebugLoc};
use declare;
use monomorphize::{Instance};
use value::Value;
use rustc::ty::{self, Ty, TyCtxt};

use rustc::hir;

fn get_self_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           closure_id: DefId,
                           fn_ty: Ty<'tcx>)
                           -> Ty<'tcx> {
    match tcx.closure_kind(closure_id) {
        ty::ClosureKind::Fn => {
            tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), fn_ty)
        }
        ty::ClosureKind::FnMut => {
            tcx.mk_mut_ref(tcx.mk_region(ty::ReErased), fn_ty)
        }
        ty::ClosureKind::FnOnce => fn_ty,
    }
}

/// Returns the LLVM function declaration for a closure, creating it if
/// necessary. If the ID does not correspond to a closure ID, returns None.
fn get_or_create_closure_declaration<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                               closure_id: DefId,
                                               substs: ty::ClosureSubsts<'tcx>)
                                               -> ValueRef {
    // Normalize type so differences in regions and typedefs don't cause
    // duplicate declarations
    let tcx = ccx.tcx();
    let substs = tcx.erase_regions(&substs);
    let instance = Instance::new(closure_id, substs.func_substs);

    if let Some(&llfn) = ccx.instances().borrow().get(&instance) {
        debug!("get_or_create_closure_declaration(): found closure {:?}: {:?}",
               instance, Value(llfn));
        return llfn;
    }

    let symbol = instance.symbol_name(ccx.shared());

    // Compute the rust-call form of the closure call method.
    let sig = &tcx.closure_type(closure_id, substs).sig;
    let sig = tcx.erase_late_bound_regions(sig);
    let sig = tcx.normalize_associated_type(&sig);
    let closure_type = tcx.mk_closure_from_closure_substs(closure_id, substs);
    let function_type = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: hir::Unsafety::Normal,
        abi: Abi::RustCall,
        sig: ty::Binder(ty::FnSig {
            inputs: Some(get_self_type(tcx, closure_id, closure_type))
                        .into_iter().chain(sig.inputs).collect(),
            output: sig.output,
            variadic: false
        })
    }));
    let llfn = declare::declare_fn(ccx, &symbol, function_type);

    attributes::set_frame_pointer_elimination(ccx, llfn);

    debug!("get_or_create_declaration_if_closure(): inserting new \
            closure {:?}: {:?}",
           instance, Value(llfn));

    // NOTE: We do *not* store llfn in the ccx.instances() map here,
    //       that is only done, when the closures body is translated.

    llfn
}

pub fn trans_closure_body_via_mir<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                                            closure_def_id: DefId,
                                            closure_substs: ty::ClosureSubsts<'tcx>) {
    // (*) Note that in the case of inlined functions, the `closure_def_id` will be the
    // defid of the closure in its original crate, whereas `id` will be the id of the local
    // inlined copy.
    debug!("trans_closure_body_via_mir(closure_def_id={:?}, closure_substs={:?})",
           closure_def_id, closure_substs);

    let tcx = ccx.tcx();
    let _icx = push_ctxt("closure::trans_closure_expr");

    let param_substs = closure_substs.func_substs;
    let instance = Instance::new(closure_def_id, param_substs);

    // If we have not done so yet, translate this closure's body
    if  !ccx.instances().borrow().contains_key(&instance) {
        let llfn = get_or_create_closure_declaration(ccx, closure_def_id, closure_substs);

        unsafe {
            if ccx.sess().target.target.options.allows_weak_linkage {
                llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::WeakODRLinkage);
                llvm::SetUniqueComdat(ccx.llmod(), llfn);
            } else {
                llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage);
            }
        }

        // set an inline hint for all closures
        attributes::inline(llfn, attributes::InlineAttr::Hint);

        // Get the type of this closure. Use the current `param_substs` as
        // the closure substitutions. This makes sense because the closure
        // takes the same set of type arguments as the enclosing fn, and
        // this function (`trans_closure`) is invoked at the point
        // of the closure expression.

        let sig = &tcx.closure_type(closure_def_id, closure_substs).sig;
        let sig = tcx.erase_late_bound_regions(sig);
        let sig = tcx.normalize_associated_type(&sig);

        let closure_type = tcx.mk_closure_from_closure_substs(closure_def_id,
                                                              closure_substs);
        let sig = ty::FnSig {
            inputs: Some(get_self_type(tcx, closure_def_id, closure_type))
                        .into_iter().chain(sig.inputs).collect(),
            output: sig.output,
            variadic: false
        };

        trans_closure(ccx,
                      llfn,
                      Instance::new(closure_def_id, param_substs),
                      &sig,
                      Abi::RustCall);

        ccx.instances().borrow_mut().insert(instance, llfn);
    }
}

pub fn trans_closure_method<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
                                      closure_def_id: DefId,
                                      substs: ty::ClosureSubsts<'tcx>,
                                      method_instance: Instance<'tcx>,
                                      trait_closure_kind: ty::ClosureKind)
                                      -> ValueRef
{
    // If this is a closure, redirect to it.
    let llfn = get_or_create_closure_declaration(ccx, closure_def_id, substs);

    // If weak linkage is not allowed, we have to make sure that a local,
    // private copy of the closure is available in this codegen unit
    if !ccx.sess().target.target.options.allows_weak_linkage &&
       !ccx.sess().opts.single_codegen_unit() {

        trans_closure_body_via_mir(ccx, closure_def_id, substs);
    }

    // If the closure is a Fn closure, but a FnOnce is needed (etc),
    // then adapt the self type
    let llfn_closure_kind = ccx.tcx().closure_kind(closure_def_id);

    let _icx = push_ctxt("trans_closure_adapter_shim");

    debug!("trans_closure_adapter_shim(llfn_closure_kind={:?}, \
           trait_closure_kind={:?}, llfn={:?})",
           llfn_closure_kind, trait_closure_kind, Value(llfn));

    match (llfn_closure_kind, trait_closure_kind) {
        (ty::ClosureKind::Fn, ty::ClosureKind::Fn) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) |
        (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => {
            // No adapter needed.
            llfn
        }
        (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
            // The closure fn `llfn` is a `fn(&self, ...)`.  We want a
            // `fn(&mut self, ...)`. In fact, at trans time, these are
            // basically the same thing, so we can just return llfn.
            llfn
        }
        (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) |
        (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
            // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
            // self, ...)`.  We want a `fn(self, ...)`. We can produce
            // this by doing something like:
            //
            //     fn call_once(self, ...) { call_mut(&self, ...) }
            //     fn call_once(mut self, ...) { call_mut(&mut self, ...) }
            //
            // These are both the same at trans time.
            trans_fn_once_adapter_shim(ccx, closure_def_id, substs, method_instance, llfn)
        }
        _ => {
            bug!("trans_closure_adapter_shim: cannot convert {:?} to {:?}",
                 llfn_closure_kind,
                 trait_closure_kind);
        }
    }
}

fn trans_fn_once_adapter_shim<'a, 'tcx>(
    ccx: &'a CrateContext<'a, 'tcx>,
    closure_def_id: DefId,
    substs: ty::ClosureSubsts<'tcx>,
    method_instance: Instance<'tcx>,
    llreffn: ValueRef)
    -> ValueRef
{
    if let Some(&llfn) = ccx.instances().borrow().get(&method_instance) {
        return llfn;
    }

    debug!("trans_fn_once_adapter_shim(closure_def_id={:?}, substs={:?}, llreffn={:?})",
           closure_def_id, substs, Value(llreffn));

    let tcx = ccx.tcx();

    // Find a version of the closure type. Substitute static for the
    // region since it doesn't really matter.
    let closure_ty = tcx.mk_closure_from_closure_substs(closure_def_id, substs);
    let ref_closure_ty = tcx.mk_imm_ref(tcx.mk_region(ty::ReErased), closure_ty);

    // Make a version with the type of by-ref closure.
    let ty::ClosureTy { unsafety, abi, mut sig } =
        tcx.closure_type(closure_def_id, substs);
    sig.0.inputs.insert(0, ref_closure_ty); // sig has no self type as of yet
    let llref_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: unsafety,
        abi: abi,
        sig: sig.clone()
    }));
    debug!("trans_fn_once_adapter_shim: llref_fn_ty={:?}",
           llref_fn_ty);

    // Make a version of the closure type with the same arguments, but
    // with argument #0 being by value.
    assert_eq!(abi, Abi::RustCall);
    sig.0.inputs[0] = closure_ty;

    let sig = tcx.erase_late_bound_regions(&sig);
    let sig = tcx.normalize_associated_type(&sig);
    let fn_ty = FnType::new(ccx, abi, &sig, &[]);

    let llonce_fn_ty = tcx.mk_fn_ptr(tcx.mk_bare_fn(ty::BareFnTy {
        unsafety: unsafety,
        abi: abi,
        sig: ty::Binder(sig)
    }));

    // Create the by-value helper.
    let function_name = method_instance.symbol_name(ccx.shared());
    let lloncefn = declare::define_internal_fn(ccx, &function_name, llonce_fn_ty);
    attributes::set_frame_pointer_elimination(ccx, lloncefn);

    let (block_arena, fcx): (TypedArena<_>, FunctionContext);
    block_arena = TypedArena::new();
    fcx = FunctionContext::new(ccx, lloncefn, fn_ty, None, &block_arena);
    let mut bcx = fcx.init(false);

    // the first argument (`self`) will be the (by value) closure env.

    let mut llargs = get_params(fcx.llfn);
    let mut self_idx = fcx.fn_ty.ret.is_indirect() as usize;
    let env_arg = &fcx.fn_ty.args[0];
    let llenv = if env_arg.is_indirect() {
        llargs[self_idx]
    } else {
        let scratch = alloc_ty(bcx, closure_ty, "self");
        let mut llarg_idx = self_idx;
        env_arg.store_fn_arg(&bcx.build(), &mut llarg_idx, scratch);
        scratch
    };

    debug!("trans_fn_once_adapter_shim: env={:?}", Value(llenv));
    // Adjust llargs such that llargs[self_idx..] has the call arguments.
    // For zero-sized closures that means sneaking in a new argument.
    if env_arg.is_ignore() {
        if self_idx > 0 {
            self_idx -= 1;
            llargs[self_idx] = llenv;
        } else {
            llargs.insert(0, llenv);
        }
    } else {
        llargs[self_idx] = llenv;
    }

    let dest = fcx.llretslotptr.get();

    let callee = Callee {
        data: callee::Fn(llreffn),
        ty: llref_fn_ty
    };

    // Call the by-ref closure body with `self` in a cleanup scope,
    // to drop `self` when the body returns, or in case it unwinds.
    let self_scope = fcx.push_custom_cleanup_scope();
    fcx.schedule_drop_mem(self_scope, llenv, closure_ty);

    bcx = callee.call(bcx, DebugLoc::None, &llargs[self_idx..], dest).bcx;

    fcx.pop_and_trans_custom_cleanup_scope(bcx, self_scope);

    fcx.finish(bcx, DebugLoc::None);

    ccx.instances().borrow_mut().insert(method_instance, lloncefn);

    lloncefn
}
<|endoftext|>


<commit_before>use chrono::Utc;
use client::Context;
use model::id::{ChannelId, GuildId, UserId};
use std::{
    collections::HashMap,
    default::Default
};

#[cfg(feature = "cache")]
type Check = Fn(&mut Context, Option<GuildId>, ChannelId, UserId) -> bool + Send + Sync + 'static;

#[cfg(not(feature = "cache"))]
type Check = Fn(&mut Context, ChannelId, UserId) -> bool + 'static;

pub(crate) struct Ratelimit {
    pub delay: i64,
    pub limit: Option<(i64, i32)>,
}

#[derive(Default)]
pub(crate) struct MemberRatelimit {
    pub last_time: i64,
    pub set_time: i64,
    pub tickets: i32,
}

pub(crate) struct Bucket {
    pub ratelimit: Ratelimit,
    pub users: HashMap<u64, MemberRatelimit>,
    pub check: Option<Box<Check>>,
}

impl Bucket {
    pub fn take(&mut self, user_id: u64) -> i64 {
        let time = Utc::now().timestamp();
        let user = self.users
            .entry(user_id)
            .or_insert_with(MemberRatelimit::default);

        if let Some((timespan, limit)) = self.ratelimit.limit {
            if (user.tickets + 1) > limit {
                if time < (user.set_time + timespan) {
                    return (user.set_time + timespan) - time;
                } else {
                    user.tickets = 0;
                    user.set_time = time;
                }
            }
        }

        if time < user.last_time + self.ratelimit.delay {
            (user.last_time + self.ratelimit.delay) - time
        } else {
            user.tickets += 1;
            user.last_time = time;

            0
        }
    }
}
<commit_msg>Add missing Send and Sync bounds<commit_after>use chrono::Utc;
use client::Context;
use model::id::{ChannelId, GuildId, UserId};
use std::{
    collections::HashMap,
    default::Default
};

#[cfg(feature = "cache")]
type Check = Fn(&mut Context, Option<GuildId>, ChannelId, UserId) -> bool + Send + Sync + 'static;

#[cfg(not(feature = "cache"))]
type Check = Fn(&mut Context, ChannelId, UserId) -> bool + Send + Sync + 'static;

pub(crate) struct Ratelimit {
    pub delay: i64,
    pub limit: Option<(i64, i32)>,
}

#[derive(Default)]
pub(crate) struct MemberRatelimit {
    pub last_time: i64,
    pub set_time: i64,
    pub tickets: i32,
}

pub(crate) struct Bucket {
    pub ratelimit: Ratelimit,
    pub users: HashMap<u64, MemberRatelimit>,
    pub check: Option<Box<Check>>,
}

impl Bucket {
    pub fn take(&mut self, user_id: u64) -> i64 {
        let time = Utc::now().timestamp();
        let user = self.users
            .entry(user_id)
            .or_insert_with(MemberRatelimit::default);

        if let Some((timespan, limit)) = self.ratelimit.limit {
            if (user.tickets + 1) > limit {
                if time < (user.set_time + timespan) {
                    return (user.set_time + timespan) - time;
                } else {
                    user.tickets = 0;
                    user.set_time = time;
                }
            }
        }

        if time < user.last_time + self.ratelimit.delay {
            (user.last_time + self.ratelimit.delay) - time
        } else {
            user.tickets += 1;
            user.last_time = time;

            0
        }
    }
}
<|endoftext|>


<commit_before>pub fn largest_number(nums: Vec<i32>) -> String {
    for n in nums {
        let this = n.to_string();
    }
}

fn main() {}
<commit_msg>try to finish it<commit_after>use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};

pub fn largest_number(nums: Vec<i32>) -> String {
    let mut cache: HashMap<u8, Vec<Vec<u8>>> = HashMap::new();
    for n in nums {
        let this = n.to_string();
        let bs = this.into_bytes();

        let this_value = cache.entry(bs[0]).or_insert(Vec::new());
        this_value.push(bs.clone());
    }

    //dbg!(&cache);

    let mut result: Vec<u8> = vec![];
    for i in 48..58 {
        if let Some(this_) = cache.get_mut(&(48 + 57 - i as u8)) {
            this_.sort_by(compare);
            this_.reverse();
            for v in this_ {
                result.append(v);
            }
        }
    }

    //dbg!(&result);
    if result[0] == 48 as u8 {
        String::from("0")
    } else {
        String::from_utf8(result).unwrap()
    }
}

//give up
fn compare(a: &Vec<u8>, b: &Vec<u8>) -> Ordering {
    let mut ind = 0;
    let (lan_a, lan_b) = (a.len(), b.len());
    while ind != lan_a && ind != lan_b {
        if a[ind] > b[ind] {
            return Ordering::Greater;
        } else if b[ind] > a[ind] {
            return Ordering::Less;
        }

        ind += 1;
    }

    if ind < lan_a {
        while ind < lan_a {
            if a[ind] > a[ind - 1] {
                return Ordering::Greater;
            } else if a[ind] < a[ind - 1] {
                return Ordering::Less;
            }
            ind += 1;
        }
        return Ordering::Equal;
    } else if ind < lan_b {
        while ind < lan_b {
            if b[ind] > b[ind - 1] {
                return Ordering::Less;
            } else if b[ind] < b[ind - 1] {
                return Ordering::Greater;
            }
            ind += 1;
        }
        return Ordering::Equal;
    } else {
        return Ordering::Equal;
    }
}

fn compare_two<'x>(a: &'x Vec<u8>, b: &'x Vec<u8>) -> (bool, &'x Vec<u8>) {
    let mut ind = 0;
    let (lan_a, lan_b) = (a.len(), b.len());
    while ind != lan_a && ind != lan_b {
        if a[ind] > b[ind] {
            return (true, a);
        } else if b[ind] > a[ind] {
            return (false, b);
        }

        ind += 1;
    }

    if ind < lan_a {
        while ind < lan_a {
            if a[ind] > a[ind - 1] {
                return (true, a);
            } else if a[ind] < a[ind - 1] {
                return (false, b);
            }
            ind += 1;
        }
        return (true, a);
    } else if ind < lan_b {
        while ind < lan_b {
            if b[ind] > b[ind - 1] {
                return (false, b);
            } else if b[ind] < b[ind - 1] {
                return (true, a);
            }
            ind += 1;
        }
        return (false, b);
    } else {
        return (true, a);
    }
}

fn which_bit(a: String, b: String) {
    let a_ = a.into_bytes();
    let b_ = b.into_bytes();

    let len = a_.len();

    for i in 0..len {
        if a_[i] != b_[i] {
            println!("bit: {}, is: {}", i, a_[i] as char);
        }
    }
}

fn main() {
    //let a = String::from("123").into_bytes();
    //let b = String::from("123").into_bytes();
    //dbg!(compare_two(&a, &b));

    dbg!(largest_number(vec![12, 23])); //=> 2312
    dbg!(largest_number(vec![10, 2])); //=> 210
    dbg!(largest_number(vec![3, 30, 34, 5, 9])); //=> 9534330
    dbg!(largest_number(vec![0, 0])); //=> 0

    dbg!(largest_number(vec![
        4704, 6306, 9385, 7536, 3462, 4798, 5422, 5529, 8070, 6241, 9094, 7846, 663, 6221, 216,
        6758, 8353, 3650, 3836, 8183, 3516, 5909, 6744, 1548, 5712, 2281, 3664, 7100, 6698, 7321,
        4980, 8937, 3163, 5784, 3298, 9890, 1090, 7605, 1380, 1147, 1495, 3699, 9448, 5208, 9456,
        3846, 3567, 6856, 2000, 3575, 7205, 2697, 5972, 7471, 1763, 1143, 1417, 6038, 2313, 6554,
        9026, 8107, 9827, 7982, 9685, 3905, 8939, 1048, 282, 7423, 6327, 2970, 4453, 5460, 3399,
        9533, 914, 3932, 192, 3084, 6806, 273, 4283, 2060, 5682, 2, 2362, 4812, 7032, 810, 2465,
        6511, 213, 2362, 3021, 2745, 3636, 6265, 1518, 8398
    ])); // => 98909827968595339456944893859149094902689398937839883538183810810780707982784676057536747174237321720571007032685668066758674466986636554651163276306626562416221603859725909578457125682552954605422520849804812479847044453428339323905384638363699366436503636357535673516346233993298316330843021297028227452732697246523622362231322812216213206020001921763154815181495141713801147114310901048

    // assert_eq!(largest_number(vec![
    //     4704, 6306, 9385, 7536, 3462, 4798, 5422, 5529, 8070, 6241, 9094, 7846, 663, 6221, 216,
    //     6758, 8353, 3650, 3836, 8183, 3516, 5909, 6744, 1548, 5712, 2281, 3664, 7100, 6698, 7321,
    //     4980, 8937, 3163, 5784, 3298, 9890, 1090, 7605, 1380, 1147, 1495, 3699, 9448, 5208, 9456,
    //     3846, 3567, 6856, 2000, 3575, 7205, 2697, 5972, 7471, 1763, 1143, 1417, 6038, 2313, 6554,
    //     9026, 8107, 9827, 7982, 9685, 3905, 8939, 1048, 282, 7423, 6327, 2970, 4453, 5460, 3399,
    //     9533, 914, 3932, 192, 3084, 6806, 273, 4283, 2060, 5682, 2, 2362, 4812, 7032, 810, 2465,
    //     6511, 213, 2362, 3021, 2745, 3636, 6265, 1518, 8398
    // ]), String::from("98909827968595339456944893859149094902689398937839883538183810810780707982784676057536747174237321720571007032685668066758674466986636554651163276306626562416221603859725909578457125682552954605422520849804812479847044453428339323905384638363699366436503636357535673516346233993298316330843021297028227452732697246523622362231322812216213206020001921763154815181495141713801147114310901048"));

    let mut a = vec![824, 938, 1399, 5607, 6973, 5703, 9609, 4398, 8247];
    dbg!(largest_number(a));

    a = vec![2, 2281];
    dbg!(largest_number(a));

    // which_bit(String::from("98909827968595339456944893859149094902689398937839883538183810810780707982784676057536747174237321720571007032685668066758674466986636554651163276306626562416221603859725909578457125682552954605422520849804812479847044453428339323905384638363699366436503636357535673516346233993298316330843021297028227452732697246523622362231322812216213206020001921763154815181495141713801147114310901048"),
    //           String::from("98909827968595339456944893859149094902689398937839883538183810781080707982784676057536747174237321720571007032685668066758674466986636554651163276306626562416221603859725909578457125682552954605422520849804812479847044453428339323905384638363699366436503636357535673516346233993298316330843021297028227452732697246523622362231322812216213206020001921763154815181495141713801147114310901048"));
}
<|endoftext|>


<commit_before>#![feature(question_mark)]
#[macro_use]
extern crate rustfbp;
extern crate capnp;

use std::path::Path;

component! {
    shells_lain_commands_dirname, contracts(generic_text, command)
    inputs(stdin: generic_text),
    inputs_array(),
    outputs(stdout: generic_text),
    outputs_array(),
    option(command),
    acc(),
    fn run(&mut self) -> Result<()> {
        let mut opt = self.recv_option();
        let mut stdin_ip = self.ports.recv("stdin")?;
        let mut out = String::new();
        let mut separator = String::new();
        {
            let reader: command::Reader = opt.get_root()?;
            let singles = reader.get_singles()?
                .iter()
                .map(|O| {
                    match O {
                        Ok(x) => x,
                        Err(e) => "",
                    }
                }).collect::<Vec<_>>();
            let sep = if singles.contains(&"--zero") || singles.contains(&"-z") {"\0"} else {"\n"};
            separator.push_str(sep);
            {
                let stdin_reader: generic_text::Reader = stdin_ip.get_root()?;
                let path = stdin_reader.get_text();

                let p = Path::new(path?);
                match p.parent() {
                    Some(d) => {
                        if d.components().next() == None {
                            out.push_str(".");
                        } else {
                            match d.to_str() {
                                Some(e) => out.push_str(e),
                                None => {},
                            }
                        }
                    }
                    None => {
                        if p.is_absolute() || path? == "/" {
                            out.push_str("/");
                        } else {
                            out.push_str(".");
                        }
                    }
                }
            }
            let mut new_ip = IP::new();
            {
                let mut ip = new_ip.init_root::<generic_text::Builder>();
                out.push_str(separator.as_str());
                ip.set_text(out.as_str());
            }
            self.ports.send("stdout", new_ip);
        }
        Ok(())
    }
}
<commit_msg>Problem: warnings need silencing... fast<commit_after>#![feature(question_mark)]
#[macro_use]
extern crate rustfbp;
extern crate capnp;

use std::path::Path;

component! {
    shells_lain_commands_dirname, contracts(generic_text, command)
    inputs(stdin: generic_text),
    inputs_array(),
    outputs(stdout: generic_text),
    outputs_array(),
    option(command),
    acc(),
    fn run(&mut self) -> Result<()> {
        let mut opt = self.recv_option();
        let mut stdin_ip = self.ports.recv("stdin")?;
        let mut out = String::new();
        let mut separator = String::new();
        {
            let reader: command::Reader = opt.get_root()?;
            let singles = reader.get_singles()?
                .iter()
                .map(|o| {
                    match o {
                        Ok(x) => x,
                        Err(_) => "",
                    }
                }).collect::<Vec<_>>();
            let sep = if singles.contains(&"--zero") || singles.contains(&"-z") {"\0"} else {"\n"};
            separator.push_str(sep);
            {
                let stdin_reader: generic_text::Reader = stdin_ip.get_root()?;
                let path = stdin_reader.get_text();

                let p = Path::new(path?);
                match p.parent() {
                    Some(d) => {
                        if d.components().next() == None {
                            out.push_str(".");
                        } else {
                            match d.to_str() {
                                Some(e) => out.push_str(e),
                                None => {},
                            }
                        }
                    }
                    None => {
                        if p.is_absolute() || path? == "/" {
                            out.push_str("/");
                        } else {
                            out.push_str(".");
                        }
                    }
                }
            }
            let mut new_ip = IP::new();
            {
                let mut ip = new_ip.init_root::<generic_text::Builder>();
                out.push_str(separator.as_str());
                ip.set_text(out.as_str());
            }
            self.ports.send("stdout", new_ip);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![feature(io)]
#![feature(core)]

extern crate rustbox;

use std::char;
use std::old_io::stdio;
use std::error::Error;
use std::default::Default;

use rustbox::{Color, RustBox, InitOptions};
use rustbox::Key;

fn main() {
    let rustbox = match RustBox::init(InitOptions {
        buffer_stderr: stdio::stderr_raw().isatty(),
        ..Default::default()
    }) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    rustbox.print(1, 1, rustbox::RB_BOLD, Color::White, Color::Black, "Hello, world!");
    rustbox.print(1, 3, rustbox::RB_BOLD, Color::White, Color::Black,
                  "Press 'q' to quit.");
    rustbox.present();
    loop {
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Some(Key::Char('q')) => { break; }
                    _ => { }
                }
            },
            Err(e) => panic!("{}", e.description()),
            _ => { }
        }
    }
}
<commit_msg>example: Present new state after every event<commit_after>#![feature(io)]
#![feature(core)]

extern crate rustbox;

use std::char;
use std::old_io::stdio;
use std::error::Error;
use std::default::Default;

use rustbox::{Color, RustBox, InitOptions};
use rustbox::Key;

fn main() {
    let rustbox = match RustBox::init(InitOptions {
        buffer_stderr: stdio::stderr_raw().isatty(),
        ..Default::default()
    }) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    rustbox.print(1, 1, rustbox::RB_BOLD, Color::White, Color::Black, "Hello, world!");
    rustbox.print(1, 3, rustbox::RB_BOLD, Color::White, Color::Black,
                  "Press 'q' to quit.");
    loop {
        rustbox.present();
        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Some(Key::Char('q')) => { break; }
                    _ => { }
                }
            },
            Err(e) => panic!("{}", e.description()),
            _ => { }
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;

extern crate glutin;

use std::thread::Thread;

mod support;

#[cfg(target_os = "android")]
android_start!(main);

#[cfg(not(feature = "window"))]
fn main() { println!("This example requires glutin to be compiled with the `window` feature"); }

#[cfg(feature = "window")]
fn main() {
    let window1 = glutin::Window::new().unwrap();
    let window2 = glutin::Window::new().unwrap();
    let window3 = glutin::Window::new().unwrap();

    let t1 = Thread::scoped(move || {
        run(window1, (0.0, 1.0, 0.0, 1.0));
    });

    let t2 = Thread::scoped(move || {
        run(window2, (0.0, 0.0, 1.0, 1.0));
    });

    let t3 = Thread::scoped(move || {
        run(window3, (1.0, 0.0, 0.0, 1.0));
    });

    t1.join();
    t2.join();
    t3.join();
}

#[cfg(feature = "window")]
fn run(window: glutin::Window, color: (f32, f32, f32, f32)) {
    unsafe { window.make_current() };

    let context = support::load(&window);

    while !window.is_closed() {
        context.draw_frame(color);
        window.swap_buffers();

        window.wait_events().collect::<Vec<glutin::Event>>();
    }
}
<commit_msg>Squash some warnings<commit_after>#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;

extern crate glutin;

use std::thread::Thread;

mod support;

#[cfg(target_os = "android")]
android_start!(main);

#[cfg(not(feature = "window"))]
fn main() { println!("This example requires glutin to be compiled with the `window` feature"); }

#[cfg(feature = "window")]
fn main() {
    let window1 = glutin::Window::new().unwrap();
    let window2 = glutin::Window::new().unwrap();
    let window3 = glutin::Window::new().unwrap();

    let t1 = Thread::scoped(move || {
        run(window1, (0.0, 1.0, 0.0, 1.0));
    });

    let t2 = Thread::scoped(move || {
        run(window2, (0.0, 0.0, 1.0, 1.0));
    });

    let t3 = Thread::scoped(move || {
        run(window3, (1.0, 0.0, 0.0, 1.0));
    });

    let _ = t1.join();
    let _ = t2.join();
    let _ = t3.join();
}

#[cfg(feature = "window")]
fn run(window: glutin::Window, color: (f32, f32, f32, f32)) {
    unsafe { window.make_current() };

    let context = support::load(&window);

    while !window.is_closed() {
        context.draw_frame(color);
        window.swap_buffers();

        window.wait_events().collect::<Vec<glutin::Event>>();
    }
}
<|endoftext|>


<commit_before>use std::io;

use self::reader::LogReader;
use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<Iterator<Item = (Lsn, LogID)>>,
    pub segment_base: Option<LogID>,
    pub segment_len: usize,
    pub use_compression: bool,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
    pub trailer: Option<Lsn>,
}

impl Iterator for LogIter {
    type Item = (Lsn, LogID, Vec<u8>);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let remaining_seg_too_small_for_msg =
                !valid_entry_offset(self.cur_lsn as LogID, self.segment_len);
            if self.trailer.is_none() && remaining_seg_too_small_for_msg {
                // We've read to the end of a torn
                // segment and should stop now.
                return None;
            } else if self.segment_base.is_none() ||
                       remaining_seg_too_small_for_msg
            {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.segment_len as Lsn) >= self.cur_lsn,
                        "caller is responsible for providing segments \
                            that contain the initial cur_lsn value or higher"
                    );
                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                            iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                return None;
            }

            let lid = self.segment_base.unwrap() +
                (self.cur_lsn % self.segment_len as Lsn) as LogID;

            if self.max_lsn < lid as Lsn {
                // we've hit the end of the log.
                trace!(
                    "in LogIter::next self.max_lsn {} < lid {}",
                    self.max_lsn,
                    lid
                );
                return None;
            }

            if let Ok(f) = self.config.file() {
                match f.read_message(
                    lid,
                    self.segment_len,
                    self.use_compression,
                ) {
                    Ok(LogRead::Flush(lsn, buf, on_disk_len)) => {
                        if lsn != self.cur_lsn {
                            error!("read Flush with bad lsn");
                            return None;
                        }
                        trace!("read flush in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                        return Some((lsn, lid, buf));
                    }
                    Ok(LogRead::Failed(lsn, on_disk_len)) => {
                        if lsn != self.cur_lsn {
                            error!("read Failed with bad lsn");
                            return None;
                        }
                        trace!("read zeroed in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                    }
                    Ok(LogRead::Corrupted(_len)) => {
                        trace!("read corrupted end in LogIter::next");
                        return None;
                    }
                    Ok(LogRead::Pad(lsn)) => {
                        if lsn != self.cur_lsn {
                            error!("read Pad with bad lsn");
                            return None;
                        }

                        if self.trailer.is_none() {
                            // This segment was torn, nothing left to read.
                            return None;
                        }

                        self.segment_base.take();
                        self.trailer.take();
                        continue;
                    }
                    Err(e) => {
                        error!(
                            "failed to read log message during iteration: {}",
                            e
                        );
                        return None;
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogID) -> CacheResult<(), ()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.segment_len as Lsn >= self.cur_lsn);
        let f = self.config.file()?;
        let segment_header = f.read_segment_header(offset)?;
        if offset % self.segment_len as LogID != 0 {
            debug!("segment offset not divisible by segment length");
            return Err(Error::Corruption {
                at: offset,
            });
        }
        if segment_header.lsn % self.segment_len as Lsn != 0 {
            debug!(
                "expected a segment header lsn that is divisible \
            by the io_buf_size ({}) instead it was {}",
                self.segment_len,
                segment_header.lsn
            );
            return Err(Error::Corruption {
                at: offset,
            });
        }

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            error!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn,
                lsn
            );
            return Err(
                io::Error::new(
                    io::ErrorKind::Other,
                    "encountered torn segment",
                ).into(),
            );
        }

        let trailer_offset = offset + self.segment_len as LogID -
            SEG_TRAILER_LEN as LogID;
        let trailer_lsn = segment_header.lsn + self.segment_len as Lsn -
            SEG_TRAILER_LEN as Lsn;

        trace!("trying to read trailer from {}", trailer_offset);
        let segment_trailer = f.read_segment_trailer(trailer_offset);

        trace!("read segment header {:?}", segment_header);
        trace!("read segment trailer {:?}", segment_trailer);

        let trailer_lsn = segment_trailer.ok().and_then(|st| if st.ok &&
            st.lsn == trailer_lsn
        {
            Some(st.lsn)
        } else {
            None
        });

        self.trailer = trailer_lsn;
        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }
}

fn valid_entry_offset(lid: LogID, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogID * segment_len as LogID;

    let max_lid = seg_start + segment_len as LogID -
        SEG_TRAILER_LEN as LogID - MSG_HEADER_LEN as LogID;

    let min_lid = seg_start + SEG_HEADER_LEN as LogID;

    lid >= min_lid && lid <= max_lid
}
<commit_msg>use FADV_WILLNEED on a segment before iterating over it<commit_after>use std::io;

use self::reader::LogReader;
use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<Iterator<Item = (Lsn, LogID)>>,
    pub segment_base: Option<LogID>,
    pub segment_len: usize,
    pub use_compression: bool,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
    pub trailer: Option<Lsn>,
}

impl Iterator for LogIter {
    type Item = (Lsn, LogID, Vec<u8>);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let remaining_seg_too_small_for_msg =
                !valid_entry_offset(self.cur_lsn as LogID, self.segment_len);

            if self.trailer.is_none() && remaining_seg_too_small_for_msg {
                // We've read to the end of a torn
                // segment and should stop now.
                return None;
            } else if self.segment_base.is_none() ||
                       remaining_seg_too_small_for_msg
            {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.segment_len as Lsn) >= self.cur_lsn,
                        "caller is responsible for providing segments \
                            that contain the initial cur_lsn value or higher"
                    );

                    self.fadvise_willneed(next_lid);

                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                            iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                return None;
            }

            let lid = self.segment_base.unwrap() +
                (self.cur_lsn % self.segment_len as Lsn) as LogID;

            if self.max_lsn < lid as Lsn {
                // we've hit the end of the log.
                trace!(
                    "in LogIter::next self.max_lsn {} < lid {}",
                    self.max_lsn,
                    lid
                );
                return None;
            }

            if let Ok(f) = self.config.file() {
                match f.read_message(
                    lid,
                    self.segment_len,
                    self.use_compression,
                ) {
                    Ok(LogRead::Flush(lsn, buf, on_disk_len)) => {
                        if lsn != self.cur_lsn {
                            error!("read Flush with bad lsn");
                            return None;
                        }
                        trace!("read flush in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                        return Some((lsn, lid, buf));
                    }
                    Ok(LogRead::Failed(lsn, on_disk_len)) => {
                        if lsn != self.cur_lsn {
                            error!("read Failed with bad lsn");
                            return None;
                        }
                        trace!("read zeroed in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                    }
                    Ok(LogRead::Corrupted(_len)) => {
                        trace!("read corrupted end in LogIter::next");
                        return None;
                    }
                    Ok(LogRead::Pad(lsn)) => {
                        if lsn != self.cur_lsn {
                            error!("read Pad with bad lsn");
                            return None;
                        }

                        if self.trailer.is_none() {
                            // This segment was torn, nothing left to read.
                            return None;
                        }

                        self.segment_base.take();

                        self.trailer.take();
                        continue;
                    }
                    Err(e) => {
                        error!(
                            "failed to read log message during iteration: {}",
                            e
                        );
                        return None;
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogID) -> CacheResult<(), ()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.segment_len as Lsn >= self.cur_lsn);
        let f = self.config.file()?;
        let segment_header = f.read_segment_header(offset)?;
        if offset % self.segment_len as LogID != 0 {
            debug!("segment offset not divisible by segment length");
            return Err(Error::Corruption {
                at: offset,
            });
        }
        if segment_header.lsn % self.segment_len as Lsn != 0 {
            debug!(
                "expected a segment header lsn that is divisible \
            by the io_buf_size ({}) instead it was {}",
                self.segment_len,
                segment_header.lsn
            );
            return Err(Error::Corruption {
                at: offset,
            });
        }

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            error!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn,
                lsn
            );
            return Err(
                io::Error::new(
                    io::ErrorKind::Other,
                    "encountered torn segment",
                ).into(),
            );
        }

        let trailer_offset = offset + self.segment_len as LogID -
            SEG_TRAILER_LEN as LogID;
        let trailer_lsn = segment_header.lsn + self.segment_len as Lsn -
            SEG_TRAILER_LEN as Lsn;

        trace!("trying to read trailer from {}", trailer_offset);
        let segment_trailer = f.read_segment_trailer(trailer_offset);

        trace!("read segment header {:?}", segment_header);
        trace!("read segment trailer {:?}", segment_trailer);

        let trailer_lsn = segment_trailer.ok().and_then(|st| if st.ok &&
            st.lsn == trailer_lsn
        {
            Some(st.lsn)
        } else {
            None
        });

        self.trailer = trailer_lsn;
        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }

    fn fadvise_willneed(&self, lid: LogID) {
        #[cfg(target_os = "linux")]
        {
            use std::os::unix::io::AsRawFd;

            if let Ok(f) = self.config.file() {
                let ret = unsafe {
                    libc::posix_fadvise(
                        f.as_raw_fd(),
                        lid as libc::off_t,
                        self.config.io_buf_size as libc::off_t,
                        libc::POSIX_FADV_WILLNEED,
                    )
                };
                if ret != 0 {
                    panic!(
                        "failed to call fadvise: {}",
                        std::io::Error::from_raw_os_error(ret)
                    );
                }
            }
        }
    }
}

fn valid_entry_offset(lid: LogID, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogID * segment_len as LogID;

    let max_lid = seg_start + segment_len as LogID -
        SEG_TRAILER_LEN as LogID - MSG_HEADER_LEN as LogID;

    let min_lid = seg_start + SEG_HEADER_LEN as LogID;

    lid >= min_lid && lid <= max_lid
}
<|endoftext|>


<commit_before>//! Verification of MIPS MSA intrinsics
#![feature(try_trait)]
#![allow(bad_style, unused)]

// This file is obtained from
// https://gcc.gnu.org/onlinedocs//gcc/MIPS-SIMD-Architecture-Built-in-Functions.html
static HEADER: &str = include_str!("../mips-msa.h");

stdarch_verify::mips_functions!(static FUNCTIONS);

struct Function {
    name: &'static str,
    arguments: &'static [&'static Type],
    ret: Option<&'static Type>,
    target_feature: Option<&'static str>,
    instrs: &'static [&'static str],
    file: &'static str,
    required_const: &'static [usize],
    has_test: bool,
}

static F16: Type = Type::PrimFloat(16);
static F32: Type = Type::PrimFloat(32);
static F64: Type = Type::PrimFloat(64);
static I8: Type = Type::PrimSigned(8);
static I16: Type = Type::PrimSigned(16);
static I32: Type = Type::PrimSigned(32);
static I64: Type = Type::PrimSigned(64);
static U8: Type = Type::PrimUnsigned(8);
static U16: Type = Type::PrimUnsigned(16);
static U32: Type = Type::PrimUnsigned(32);
static U64: Type = Type::PrimUnsigned(64);
static NEVER: Type = Type::Never;
static TUPLE: Type = Type::Tuple;
static v16i8: Type = Type::I(8, 16, 1);
static v8i16: Type = Type::I(16, 8, 1);
static v4i32: Type = Type::I(32, 4, 1);
static v2i64: Type = Type::I(64, 2, 1);
static v16u8: Type = Type::U(8, 16, 1);
static v8u16: Type = Type::U(16, 8, 1);
static v4u32: Type = Type::U(32, 4, 1);
static v2u64: Type = Type::U(64, 2, 1);
static v8f16: Type = Type::F(16, 8, 1);
static v4f32: Type = Type::F(32, 4, 1);
static v2f64: Type = Type::F(64, 2, 1);

#[derive(Debug, Copy, Clone, PartialEq)]
enum Type {
    PrimFloat(u8),
    PrimSigned(u8),
    PrimUnsigned(u8),
    PrimPoly(u8),
    MutPtr(&'static Type),
    ConstPtr(&'static Type),
    Tuple,
    I(u8, u8, u8),
    U(u8, u8, u8),
    P(u8, u8, u8),
    F(u8, u8, u8),
    Never,
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)]
enum MsaTy {
    v16i8,
    v8i16,
    v4i32,
    v2i64,
    v16u8,
    v8u16,
    v4u32,
    v2u64,
    v8f16,
    v4f32,
    v2f64,
    imm0_1,
    imm0_3,
    imm0_7,
    imm0_15,
    imm0_31,
    imm0_63,
    imm0_255,
    imm_n16_15,
    imm_n512_511,
    imm_n1024_1022,
    imm_n2048_2044,
    imm_n4096_4088,
    i32,
    u32,
    i64,
    u64,
    Void,
    MutVoidPtr,
}

impl<'a> From<&'a str> for MsaTy {
    fn from(s: &'a str) -> MsaTy {
        match s {
            "v16i8" => MsaTy::v16i8,
            "v8i16" => MsaTy::v8i16,
            "v4i32" => MsaTy::v4i32,
            "v2i64" => MsaTy::v2i64,
            "v16u8" => MsaTy::v16u8,
            "v8u16" => MsaTy::v8u16,
            "v4u32" => MsaTy::v4u32,
            "v2u64" => MsaTy::v2u64,
            "v8f16" => MsaTy::v8f16,
            "v4f32" => MsaTy::v4f32,
            "v2f64" => MsaTy::v2f64,
            "imm0_1" => MsaTy::imm0_1,
            "imm0_3" => MsaTy::imm0_3,
            "imm0_7" => MsaTy::imm0_7,
            "imm0_15" => MsaTy::imm0_15,
            "imm0_31" => MsaTy::imm0_31,
            "imm0_63" => MsaTy::imm0_63,
            "imm0_255" => MsaTy::imm0_255,
            "imm_n16_15" => MsaTy::imm_n16_15,
            "imm_n512_511" => MsaTy::imm_n512_511,
            "imm_n1024_1022" => MsaTy::imm_n1024_1022,
            "imm_n2048_2044" => MsaTy::imm_n2048_2044,
            "imm_n4096_4088" => MsaTy::imm_n4096_4088,
            "i32" => MsaTy::i32,
            "u32" => MsaTy::u32,
            "i64" => MsaTy::i64,
            "u64" => MsaTy::u64,
            "void" => MsaTy::Void,
            "void *" => MsaTy::MutVoidPtr,
            v => panic!("unknown ty: \"{}\"", v),
        }
    }
}

#[derive(Debug, Clone)]
struct MsaIntrinsic {
    id: String,
    arg_tys: Vec<MsaTy>,
    ret_ty: MsaTy,
    instruction: String,
}

impl std::convert::TryFrom<&'static str> for MsaIntrinsic {
    // The intrinsics are just C function declarations of the form:
    // $ret_ty __builtin_${fn_id}($($arg_ty),*);
    type Error = std::option::NoneError;
    fn try_from(line: &'static str) -> Result<Self, Self::Error> {
        let first_whitespace = line.find(char::is_whitespace)?;
        let ret_ty = &line[0..first_whitespace];
        let ret_ty = MsaTy::from(ret_ty);

        let first_parentheses = line.find('(')?;
        assert!(first_parentheses > first_whitespace);
        let id = &line[first_whitespace + 1..first_parentheses].trim();
        assert!(id.starts_with("__builtin"));
        let mut id_str = "_".to_string();
        id_str += &id[9..];
        let id = id_str;

        let mut arg_tys = Vec::new();

        let last_parentheses = line.find(')')?;
        for arg in (&line[first_parentheses + 1..last_parentheses]).split(',') {
            let arg = arg.trim();
            arg_tys.push(MsaTy::from(arg));
        }

        // The instruction is the intrinsic name without the __msa_ prefix.
        let instruction = &id[6..];
        let mut instruction = instruction.to_string();
        // With all underscores but the first one replaced with a `.`
        if let Some(first_underscore) = instruction.find('_') {
            let postfix = instruction[first_underscore + 1..].replace('_', ".");
            instruction = instruction[0..=first_underscore].to_string();
            instruction += &postfix;
        }

        Ok(MsaIntrinsic {
            id,
            ret_ty,
            arg_tys,
            instruction,
        })
    }
}

#[test]
fn verify_all_signatures() {
    // Parse the C intrinsic header file:
    let mut intrinsics = std::collections::HashMap::<String, MsaIntrinsic>::new();
    for line in HEADER.lines() {
        if line.is_empty() {
            continue;
        }

        use std::convert::TryFrom;
        let intrinsic: MsaIntrinsic = TryFrom::try_from(line)
            .unwrap_or_else(|_| panic!("failed to parse line: \"{}\"", line));
        assert!(!intrinsics.contains_key(&intrinsic.id));
        intrinsics.insert(intrinsic.id.clone(), intrinsic);
    }

    let mut all_valid = true;
    for rust in FUNCTIONS {
        if !rust.has_test {
            let skip = [
                "__msa_ceqi_d",
                "__msa_cfcmsa",
                "__msa_clei_s_d",
                "__msa_clti_s_d",
                "__msa_ctcmsa",
                "__msa_ldi_d",
                "__msa_maxi_s_d",
                "__msa_mini_s_d",
                "break_",
            ];
            if !skip.contains(&rust.name) {
                println!(
                    "missing run-time test named `test_{}` for `{}`",
                    {
                        let mut id = rust.name;
                        while id.starts_with('_') {
                            id = &id[1..];
                        }
                        id
                    },
                    rust.name
                );
                all_valid = false;
            }
        }

        // Skip some intrinsics that aren't part of MSA
        match rust.name {
            "break_" => continue,
            _ => {}
        }
        let mips = match intrinsics.get(rust.name) {
            Some(i) => i,
            None => {
                eprintln!(
                    "missing mips definition for {:?} in {}",
                    rust.name, rust.file
                );
                all_valid = false;
                continue;
            }
        };

        if let Err(e) = matches(rust, mips) {
            println!("failed to verify `{}`", rust.name);
            println!("  * {}", e);
            all_valid = false;
        }
    }
    assert!(all_valid);
}

fn matches(rust: &Function, mips: &MsaIntrinsic) -> Result<(), String> {
    macro_rules! bail {
        ($($t:tt)*) => (return Err(format!($($t)*)))
    }

    if rust.ret.is_none() && mips.ret_ty != MsaTy::Void {
        bail!("mismatched return value")
    }

    if rust.arguments.len() != mips.arg_tys.len() {
        bail!("mismatched argument lengths");
    }

    let mut nconst = 0;
    for (i, (rust_arg, mips_arg)) in rust.arguments.iter().zip(mips.arg_tys.iter()).enumerate() {
        match mips_arg {
            MsaTy::v16i8 if **rust_arg == v16i8 => (),
            MsaTy::v8i16 if **rust_arg == v8i16 => (),
            MsaTy::v4i32 if **rust_arg == v4i32 => (),
            MsaTy::v2i64 if **rust_arg == v2i64 => (),
            MsaTy::v16u8 if **rust_arg == v16u8 => (),
            MsaTy::v8u16 if **rust_arg == v8u16 => (),
            MsaTy::v4u32 if **rust_arg == v4u32 => (),
            MsaTy::v2u64 if **rust_arg == v2u64 => (),
            MsaTy::v4f32 if **rust_arg == v4f32 => (),
            MsaTy::v2f64 if **rust_arg == v2f64 => (),
            MsaTy::imm0_1
            | MsaTy::imm0_3
            | MsaTy::imm0_7
            | MsaTy::imm0_15
            | MsaTy::imm0_31
            | MsaTy::imm0_63
            | MsaTy::imm0_255
            | MsaTy::imm_n16_15
            | MsaTy::imm_n512_511
            | MsaTy::imm_n1024_1022
            | MsaTy::imm_n2048_2044
            | MsaTy::imm_n4096_4088
                if **rust_arg == I32 => {}
            MsaTy::i32 if **rust_arg == I32 => (),
            MsaTy::i64 if **rust_arg == I64 => (),
            MsaTy::u32 if **rust_arg == U32 => (),
            MsaTy::u64 if **rust_arg == U64 => (),
            MsaTy::MutVoidPtr if **rust_arg == Type::MutPtr(&U8) => (),
            m => bail!(
                "mismatched argument \"{}\"= \"{:?}\" != \"{:?}\"",
                i,
                m,
                *rust_arg
            ),
        }

        let is_const = matches!(
            mips_arg,
            MsaTy::imm0_1
                | MsaTy::imm0_3
                | MsaTy::imm0_7
                | MsaTy::imm0_15
                | MsaTy::imm0_31
                | MsaTy::imm0_63
                | MsaTy::imm0_255
                | MsaTy::imm_n16_15
                | MsaTy::imm_n512_511
                | MsaTy::imm_n1024_1022
                | MsaTy::imm_n2048_2044
                | MsaTy::imm_n4096_4088
        );
        if is_const {
            nconst += 1;
            if !rust.required_const.contains(&i) {
                bail!("argument const mismatch");
            }
        }
    }

    if nconst != rust.required_const.len() {
        bail!("wrong number of const arguments");
    }

    if rust.target_feature != Some("msa") {
        bail!("wrong target_feature");
    }

    if !rust.instrs.is_empty() {
        // Normalize slightly to get rid of assembler differences
        let actual = rust.instrs[0].replace(".", "_");
        let expected = mips.instruction.replace(".", "_");
        if actual != expected {
            bail!(
                "wrong instruction: \"{}\" != \"{}\"",
                rust.instrs[0],
                mips.instruction
            );
        }
    } else {
        bail!(
            "missing assert_instr for \"{}\" (should be \"{}\")",
            mips.id,
            mips.instruction
        );
    }

    Ok(())
}
<commit_msg>Remove `#![feature(try_trait)]` from a test (#1142)<commit_after>//! Verification of MIPS MSA intrinsics
#![allow(bad_style, unused)]

// This file is obtained from
// https://gcc.gnu.org/onlinedocs//gcc/MIPS-SIMD-Architecture-Built-in-Functions.html
static HEADER: &str = include_str!("../mips-msa.h");

stdarch_verify::mips_functions!(static FUNCTIONS);

struct Function {
    name: &'static str,
    arguments: &'static [&'static Type],
    ret: Option<&'static Type>,
    target_feature: Option<&'static str>,
    instrs: &'static [&'static str],
    file: &'static str,
    required_const: &'static [usize],
    has_test: bool,
}

static F16: Type = Type::PrimFloat(16);
static F32: Type = Type::PrimFloat(32);
static F64: Type = Type::PrimFloat(64);
static I8: Type = Type::PrimSigned(8);
static I16: Type = Type::PrimSigned(16);
static I32: Type = Type::PrimSigned(32);
static I64: Type = Type::PrimSigned(64);
static U8: Type = Type::PrimUnsigned(8);
static U16: Type = Type::PrimUnsigned(16);
static U32: Type = Type::PrimUnsigned(32);
static U64: Type = Type::PrimUnsigned(64);
static NEVER: Type = Type::Never;
static TUPLE: Type = Type::Tuple;
static v16i8: Type = Type::I(8, 16, 1);
static v8i16: Type = Type::I(16, 8, 1);
static v4i32: Type = Type::I(32, 4, 1);
static v2i64: Type = Type::I(64, 2, 1);
static v16u8: Type = Type::U(8, 16, 1);
static v8u16: Type = Type::U(16, 8, 1);
static v4u32: Type = Type::U(32, 4, 1);
static v2u64: Type = Type::U(64, 2, 1);
static v8f16: Type = Type::F(16, 8, 1);
static v4f32: Type = Type::F(32, 4, 1);
static v2f64: Type = Type::F(64, 2, 1);

#[derive(Debug, Copy, Clone, PartialEq)]
enum Type {
    PrimFloat(u8),
    PrimSigned(u8),
    PrimUnsigned(u8),
    PrimPoly(u8),
    MutPtr(&'static Type),
    ConstPtr(&'static Type),
    Tuple,
    I(u8, u8, u8),
    U(u8, u8, u8),
    P(u8, u8, u8),
    F(u8, u8, u8),
    Never,
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(non_camel_case_types)]
enum MsaTy {
    v16i8,
    v8i16,
    v4i32,
    v2i64,
    v16u8,
    v8u16,
    v4u32,
    v2u64,
    v8f16,
    v4f32,
    v2f64,
    imm0_1,
    imm0_3,
    imm0_7,
    imm0_15,
    imm0_31,
    imm0_63,
    imm0_255,
    imm_n16_15,
    imm_n512_511,
    imm_n1024_1022,
    imm_n2048_2044,
    imm_n4096_4088,
    i32,
    u32,
    i64,
    u64,
    Void,
    MutVoidPtr,
}

impl<'a> From<&'a str> for MsaTy {
    fn from(s: &'a str) -> MsaTy {
        match s {
            "v16i8" => MsaTy::v16i8,
            "v8i16" => MsaTy::v8i16,
            "v4i32" => MsaTy::v4i32,
            "v2i64" => MsaTy::v2i64,
            "v16u8" => MsaTy::v16u8,
            "v8u16" => MsaTy::v8u16,
            "v4u32" => MsaTy::v4u32,
            "v2u64" => MsaTy::v2u64,
            "v8f16" => MsaTy::v8f16,
            "v4f32" => MsaTy::v4f32,
            "v2f64" => MsaTy::v2f64,
            "imm0_1" => MsaTy::imm0_1,
            "imm0_3" => MsaTy::imm0_3,
            "imm0_7" => MsaTy::imm0_7,
            "imm0_15" => MsaTy::imm0_15,
            "imm0_31" => MsaTy::imm0_31,
            "imm0_63" => MsaTy::imm0_63,
            "imm0_255" => MsaTy::imm0_255,
            "imm_n16_15" => MsaTy::imm_n16_15,
            "imm_n512_511" => MsaTy::imm_n512_511,
            "imm_n1024_1022" => MsaTy::imm_n1024_1022,
            "imm_n2048_2044" => MsaTy::imm_n2048_2044,
            "imm_n4096_4088" => MsaTy::imm_n4096_4088,
            "i32" => MsaTy::i32,
            "u32" => MsaTy::u32,
            "i64" => MsaTy::i64,
            "u64" => MsaTy::u64,
            "void" => MsaTy::Void,
            "void *" => MsaTy::MutVoidPtr,
            v => panic!("unknown ty: \"{}\"", v),
        }
    }
}

#[derive(Debug, Clone)]
struct MsaIntrinsic {
    id: String,
    arg_tys: Vec<MsaTy>,
    ret_ty: MsaTy,
    instruction: String,
}

struct NoneError;

impl std::convert::TryFrom<&'static str> for MsaIntrinsic {
    // The intrinsics are just C function declarations of the form:
    // $ret_ty __builtin_${fn_id}($($arg_ty),*);
    type Error = NoneError;
    fn try_from(line: &'static str) -> Result<Self, Self::Error> {
        return inner(line).ok_or(NoneError);

        fn inner(line: &'static str) -> Option<MsaIntrinsic> {
            let first_whitespace = line.find(char::is_whitespace)?;
            let ret_ty = &line[0..first_whitespace];
            let ret_ty = MsaTy::from(ret_ty);

            let first_parentheses = line.find('(')?;
            assert!(first_parentheses > first_whitespace);
            let id = &line[first_whitespace + 1..first_parentheses].trim();
            assert!(id.starts_with("__builtin"));
            let mut id_str = "_".to_string();
            id_str += &id[9..];
            let id = id_str;

            let mut arg_tys = Vec::new();

            let last_parentheses = line.find(')')?;
            for arg in (&line[first_parentheses + 1..last_parentheses]).split(',') {
                let arg = arg.trim();
                arg_tys.push(MsaTy::from(arg));
            }

            // The instruction is the intrinsic name without the __msa_ prefix.
            let instruction = &id[6..];
            let mut instruction = instruction.to_string();
            // With all underscores but the first one replaced with a `.`
            if let Some(first_underscore) = instruction.find('_') {
                let postfix = instruction[first_underscore + 1..].replace('_', ".");
                instruction = instruction[0..=first_underscore].to_string();
                instruction += &postfix;
            }

            Some(MsaIntrinsic {
                id,
                ret_ty,
                arg_tys,
                instruction,
            })
        }
    }
}

#[test]
fn verify_all_signatures() {
    // Parse the C intrinsic header file:
    let mut intrinsics = std::collections::HashMap::<String, MsaIntrinsic>::new();
    for line in HEADER.lines() {
        if line.is_empty() {
            continue;
        }

        use std::convert::TryFrom;
        let intrinsic: MsaIntrinsic = TryFrom::try_from(line)
            .unwrap_or_else(|_| panic!("failed to parse line: \"{}\"", line));
        assert!(!intrinsics.contains_key(&intrinsic.id));
        intrinsics.insert(intrinsic.id.clone(), intrinsic);
    }

    let mut all_valid = true;
    for rust in FUNCTIONS {
        if !rust.has_test {
            let skip = [
                "__msa_ceqi_d",
                "__msa_cfcmsa",
                "__msa_clei_s_d",
                "__msa_clti_s_d",
                "__msa_ctcmsa",
                "__msa_ldi_d",
                "__msa_maxi_s_d",
                "__msa_mini_s_d",
                "break_",
            ];
            if !skip.contains(&rust.name) {
                println!(
                    "missing run-time test named `test_{}` for `{}`",
                    {
                        let mut id = rust.name;
                        while id.starts_with('_') {
                            id = &id[1..];
                        }
                        id
                    },
                    rust.name
                );
                all_valid = false;
            }
        }

        // Skip some intrinsics that aren't part of MSA
        match rust.name {
            "break_" => continue,
            _ => {}
        }
        let mips = match intrinsics.get(rust.name) {
            Some(i) => i,
            None => {
                eprintln!(
                    "missing mips definition for {:?} in {}",
                    rust.name, rust.file
                );
                all_valid = false;
                continue;
            }
        };

        if let Err(e) = matches(rust, mips) {
            println!("failed to verify `{}`", rust.name);
            println!("  * {}", e);
            all_valid = false;
        }
    }
    assert!(all_valid);
}

fn matches(rust: &Function, mips: &MsaIntrinsic) -> Result<(), String> {
    macro_rules! bail {
        ($($t:tt)*) => (return Err(format!($($t)*)))
    }

    if rust.ret.is_none() && mips.ret_ty != MsaTy::Void {
        bail!("mismatched return value")
    }

    if rust.arguments.len() != mips.arg_tys.len() {
        bail!("mismatched argument lengths");
    }

    let mut nconst = 0;
    for (i, (rust_arg, mips_arg)) in rust.arguments.iter().zip(mips.arg_tys.iter()).enumerate() {
        match mips_arg {
            MsaTy::v16i8 if **rust_arg == v16i8 => (),
            MsaTy::v8i16 if **rust_arg == v8i16 => (),
            MsaTy::v4i32 if **rust_arg == v4i32 => (),
            MsaTy::v2i64 if **rust_arg == v2i64 => (),
            MsaTy::v16u8 if **rust_arg == v16u8 => (),
            MsaTy::v8u16 if **rust_arg == v8u16 => (),
            MsaTy::v4u32 if **rust_arg == v4u32 => (),
            MsaTy::v2u64 if **rust_arg == v2u64 => (),
            MsaTy::v4f32 if **rust_arg == v4f32 => (),
            MsaTy::v2f64 if **rust_arg == v2f64 => (),
            MsaTy::imm0_1
            | MsaTy::imm0_3
            | MsaTy::imm0_7
            | MsaTy::imm0_15
            | MsaTy::imm0_31
            | MsaTy::imm0_63
            | MsaTy::imm0_255
            | MsaTy::imm_n16_15
            | MsaTy::imm_n512_511
            | MsaTy::imm_n1024_1022
            | MsaTy::imm_n2048_2044
            | MsaTy::imm_n4096_4088
                if **rust_arg == I32 => {}
            MsaTy::i32 if **rust_arg == I32 => (),
            MsaTy::i64 if **rust_arg == I64 => (),
            MsaTy::u32 if **rust_arg == U32 => (),
            MsaTy::u64 if **rust_arg == U64 => (),
            MsaTy::MutVoidPtr if **rust_arg == Type::MutPtr(&U8) => (),
            m => bail!(
                "mismatched argument \"{}\"= \"{:?}\" != \"{:?}\"",
                i,
                m,
                *rust_arg
            ),
        }

        let is_const = matches!(
            mips_arg,
            MsaTy::imm0_1
                | MsaTy::imm0_3
                | MsaTy::imm0_7
                | MsaTy::imm0_15
                | MsaTy::imm0_31
                | MsaTy::imm0_63
                | MsaTy::imm0_255
                | MsaTy::imm_n16_15
                | MsaTy::imm_n512_511
                | MsaTy::imm_n1024_1022
                | MsaTy::imm_n2048_2044
                | MsaTy::imm_n4096_4088
        );
        if is_const {
            nconst += 1;
            if !rust.required_const.contains(&i) {
                bail!("argument const mismatch");
            }
        }
    }

    if nconst != rust.required_const.len() {
        bail!("wrong number of const arguments");
    }

    if rust.target_feature != Some("msa") {
        bail!("wrong target_feature");
    }

    if !rust.instrs.is_empty() {
        // Normalize slightly to get rid of assembler differences
        let actual = rust.instrs[0].replace(".", "_");
        let expected = mips.instruction.replace(".", "_");
        if actual != expected {
            bail!(
                "wrong instruction: \"{}\" != \"{}\"",
                rust.instrs[0],
                mips.instruction
            );
        }
    } else {
        bail!(
            "missing assert_instr for \"{}\" (should be \"{}\")",
            mips.id,
            mips.instruction
        );
    }

    Ok(())
}
<|endoftext|>