On Fri, Oct 5, 2012 at 1:24 PM, Richard Sandiford
<rdsandif...@googlemail.com> wrote:
> Richard Guenther <richard.guent...@gmail.com> writes:
>> On Fri, Oct 5, 2012 at 11:55 AM, Richard Sandiford
>> <rdsandif...@googlemail.com> wrote:
>>> Richard Guenther <richard.guent...@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.)

Ok, let's rephrase it this way then: the "bare metal" thing used
for the storage should ideally be the same in the tree IL and the RTL
IL _and_ the higher-level abstract wide-int.

>> 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.

I'm not arguing against this.  I'm just saying that the internal
representation will depend on the host - not the number of total
bits, but the number of pieces.

> 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.

Well, you don't want to pay the cost dividing 256bit numbers all
the time when most of your numbers are only 128bit.  So we don't
really want to perform compile-time arithmetic on the biggest
possible precision either.  Ideally, of course - at the moment
we have double-ints and what precision we internally use
is an implementation detail (once it is sufficient precision).

> 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.

How do you store larger ints there then?  How is CONST_WIDE_INT
variable size?  Why can wide-int not be variable-size?

> 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.)

Keenys patch truncates wide-ints to two HWIs in wide-int-to-tree
without any checking (I claimed it's a bug, Kenny says its a feature).

>  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.

Sure!  And I agree totally with this!  Just don't mix enlarging that thing
from double_int in the first round of implementation!

> 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.

Sure.

We seem to be talking in circles.  You don't seem to want (or care)
about a common storage for tree, RTL and wide-int.  You seem to
care about that operate-on-wide-ints thing.  I am saying if you keep
double-ints as-is you create two similar things which should be one thing.

So, make double-int storage only.  Introduce wide-int and use that
everywhere we compute on double-ints.  Whether wide-int is variable-size
or not is an implementation detail (if it's not we simply ICE if you require
a too large wide-int).  Wheter IL storage is variable-size or not is an
implementation detail as well.

What I don't see is that the patch just introduces wide-int as type
to do compile-time math on in RTL.  It does more (the patch is large
and I didn't read it in detail).  It doesn't even try to address the same
on the tree level.  It doesn't address the fundamental issue of
double-int size being host dependent.

Instead it seems to focus on getting even larger constants "work"
(on RTL level only).

What I'd like to see is _just_ the wide-int abstraction, suitable to
replace both tree and RTL level compile-time math (without even
trying to conver all uses, that's just too much noise for a thorough
review).

Richard.

>> [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

Reply via email to