This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: patch to fix constant math


Richard Guenther <richard.guenther@gmail.com> writes:
> On Fri, Oct 5, 2012 at 11:55 AM, Richard Sandiford
> <rdsandiford@googlemail.com> wrote:
>> Richard Guenther <richard.guenther@gmail.com> writes:
>>>> As far as the wide_ints recording a mode or precision goes: we're in
>>>> the "lucky" position of having tried both options.  Trees record the
>>>> type (and thus precision) of all compile-time integer constants.
>>>> RTL doesn't.  And the RTL way is a right pain.  It leads to interfaces
>>>> in which rtx arguments often have to be accompanied by a mode.  And it
>>>> leads to clunky differences like those between simplify_unary_operation
>>>> (which takes two mode arguments) and simplify_binary_operation
>>>> (which with our current set of binary operations requires only one).
>>>
>>> But the issue here is not that double_int doesn't record a mode
>>> or precision (or a sign).  The issue is that CONST_DOUBLE and CONST_INT
>>> don't!  The _tree_ INTEGER_CST contains of a type and a double-int.
>>> I see double-int (and the proposed wide-int) as a common building-block
>>> used for kind of "arbitrary precision" (as far as the target needs) integer
>>> constants on both tree and RTL.  And having a common worker implementation
>>> requires it to operate on something different than tree types or RTL mode
>>> plus signedness.
>>>
>>>> To put it another way: every wide_int operation needs to know
>>>> the precision of its arguments and the precision of its result.
>>>
>>> That's not true.  Every tree or RTL operation does, not every
>>> wide_int operation.  double_int's are just twos-complement numbers
>>> of precision 2 * HOST_BITS_PER_WIDE_INT.  wide_int's should
>>> be just twos-complement numbers of precision len *
>>> WHATEVER_HOST_COMPONENT_TYPE_IS_SUITABLE_FOR_A_FAST_IMPLEMENTATION.
>>> Operations on double_int and wide_int are bare-metal,
>>> in nearly all of the times you should use routines that do
>>> proper sign-/zero-extending to a possibly smaller precision.  When
>>> using bare-metal you are supposed to do that yourself.
>>>
>>> Which is why I was suggesting to add double_sint, double_uint,
>>> double_sintp (with precision), etc., wrappers and operations.
>>>
>>>> Not storing the precision in the wide_int is putting the onus
>>>> on the caller to keep track of the precision separately.
>>>
>>> But that's a matter of providing something high-level ontop of
>>> the bare-metal double-int/wide-int (something shared between
>>> RTL and trees).  Duplicating information in the bare-metal
>>> doesn't make sense (and no, INTEGER_CSTs on the tree level
>>> are _not_ short-lived, and how can a double-int make sense on
>>> the tree level when you say it doesn't make sense on the RTL level?)
>>
>> I think we're talking at cross purposes here.  To the extent that
>> I'm not really sure where to begin. :-)  Just in case this is it:
>> the idea is that wide_int is the type used to _process_ integers.
>> It is not suppoed to be the type used to store integers in the IRs.
>> The way trees store integers and the way that RTL stores integers
>> are up to them.  For RTL the idea is that we continue to store integers
>> that happen to fit into a HWI as a CONST_INT (regardless of the size of
>> the CONST_INT's context-determined mode).  Wider integers are stored
>> as a CONST_DOUBLE (for unconverted targets) or a CONST_WIDE_INT
>> (for converted targets).  None of the three use the wide_int type;
>> they use more compact representations instead.  And as Kenny says,
>> using CONST_INT continues to be an important way of reducing the
>> IR footprint.
>>
>> Whenever we want to do something interesting with the integers,
>> we construct a wide_int for them and use the same processing code
>> for all three rtx types.  This avoids having the separate single-HWI
>> and double-HWI paths that we have now.  It also copes naturally with
>> cases where we start out with single-HWI values but end up with wider
>> ones.
>>
>> But the operations that we do on these wide_ints will all be to a mode
>> or precision.  Shifts of QImode integers are different from shifts of
>> HImode integers, etc.
>>
>> If you knew all that already (you probably did) and I've completely
>> missed the point, please say so. :-)
>>
>> I'm not sure what you mean by "bare metal".
>
> The issue is that unlike RTL where we "construct" double-ints from
> CONST_INT/CONST_DOUBLE right now, tree has the double-int
> _embedded_.  That's why I say that the thing we embed in
> a CONST_WIDE_INT or a tree INTEGER_CST needs to be
> "bare metal", and that's what I would call wide-int.

OK, but that's deliberately not what Kenny's patch calls "wide int".
The whole idea is that the "bare metal" thing we embed in a
CONST_WIDE_INT or tree isn't (and doesn't need to be) the same
as the type that we use to operate on integers.  That bare-metal
thing doesn't even have to have a fixed width.  (CONST_WIDE_INT
doesn't have a fixed width, it's only as big as the integer
needs it to be.)

> So to me wide-ints provide the higher-level abstraction ontop of
> double-ints (which would remain the storage entity).
>
> Such higher-level abstraction is very useful, also for double-ints and
> also on the tree level.  There is no requirement to provide bigger
> double-int (or wide-int) for this.  Let's call this abstraction
> wide-int (as opposed to my suggested more piecemail double_sint,
> double_uint).  You can perfectly model it ontop of the existing
> double_int storage.
>
> As of providing larger "double-ints" - there is not much code left
> (ok, quite an overstatement ;)) that relies on the implementation
> detail of double-int containing exactly two HOST_WIDE_INTs.
> The exact purpose of double-ints was to _hide_ this (previously
> we only had routines like mul_double_with_sign which take
> two HOST_WIDE_INT components).  Most code dealing with
> the implementation detail is code interfacing with middle-end
> routines that take a HOST_WIDE_INT argument (thus the
> double_int_fits_hwi_p predicates) - even wide_int has to support
> this kind of interfacing.
>
> So, after introducing wide_int that just wraps double_int and
> changing all user code (hopefully all, I guess mostly all), we'd
> tackle the issue that the size of double_int's is host dependent.
> A simple solution is to make it's size dependent on a target macro
> (yes, macro ...), so on a 32bit HWI host targeting a 64bit 'HWI' target
> you'd simply have four HWIs in the 'double-int' storage (and
> arithmetic needs to be generalized to support this).

I just don't see why this is a good thing.  The constraints
are different when storing integers and when operating on them.
When operating on them, we want something that is easily constructed
on the stack, so we can create temporary structures very cheaply,
and can easily pass and return them.  We happen to know at GCC's
compile time how big the biggest integer will ever be, so it makes
sense for wide_int to be that wide.

But when storing integers we want something compact.  If your
machine supports 256-bit integers, but the code you're compiling
makes heavy use of 128-bit integers, why would you want to waste
128 of 256 bits on every stored integer?  Which is why even
CONST_WIDE_INT doesn't have a fixed width.

You seem to be arguing that the type used to store integers in the IR
has to be same as the one that we use when performing compile-time
arithmetic, but I think it's better to have an abstraction between
the two.

So if you think a pair of HWIs continues to be a useful way of
storing integers at the tree level, then we can easily continue
to use a pair of HWIs there.  Or if you'd prefer every tree integer
to be the same width as a wide_int, we can do that too.  (I don't
know what Kenny's tree patch does.)  But the whole point of having
wide_int as an abstraction is that most code _operating_ on integers
doesn't care what the representation is.  It becomes much easier to
change that representation to whatever gives the best performance.

Another advantage of abstracting away the storage type is that
we could store things like an overflow flag in the wide_int
(if that ever proves useful) without worrying about the impact
on the tree and RTL footprint.

> [The more complex solution makes double-int* just a pointer to the first
> HWI, so it cannot be used without being "wrapped" in a wide-int
> which implicitely would provide the number of HWIs pointed to
> (yes, I think variable-sized wide-int storage is the ultimate way to go).]
>
> So ... can you possibly split the patch in a way that just does the
> first thing (provide the high-level wrapper and make use of it from RTL)?
> I don't see the reason to have both CONST_DOUBLE and CONST_WIDE,
> in fact they should be the same given choice one for the 2nd step.
> And you can of course trivially construct a wide-int from a CONST_INT
> as well (given it has a mode).
>
> As of using a mode or precision in wide-int - for re-use on the tree level
> you need a precision as not all precisions have a distinct mode.  As on
> RTL you don't have a sign we probably don't want wide-ints to have a sign
> but continue double-int-like to specify the sign as part of the operation
> where it matters.

Definitely agree that signedness is a property of the operation.
(Saturation too IMO.)  I don't really mind about mode vs. precision.

Richard


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]