<commit_before>#ifndef __FORK_JOIN_HPP__
#define __FORK_JOIN_HPP__

#include "SoftXMT.hpp"
#include "Addressing.hpp"
#include "tasks/Thread.hpp"
#include "Delegate.hpp"
#include "Tasking.hpp"
#include "Cache.hpp"
#include "common.hpp"

#include <iostream>
#include <fstream>

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/empty.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

DECLARE_int64(max_forkjoin_threads_per_node);

class Semaphore {
protected:
  int total;
  int count;
  Thread * sleeper;
public:
  Semaphore(int total, int starting): total(total), count(starting), sleeper(NULL) {}
  void acquire_all(Thread * me) {
    while (count < total) {
      VLOG(3) << "Semaphore.count = " << count << " of " << total << ", suspending...";
      sleeper = me;
      SoftXMT_suspend();
    }
  }
  void release(int n=1) {
    count += n;
    if (count >= total && sleeper) {
      SoftXMT_wake(sleeper);
      sleeper = NULL;
    }
  }
  static void am_release(GlobalAddress<Semaphore>* gaddr, size_t sz, void* payload, size_t psz) {
    VLOG(3) << "in am_release()";
    assert(gaddr->node() == SoftXMT_mynode());
    int n = (int)*((int64_t*)payload);
    VLOG(3) << "am_release n=" << n;
    gaddr->pointer()->release((int)n);
  }
  static void release(const GlobalAddress<Semaphore>* gaddr, int n) {
    VLOG(3) << "about to call on " << gaddr->node();
    SoftXMT_call_on(gaddr->node(), &Semaphore::am_release, gaddr, sizeof(GlobalAddress<Semaphore>), &n, sizeof(int64_t));
  }
};

struct LocalTaskJoiner {
  ThreadQueue wakelist;
  int64_t outstanding;
  LocalTaskJoiner(): outstanding(0) {}
  void reset() {
    outstanding = 0;
    while (!wakelist.empty()) SoftXMT_wake(wakelist.dequeue());
  }
  void registerTask() {
    outstanding++;
  }
  void signal() {
    if (outstanding == 0) {
      LOG(ERROR) << "too many calls to signal()";
    } else {
      outstanding--;
    }
    VLOG(3) << "barrier(outstanding=" << outstanding << ")";
    if (outstanding == 0) {
      while (!wakelist.empty()) {
        SoftXMT_wake(wakelist.dequeue());
      }
    }
  }
  void wait() {
    if (outstanding > 0) {
      wakelist.enqueue(CURRENT_THREAD);
      while (outstanding > 0) SoftXMT_suspend();
    }
  }
  static void am_remoteSignal(GlobalAddress<LocalTaskJoiner>* joinAddr, size_t sz, void* payload, size_t psz) {
    CHECK(joinAddr->node() == SoftXMT_mynode());
    joinAddr->pointer()->signal();
  }
  static void remoteSignal(GlobalAddress<LocalTaskJoiner> joinAddr) {
    if (joinAddr.node() == SoftXMT_mynode()) {
      joinAddr.pointer()->signal();
    } else {
      //VLOG(1) << "remoteSignal -> " << joinAddr.node();
      SoftXMT_call_on(joinAddr.node(), &LocalTaskJoiner::am_remoteSignal, &joinAddr);
    }
  }
};

struct ForkJoinIteration {
  void operator()(int64_t index);
};

template<typename T>
struct NodeForkJoinArgs {
  BOOST_STATIC_ASSERT((boost::is_base_of<ForkJoinIteration, T>::value));
  size_t start;
  size_t end;
  T func;
  GlobalAddress<Semaphore> sem;
};

template<typename T>
struct forkjoin_data_t {
  const T* func;
  size_t nthreads;
  size_t* finished;
  Thread * node_th;
  size_t local_start;
  size_t local_end;

  forkjoin_data_t(Thread * me, const T* f, int64_t start, int64_t end, size_t * finished) {
    size_t each_n = (end-start);
    local_start = start;
    local_end = end;
    func = f;
    nthreads = MIN(FLAGS_max_forkjoin_threads_per_node, each_n);
    this->finished = finished;
    node_th = me;
  }
};

struct iters_args {
  size_t rank;
  const void* fjdata;
};

template<typename T>
void task_iters(iters_args * arg) {
  const forkjoin_data_t<T> * fj = static_cast<const forkjoin_data_t<T>*>(arg->fjdata);
  range_t myblock = blockDist(fj->local_start, fj->local_end, arg->rank, fj->nthreads);
  VLOG(4) << "iters_block: " << myblock.start << " - " << myblock.end;

  for (int64_t i=myblock.start; i < myblock.end; i++) {
    (*fj->func)(i);
  }
  (*fj->finished)++;
  if (*fj->finished == fj->nthreads) {
    SoftXMT_wake(fj->node_th);
  }
}

template<typename T>
void fork_join_onenode(const T* func, int64_t start, int64_t end) {
  size_t finished = 0;

  forkjoin_data_t<T> fj(CURRENT_THREAD, func, start, end, &finished);
  iters_args args[fj.nthreads];
  VLOG(2) << "fj.nthreads = " << fj.nthreads;

  for (int i=0; i<fj.nthreads; i++) {
    args[i].fjdata = &fj;
    args[i].rank = i;

    SoftXMT_privateTask(task_iters<T>, &args[i]);
  }
  while (*fj.finished < fj.nthreads) SoftXMT_suspend();

}

template<typename T>
void th_node_fork_join(const NodeForkJoinArgs<T>* a) {
  range_t myblock = blockDist(a->start, a->end, SoftXMT_mynode(), SoftXMT_nodes());
  VLOG(2) << "myblock: " << myblock.start << " - " << myblock.end;
  fork_join_onenode(&a->func, myblock.start, myblock.end);

  VLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
void fork_join(T* func, int64_t start, int64_t end) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = start;
  fj.end = end;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (Node i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_privateTask(CACHE_WRAP(th_node_fork_join, &fj), i);

    SoftXMT_flush(i); // TODO: remove this?
  }
  VLOG(2) << "waiting to acquire all";
  sem.acquire_all(CURRENT_THREAD);

  VLOG(2) << "fork_join done";
}

template<typename T>
void th_node_fork_join_custom(const NodeForkJoinArgs<T>* a) {
  a->func(SoftXMT_mynode());

  VLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
void fork_join_custom(T* func) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = 0;
  fj.end = 0;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (Node i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_privateTask(CACHE_WRAP(th_node_fork_join_custom, &fj), i);
    SoftXMT_flush(i); // TODO: remove this?
  }
  VLOG(2) << "waiting to acquire all";
  sem.acquire_all(CURRENT_THREAD);

  VLOG(2) << "fork_join done";
}

/// Create a functor for iterations of a loop. This automatically creates a struct which is a subtype of ForkJoinIteration, with the given "state" arguments as fields and a constructor with the fields enumerated in the given order.
///
/// Arguments:
///   name: struct which is created
///   index: name of variable used for loop iteration
///   state: seq of type/name pairs for functor state, of the form:
///     ((type1,name1)) ((type2,name2)) ...
///
/// Example:
///   LOOP_FUNCTOR(set_all, i, ((GlobalAddress<int64_t>,array)) ((int64_t,value)) ) {
///     SoftXMT_delegate_write_word(array+i, value);
///   }
#define LOOP_FUNCTOR(name, index_var, members) \
struct name : ForkJoinIteration { \
AUTO_DECLS(members) \
AUTO_CONSTRUCTOR( name, members ) \
name() {} /* default constructor */\
inline void operator()(int64_t) const; \
}; \
inline void name::operator()(int64_t index_var) const

#define LOOP_FUNCTOR_TEMPLATED(T, name, index_var, members) \
template< typename T > \
struct name : ForkJoinIteration { \
AUTO_DECLS(members) \
AUTO_CONSTRUCTOR( name, members ) \
name() {} /* default constructor */\
inline void operator()(int64_t) const; \
}; \
template< typename T > \
inline void name::operator()(int64_t index_var) const

#define LOOP_FUNCTION(name, index_var) \
struct name : ForkJoinIteration { \
inline void operator()(int64_t) const; \
}; \
inline void name::operator()(int64_t index_var) const

struct ConstReplyArgs {
  int64_t replies_left;
  Thread * sleeper;
};

template< typename T >
struct ConstRequestArgs {
  GlobalAddress<T> addr;
  size_t count;
  T value;
  GlobalAddress<ConstReplyArgs> reply;
};

static void memset_reply_am(GlobalAddress<ConstReplyArgs> * reply, size_t sz, void * payload, size_t psz) {
  CHECK(reply->node() == SoftXMT_mynode());
  ConstReplyArgs * r = reply->pointer();
  (r->replies_left)--;
  if (r->replies_left == 0) {
    SoftXMT_wake(r->sleeper);
  }
}

template< typename T >
static void memset_request_am(ConstRequestArgs<T> * args, size_t sz, void* payload, size_t psz) {
  CHECK(args->addr.node() == SoftXMT_mynode()) << "args->addr.node() = " << args->addr.node();
  T * ptr = args->addr.pointer();
  for (size_t i=0; i<args->count; i++) {
    ptr[i] = args->value;
  }
  SoftXMT_call_on(args->reply.node(), &memset_reply_am, &args->reply);
}

template< typename T >
static void SoftXMT_memset(GlobalAddress<T> request_address, T value, size_t count) {
  size_t offset = 0;
  size_t request_bytes = 0;

  ConstReplyArgs reply;
  reply.replies_left = 0;
  reply.sleeper = CURRENT_THREAD;

  ConstRequestArgs<T> args;
  args.addr = request_address;
  args.value = value;
  args.reply = make_global(&reply);

  for (size_t total_bytes = count*sizeof(T); offset < total_bytes; offset += request_bytes) {
    // compute number of bytes remaining in the block containing args.addr
    request_bytes = (args.addr.first_byte().block_max() - args.addr.first_byte());
    if (request_bytes > total_bytes - offset) {
      request_bytes = total_bytes - offset;
    }
    CHECK(request_bytes % sizeof(T) == 0);
    args.count = request_bytes / sizeof(T);

    reply.replies_left++;
    SoftXMT_call_on(args.addr.node(), &memset_request_am, &args);

    args.addr += args.count;
  }

  while (reply.replies_left > 0) SoftXMT_suspend();
}

#endif /* define __FORK_JOIN_HPP__ */
<commit_msg>Fix templated version of loop_functor.<commit_after>#ifndef __FORK_JOIN_HPP__
#define __FORK_JOIN_HPP__

#include "SoftXMT.hpp"
#include "Addressing.hpp"
#include "tasks/Thread.hpp"
#include "Delegate.hpp"
#include "Tasking.hpp"
#include "Cache.hpp"
#include "common.hpp"

#include <iostream>
#include <fstream>

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/empty.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

DECLARE_int64(max_forkjoin_threads_per_node);

class Semaphore {
protected:
  int total;
  int count;
  Thread * sleeper;
public:
  Semaphore(int total, int starting): total(total), count(starting), sleeper(NULL) {}
  void acquire_all(Thread * me) {
    while (count < total) {
      VLOG(3) << "Semaphore.count = " << count << " of " << total << ", suspending...";
      sleeper = me;
      SoftXMT_suspend();
    }
  }
  void release(int n=1) {
    count += n;
    if (count >= total && sleeper) {
      SoftXMT_wake(sleeper);
      sleeper = NULL;
    }
  }
  static void am_release(GlobalAddress<Semaphore>* gaddr, size_t sz, void* payload, size_t psz) {
    VLOG(3) << "in am_release()";
    assert(gaddr->node() == SoftXMT_mynode());
    int n = (int)*((int64_t*)payload);
    VLOG(3) << "am_release n=" << n;
    gaddr->pointer()->release((int)n);
  }
  static void release(const GlobalAddress<Semaphore>* gaddr, int n) {
    VLOG(3) << "about to call on " << gaddr->node();
    SoftXMT_call_on(gaddr->node(), &Semaphore::am_release, gaddr, sizeof(GlobalAddress<Semaphore>), &n, sizeof(int64_t));
  }
};

struct LocalTaskJoiner {
  ThreadQueue wakelist;
  int64_t outstanding;
  LocalTaskJoiner(): outstanding(0) {}
  void reset() {
    outstanding = 0;
    while (!wakelist.empty()) SoftXMT_wake(wakelist.dequeue());
  }
  void registerTask() {
    outstanding++;
  }
  void signal() {
    if (outstanding == 0) {
      LOG(ERROR) << "too many calls to signal()";
    } else {
      outstanding--;
    }
    VLOG(3) << "barrier(outstanding=" << outstanding << ")";
    if (outstanding == 0) {
      while (!wakelist.empty()) {
        SoftXMT_wake(wakelist.dequeue());
      }
    }
  }
  void wait() {
    if (outstanding > 0) {
      wakelist.enqueue(CURRENT_THREAD);
      while (outstanding > 0) SoftXMT_suspend();
    }
  }
  static void am_remoteSignal(GlobalAddress<LocalTaskJoiner>* joinAddr, size_t sz, void* payload, size_t psz) {
    CHECK(joinAddr->node() == SoftXMT_mynode());
    joinAddr->pointer()->signal();
  }
  static void remoteSignal(GlobalAddress<LocalTaskJoiner> joinAddr) {
    if (joinAddr.node() == SoftXMT_mynode()) {
      joinAddr.pointer()->signal();
    } else {
      //VLOG(1) << "remoteSignal -> " << joinAddr.node();
      SoftXMT_call_on(joinAddr.node(), &LocalTaskJoiner::am_remoteSignal, &joinAddr);
    }
  }
};

struct ForkJoinIteration {
  void operator()(int64_t index);
};

template<typename T>
struct NodeForkJoinArgs {
  BOOST_STATIC_ASSERT((boost::is_base_of<ForkJoinIteration, T>::value));
  size_t start;
  size_t end;
  T func;
  GlobalAddress<Semaphore> sem;
};

template<typename T>
struct forkjoin_data_t {
  const T* func;
  size_t nthreads;
  size_t* finished;
  Thread * node_th;
  size_t local_start;
  size_t local_end;

  forkjoin_data_t(Thread * me, const T* f, int64_t start, int64_t end, size_t * finished) {
    size_t each_n = (end-start);
    local_start = start;
    local_end = end;
    func = f;
    nthreads = MIN(FLAGS_max_forkjoin_threads_per_node, each_n);
    this->finished = finished;
    node_th = me;
  }
};

struct iters_args {
  size_t rank;
  const void* fjdata;
};

template<typename T>
void task_iters(iters_args * arg) {
  const forkjoin_data_t<T> * fj = static_cast<const forkjoin_data_t<T>*>(arg->fjdata);
  range_t myblock = blockDist(fj->local_start, fj->local_end, arg->rank, fj->nthreads);
  VLOG(4) << "iters_block: " << myblock.start << " - " << myblock.end;

  for (int64_t i=myblock.start; i < myblock.end; i++) {
    (*fj->func)(i);
  }
  (*fj->finished)++;
  if (*fj->finished == fj->nthreads) {
    SoftXMT_wake(fj->node_th);
  }
}

template<typename T>
void fork_join_onenode(const T* func, int64_t start, int64_t end) {
  size_t finished = 0;

  forkjoin_data_t<T> fj(CURRENT_THREAD, func, start, end, &finished);
  iters_args args[fj.nthreads];
  VLOG(2) << "fj.nthreads = " << fj.nthreads;

  for (int i=0; i<fj.nthreads; i++) {
    args[i].fjdata = &fj;
    args[i].rank = i;

    SoftXMT_privateTask(task_iters<T>, &args[i]);
  }
  while (*fj.finished < fj.nthreads) SoftXMT_suspend();

}

template<typename T>
void th_node_fork_join(const NodeForkJoinArgs<T>* a) {
  range_t myblock = blockDist(a->start, a->end, SoftXMT_mynode(), SoftXMT_nodes());
  VLOG(2) << "myblock: " << myblock.start << " - " << myblock.end;
  fork_join_onenode(&a->func, myblock.start, myblock.end);

  VLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
void fork_join(T* func, int64_t start, int64_t end) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = start;
  fj.end = end;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (Node i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_privateTask(CACHE_WRAP(th_node_fork_join, &fj), i);

    SoftXMT_flush(i); // TODO: remove this?
  }
  VLOG(2) << "waiting to acquire all";
  sem.acquire_all(CURRENT_THREAD);

  VLOG(2) << "fork_join done";
}

template<typename T>
void th_node_fork_join_custom(const NodeForkJoinArgs<T>* a) {
  a->func(SoftXMT_mynode());

  VLOG(2) << "about to update sem on " << a->sem.node();
  Semaphore::release(&a->sem, 1);
}

template<typename T>
void fork_join_custom(T* func) {
  Semaphore sem(SoftXMT_nodes(), 0);

  NodeForkJoinArgs<T> fj;
  fj.start = 0;
  fj.end = 0;
  fj.func = *func;
  fj.sem = make_global(&sem);

  for (Node i=0; i < SoftXMT_nodes(); i++) {
    SoftXMT_remote_privateTask(CACHE_WRAP(th_node_fork_join_custom, &fj), i);
    SoftXMT_flush(i); // TODO: remove this?
  }
  VLOG(2) << "waiting to acquire all";
  sem.acquire_all(CURRENT_THREAD);

  VLOG(2) << "fork_join done";
}

/// Create a functor for iterations of a loop. This automatically creates a struct which is a subtype of ForkJoinIteration, with the given "state" arguments as fields and a constructor with the fields enumerated in the given order.
///
/// Arguments:
///   name: struct which is created
///   index: name of variable used for loop iteration
///   state: seq of type/name pairs for functor state, of the form:
///     ((type1,name1)) ((type2,name2)) ...
///
/// Example:
///   LOOP_FUNCTOR(set_all, i, ((GlobalAddress<int64_t>,array)) ((int64_t,value)) ) {
///     SoftXMT_delegate_write_word(array+i, value);
///   }
#define LOOP_FUNCTOR(name, index_var, members) \
struct name : ForkJoinIteration { \
AUTO_DECLS(members) \
AUTO_CONSTRUCTOR( name, members ) \
name() {} /* default constructor */\
inline void operator()(int64_t) const; \
}; \
inline void name::operator()(int64_t index_var) const

#define LOOP_FUNCTOR_TEMPLATED(T, name, index_var, members) \
template< typename T > \
struct name : ForkJoinIteration { \
AUTO_DECLS(members) \
AUTO_CONSTRUCTOR( name, members ) \
name() {} /* default constructor */\
inline void operator()(int64_t) const; \
}; \
template< typename T > \
inline void name<T>::operator()(int64_t index_var) const

#define LOOP_FUNCTION(name, index_var) \
struct name : ForkJoinIteration { \
inline void operator()(int64_t) const; \
}; \
inline void name::operator()(int64_t index_var) const

struct ConstReplyArgs {
  int64_t replies_left;
  Thread * sleeper;
};

template< typename T >
struct ConstRequestArgs {
  GlobalAddress<T> addr;
  size_t count;
  T value;
  GlobalAddress<ConstReplyArgs> reply;
};

static void memset_reply_am(GlobalAddress<ConstReplyArgs> * reply, size_t sz, void * payload, size_t psz) {
  CHECK(reply->node() == SoftXMT_mynode());
  ConstReplyArgs * r = reply->pointer();
  (r->replies_left)--;
  if (r->replies_left == 0) {
    SoftXMT_wake(r->sleeper);
  }
}

template< typename T >
static void memset_request_am(ConstRequestArgs<T> * args, size_t sz, void* payload, size_t psz) {
  CHECK(args->addr.node() == SoftXMT_mynode()) << "args->addr.node() = " << args->addr.node();
  T * ptr = args->addr.pointer();
  for (size_t i=0; i<args->count; i++) {
    ptr[i] = args->value;
  }
  SoftXMT_call_on(args->reply.node(), &memset_reply_am, &args->reply);
}

template< typename T >
static void SoftXMT_memset(GlobalAddress<T> request_address, T value, size_t count) {
  size_t offset = 0;
  size_t request_bytes = 0;

  ConstReplyArgs reply;
  reply.replies_left = 0;
  reply.sleeper = CURRENT_THREAD;

  ConstRequestArgs<T> args;
  args.addr = request_address;
  args.value = value;
  args.reply = make_global(&reply);

  for (size_t total_bytes = count*sizeof(T); offset < total_bytes; offset += request_bytes) {
    // compute number of bytes remaining in the block containing args.addr
    request_bytes = (args.addr.first_byte().block_max() - args.addr.first_byte());
    if (request_bytes > total_bytes - offset) {
      request_bytes = total_bytes - offset;
    }
    CHECK(request_bytes % sizeof(T) == 0);
    args.count = request_bytes / sizeof(T);

    reply.replies_left++;
    SoftXMT_call_on(args.addr.node(), &memset_request_am, &args);

    args.addr += args.count;
  }

  while (reply.replies_left > 0) SoftXMT_suspend();
}

#endif /* define __FORK_JOIN_HPP__ */
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2010 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real
    Copyright (C) 2017 Roman Lebedev

    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 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
*/

#include "decompressors/SamsungV1Decompressor.h"
#include "common/Common.h"                // for uint32, ushort16, int32
#include "common/Point.h"                 // for iPoint2D
#include "common/RawImage.h"              // for RawImage, RawImageData
#include "decoders/RawDecoderException.h" // for ThrowRDE
#include "decompressors/HuffmanTable.h"   // for HuffmanTable
#include "io/BitPumpMSB.h"                // for BitPumpMSB
#include <memory>                         // for allocator_traits<>::value_...
#include <vector>                         // for vector

namespace rawspeed {

struct SamsungV1Decompressor::encTableItem {
  uchar8 encLen;
  uchar8 diffLen;
};

SamsungV1Decompressor::SamsungV1Decompressor(const RawImage& image,
                                             const ByteStream* bs_, int bit)
    : AbstractSamsungDecompressor(image), bs(bs_), bits(bit) {
  const uint32 width = mRaw->dim.x;
  const uint32 height = mRaw->dim.y;

  if (width == 0 || height == 0 || width > 5664 || height > 3714)
    ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
}

int32 SamsungV1Decompressor::samsungDiff(BitPumpMSB* pump,
                                         const std::vector<encTableItem>& tbl) {
  // We read 10 bits to index into our table
  uint32 c = pump->peekBits(10);
  // Skip the bits that were used to encode this case
  pump->getBits(tbl[c].encLen);
  // Read the number of bits the table tells me
  int32 len = tbl[c].diffLen;
  int32 diff = pump->getBits(len);

  // If the first bit is 0 we need to turn this into a negative number
  diff = len != 0 ? HuffmanTable::signExtended(diff, len) : diff;

  return diff;
}

void SamsungV1Decompressor::decompress() {
  const uint32 width = mRaw->dim.x;
  const uint32 height = mRaw->dim.y;

  // This format has a variable length encoding of how many bits are needed
  // to encode the difference between pixels, we use a table to process it
  // that has two values, the first the number of bits that were used to
  // encode, the second the number of bits that come after with the difference
  // The table has 14 entries because the difference can have between 0 (no
  // difference) and 13 bits (differences between 12 bits numbers can need 13)
  const uchar8 tab[14][2] = {{3, 4},   {3, 7}, {2, 6},  {2, 5},  {4, 3},
                             {6, 0},   {7, 9}, {8, 10}, {9, 11}, {10, 12},
                             {10, 13}, {5, 1}, {4, 8},  {4, 2}};
  std::vector<encTableItem> tbl(1024);
  ushort16 vpred[2][2] = {{0, 0}, {0, 0}};
  ushort16 hpred[2];

  // We generate a 1024 entry table (to be addressed by reading 10 bits) by
  // consecutively filling in 2^(10-N) positions where N is the variable number
  // of bits of the encoding. So for example 4 is encoded with 3 bits so the
  // first 2^(10-3)=128 positions are set with 3,4 so that any time we read 000
  // we know the next 4 bits are the difference. We read 10 bits because that is
  // the maximum number of bits used in the variable encoding (for the 12 and
  // 13 cases)
  uint32 n = 0;
  for (auto i : tab) {
    for (int32 c = 0; c < (1024 >> i[0]); c++) {
      tbl[n].encLen = i[0];
      tbl[n].diffLen = i[1];
      n++;
    }
  }

  BitPumpMSB pump(*bs);
  for (uint32 y = 0; y < height; y++) {
    auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
    for (uint32 x = 0; x < width; x++) {
      int32 diff = samsungDiff(&pump, tbl);
      if (x < 2)
        hpred[x] = vpred[y & 1][x] += diff;
      else
        hpred[x & 1] += diff;
      img[x] = hpred[x & 1];
      if (img[x] >> bits)
        ThrowRDE("decoded value out of bounds at %d:%d", x, y);
    }
  }
}

} // namespace rawspeed
<commit_msg>SamsungV1Decompressor: improve sanitization of dims/bpp<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2010 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real
    Copyright (C) 2017 Roman Lebedev

    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 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
*/

#include "decompressors/SamsungV1Decompressor.h"
#include "common/Common.h"                // for uint32, ushort16, int32
#include "common/Point.h"                 // for iPoint2D
#include "common/RawImage.h"              // for RawImage, RawImageData
#include "decoders/RawDecoderException.h" // for ThrowRDE
#include "decompressors/HuffmanTable.h"   // for HuffmanTable
#include "io/BitPumpMSB.h"                // for BitPumpMSB
#include <memory>                         // for allocator_traits<>::value_...
#include <vector>                         // for vector

namespace rawspeed {

struct SamsungV1Decompressor::encTableItem {
  uchar8 encLen;
  uchar8 diffLen;
};

SamsungV1Decompressor::SamsungV1Decompressor(const RawImage& image,
                                             const ByteStream* bs_, int bit)
    : AbstractSamsungDecompressor(image), bs(bs_), bits(bit) {
  if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
      mRaw->getBpp() != 2)
    ThrowRDE("Unexpected component count / data type");

  switch (bit) {
  case 12:
    break;
  default:
    ThrowRDE("Unexpected bit per pixel (%u)", bit);
  }

  const uint32 width = mRaw->dim.x;
  const uint32 height = mRaw->dim.y;

  if (width == 0 || height == 0 || width > 5664 || height > 3714)
    ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
}

int32 SamsungV1Decompressor::samsungDiff(BitPumpMSB* pump,
                                         const std::vector<encTableItem>& tbl) {
  // We read 10 bits to index into our table
  uint32 c = pump->peekBits(10);
  // Skip the bits that were used to encode this case
  pump->getBits(tbl[c].encLen);
  // Read the number of bits the table tells me
  int32 len = tbl[c].diffLen;
  int32 diff = pump->getBits(len);

  // If the first bit is 0 we need to turn this into a negative number
  diff = len != 0 ? HuffmanTable::signExtended(diff, len) : diff;

  return diff;
}

void SamsungV1Decompressor::decompress() {
  const uint32 width = mRaw->dim.x;
  const uint32 height = mRaw->dim.y;

  // This format has a variable length encoding of how many bits are needed
  // to encode the difference between pixels, we use a table to process it
  // that has two values, the first the number of bits that were used to
  // encode, the second the number of bits that come after with the difference
  // The table has 14 entries because the difference can have between 0 (no
  // difference) and 13 bits (differences between 12 bits numbers can need 13)
  const uchar8 tab[14][2] = {{3, 4},   {3, 7}, {2, 6},  {2, 5},  {4, 3},
                             {6, 0},   {7, 9}, {8, 10}, {9, 11}, {10, 12},
                             {10, 13}, {5, 1}, {4, 8},  {4, 2}};
  std::vector<encTableItem> tbl(1024);
  ushort16 vpred[2][2] = {{0, 0}, {0, 0}};
  ushort16 hpred[2];

  // We generate a 1024 entry table (to be addressed by reading 10 bits) by
  // consecutively filling in 2^(10-N) positions where N is the variable number
  // of bits of the encoding. So for example 4 is encoded with 3 bits so the
  // first 2^(10-3)=128 positions are set with 3,4 so that any time we read 000
  // we know the next 4 bits are the difference. We read 10 bits because that is
  // the maximum number of bits used in the variable encoding (for the 12 and
  // 13 cases)
  uint32 n = 0;
  for (auto i : tab) {
    for (int32 c = 0; c < (1024 >> i[0]); c++) {
      tbl[n].encLen = i[0];
      tbl[n].diffLen = i[1];
      n++;
    }
  }

  BitPumpMSB pump(*bs);
  for (uint32 y = 0; y < height; y++) {
    auto* img = reinterpret_cast<ushort16*>(mRaw->getData(0, y));
    for (uint32 x = 0; x < width; x++) {
      int32 diff = samsungDiff(&pump, tbl);
      if (x < 2)
        hpred[x] = vpred[y & 1][x] += diff;
      else
        hpred[x & 1] += diff;
      img[x] = hpred[x & 1];
      if (img[x] >> bits)
        ThrowRDE("decoded value out of bounds at %d:%d", x, y);
    }
  }
}

} // namespace rawspeed
<|endoftext|>


<commit_before>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2010 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real
    Copyright (C) 2017 Roman Lebedev

    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 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
*/

#include "decompressors/SamsungV1Decompressor.h"
#include "common/Common.h"                // for uint32_t, uint16_t, int32_t
#include "common/Point.h"                 // for iPoint2D
#include "common/RawImage.h"              // for RawImage, RawImageData
#include "decoders/RawDecoderException.h" // for ThrowRDE
#include "decompressors/HuffmanTable.h"   // for HuffmanTable
#include "io/BitPumpMSB.h"                // for BitPumpMSB
#include <memory>                         // for allocator_traits<>::value_...
#include <vector>                         // for vector

namespace rawspeed {

struct SamsungV1Decompressor::encTableItem {
  uint8_t encLen;
  uint8_t diffLen;
};

SamsungV1Decompressor::SamsungV1Decompressor(const RawImage& image,
                                             const ByteStream* bs_, int bit)
    : AbstractSamsungDecompressor(image), bs(bs_), bits(bit) {
  if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
      mRaw->getBpp() != 2)
    ThrowRDE("Unexpected component count / data type");

  switch (bit) {
  case 12:
    break;
  default:
    ThrowRDE("Unexpected bit per pixel (%u)", bit);
  }

  const uint32_t width = mRaw->dim.x;
  const uint32_t height = mRaw->dim.y;

  if (width == 0 || height == 0 || width > 5664 || height > 3714)
    ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
}

inline int32_t
SamsungV1Decompressor::samsungDiff(BitPumpMSB* pump,
                                   const std::vector<encTableItem>& tbl) {
  pump->fill(23); // That is the maximal number of bits we will need here.
  // We read 10 bits to index into our table
  uint32_t c = pump->peekBitsNoFill(10);
  // Skip the bits that were used to encode this case
  pump->skipBitsNoFill(tbl[c].encLen);
  // Read the number of bits the table tells me
  int32_t len = tbl[c].diffLen;
  if (len == 0)
    return 0;
  int32_t diff = pump->getBitsNoFill(len);
  // If the first bit is 0 we need to turn this into a negative number
  diff = HuffmanTable::extend(diff, len);
  return diff;
}

void SamsungV1Decompressor::decompress() {
  // This format has a variable length encoding of how many bits are needed
  // to encode the difference between pixels, we use a table to process it
  // that has two values, the first the number of bits that were used to
  // encode, the second the number of bits that come after with the difference
  // The table has 14 entries because the difference can have between 0 (no
  // difference) and 13 bits (differences between 12 bits numbers can need 13)
  static const std::array<std::array<uint8_t, 2>, 14> tab = {{{3, 4},
                                                              {3, 7},
                                                              {2, 6},
                                                              {2, 5},
                                                              {4, 3},
                                                              {6, 0},
                                                              {7, 9},
                                                              {8, 10},
                                                              {9, 11},
                                                              {10, 12},
                                                              {10, 13},
                                                              {5, 1},
                                                              {4, 8},
                                                              {4, 2}}};
  std::vector<encTableItem> tbl(1024);
  std::array<std::array<uint16_t, 2>, 2> vpred = {{}};
  std::array<uint16_t, 2> hpred;

  // We generate a 1024 entry table (to be addressed by reading 10 bits) by
  // consecutively filling in 2^(10-N) positions where N is the variable number
  // of bits of the encoding. So for example 4 is encoded with 3 bits so the
  // first 2^(10-3)=128 positions are set with 3,4 so that any time we read 000
  // we know the next 4 bits are the difference. We read 10 bits because that is
  // the maximum number of bits used in the variable encoding (for the 12 and
  // 13 cases)
  uint32_t n = 0;
  for (auto i : tab) {
    for (int32_t c = 0; c < (1024 >> i[0]); c++) {
      tbl[n].encLen = i[0];
      tbl[n].diffLen = i[1];
      n++;
    }
  }

  const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
  BitPumpMSB pump(*bs);
  for (int row = 0; row < out.height; row++) {
    for (int col = 0; col < out.width; col++) {
      int32_t diff = samsungDiff(&pump, tbl);
      if (col < 2)
        hpred[col] = vpred[row & 1][col] += diff;
      else
        hpred[col & 1] += diff;
      // FIXME: this is broken.
      out(row, col) = hpred[col & 1];
      if (out(row, col) >> bits)
        ThrowRDE("decoded value out of bounds at %d:%d", col, row);
    }
  }
}

} // namespace rawspeed
<commit_msg>SamsungV1Decompressor::decompress(): simplify/fix pred handling (-12%)<commit_after>/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2010 Klaus Post
    Copyright (C) 2014-2015 Pedro Côrte-Real
    Copyright (C) 2017 Roman Lebedev

    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 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
*/

#include "decompressors/SamsungV1Decompressor.h"
#include "common/Common.h"                // for uint32_t, uint16_t, int32_t
#include "common/Point.h"                 // for iPoint2D
#include "common/RawImage.h"              // for RawImage, RawImageData
#include "decoders/RawDecoderException.h" // for ThrowRDE
#include "decompressors/HuffmanTable.h"   // for HuffmanTable
#include "io/BitPumpMSB.h"                // for BitPumpMSB
#include <memory>                         // for allocator_traits<>::value_...
#include <vector>                         // for vector

namespace rawspeed {

struct SamsungV1Decompressor::encTableItem {
  uint8_t encLen;
  uint8_t diffLen;
};

SamsungV1Decompressor::SamsungV1Decompressor(const RawImage& image,
                                             const ByteStream* bs_, int bit)
    : AbstractSamsungDecompressor(image), bs(bs_), bits(bit) {
  if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
      mRaw->getBpp() != 2)
    ThrowRDE("Unexpected component count / data type");

  switch (bit) {
  case 12:
    break;
  default:
    ThrowRDE("Unexpected bit per pixel (%u)", bit);
  }

  const uint32_t width = mRaw->dim.x;
  const uint32_t height = mRaw->dim.y;

  if (width == 0 || height == 0 || width > 5664 || height > 3714)
    ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
}

inline int32_t
SamsungV1Decompressor::samsungDiff(BitPumpMSB* pump,
                                   const std::vector<encTableItem>& tbl) {
  pump->fill(23); // That is the maximal number of bits we will need here.
  // We read 10 bits to index into our table
  uint32_t c = pump->peekBitsNoFill(10);
  // Skip the bits that were used to encode this case
  pump->skipBitsNoFill(tbl[c].encLen);
  // Read the number of bits the table tells me
  int32_t len = tbl[c].diffLen;
  if (len == 0)
    return 0;
  int32_t diff = pump->getBitsNoFill(len);
  // If the first bit is 0 we need to turn this into a negative number
  diff = HuffmanTable::extend(diff, len);
  return diff;
}

void SamsungV1Decompressor::decompress() {
  // This format has a variable length encoding of how many bits are needed
  // to encode the difference between pixels, we use a table to process it
  // that has two values, the first the number of bits that were used to
  // encode, the second the number of bits that come after with the difference
  // The table has 14 entries because the difference can have between 0 (no
  // difference) and 13 bits (differences between 12 bits numbers can need 13)
  static const std::array<std::array<uint8_t, 2>, 14> tab = {{{3, 4},
                                                              {3, 7},
                                                              {2, 6},
                                                              {2, 5},
                                                              {4, 3},
                                                              {6, 0},
                                                              {7, 9},
                                                              {8, 10},
                                                              {9, 11},
                                                              {10, 12},
                                                              {10, 13},
                                                              {5, 1},
                                                              {4, 8},
                                                              {4, 2}}};
  std::vector<encTableItem> tbl(1024);

  // We generate a 1024 entry table (to be addressed by reading 10 bits) by
  // consecutively filling in 2^(10-N) positions where N is the variable number
  // of bits of the encoding. So for example 4 is encoded with 3 bits so the
  // first 2^(10-3)=128 positions are set with 3,4 so that any time we read 000
  // we know the next 4 bits are the difference. We read 10 bits because that is
  // the maximum number of bits used in the variable encoding (for the 12 and
  // 13 cases)
  uint32_t n = 0;
  for (auto i : tab) {
    for (int32_t c = 0; c < (1024 >> i[0]); c++) {
      tbl[n].encLen = i[0];
      tbl[n].diffLen = i[1];
      n++;
    }
  }

  const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
  BitPumpMSB pump(*bs);
  for (int row = 0; row < out.height; row++) {
    std::array<int, 2> pred = {{}};
    if (row >= 2)
      pred = {out(row - 2, 0), out(row - 2, 1)};

    for (int col = 0; col < out.width; col++) {
      int32_t diff = samsungDiff(&pump, tbl);
      pred[col & 1] += diff;

      int value = pred[col & 1];
      if (!isIntN(value, bits))
        ThrowRDE("decoded value out of bounds at %d:%d", col, row);
      out(row, col) = value;
    }
  }
}

} // namespace rawspeed
<|endoftext|>


<commit_before>// Copyright 2021 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "wfa/panelmatch/common/compression/brotli.h"

#include <memory>
#include <string>

#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "riegeli/brotli/brotli_reader.h"
#include "riegeli/brotli/brotli_writer.h"
#include "riegeli/bytes/string_reader.h"
#include "riegeli/bytes/string_writer.h"
#include "wfa/panelmatch/common/compression/compressor.h"

namespace wfa::panelmatch {
namespace {
using ::riegeli::BrotliDictionary;
using ::riegeli::BrotliReader;
using ::riegeli::BrotliReaderBase;
using ::riegeli::BrotliWriter;
using ::riegeli::BrotliWriterBase;
using ::riegeli::StringReader;
using ::riegeli::StringWriter;

BrotliWriterBase::Options MakeWriterOptions(absl::string_view dictionary) {
  return BrotliWriterBase::Options()
      .set_compression_level(BrotliWriterBase::Options::kMaxCompressionLevel)
      .set_dictionary(BrotliDictionary().add_raw(dictionary));
}

BrotliReaderBase::Options MakeReaderOptions(absl::string_view dictionary) {
  return BrotliReaderBase::Options().set_dictionary(
      BrotliDictionary().add_raw(dictionary));
}

class Brotli : public Compressor {
 public:
  explicit Brotli(absl::string_view dictionary)
      : writer_options_(MakeWriterOptions(dictionary)),
        reader_options_(MakeReaderOptions(dictionary)) {}

  absl::StatusOr<std::string> Compress(
      absl::string_view uncompressed_data) const override {
    std::string result;
    BrotliWriter brotli_writer(StringWriter(&result), writer_options_);
    if (!brotli_writer.Write(uncompressed_data) || !brotli_writer.Close()) {
      return brotli_writer.status();
    }
    return result;
  }

  absl::StatusOr<std::string> Decompress(
      absl::string_view compressed_data) const override {
    std::string result;
    BrotliReader brotli_reader(StringReader(&compressed_data), reader_options_);
    if (!brotli_reader.ReadAll(result) || !brotli_reader.Close()) {
      return brotli_reader.status();
    }
    return result;
  }

 private:
  BrotliWriterBase::Options writer_options_;
  BrotliReaderBase::Options reader_options_;
};

}  // namespace

std::unique_ptr<Compressor> BuildBrotliCompressor(
    absl::string_view dictionary) {
  return absl::make_unique<Brotli>(dictionary);
}

}  // namespace wfa::panelmatch
<commit_msg>Add typing to c++ template for brotli for compiler compatibility (#237)<commit_after>// Copyright 2021 The Cross-Media Measurement Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "wfa/panelmatch/common/compression/brotli.h"

#include <memory>
#include <string>

#include "absl/memory/memory.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "riegeli/brotli/brotli_reader.h"
#include "riegeli/brotli/brotli_writer.h"
#include "riegeli/bytes/string_reader.h"
#include "riegeli/bytes/string_writer.h"
#include "wfa/panelmatch/common/compression/compressor.h"

namespace wfa::panelmatch {
namespace {
using ::riegeli::BrotliDictionary;
using ::riegeli::BrotliReader;
using ::riegeli::BrotliReaderBase;
using ::riegeli::BrotliWriter;
using ::riegeli::BrotliWriterBase;
using ::riegeli::StringReader;
using ::riegeli::StringWriter;

BrotliWriterBase::Options MakeWriterOptions(absl::string_view dictionary) {
  return BrotliWriterBase::Options()
      .set_compression_level(BrotliWriterBase::Options::kMaxCompressionLevel)
      .set_dictionary(BrotliDictionary().add_raw(dictionary));
}

BrotliReaderBase::Options MakeReaderOptions(absl::string_view dictionary) {
  return BrotliReaderBase::Options().set_dictionary(
      BrotliDictionary().add_raw(dictionary));
}

class Brotli : public Compressor {
 public:
  explicit Brotli(absl::string_view dictionary)
      : writer_options_(MakeWriterOptions(dictionary)),
        reader_options_(MakeReaderOptions(dictionary)) {}

  absl::StatusOr<std::string> Compress(
      absl::string_view uncompressed_data) const override {
    std::string result;
    BrotliWriter brotli_writer(StringWriter<std::string *>(&result),
                               writer_options_);
    if (!brotli_writer.Write(uncompressed_data) || !brotli_writer.Close()) {
      return brotli_writer.status();
    }
    return result;
  }

  absl::StatusOr<std::string> Decompress(
      absl::string_view compressed_data) const override {
    std::string result;
    BrotliReader brotli_reader(
        StringReader<absl::string_view *>(&compressed_data), reader_options_);
    if (!brotli_reader.ReadAll(result) || !brotli_reader.Close()) {
      return brotli_reader.status();
    }
    return result;
  }

 private:
  BrotliWriterBase::Options writer_options_;
  BrotliReaderBase::Options reader_options_;
};

}  // namespace

std::unique_ptr<Compressor> BuildBrotliCompressor(
    absl::string_view dictionary) {
  return absl::make_unique<Brotli>(dictionary);
}

}  // namespace wfa::panelmatch
<|endoftext|>


<commit_before>/*
 * Copyright © 2011 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * @file brw_vec4_copy_propagation.cpp
 *
 * Implements tracking of values copied between registers, and
 * optimizations based on that: copy propagation and constant
 * propagation.
 */

#include "brw_vec4.h"
extern "C" {
#include "main/macros.h"
}

namespace brw {

static bool
is_direct_copy(vec4_instruction *inst)
{
   return (inst->opcode == BRW_OPCODE_MOV &&
	   !inst->predicate &&
	   inst->dst.file == GRF &&
	   !inst->saturate &&
	   !inst->dst.reladdr &&
	   !inst->src[0].reladdr &&
	   inst->dst.type == inst->src[0].type);
}

static bool
is_dominated_by_previous_instruction(vec4_instruction *inst)
{
   return (inst->opcode != BRW_OPCODE_DO &&
	   inst->opcode != BRW_OPCODE_WHILE &&
	   inst->opcode != BRW_OPCODE_ELSE &&
	   inst->opcode != BRW_OPCODE_ENDIF);
}

static bool
is_channel_updated(vec4_instruction *inst, src_reg *values[4], int ch)
{
   const src_reg *src = values[ch];

   /* consider GRF only */
   assert(inst->dst.file == GRF);
   if (!src || src->file != GRF)
      return false;

   return (src->reg == inst->dst.reg &&
	   src->reg_offset == inst->dst.reg_offset &&
	   inst->dst.writemask & (1 << BRW_GET_SWZ(src->swizzle, ch)));
}

static bool
try_constant_propagate(struct brw_context *brw, vec4_instruction *inst,
                       int arg, src_reg *values[4])
{
   /* For constant propagation, we only handle the same constant
    * across all 4 channels.  Some day, we should handle the 8-bit
    * float vector format, which would let us constant propagate
    * vectors better.
    */
   src_reg value = *values[0];
   for (int i = 1; i < 4; i++) {
      if (!value.equals(*values[i]))
	 return false;
   }

   if (value.file != IMM)
      return false;

   if (inst->src[arg].abs) {
      if (value.type == BRW_REGISTER_TYPE_F) {
	 value.imm.f = fabs(value.imm.f);
      } else if (value.type == BRW_REGISTER_TYPE_D) {
	 if (value.imm.i < 0)
	    value.imm.i = -value.imm.i;
      }
   }

   if (inst->src[arg].negate) {
      if (value.type == BRW_REGISTER_TYPE_F)
	 value.imm.f = -value.imm.f;
      else
	 value.imm.u = -value.imm.u;
   }

   switch (inst->opcode) {
   case BRW_OPCODE_MOV:
      inst->src[arg] = value;
      return true;

   case SHADER_OPCODE_POW:
   case SHADER_OPCODE_INT_QUOTIENT:
   case SHADER_OPCODE_INT_REMAINDER:
      if (brw->gen < 8)
         break;
      /* fallthrough */
   case BRW_OPCODE_DP2:
   case BRW_OPCODE_DP3:
   case BRW_OPCODE_DP4:
   case BRW_OPCODE_DPH:
   case BRW_OPCODE_BFI1:
   case BRW_OPCODE_ASR:
   case BRW_OPCODE_SHL:
   case BRW_OPCODE_SHR:
   case BRW_OPCODE_SUBB:
      if (arg == 1) {
         inst->src[arg] = value;
         return true;
      }
      break;

   case BRW_OPCODE_MACH:
   case BRW_OPCODE_MUL:
   case BRW_OPCODE_ADD:
   case BRW_OPCODE_OR:
   case BRW_OPCODE_AND:
   case BRW_OPCODE_XOR:
   case BRW_OPCODE_ADDC:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 /* Fit this constant in by commuting the operands.  Exception: we
	  * can't do this for 32-bit integer MUL/MACH because it's asymmetric.
	  */
	 if ((inst->opcode == BRW_OPCODE_MUL ||
              inst->opcode == BRW_OPCODE_MACH) &&
	     (inst->src[1].type == BRW_REGISTER_TYPE_D ||
	      inst->src[1].type == BRW_REGISTER_TYPE_UD))
	    break;
	 inst->src[0] = inst->src[1];
	 inst->src[1] = value;
	 return true;
      }
      break;

   case BRW_OPCODE_CMP:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 uint32_t new_cmod;

	 new_cmod = brw_swap_cmod(inst->conditional_mod);
	 if (new_cmod != ~0u) {
	    /* Fit this constant in by swapping the operands and
	     * flipping the test.
	     */
	    inst->src[0] = inst->src[1];
	    inst->src[1] = value;
	    inst->conditional_mod = new_cmod;
	    return true;
	 }
      }
      break;

   case BRW_OPCODE_SEL:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 inst->src[0] = inst->src[1];
	 inst->src[1] = value;

	 /* If this was predicated, flipping operands means
	  * we also need to flip the predicate.
	  */
	 if (inst->conditional_mod == BRW_CONDITIONAL_NONE) {
	    inst->predicate_inverse = !inst->predicate_inverse;
	 }
	 return true;
      }
      break;

   default:
      break;
   }

   return false;
}

static bool
is_logic_op(enum opcode opcode)
{
   return (opcode == BRW_OPCODE_AND ||
           opcode == BRW_OPCODE_OR  ||
           opcode == BRW_OPCODE_XOR ||
           opcode == BRW_OPCODE_NOT);
}

static bool
try_copy_propagate(struct brw_context *brw, vec4_instruction *inst,
                   int arg, src_reg *values[4])
{
   /* For constant propagation, we only handle the same constant
    * across all 4 channels.  Some day, we should handle the 8-bit
    * float vector format, which would let us constant propagate
    * vectors better.
    */
   src_reg value = *values[0];
   for (int i = 1; i < 4; i++) {
      /* This is equals() except we don't care about the swizzle. */
      if (value.file != values[i]->file ||
	  value.reg != values[i]->reg ||
	  value.reg_offset != values[i]->reg_offset ||
	  value.type != values[i]->type ||
	  value.negate != values[i]->negate ||
	  value.abs != values[i]->abs) {
	 return false;
      }
   }

   /* Compute the swizzle of the original register by swizzling the
    * component loaded from each value according to the swizzle of
    * operand we're going to change.
    */
   int s[4];
   for (int i = 0; i < 4; i++) {
      s[i] = BRW_GET_SWZ(values[i]->swizzle,
			 BRW_GET_SWZ(inst->src[arg].swizzle, i));
   }
   value.swizzle = BRW_SWIZZLE4(s[0], s[1], s[2], s[3]);

   if (value.file != UNIFORM &&
       value.file != GRF &&
       value.file != ATTR)
      return false;

   if (brw->gen >= 8) {
      if (value.negate) {
         if (is_logic_op(inst->opcode)) {
            return false;
         }
      }
   }

   if (inst->src[arg].abs) {
      value.negate = false;
      value.abs = true;
   }
   if (inst->src[arg].negate)
      value.negate = !value.negate;

   bool has_source_modifiers = value.negate || value.abs;

   /* gen6 math and gen7+ SENDs from GRFs ignore source modifiers on
    * instructions.
    */
   if ((has_source_modifiers || value.file == UNIFORM ||
        value.swizzle != BRW_SWIZZLE_XYZW) && !inst->can_do_source_mods(brw))
      return false;

   if (has_source_modifiers && value.type != inst->src[arg].type)
      return false;

   bool is_3src_inst = (inst->opcode == BRW_OPCODE_LRP ||
                        inst->opcode == BRW_OPCODE_MAD ||
                        inst->opcode == BRW_OPCODE_BFE ||
                        inst->opcode == BRW_OPCODE_BFI2);
   if (is_3src_inst && value.file == UNIFORM)
      return false;

   if (inst->is_send_from_grf())
      return false;

   /* We can't copy-propagate a UD negation into a condmod
    * instruction, because the condmod ends up looking at the 33-bit
    * signed accumulator value instead of the 32-bit value we wanted
    */
   if (inst->conditional_mod &&
       value.negate &&
       value.type == BRW_REGISTER_TYPE_UD)
      return false;

   /* Don't report progress if this is a noop. */
   if (value.equals(inst->src[arg]))
      return false;

   value.type = inst->src[arg].type;
   inst->src[arg] = value;
   return true;
}

bool
vec4_visitor::opt_copy_propagation()
{
   bool progress = false;
   src_reg *cur_value[virtual_grf_reg_count][4];

   memset(&cur_value, 0, sizeof(cur_value));

   foreach_list(node, &this->instructions) {
      vec4_instruction *inst = (vec4_instruction *)node;

      /* This pass only works on basic blocks.  If there's flow
       * control, throw out all our information and start from
       * scratch.
       *
       * This should really be fixed by using a structure like in
       * src/glsl/opt_copy_propagation.cpp to track available copies.
       */
      if (!is_dominated_by_previous_instruction(inst)) {
	 memset(cur_value, 0, sizeof(cur_value));
	 continue;
      }

      /* For each source arg, see if each component comes from a copy
       * from the same type file (IMM, GRF, UNIFORM), and try
       * optimizing out access to the copy result
       */
      for (int i = 2; i >= 0; i--) {
	 /* Copied values end up in GRFs, and we don't track reladdr
	  * accesses.
	  */
	 if (inst->src[i].file != GRF ||
	     inst->src[i].reladdr)
	    continue;

	 int reg = (virtual_grf_reg_map[inst->src[i].reg] +
		    inst->src[i].reg_offset);

	 /* Find the regs that each swizzle component came from.
	  */
	 src_reg *values[4];
	 int c;
	 for (c = 0; c < 4; c++) {
	    values[c] = cur_value[reg][BRW_GET_SWZ(inst->src[i].swizzle, c)];

	    /* If there's no available copy for this channel, bail.
	     * We could be more aggressive here -- some channels might
	     * not get used based on the destination writemask.
	     */
	    if (!values[c])
	       break;

	    /* We'll only be able to copy propagate if the sources are
	     * all from the same file -- there's no ability to swizzle
	     * 0 or 1 constants in with source registers like in i915.
	     */
	    if (c > 0 && values[c - 1]->file != values[c]->file)
	       break;
	 }

	 if (c != 4)
	    continue;

	 if (try_constant_propagate(brw, inst, i, values) ||
	     try_copy_propagate(brw, inst, i, values))
	    progress = true;
      }

      /* Track available source registers. */
      if (inst->dst.file == GRF) {
	 const int reg =
	    virtual_grf_reg_map[inst->dst.reg] + inst->dst.reg_offset;

	 /* Update our destination's current channel values.  For a direct copy,
	  * the value is the newly propagated source.  Otherwise, we don't know
	  * the new value, so clear it.
	  */
	 bool direct_copy = is_direct_copy(inst);
	 for (int i = 0; i < 4; i++) {
	    if (inst->dst.writemask & (1 << i)) {
	       cur_value[reg][i] = direct_copy ? &inst->src[0] : NULL;
	    }
	 }

	 /* Clear the records for any registers whose current value came from
	  * our destination's updated channels, as the two are no longer equal.
	  */
	 if (inst->dst.reladdr)
	    memset(cur_value, 0, sizeof(cur_value));
	 else {
	    for (int i = 0; i < virtual_grf_reg_count; i++) {
	       for (int j = 0; j < 4; j++) {
		  if (is_channel_updated(inst, cur_value[i], j)){
		     cur_value[i][j] = NULL;
		  }
	       }
	    }
	 }
      }
   }

   if (progress)
      invalidate_live_intervals();

   return progress;
}

} /* namespace brw */
<commit_msg>i965/vec4: Try constant propagate after copy propagate made progress.<commit_after>/*
 * Copyright © 2011 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * @file brw_vec4_copy_propagation.cpp
 *
 * Implements tracking of values copied between registers, and
 * optimizations based on that: copy propagation and constant
 * propagation.
 */

#include "brw_vec4.h"
extern "C" {
#include "main/macros.h"
}

namespace brw {

static bool
is_direct_copy(vec4_instruction *inst)
{
   return (inst->opcode == BRW_OPCODE_MOV &&
	   !inst->predicate &&
	   inst->dst.file == GRF &&
	   !inst->saturate &&
	   !inst->dst.reladdr &&
	   !inst->src[0].reladdr &&
	   inst->dst.type == inst->src[0].type);
}

static bool
is_dominated_by_previous_instruction(vec4_instruction *inst)
{
   return (inst->opcode != BRW_OPCODE_DO &&
	   inst->opcode != BRW_OPCODE_WHILE &&
	   inst->opcode != BRW_OPCODE_ELSE &&
	   inst->opcode != BRW_OPCODE_ENDIF);
}

static bool
is_channel_updated(vec4_instruction *inst, src_reg *values[4], int ch)
{
   const src_reg *src = values[ch];

   /* consider GRF only */
   assert(inst->dst.file == GRF);
   if (!src || src->file != GRF)
      return false;

   return (src->reg == inst->dst.reg &&
	   src->reg_offset == inst->dst.reg_offset &&
	   inst->dst.writemask & (1 << BRW_GET_SWZ(src->swizzle, ch)));
}

static bool
try_constant_propagate(struct brw_context *brw, vec4_instruction *inst,
                       int arg, src_reg *values[4])
{
   /* For constant propagation, we only handle the same constant
    * across all 4 channels.  Some day, we should handle the 8-bit
    * float vector format, which would let us constant propagate
    * vectors better.
    */
   src_reg value = *values[0];
   for (int i = 1; i < 4; i++) {
      if (!value.equals(*values[i]))
	 return false;
   }

   if (value.file != IMM)
      return false;

   if (inst->src[arg].abs) {
      if (value.type == BRW_REGISTER_TYPE_F) {
	 value.imm.f = fabs(value.imm.f);
      } else if (value.type == BRW_REGISTER_TYPE_D) {
	 if (value.imm.i < 0)
	    value.imm.i = -value.imm.i;
      }
   }

   if (inst->src[arg].negate) {
      if (value.type == BRW_REGISTER_TYPE_F)
	 value.imm.f = -value.imm.f;
      else
	 value.imm.u = -value.imm.u;
   }

   switch (inst->opcode) {
   case BRW_OPCODE_MOV:
      inst->src[arg] = value;
      return true;

   case SHADER_OPCODE_POW:
   case SHADER_OPCODE_INT_QUOTIENT:
   case SHADER_OPCODE_INT_REMAINDER:
      if (brw->gen < 8)
         break;
      /* fallthrough */
   case BRW_OPCODE_DP2:
   case BRW_OPCODE_DP3:
   case BRW_OPCODE_DP4:
   case BRW_OPCODE_DPH:
   case BRW_OPCODE_BFI1:
   case BRW_OPCODE_ASR:
   case BRW_OPCODE_SHL:
   case BRW_OPCODE_SHR:
   case BRW_OPCODE_SUBB:
      if (arg == 1) {
         inst->src[arg] = value;
         return true;
      }
      break;

   case BRW_OPCODE_MACH:
   case BRW_OPCODE_MUL:
   case BRW_OPCODE_ADD:
   case BRW_OPCODE_OR:
   case BRW_OPCODE_AND:
   case BRW_OPCODE_XOR:
   case BRW_OPCODE_ADDC:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 /* Fit this constant in by commuting the operands.  Exception: we
	  * can't do this for 32-bit integer MUL/MACH because it's asymmetric.
	  */
	 if ((inst->opcode == BRW_OPCODE_MUL ||
              inst->opcode == BRW_OPCODE_MACH) &&
	     (inst->src[1].type == BRW_REGISTER_TYPE_D ||
	      inst->src[1].type == BRW_REGISTER_TYPE_UD))
	    break;
	 inst->src[0] = inst->src[1];
	 inst->src[1] = value;
	 return true;
      }
      break;

   case BRW_OPCODE_CMP:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 uint32_t new_cmod;

	 new_cmod = brw_swap_cmod(inst->conditional_mod);
	 if (new_cmod != ~0u) {
	    /* Fit this constant in by swapping the operands and
	     * flipping the test.
	     */
	    inst->src[0] = inst->src[1];
	    inst->src[1] = value;
	    inst->conditional_mod = new_cmod;
	    return true;
	 }
      }
      break;

   case BRW_OPCODE_SEL:
      if (arg == 1) {
	 inst->src[arg] = value;
	 return true;
      } else if (arg == 0 && inst->src[1].file != IMM) {
	 inst->src[0] = inst->src[1];
	 inst->src[1] = value;

	 /* If this was predicated, flipping operands means
	  * we also need to flip the predicate.
	  */
	 if (inst->conditional_mod == BRW_CONDITIONAL_NONE) {
	    inst->predicate_inverse = !inst->predicate_inverse;
	 }
	 return true;
      }
      break;

   default:
      break;
   }

   return false;
}

static bool
is_logic_op(enum opcode opcode)
{
   return (opcode == BRW_OPCODE_AND ||
           opcode == BRW_OPCODE_OR  ||
           opcode == BRW_OPCODE_XOR ||
           opcode == BRW_OPCODE_NOT);
}

static bool
try_copy_propagate(struct brw_context *brw, vec4_instruction *inst,
                   int arg, src_reg *values[4])
{
   /* For constant propagation, we only handle the same constant
    * across all 4 channels.  Some day, we should handle the 8-bit
    * float vector format, which would let us constant propagate
    * vectors better.
    */
   src_reg value = *values[0];
   for (int i = 1; i < 4; i++) {
      /* This is equals() except we don't care about the swizzle. */
      if (value.file != values[i]->file ||
	  value.reg != values[i]->reg ||
	  value.reg_offset != values[i]->reg_offset ||
	  value.type != values[i]->type ||
	  value.negate != values[i]->negate ||
	  value.abs != values[i]->abs) {
	 return false;
      }
   }

   /* Compute the swizzle of the original register by swizzling the
    * component loaded from each value according to the swizzle of
    * operand we're going to change.
    */
   int s[4];
   for (int i = 0; i < 4; i++) {
      s[i] = BRW_GET_SWZ(values[i]->swizzle,
			 BRW_GET_SWZ(inst->src[arg].swizzle, i));
   }
   value.swizzle = BRW_SWIZZLE4(s[0], s[1], s[2], s[3]);

   if (value.file != UNIFORM &&
       value.file != GRF &&
       value.file != ATTR)
      return false;

   if (brw->gen >= 8) {
      if (value.negate) {
         if (is_logic_op(inst->opcode)) {
            return false;
         }
      }
   }

   if (inst->src[arg].abs) {
      value.negate = false;
      value.abs = true;
   }
   if (inst->src[arg].negate)
      value.negate = !value.negate;

   bool has_source_modifiers = value.negate || value.abs;

   /* gen6 math and gen7+ SENDs from GRFs ignore source modifiers on
    * instructions.
    */
   if ((has_source_modifiers || value.file == UNIFORM ||
        value.swizzle != BRW_SWIZZLE_XYZW) && !inst->can_do_source_mods(brw))
      return false;

   if (has_source_modifiers && value.type != inst->src[arg].type)
      return false;

   bool is_3src_inst = (inst->opcode == BRW_OPCODE_LRP ||
                        inst->opcode == BRW_OPCODE_MAD ||
                        inst->opcode == BRW_OPCODE_BFE ||
                        inst->opcode == BRW_OPCODE_BFI2);
   if (is_3src_inst && value.file == UNIFORM)
      return false;

   if (inst->is_send_from_grf())
      return false;

   /* We can't copy-propagate a UD negation into a condmod
    * instruction, because the condmod ends up looking at the 33-bit
    * signed accumulator value instead of the 32-bit value we wanted
    */
   if (inst->conditional_mod &&
       value.negate &&
       value.type == BRW_REGISTER_TYPE_UD)
      return false;

   /* Don't report progress if this is a noop. */
   if (value.equals(inst->src[arg]))
      return false;

   value.type = inst->src[arg].type;
   inst->src[arg] = value;
   return true;
}

bool
vec4_visitor::opt_copy_propagation()
{
   bool progress = false;
   src_reg *cur_value[virtual_grf_reg_count][4];

   memset(&cur_value, 0, sizeof(cur_value));

   foreach_list(node, &this->instructions) {
      vec4_instruction *inst = (vec4_instruction *)node;

      /* This pass only works on basic blocks.  If there's flow
       * control, throw out all our information and start from
       * scratch.
       *
       * This should really be fixed by using a structure like in
       * src/glsl/opt_copy_propagation.cpp to track available copies.
       */
      if (!is_dominated_by_previous_instruction(inst)) {
	 memset(cur_value, 0, sizeof(cur_value));
	 continue;
      }

      /* For each source arg, see if each component comes from a copy
       * from the same type file (IMM, GRF, UNIFORM), and try
       * optimizing out access to the copy result
       */
      for (int i = 2; i >= 0; i--) {
	 /* Copied values end up in GRFs, and we don't track reladdr
	  * accesses.
	  */
	 if (inst->src[i].file != GRF ||
	     inst->src[i].reladdr)
	    continue;

	 int reg = (virtual_grf_reg_map[inst->src[i].reg] +
		    inst->src[i].reg_offset);

	 /* Find the regs that each swizzle component came from.
	  */
	 src_reg *values[4];
	 int c;
	 for (c = 0; c < 4; c++) {
	    values[c] = cur_value[reg][BRW_GET_SWZ(inst->src[i].swizzle, c)];

	    /* If there's no available copy for this channel, bail.
	     * We could be more aggressive here -- some channels might
	     * not get used based on the destination writemask.
	     */
	    if (!values[c])
	       break;

	    /* We'll only be able to copy propagate if the sources are
	     * all from the same file -- there's no ability to swizzle
	     * 0 or 1 constants in with source registers like in i915.
	     */
	    if (c > 0 && values[c - 1]->file != values[c]->file)
	       break;
	 }

	 if (c != 4)
	    continue;

	 if (try_constant_propagate(brw, inst, i, values))
            progress = true;

	 if (try_copy_propagate(brw, inst, i, values))
	    progress = true;
      }

      /* Track available source registers. */
      if (inst->dst.file == GRF) {
	 const int reg =
	    virtual_grf_reg_map[inst->dst.reg] + inst->dst.reg_offset;

	 /* Update our destination's current channel values.  For a direct copy,
	  * the value is the newly propagated source.  Otherwise, we don't know
	  * the new value, so clear it.
	  */
	 bool direct_copy = is_direct_copy(inst);
	 for (int i = 0; i < 4; i++) {
	    if (inst->dst.writemask & (1 << i)) {
	       cur_value[reg][i] = direct_copy ? &inst->src[0] : NULL;
	    }
	 }

	 /* Clear the records for any registers whose current value came from
	  * our destination's updated channels, as the two are no longer equal.
	  */
	 if (inst->dst.reladdr)
	    memset(cur_value, 0, sizeof(cur_value));
	 else {
	    for (int i = 0; i < virtual_grf_reg_count; i++) {
	       for (int j = 0; j < 4; j++) {
		  if (is_channel_updated(inst, cur_value[i], j)){
		     cur_value[i][j] = NULL;
		  }
	       }
	    }
	 }
      }
   }

   if (progress)
      invalidate_live_intervals();

   return progress;
}

} /* namespace brw */
<|endoftext|>


<commit_before>#include <limits>

#include "KVPairBuffer.h"
#include "core/ByteOrder.h"
#include "mapreduce/common/KeyValuePair.h"

KVPairBuffer::KVPairBuffer(
  MemoryAllocatorInterface& memoryAllocator, uint64_t callerID,
  uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryAllocator, callerID, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(
  MemoryAllocatorInterface& memoryAllocator, uint8_t* memoryRegion,
  uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryAllocator, memoryRegion, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(
  uint8_t* memoryRegion, uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryRegion, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(capacity, alignmentMultiple) {
  init();
}

void KVPairBuffer::init() {
  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;

  partitionGroup = std::numeric_limits<uint64_t>::max();
  // poolID should persist across clear() so initialize it here
  poolID = std::numeric_limits<uint64_t>::max();
  clear();
}

void KVPairBuffer::addKVPair(KeyValuePair& kvPair) {
  uint64_t kvPairSize = kvPair.getWriteSize();

  bool cachedBeforeCommitAppend = cached;

  const uint8_t* bufPtr = setupAppend(kvPairSize);
  kvPair.serialize(const_cast<uint8_t*>(bufPtr));
  commitAppend(bufPtr, kvPairSize);

  // Update tuple metadata.
  if (cachedBeforeCommitAppend) {
    cached = true;

    ++numTuples;
    uint32_t keyLength = kvPair.getKeyLength();
    minKeyLength = std::min(minKeyLength, keyLength);
    maxKeyLength = std::max(maxKeyLength, keyLength);
  }
}

bool KVPairBuffer::getNextKVPair(KeyValuePair& kvPair) {
  if (offset == currentSize) {
    return false;
  } else {
    uint8_t* recordPtr = const_cast<uint8_t*>(getRawBuffer()) + offset;
    uint32_t recordSize = 0;

    kvPair.deserialize(recordPtr);
    recordSize = kvPair.getReadSize();

    ASSERT(offset + recordSize <= currentSize, "Deserialized a tuple ("
           "%llu bytes) that is too large to be where it is in the buffer ("
           "started at offset %llu with max size %llu).", kvPair.getReadSize(),
           offset, currentSize);
    offset += recordSize;

    return true;
  }
}

void KVPairBuffer::resetIterator() {
  offset = 0;
}

void KVPairBuffer::setIteratorPosition(uint64_t position) {
  offset = position;
}

uint64_t KVPairBuffer::getIteratorPosition() {
  return offset;
}

void KVPairBuffer::setupAppendKVPair(
  uint32_t keyLength, uint32_t maxValueLength, uint8_t*& key, uint8_t*& value,
  bool writeWithoutHeader) {

  ASSERT(pendingAppendPtr == NULL, "More than one setupAppendKVPair cannot be "
         "called without a corresponding commitAppendKVPair");

  uint64_t tupleSize = KeyValuePair::tupleSize(
    keyLength, maxValueLength, !writeWithoutHeader);

  pendingAppendPtr = const_cast<uint8_t*>(setupAppend(tupleSize));
  pendingKeyLength = keyLength;
  pendingMaxValueLength = maxValueLength;

  KeyValuePair::getKeyValuePointers(
    pendingAppendPtr, keyLength, key, value, writeWithoutHeader);
}

void KVPairBuffer::commitAppendKVPair(
  const uint8_t* key, const uint8_t* value, uint32_t valueLength,
  bool writeWithoutHeader) {

  // Sanity check that the key and value are the same as before.
  ASSERT((writeWithoutHeader && key == KeyValuePair::keyWithoutHeader(
            pendingAppendPtr)) ||
         (!writeWithoutHeader && key == KeyValuePair::key(pendingAppendPtr)),
         "Can't alter the address of the key between setupAppendKVPair and "
         "commitAppendKVPair");

  ASSERT((writeWithoutHeader && value == KeyValuePair::valueWithoutHeader(
            pendingAppendPtr, pendingKeyLength)) ||
         (!writeWithoutHeader && value == KeyValuePair::value(
           pendingAppendPtr)),
         "Can't alter the address of the value between setupAppendKVPair and "
         "commitAppendKVPair");

  // Sanity check that the value didn't increase beyond the max.
  ASSERT(valueLength <= pendingMaxValueLength,
         "Value length %llu cannot be larger than max value length %llu",
         valueLength, pendingMaxValueLength);

  bool cachedBeforeCommitAppend = cached;

  if (!writeWithoutHeader) {
    // Update the header for the new value length.
    KeyValuePair::setValueLength(pendingAppendPtr, valueLength);
  }

  commitAppend(
    pendingAppendPtr, KeyValuePair::tupleSize(
      pendingKeyLength, valueLength, !writeWithoutHeader));

  // Update tuple metadata.
  if (cachedBeforeCommitAppend) {
    cached = true;

    ++numTuples;
    minKeyLength = std::min(minKeyLength, pendingKeyLength);
    maxKeyLength = std::max(maxKeyLength, pendingKeyLength);
  }

  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;
}

void KVPairBuffer::abortAppendKVPair(
  const uint8_t* key, const uint8_t* value, bool writeWithoutHeader) {
  // Sanity check that the key and value are the same as before.
  ASSERT((writeWithoutHeader && key == KeyValuePair::keyWithoutHeader(
            pendingAppendPtr)) ||
         (!writeWithoutHeader && key == KeyValuePair::key(pendingAppendPtr)),
         "Can't alter the address of the key between setupAppendKVPair and "
         "commitAppendKVPair");

  ASSERT((writeWithoutHeader && value == KeyValuePair::valueWithoutHeader(
            pendingAppendPtr, pendingKeyLength)) ||
         (!writeWithoutHeader && value == KeyValuePair::value(
           pendingAppendPtr)),
         "Can't alter the address of the value between setupAppendKVPair and "
         "commitAppendKVPair");

  abortAppend(pendingAppendPtr);

  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;
}

void KVPairBuffer::clear() {
  BaseBuffer::clear();
  offset = 0;
  cached = false;
  node = std::numeric_limits<uint64_t>::max();
  logicalDiskID = std::numeric_limits<uint64_t>::max();
  chunkID = std::numeric_limits<uint64_t>::max();
  sourceName.clear();
  jobIDSet.clear();
}

uint64_t KVPairBuffer::getNumTuples() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return numTuples;
}

uint32_t KVPairBuffer::getMinKeyLength() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return minKeyLength;
}

uint32_t KVPairBuffer::getMaxKeyLength() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return maxKeyLength;
}

void KVPairBuffer::calculateTupleMetadata() {
  uint8_t* iterator = const_cast<uint8_t*>(getRawBuffer());
  uint8_t* endOfBuffer = iterator + currentSize;
  numTuples = 0;
  minKeyLength = std::numeric_limits<uint64_t>::max();
  maxKeyLength = 0;

  // Iterate through the buffer counting tuples and calculating maximum key
  // length.
  while (iterator < endOfBuffer) {
    ++numTuples;
    uint32_t keyLength = KeyValuePair::keyLength(iterator);
    minKeyLength = std::min(minKeyLength, keyLength);
    maxKeyLength = std::max(maxKeyLength, keyLength);
    iterator = KeyValuePair::nextTuple(iterator);
  }

  ASSERT(iterator == endOfBuffer,
         "KVPairBuffer must end on clean tuple boundary, but found %llu extra "
         "bytes", iterator - endOfBuffer);

  cached = true;
}

KVPairBuffer::NetworkMetadata* KVPairBuffer::getNetworkMetadata() {
  ABORT_IF(jobIDSet.size() != 1,
           "Expected send buffer to have exactly 1 job ID, but found %llu",
           jobIDSet.size());

  NetworkMetadata* metadata = new NetworkMetadata();
  metadata->bufferLength = hostToBigEndian64(currentSize);
  metadata->jobID = hostToBigEndian64(*(jobIDSet.begin()));
  metadata->partitionGroup = hostToBigEndian64(partitionGroup);
  metadata->partitionID = hostToBigEndian64(logicalDiskID);

  return metadata;
}
<commit_msg>fix incorrect numeric limit type in kvpairbuffer<commit_after>#include <limits>

#include "KVPairBuffer.h"
#include "core/ByteOrder.h"
#include "mapreduce/common/KeyValuePair.h"

KVPairBuffer::KVPairBuffer(
  MemoryAllocatorInterface& memoryAllocator, uint64_t callerID,
  uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryAllocator, callerID, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(
  MemoryAllocatorInterface& memoryAllocator, uint8_t* memoryRegion,
  uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryAllocator, memoryRegion, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(
  uint8_t* memoryRegion, uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(memoryRegion, capacity, alignmentMultiple) {
  init();
}

KVPairBuffer::KVPairBuffer(uint64_t capacity, uint64_t alignmentMultiple)
  : BaseBuffer(capacity, alignmentMultiple) {
  init();
}

void KVPairBuffer::init() {
  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;

  partitionGroup = std::numeric_limits<uint64_t>::max();
  // poolID should persist across clear() so initialize it here
  poolID = std::numeric_limits<uint64_t>::max();
  clear();
}

void KVPairBuffer::addKVPair(KeyValuePair& kvPair) {
  uint64_t kvPairSize = kvPair.getWriteSize();

  bool cachedBeforeCommitAppend = cached;

  const uint8_t* bufPtr = setupAppend(kvPairSize);
  kvPair.serialize(const_cast<uint8_t*>(bufPtr));
  commitAppend(bufPtr, kvPairSize);

  // Update tuple metadata.
  if (cachedBeforeCommitAppend) {
    cached = true;

    ++numTuples;
    uint32_t keyLength = kvPair.getKeyLength();
    minKeyLength = std::min(minKeyLength, keyLength);
    maxKeyLength = std::max(maxKeyLength, keyLength);
  }
}

bool KVPairBuffer::getNextKVPair(KeyValuePair& kvPair) {
  if (offset == currentSize) {
    return false;
  } else {
    uint8_t* recordPtr = const_cast<uint8_t*>(getRawBuffer()) + offset;
    uint32_t recordSize = 0;

    kvPair.deserialize(recordPtr);
    recordSize = kvPair.getReadSize();

    ASSERT(offset + recordSize <= currentSize, "Deserialized a tuple ("
           "%llu bytes) that is too large to be where it is in the buffer ("
           "started at offset %llu with max size %llu).", kvPair.getReadSize(),
           offset, currentSize);
    offset += recordSize;

    return true;
  }
}

void KVPairBuffer::resetIterator() {
  offset = 0;
}

void KVPairBuffer::setIteratorPosition(uint64_t position) {
  offset = position;
}

uint64_t KVPairBuffer::getIteratorPosition() {
  return offset;
}

void KVPairBuffer::setupAppendKVPair(
  uint32_t keyLength, uint32_t maxValueLength, uint8_t*& key, uint8_t*& value,
  bool writeWithoutHeader) {

  ASSERT(pendingAppendPtr == NULL, "More than one setupAppendKVPair cannot be "
         "called without a corresponding commitAppendKVPair");

  uint64_t tupleSize = KeyValuePair::tupleSize(
    keyLength, maxValueLength, !writeWithoutHeader);

  pendingAppendPtr = const_cast<uint8_t*>(setupAppend(tupleSize));
  pendingKeyLength = keyLength;
  pendingMaxValueLength = maxValueLength;

  KeyValuePair::getKeyValuePointers(
    pendingAppendPtr, keyLength, key, value, writeWithoutHeader);
}

void KVPairBuffer::commitAppendKVPair(
  const uint8_t* key, const uint8_t* value, uint32_t valueLength,
  bool writeWithoutHeader) {

  // Sanity check that the key and value are the same as before.
  ASSERT((writeWithoutHeader && key == KeyValuePair::keyWithoutHeader(
            pendingAppendPtr)) ||
         (!writeWithoutHeader && key == KeyValuePair::key(pendingAppendPtr)),
         "Can't alter the address of the key between setupAppendKVPair and "
         "commitAppendKVPair");

  ASSERT((writeWithoutHeader && value == KeyValuePair::valueWithoutHeader(
            pendingAppendPtr, pendingKeyLength)) ||
         (!writeWithoutHeader && value == KeyValuePair::value(
           pendingAppendPtr)),
         "Can't alter the address of the value between setupAppendKVPair and "
         "commitAppendKVPair");

  // Sanity check that the value didn't increase beyond the max.
  ASSERT(valueLength <= pendingMaxValueLength,
         "Value length %llu cannot be larger than max value length %llu",
         valueLength, pendingMaxValueLength);

  bool cachedBeforeCommitAppend = cached;

  if (!writeWithoutHeader) {
    // Update the header for the new value length.
    KeyValuePair::setValueLength(pendingAppendPtr, valueLength);
  }

  commitAppend(
    pendingAppendPtr, KeyValuePair::tupleSize(
      pendingKeyLength, valueLength, !writeWithoutHeader));

  // Update tuple metadata.
  if (cachedBeforeCommitAppend) {
    cached = true;

    ++numTuples;
    minKeyLength = std::min(minKeyLength, pendingKeyLength);
    maxKeyLength = std::max(maxKeyLength, pendingKeyLength);
  }

  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;
}

void KVPairBuffer::abortAppendKVPair(
  const uint8_t* key, const uint8_t* value, bool writeWithoutHeader) {
  // Sanity check that the key and value are the same as before.
  ASSERT((writeWithoutHeader && key == KeyValuePair::keyWithoutHeader(
            pendingAppendPtr)) ||
         (!writeWithoutHeader && key == KeyValuePair::key(pendingAppendPtr)),
         "Can't alter the address of the key between setupAppendKVPair and "
         "commitAppendKVPair");

  ASSERT((writeWithoutHeader && value == KeyValuePair::valueWithoutHeader(
            pendingAppendPtr, pendingKeyLength)) ||
         (!writeWithoutHeader && value == KeyValuePair::value(
           pendingAppendPtr)),
         "Can't alter the address of the value between setupAppendKVPair and "
         "commitAppendKVPair");

  abortAppend(pendingAppendPtr);

  pendingAppendPtr = NULL;
  pendingKeyLength = 0;
  pendingMaxValueLength = 0;
}

void KVPairBuffer::clear() {
  BaseBuffer::clear();
  offset = 0;
  cached = false;
  node = std::numeric_limits<uint64_t>::max();
  logicalDiskID = std::numeric_limits<uint64_t>::max();
  chunkID = std::numeric_limits<uint64_t>::max();
  sourceName.clear();
  jobIDSet.clear();
}

uint64_t KVPairBuffer::getNumTuples() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return numTuples;
}

uint32_t KVPairBuffer::getMinKeyLength() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return minKeyLength;
}

uint32_t KVPairBuffer::getMaxKeyLength() {
  if (!cached) {
    calculateTupleMetadata();
  }
  ASSERT(cached);
  return maxKeyLength;
}

void KVPairBuffer::calculateTupleMetadata() {
  uint8_t* iterator = const_cast<uint8_t*>(getRawBuffer());
  uint8_t* endOfBuffer = iterator + currentSize;
  numTuples = 0;
  minKeyLength = std::numeric_limits<uint32_t>::max();
  maxKeyLength = 0;

  // Iterate through the buffer counting tuples and calculating maximum key
  // length.
  while (iterator < endOfBuffer) {
    ++numTuples;
    uint32_t keyLength = KeyValuePair::keyLength(iterator);
    minKeyLength = std::min(minKeyLength, keyLength);
    maxKeyLength = std::max(maxKeyLength, keyLength);
    iterator = KeyValuePair::nextTuple(iterator);
  }

  ASSERT(iterator == endOfBuffer,
         "KVPairBuffer must end on clean tuple boundary, but found %llu extra "
         "bytes", iterator - endOfBuffer);

  cached = true;
}

KVPairBuffer::NetworkMetadata* KVPairBuffer::getNetworkMetadata() {
  ABORT_IF(jobIDSet.size() != 1,
           "Expected send buffer to have exactly 1 job ID, but found %llu",
           jobIDSet.size());

  NetworkMetadata* metadata = new NetworkMetadata();
  metadata->bufferLength = hostToBigEndian64(currentSize);
  metadata->jobID = hostToBigEndian64(*(jobIDSet.begin()));
  metadata->partitionGroup = hostToBigEndian64(partitionGroup);
  metadata->partitionID = hostToBigEndian64(logicalDiskID);

  return metadata;
}
<|endoftext|>