On Wed, Feb 27, 2013 at 2:59 AM, Kenneth Zadeck
<zad...@naturalbridge.com> wrote:
> This patch contains a large number of the changes requested by Richi.   It
> does not contain any of the changes that he requested to abstract the
> storage layer.   That suggestion appears to be quite unworkable.

I of course took this claim as a challenge ... with the following result.  It is
of course quite workable ;)

The attached patch implements the core wide-int class and three storage
models (fixed size for things like plain HWI and double-int, variable size
similar to how your wide-int works and an adaptor for the double-int as
contained in trees).  With that you can now do

HOST_WIDE_INT
wi_test (tree x)
{
  // template argument deduction doesn't do the magic we want it to do
  // to make this kind of implicit conversions work
  // overload resolution considers this kind of conversions so we
  // need some magic that combines both ... but seeding the overload
  // set with some instantiations doesn't seem to be possible :/
  // wide_int<> w = x + 1;
  wide_int<> w;
  w += x;
  w += 1;
  // template argument deduction doesn't deduce the return value type,
  // not considering the template default argument either ...
  // w = wi (x) + 1;
  // we could support this by providing rvalue-to-lvalue promotion
  // via a traits class?
  // otoh it would lead to sub-optimal code anyway so we should
  // make the result available as reference parameter and only support
  // wide_int <> res; add (res, x, 1); ?
  w = wi (x).operator+<wide_int<> >(1);
  wide_int<>::add(w, x, 1);
  return w.to_hwi ();
}

we are somewhat limited with C++ unless we want to get really fancy.
Eventually providing operator+ just doesn't make much sense for
generic wide-int combinations (though then the issue is its operands
are no longer commutative which I think is the case with your wide-int
or double-int as well - they don't suport "1 + wide_int" for obvious reasons).

So there are implementation design choices left undecided.

Oh, and the operation implementations are crap (they compute nonsense).

But you should get the idea.

Richard.
#include "config.h"
#include "system.h"

#include "coretypes.h"
#include "hwint.h"
#include "tree.h"


/* ???  wide-int should probably use HOST_WIDEST_FAST_INT as storage,
   not HOST_WIDE_INT.  Yeah, we could even template on that ...  */

/* Fixed-length embedded storage.  wi_embed<2> is double-int,
   wi_embed<1> is a plain HOST_WIDE_INT.  Can be used for
   small fixed-(minimum)-size calculations on hosts that have
   no suitable integer type.  */

template <unsigned sz>
class wi_embed
{
private:
  HOST_WIDE_INT s[sz];

public:
  void construct () {}
  HOST_WIDE_INT* storage() { return s; }
  const HOST_WIDE_INT* storage() const { return s; }
  unsigned len() const { return sz; }
  void set_len(unsigned l) { gcc_checking_assert (l <= sz); }
};


/* Fixed maximum-length embedded storage but variable dynamic size.  */

//#define MAXSZ (4 * (MAX_MODE_INT_SIZE / HOST_BITS_PER_WIDE_INT))
#define MAXSZ 8

template <unsigned max_sz>
class wi_embed_var
{
private:
  unsigned len_;
  HOST_WIDE_INT s[max_sz];

public:
  void construct () { len_ = 0; }
  HOST_WIDE_INT* storage() { return s; }
  const HOST_WIDE_INT* storage() const { return s; }
  unsigned len() const { return len_; }
  void set_len(unsigned l) { len_ =  l; }
};


/* The wide-int class.  Defaults to variable-length storage
   (alternatively use a typedef to avoid the need to use wide_int <>).  */

template <class S = wi_embed_var<MAXSZ> >
class wide_int;


/* Avoid constructors / destructors to make sure this is a C++04 POD.  */

/* Basic wide_int class.  The storage model allows for rvalue
   storage abstraction avoiding copying from for example tree
   or RTX and to avoid the need of explicit construction for
   integral arguments of up to HWI size.

   A storage model needs to provide the following methods:
    - construct (), default-initialize the storage
    - unsigned len () const, the size of the storage in HWI quantities
    - const HOST_WIDE_INT *storage () const, return a pointer
      to read-only HOST_WIDE_INT storage of size len ().
    - HOST_WIDE_INT *storage (), return a pointer to writable
      HOST_WIDE_INT storage of size len ().  This method is optional.
    - void set_len (unsigned l), adjust the size of the storage
      to at least l HWI words.

   Conversions of wide_int _to_ tree or RTX or HWI are explicit.

   Conversions to wide_int happen with overloads to the global
   function template wi () or via wide_int_traits specializations.  */

/* ???  With mixed length operations there are encoding issues
   for signed vs. unsigned numbers.  The easiest encoding is to
   say wide-ints are always signed which means that -1U needs
   the MSB of the wide-int storage as zero which means an extra
   word with zeros.  The sign-bit of a wide-int is then always
   storage()[len() & (1 << (HOST_BITS_PER_WIDE_INT - 1))].  */

template <class S>
class wide_int : private S
{
  /* Allow access to the storage object of operands.  */
  template <class S2>
  friend class wide_int;

public:
  typedef wide_int<S> WideInt_t;

  void construct (const S& s) { static_cast<S&>(*this) = s; }

  /* ???  We'd really want the following member templates to
     behave as a set of overloads available for each wide-int
     storage model.  Like
     WideInt_t& operator (const wide_int <wi_tree_int_cst>&);
     WideInt_t& operator (const wide_int <wi_embed>&);
     WideInt_t& operator (const wide_int <wi_embed_var>&);
     forwarding to a common implementation.  But that's not
     extensible from the outside (tree bits should reside
     in tree.h), nor is it possible without explicitely writing
     down all overloads and explicitely forwarding to the common
     implementation.  Thus we require the user to explicitely
     convert to wide-int via a function overload (see below).  */
  template <class S2>
  WideInt_t& operator=(const wide_int <S2>&);

  /* Self-modify operators have obvious result types.  */
  template <class T>
  WideInt_t& operator+=(const T&);

  /* Note the result must support an lvalue storage.  */
  /* Automatic promotion is difficult but could be done via another
     traits class wi_promote_lvalue<S1, S2>::S - of course then
     storage models would need to know about each others.  */
  // otoh it would lead to sub-optimal code anyway so we should
  // make the result available as reference parameter and only support
  // wide_int <> res; add (res, x, 1); ?
  template <class S1, class T>
  wide_int<S1> operator+(const T&);

  template <class T1, class T2>
  static wide_int<S>&
  add (wide_int<S>& res, const T1 &, const T2 &);

  HOST_WIDE_INT
  to_hwi () const { return this->storage()[0]; }
};


/* This traits class is to provide a means of accessing T as
   an rvalue wide-int.  This allows us to omit the explicit
   conversion to wide_int <storage> in most places and allow
   extending the wide-int interface outside of wide-int.h.

   Extend this by providing a constructor that builds a wrapper
   around type T and operator-> that provides access to a
   wide_int<> representing it.  */

template <class T>
class wi_traits;

/* Wrap a wide_int as itself.  */

template <class S>
class wi_traits <wide_int <S> >
{
public:
    typedef wide_int <S> wi_t;
    wi_traits(const wide_int <S> &w_) : w (w_) {}
    const wi_t* operator->() const { return &w; }
private:
    const wi_t &w;
};

/* Wrap any integral type up to the size of a (unsigned) HWI
   as wide_int <wi_embed <1> >.
   ???  The following only handles 'int', handling the rest
   of the suitable integer types via a separate helper trait is
   left as an excercise for the reader.  */

template <>
class wi_traits <int>
{
public:
    typedef wide_int <wi_embed <1> > wi_t;
    wi_traits(HOST_WIDE_INT hwi)
  {
    wi_embed <1> ws;
    ws.construct ();
    ws.storage()[0] = hwi;
    w.construct (ws);
  }
    wi_t* operator->() { return &w; }
private:
    wi_t w;
};


/* wide-int operations.  To avoid code-bloat the actual workers
   can be trivially outlined by giving them a non-template interface
   working on HOST_WIDE_INT * arrays.  */

template <class S>
template <class S2>
wide_int<S>&
wide_int<S>::operator=(const wide_int<S2>&b)
{
  unsigned i;
  this->set_len (b.len());
  for (i = 0; i < b.len(); ++i)
    this->storage()[i] = b.storage()[i];
  for (; i < this->len(); ++i)
    this->storage()[i] = 0;
  return *this;
}

template <class S>
template <class T>
wide_int<S>&
wide_int<S>::operator+=(const T &b_)
{
  wi_traits<T> b(b_);
  /* Compute a += b.  */
  unsigned i;
  if (b->len () > this->len ())
    this->set_len (b->len ());
  for (i = 0; i < this->len (); ++i)
    this->storage()[i] += b->storage()[i];
  return *this;
}

template <class S>
template <class S1, class T>
wide_int<S1>
wide_int<S>::operator+(const T &w)
{
  /* Compute a + b */
  wide_int<S1> res;
  // ???  Initialization from *this not possible via implicit
  // use of operator=  */
  // wide_int<S1> res = *this;
  res = *this;
  res += w;
  return res;
}

template <class S>
template <class T1, class T2>
wide_int<S>&
wide_int<S>::add(wide_int<S>& res, const T1&a_, const T2&b_)
{
  wi_traits<T1> a(a_);
  wi_traits<T2> b(b_);
  res.set_len (a->len () > b->len () ? a->len() : b->len ());
  for (unsigned i = 0; i < res.len (); ++i)
    res.storage()[i] = a->storage()[i] + b->storage()[i];
  return res;
}

// ----- for tree.h

class wi_tree_int_cst
{
  tree cst;
public:
  void construct (tree c) { cst = c; }
  const HOST_WIDE_INT *storage() const { return reinterpret_cast <HOST_WIDE_INT *>(&TREE_INT_CST (cst)); }
  unsigned len() const { return 2; }
};

template <>
class wi_traits <tree>
{
public:
    typedef wide_int <wi_tree_int_cst> wi_t;
    wi_traits(tree t)
  {
    wi_tree_int_cst ws;
    ws.construct (t);
    w.construct (ws);
  }
    wi_t* operator->() { return &w; }
private:
    wi_t w;
};


union T {
  wide_int<> w;
  unsigned x;
} test;

wide_int<wi_tree_int_cst> wi (tree t)
{
  wide_int<wi_tree_int_cst> w;
  wi_tree_int_cst ws;
  ws.construct (t);
  w.construct (ws);
  return w;
}
wide_int<wi_embed <1> > wi (HOST_WIDE_INT hwi)
{
  wide_int<wi_embed <1> > w;
  wi_embed <1> ws;
  ws.construct ();
  ws.storage()[0] = hwi;
  w.construct (ws);
  return w;
}

HOST_WIDE_INT
wi_test (tree x)
{
  // template argument deduction doesn't do the magic we want it to do
  // to make this kind of implicit conversions work
  // overload resolution considers this kind of conversions so we
  // need some magic that combines both ... but seeding the overload
  // set with some instantiations doesn't seem to be possible :/
  // wide_int<> w = x + 1;
  wide_int<> w;
  w += x;
  w += 1;
  // template argument deduction doesn't deduce the return value type,
  // not considering the template default argument either ...
  // w = wi (x) + 1;
  // we could support this by providing rvalue-to-lvalue promotion
  // via a traits class?
  // otoh it would lead to sub-optimal code anyway so we should
  // make the result available as reference parameter and only support
  // wide_int <> res; add (res, x, 1); ?
  w = wi (x).operator+<wide_int<> >(1);
  wide_int<>::add(w, x, 1);
  return w.to_hwi ();
}

Reply via email to