On Wed, Oct 3, 2012 at 7:15 PM, Kenneth Zadeck <[email protected]> wrote:
> The enclosed patch is the third of at least four patches that fix the
> problems associated with supporting integers on the target that are
> wider than two HOST_WIDE_INTs.
>
> While GCC claims to support OI mode, and we have two public ports that
> make minor use of this mode, in practice, compilation that uses OImode
> mode commonly gives the wrong result or ices. We have a private port
> of GCC for an architecture that is further down the road to needing
> comprehensive OImode and we have discovered that this is unusable. We
> have decided to fix it in a general way that so that it is most
> beneficial to the GCC community. It is our belief that we are just a
> little ahead of the X86 and the NEON and these patches will shortly be
> essential.
>
> The first two of these patches were primarily lexigraphical and have
> already been committed. They transformed the uses of CONST_DOUBLE
> so that it is easy to tell what the intended usage is.
>
> The underlying structures in the next two patches are very general:
> once they are added to the compiler, the compiler will be able to
> support targets with any size of integer from hosts of any size
> integer.
>
> The patch enclosed deals with the portable RTL parts of the compiler.
> The next patch, which is currently under construction deals with the
> tree level. However, this patch can be put on the trunk as is, and it
> will eleviate many, but not all of the current limitations in the rtl
> parts of the compiler.
>
> Some of the patch is conditional, depending on a port defining the
> symbol 'TARGET_SUPPORTS_WIDE_INT' to be non zero. Defining this
> symbol to be non zero is declaring that the port has been converted to
> use the new form or integer constants. However, the patch is
> completely backwards compatible to allow ports that do not need this
> immediately to convert at their leasure. The conversion process is
> not difficult, but it does require some knowledge of the port, so we
> are not volinteering to do this for all ports.
>
> OVERVIEW OF THE PATCH:
>
> The patch defines a new datatype, a 'wide_int' (defined in
> wide-int.[ch], and this datatype will be used to perform all of the
> integer constant math in the compiler. Externally, wide-int is very
> similar to double-int except that it does not have the limitation that
> math must be done on exactly two HOST_WIDE_INTs.
>
> Internally, a wide_int is a structure that contains a fixed sized
> array of HOST_WIDE_INTs, a length field and a mode. The size of the
That it has a mode sounds odd to me and makes it subtly different
from HOST_WIDE_INT and double-int. Maybe the patch will tell
why this is so.
> array is determined at generation time by dividing the number of bits
> of the largest integer supported on the target by the number of bits
> in a HOST_WIDE_INT of the host. Thus, with this format, any length of
> integer can be supported on any host.
>
> A new rtx type is created, the CONST_WIDE_INT, which contains a
> garbage collected array of HOST_WIDE_INTS that is large enough to hold
> the constant. For the targets that define TARGET_SUPPORTS_WIDE_INT to
> be non zero, CONST_DOUBLES are only used to hold floating point
> values. If the target leaves TARGET_SUPPORTS_WIDE_INT defined as 0,
> CONST_WIDE_INTs are not used and CONST_DOUBLEs are as they were
> before.
>
> CONST_INT does not change except that it is defined to hold all
> constants that fit in exactly one HOST_WIDE_INT. Note that is slightly
> different than the current trunk. Before this patch, the TImode
> constant '5' could either be in a CONST_INT or CONST_DOUBLE depending
> on which code path was used to create it. This patch changes this so
> that if the constant fits in a CONST_INT then it is represented in a
> CONST_INT no matter how it is created.
>
> For the array inside a CONST_WIDE_INT, and internally in wide-int, we
> use a compressed form for integers that need more than one
> HOST_WIDE_INT. Higher elements of the array are not needed if they
> are just a sign extension of the elements below them. This does not
> imply that constants are signed or are sign extended, this is only a
> compression technique.
>
> While it might seem to be more esthetically pleasing to have not
> introduced the CONST_WIDE_INT and to have changed the representation
> of the CONST_INT to accomodate larger numbers, this would have both
> used more space and would be a time consuming change for the port
> maintainers. We believe that most ports can be quickly converted with
> the current scheme because there is just not a lot of code in the back
> ends that cares about large constants. Furthermore, the CONST_INT is
> very space efficient and even in a program that was heavy in large
> values, most constants would still fit in a CONST_INT.
>
> All of the parts of the rtl level that deal with CONST_DOUBLE as an
> now conditionally work with CONST_WIDE_INTs depending on the value
> of TARGET_SUPPORTS_WIDE_INT. We believe that this patch removes all
> of the ices and wrong code places at the portable rtl level. However,
> there are still places in the portable rtl code that refuse to
> transform the code unless it is a CONST_INT. Since these do not cause
> failures, they can be handled later. The patch is already very large.
>
> It should be noted that much of the constant overflow checking in the
> constant math dissappears with these patches. The overflow checking
> code in the current compiler is really divided into two cases:
> overflow on the host and overflow on the target. The overflow
> checking on the host was to make sure that the math did overflow when
> done on two HOST_WIDE_INTs. All of this code goes away. These
> patches allow the constant math to be done exactly the way it is done
> on the target.
>
> This patch also aids other cleanups that are being considered at the
> rtl level:
>
> 1) These patches remove most of the host dependencies on the
> optimizations. Currently a 32 bit GCC host will produce different
> code for a specific target than a 64 bit host will. This is because
> many of the transformations only work on constants that can be a
> represented with a single HWI or two HWIs. If the target has larger
> integers than the host, the compilation suffers.
>
> 2) Bernd's need to make GCC correctly support partial its is made
> easier by the wide-int library. This library carefully does all
> arithmetic in the precision of the mode included in it. While there
> are still places at the rtl level that still do arithmetic inline,
> we plan to convert those to use the library over time. This patch
> converts a substantial number of those places.
>
> 3) This patch is one step along the path to add modes to rtl integer
> constants. There is no longer any checking to see if a CONST_DOUBLE
> has VOIDmode as its mode. Furthermore, all constructors for various
> wide ints do take a mode and require that it not be VOIDmode. There
> is still a lot of work to do to make this conversion possible.
>
> Richard Sandiford has been over the rtl portions of this patch a few
> times. He has not looked at the wide-int files in any detail. This
> patch has been heavily tested on my private ports and also on x86-64.
>
>
> CONVERSION PROCESS
>
> Converting a port mostly requires looking for the places where
> CONST_DOUBLES are used with VOIDmode and replacing that code with code
> that accesses CONST_WIDE_INTs. "grep -i const_double" at the port
> level gets you to 95% of the changes that need to be made. There are
> a few places that require a deeper look.
>
> 1) There is no equivalent to hval and lval for CONST_WIDE_INTs.
> This would be difficult to express in the md language since there
> are a variable number of elements.
>
> Most ports only check that hval is either 0 or -1 to see if the int
> is small. As mentioned above, this will no longer be necessary
> since small constants are always CONST_INT. Of course there are
> still a few exceptions, the alpha's constraint used by the zap
> instruction certainly requires careful examination by C code.
> However, all the current code does is pass the hval and lval to C
> code, so evolving the c code to look at the CONST_WIDE_INT is not
> really a large change.
>
> 2) Because there is no standard template that ports use to
> materialize constants, there is likely to be some futzing that is
> unique to each port in this code.
>
> 3) The rtx costs may have to be adjusted to properly account for
> larger constants that are represented as CONST_WIDE_INT.
>
> All and all it has not taken us long to convert ports that we are
> familiar with.
>
> OTHER COMMENTS
>
> I did find what i believe is one interesting bug in the double-int
> code. I believe that the code that performs divide and mod with round
> to nearest is seriously wrong for unsigned integers. I believe that
> it will get the wrong answer for any numbers that are large enough to
> look negative if they consider signed integers. Asside from that,
> wide-int should perform in a very similar manner to double-int.
>
> I am sorry for the size of this patch. However, there does not appear
> to change the underlying data structure to support wider integers
> without doing something like this.
Some pieces can be easily split out, like the introduction and use
of CONST_SCALAR_INT_P.
As my general comment I would like to see double-int and wide-int
unified from an interface perspective. Which means that double-int
should be a specialization of wide-int which should be a template
(which also means its size is constant). Thus,
typedef wide_int<2> double_int;
should be the way to expose the double_int type.
The main question remains - why does wide_int have a mode?
That looks redundant, both with information stored in types
and the RTL constant, and with the len field (that would be
always GET_MODE_SIZE () / ...?). Also when you associate
a mode it's weird you don't associate a signedness.
Thus I'd ask you to rework this to be a template on 'len'
(number of HOST_WIDE_INT words), drop the mode member
and unify double-int and wide-int. Co-incidentially incrementally
doing this by converting double-int to a typedef of a
wide_int<2> specialization (thus moving double-int implementation
stuff to be wide_int<2> specialized) would be prefered.
Btw,
+/* Constructs tree in type TYPE from with value given by CST. Signedness
+ of CST is assumed to be the same as the signedness of TYPE. */
+
+tree
+wide_int_to_tree (tree type, const wide_int &cst)
+{
+ wide_int v;
+ if (TYPE_UNSIGNED (type))
+ v = cst.zext (TYPE_PRECISION (type));
+ else
+ v = cst.sext (TYPE_PRECISION (type));
+
+ return build_int_cst_wide (type, v.elt (0), v.elt (1));
+}
is surely broken. A wide-int does not fit a double-int. How are you
going to "fix" this?
Thanks,
Richard.
> kenny