From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by sourceware.org (Postfix) with ESMTP id 7A8E23858D28 for ; Tue, 10 Oct 2023 17:41:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7A8E23858D28 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id AA478C15; Tue, 10 Oct 2023 10:42:32 -0700 (PDT) Received: from localhost (unknown [10.32.110.65]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4831A3F5A1; Tue, 10 Oct 2023 10:41:51 -0700 (PDT) From: Richard Sandiford To: Jakub Jelinek Mail-Followup-To: Jakub Jelinek ,Richard Biener , gcc-patches@gcc.gnu.org, richard.sandiford@arm.com Cc: Richard Biener , gcc-patches@gcc.gnu.org Subject: Re: [PATCH] wide-int: Allow up to 16320 bits wide_int and change widest_int precision to 32640 bits [PR102989] References: Date: Tue, 10 Oct 2023 18:41:50 +0100 In-Reply-To: (Jakub Jelinek's message of "Mon, 9 Oct 2023 20:28:31 +0200") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Spam-Status: No, score=-18.3 required=5.0 tests=BAYES_00,KAM_DMARC_NONE,KAM_DMARC_STATUS,KAM_LAZY_DOMAIN_SECURITY,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Jakub Jelinek writes: > On Mon, Oct 09, 2023 at 03:44:10PM +0200, Jakub Jelinek wrote: >> Thanks, just quick answers, will work on patch adjustments after trying to >> get rid of rwide_int (seems dwarf2out has very limited needs from it, just >> some routine to construct it in GCed memory (and never change afterwards) >> from const wide_int_ref & or so, and then working operator ==, >> get_precision, elt, get_len and get_val methods, so I think we could just >> have a struct dw_wide_int { unsigned int prec, len; HOST_WIDE_INT val[1]; }; >> and perform the methods on it after converting to a storage ref. > > Now in patch form (again, incremental). > >> > Does the variable-length memcpy pay for itself? If so, perhaps that's a >> > sign that we should have a smaller inline buffer for this class (say 2 HWIs). >> >> Guess I'll try to see what results in smaller .text size. > > I've left the memcpy changes into a separate patch (incremental, attached). > Seems that second patch results in .text growth by 16256 bytes (0.04%), > though I'd bet it probably makes compile time tiny bit faster because it > replaces an out of line memcpy (caused by variable length) with inlined one. > > With even the third one it shrinks by 84544 bytes (0.21% down), but the > extra statistics patch then shows massive number of allocations after > running make check-gcc check-g++ check-gfortran for just a minute or two. > On the widest_int side, I see (first number from sort | uniq -c | sort -nr, > second the estimated or final len) > 7289034 4 > 173586 5 > 21819 6 > i.e. there are tons of widest_ints which need len 4 (or perhaps just > have it as upper estimation), maybe even 5 would be nice. Thanks for running the stats. That's definitely a lot more than I expected. > On the wide_int side, I see > 155291 576 > (supposedly because of bound_wide_int, where we create wide_int_ref from > the 576-bit precision bound_wide_int and then create 576-bit wide_int when > using unary or binary operation on that). 576 bits seems quite a lot for a loop bound. We're enterning near-infinite territory with 128 bits. :) But I don't want to rehash old discussions. If we take this size for wide_int as given, then... > So, perhaps we could get away with say WIDEST_INT_MAX_INL_ELTS of 5 or 6 > instead of 9 but keep WIDE_INT_MAX_INL_ELTS at 9 (or whatever is computed > from MAX_BITSIZE_MODE_ANY_INT?). Or keep it at 9 for both (i.e. without > the third patch). ...I agree we might as well keep the widest_int size the same for simplicity. It'd only be worth distinguishing them if we have positive proof that it's worthwhile. So going with patches 1 + 2 sounds good to me, but I don't have a strong preference. On the wide-int.cc changes: > @@ -1469,6 +1452,36 @@ wi::mul_internal (HOST_WIDE_INT *val, co > return 1; > } > > + /* The sizes here are scaled to support a 2x WIDE_INT_MAX_INL_PRECISION by 2x > + WIDE_INT_MAX_INL_PRECISION yielding a 4x WIDE_INT_MAX_INL_PRECISION > + result. */ > + > + unsigned HOST_HALF_WIDE_INT > + ubuf[4 * WIDE_INT_MAX_INL_PRECISION / HOST_BITS_PER_HALF_WIDE_INT]; > + unsigned HOST_HALF_WIDE_INT > + vbuf[4 * WIDE_INT_MAX_INL_PRECISION / HOST_BITS_PER_HALF_WIDE_INT]; > + /* The '2' in 'R' is because we are internally doing a full > + multiply. */ > + unsigned HOST_HALF_WIDE_INT > + rbuf[2 * 4 * WIDE_INT_MAX_INL_PRECISION / HOST_BITS_PER_HALF_WIDE_INT]; > + const HOST_WIDE_INT mask = ((HOST_WIDE_INT)1 << HOST_BITS_PER_HALF_WIDE_INT) - 1; > + unsigned HOST_HALF_WIDE_INT *u = ubuf; > + unsigned HOST_HALF_WIDE_INT *v = vbuf; > + unsigned HOST_HALF_WIDE_INT *r = rbuf; > + > + if (prec > WIDE_INT_MAX_INL_PRECISION && !high) > + prec = (op1len + op2len + 1) * HOST_BITS_PER_WIDE_INT; Changing the precision looked a bit dangerous at first, but I agree it looks correct in context, in that nothing later on seems to require prec to be the real precision of the number. But I wonder whether we should instead do: if (!high) prec = MIN ((op1len + op2len + 1) * HOST_BITS_PER_WIDE_INT, prec); so that the assumption gets a bit more testing. Same idea for the others. I.e. in any case where we think it's safe to reduce a precision or length for out-of-line buffers, I think we should try to do the same for inline ones. I'm not sure off-hand why + 1 is safe here but + 2 is needed for the write_val estimate. Might be worth a comment in one place or the other. > + unsigned int blocks_needed = BLOCKS_NEEDED (prec); > + unsigned int half_blocks_needed = blocks_needed * 2; > + if (UNLIKELY (prec > WIDE_INT_MAX_INL_PRECISION)) > + { > + unsigned HOST_HALF_WIDE_INT *buf > + = XALLOCAVEC (unsigned HOST_HALF_WIDE_INT, 4 * 4 * blocks_needed); > + u = buf; > + v = u + 4 * blocks_needed; > + r = v + 4 * blocks_needed; > + } > + > /* We do unsigned mul and then correct it. */ > wi_unpack (u, op1val, op1len, half_blocks_needed, prec, SIGNED); > wi_unpack (v, op2val, op2len, half_blocks_needed, prec, SIGNED); > [...] > @@ -2399,9 +2453,12 @@ from_int (int i) > static void > assert_deceq (const char *expected, const wide_int_ref &wi, signop sgn) > { > - char buf[WIDE_INT_PRINT_BUFFER_SIZE]; > - print_dec (wi, buf, sgn); > - ASSERT_STREQ (expected, buf); > + char buf[WIDE_INT_PRINT_BUFFER_SIZE], *p = buf; > + unsigned len = wi.get_len (); > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > + p = XALLOCAVEC (char, len * HOST_BITS_PER_WIDE_INT / 4 + 4); Is this size enough for decimal? E.g. to go back to the 576-bit example, 2^575 is O(10^173), but 576/4+4 == 148. > + print_dec (wi, p, sgn); > + ASSERT_STREQ (expected, p); > } > > /* Likewise for base 16. */ The wide-int.cc changes LGTM otherwise. I don't think I'm the right person to review the other changes. Thanks, Richard > > --- gcc/poly-int.h.jj 2023-10-09 14:37:45.883940062 +0200 > +++ gcc/poly-int.h 2023-10-09 17:05:26.629828329 +0200 > @@ -96,7 +96,7 @@ struct poly_coeff_traits }; > > template > -struct poly_coeff_traits > +struct poly_coeff_traits > { > typedef WI_UNARY_RESULT (T) result; > typedef int int_type; > @@ -110,14 +110,13 @@ struct poly_coeff_traits }; > > template > -struct poly_coeff_traits > +struct poly_coeff_traits > { > typedef WI_UNARY_RESULT (T) result; > typedef int int_type; > /* These types are always signed. */ > static const int signedness = 1; > static const int precision = wi::int_traits::precision; > - static const int inl_precision = wi::int_traits::inl_precision; > static const int rank = precision * 2 / CHAR_BIT; > > template > --- gcc/double-int.h.jj 2023-01-02 09:32:22.747280053 +0100 > +++ gcc/double-int.h 2023-10-09 17:06:03.446317336 +0200 > @@ -440,7 +440,7 @@ namespace wi > template <> > struct int_traits > { > - static const enum precision_type precision_type = CONST_PRECISION; > + static const enum precision_type precision_type = INL_CONST_PRECISION; > static const bool host_dependent_precision = true; > static const unsigned int precision = HOST_BITS_PER_DOUBLE_INT; > static unsigned int get_precision (const double_int &); > --- gcc/wide-int.h.jj 2023-10-09 16:06:39.326805176 +0200 > +++ gcc/wide-int.h 2023-10-09 17:29:20.016951691 +0200 > @@ -343,8 +343,8 @@ template class widest_int_storag > > typedef generic_wide_int wide_int; > typedef FIXED_WIDE_INT (ADDR_MAX_PRECISION) offset_int; > -typedef generic_wide_int > widest_int; > -typedef generic_wide_int > widest2_int; > +typedef generic_wide_int > widest_int; > +typedef generic_wide_int > widest2_int; > > /* wi::storage_ref can be a reference to a primitive type, > so this is the conservatively-correct setting. */ > @@ -394,13 +394,13 @@ namespace wi > /* The integer has a variable precision but no defined signedness. */ > VAR_PRECISION, > > - /* The integer has a constant precision (known at GCC compile time) > - and is signed. */ > - CONST_PRECISION, > - > - /* Like CONST_PRECISION, but with WIDEST_INT_MAX_PRECISION or larger > - precision where not all elements of arrays are always present. */ > - WIDEST_CONST_PRECISION > + /* The integer has a constant precision (known at GCC compile time), > + is signed and all elements are in inline buffer. */ > + INL_CONST_PRECISION, > + > + /* Like INL_CONST_PRECISION, but elements can be heap allocated for > + larger lengths. */ > + CONST_PRECISION > }; > > /* This class, which has no default implementation, is expected to > @@ -410,15 +410,10 @@ namespace wi > Classifies the type of T. > > static const unsigned int precision; > - Only defined if precision_type == CONST_PRECISION or > - precision_type == WIDEST_CONST_PRECISION. Specifies the > + Only defined if precision_type == INL_CONST_PRECISION or > + precision_type == CONST_PRECISION. Specifies the > precision of all integers of type T. > > - static const unsigned int inl_precision; > - Only defined if precision_type == WIDEST_CONST_PRECISION. > - Specifies precision which is represented in the inline > - arrays. > - > static const bool host_dependent_precision; > True if the precision of T depends (or can depend) on the host. > > @@ -441,10 +436,10 @@ namespace wi > struct binary_traits; > > /* Specify the result type for each supported combination of binary > - inputs. Note that CONST_PRECISION, WIDEST_CONST_PRECISION and > + inputs. Note that INL_CONST_PRECISION, CONST_PRECISION and > VAR_PRECISION cannot be mixed, in order to give stronger type > - checking. When both inputs are CONST_PRECISION or both are > - WIDEST_CONST_PRECISION, they must have the same precision. */ > + checking. When both inputs are INL_CONST_PRECISION or both are > + CONST_PRECISION, they must have the same precision. */ > template > struct binary_traits > { > @@ -461,7 +456,7 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > /* Spelled out explicitly (rather than through FIXED_WIDE_INT) > so as not to confuse gengtype. */ > @@ -474,10 +469,10 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > typedef generic_wide_int < widest_int_storage > - ::inl_precision> > result_type; > + ::precision> > result_type; > typedef result_type operator_result; > typedef bool predicate_result; > typedef result_type signed_shift_result_type; > @@ -493,7 +488,7 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > /* Spelled out explicitly (rather than through FIXED_WIDE_INT) > so as not to confuse gengtype. */ > @@ -506,10 +501,10 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > typedef generic_wide_int < widest_int_storage > - ::inl_precision> > result_type; > + ::precision> > result_type; > typedef result_type operator_result; > typedef bool predicate_result; > typedef result_type signed_shift_result_type; > @@ -517,7 +512,7 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > STATIC_ASSERT (int_traits ::precision == int_traits ::precision); > /* Spelled out explicitly (rather than through FIXED_WIDE_INT) > @@ -531,11 +526,11 @@ namespace wi > }; > > template > - struct binary_traits > + struct binary_traits > { > STATIC_ASSERT (int_traits ::precision == int_traits ::precision); > typedef generic_wide_int < widest_int_storage > - ::inl_precision> > result_type; > + ::precision> > result_type; > typedef result_type operator_result; > typedef bool predicate_result; > typedef result_type signed_shift_result_type; > @@ -1191,8 +1186,7 @@ inline wide_int_storage::wide_int_storag > { > STATIC_ASSERT (!wi::int_traits::host_dependent_precision); > STATIC_ASSERT (wi::int_traits::precision_type != wi::CONST_PRECISION); > - STATIC_ASSERT (wi::int_traits::precision_type > - != wi::WIDEST_CONST_PRECISION); > + STATIC_ASSERT (wi::int_traits::precision_type != wi::INL_CONST_PRECISION); > WIDE_INT_REF_FOR (T) xi (x); > precision = xi.precision; > if (UNLIKELY (precision > WIDE_INT_MAX_INL_PRECISION)) > @@ -1246,8 +1240,7 @@ wide_int_storage::operator = (const T &x > { > STATIC_ASSERT (!wi::int_traits::host_dependent_precision); > STATIC_ASSERT (wi::int_traits::precision_type != wi::CONST_PRECISION); > - STATIC_ASSERT (wi::int_traits::precision_type > - != wi::WIDEST_CONST_PRECISION); > + STATIC_ASSERT (wi::int_traits::precision_type != wi::INL_CONST_PRECISION); > WIDE_INT_REF_FOR (T) xi (x); > if (UNLIKELY (precision != xi.precision)) > { > @@ -1391,7 +1384,7 @@ namespace wi > template > struct int_traits < fixed_wide_int_storage > > { > - static const enum precision_type precision_type = CONST_PRECISION; > + static const enum precision_type precision_type = INL_CONST_PRECISION; > static const bool host_dependent_precision = false; > static const bool is_sign_extended = true; > static const bool needs_write_val_arg = false; > @@ -1504,7 +1497,7 @@ class GTY(()) widest_int_storage > private: > union > { > - HOST_WIDE_INT val[WIDE_INT_MAX_HWIS (N)]; > + HOST_WIDE_INT val[WIDE_INT_MAX_INL_ELTS]; > HOST_WIDE_INT *valp; > } GTY((skip)) u; > unsigned int len; > @@ -1536,13 +1529,11 @@ namespace wi > template > struct int_traits < widest_int_storage > > { > - static const enum precision_type precision_type = WIDEST_CONST_PRECISION; > + static const enum precision_type precision_type = CONST_PRECISION; > static const bool host_dependent_precision = false; > static const bool is_sign_extended = true; > static const bool needs_write_val_arg = true; > - static const unsigned int precision > - = N / WIDE_INT_MAX_INL_PRECISION * WIDEST_INT_MAX_PRECISION; > - static const unsigned int inl_precision = N; > + static const unsigned int precision = N; > template > static WIDEST_INT (N) get_binary_result (const T1 &, const T2 &); > template > @@ -1561,8 +1552,7 @@ inline widest_int_storage ::widest_in > /* Check for type compatibility. We don't want to initialize a > widest integer from something like a wide_int. */ > WI_BINARY_RESULT (T, WIDEST_INT (N)) *assertion ATTRIBUTE_UNUSED; > - wi::copy (*this, WIDE_INT_REF_FOR (T) (x, N / WIDE_INT_MAX_INL_PRECISION > - * WIDEST_INT_MAX_PRECISION)); > + wi::copy (*this, WIDE_INT_REF_FOR (T) (x, N)); > } > > template > @@ -1570,7 +1560,7 @@ inline > widest_int_storage ::widest_int_storage (const widest_int_storage &x) > { > len = x.len; > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > { > u.valp = XNEWVEC (HOST_WIDE_INT, len); > memcpy (u.valp, x.u.valp, len * sizeof (HOST_WIDE_INT)); > @@ -1582,7 +1572,7 @@ widest_int_storage ::widest_int_stora > template > inline widest_int_storage ::~widest_int_storage () > { > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > XDELETEVEC (u.valp); > } > > @@ -1590,14 +1580,14 @@ template > inline widest_int_storage & > widest_int_storage ::operator = (const widest_int_storage &x) > { > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > { > if (this == &x) > return *this; > XDELETEVEC (u.valp); > } > len = x.len; > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > { > u.valp = XNEWVEC (HOST_WIDE_INT, len); > memcpy (u.valp, x.u.valp, len * sizeof (HOST_WIDE_INT)); > @@ -1615,11 +1605,10 @@ widest_int_storage ::operator = (cons > /* Check for type compatibility. We don't want to assign a > widest integer from something like a wide_int. */ > WI_BINARY_RESULT (T, WIDEST_INT (N)) *assertion ATTRIBUTE_UNUSED; > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > XDELETEVEC (u.valp); > len = 0; > - wi::copy (*this, WIDE_INT_REF_FOR (T) (x, N / WIDE_INT_MAX_INL_PRECISION > - * WIDEST_INT_MAX_PRECISION)); > + wi::copy (*this, WIDE_INT_REF_FOR (T) (x, N)); > return *this; > } > > @@ -1627,14 +1616,14 @@ template > inline unsigned int > widest_int_storage ::get_precision () const > { > - return N / WIDE_INT_MAX_INL_PRECISION * WIDEST_INT_MAX_PRECISION; > + return N; > } > > template > inline const HOST_WIDE_INT * > widest_int_storage ::get_val () const > { > - return UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT) ? u.valp : u.val; > + return UNLIKELY (len > WIDE_INT_MAX_INL_ELTS) ? u.valp : u.val; > } > > template > @@ -1648,10 +1637,10 @@ template > inline HOST_WIDE_INT * > widest_int_storage ::write_val (unsigned int l) > { > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS)) > XDELETEVEC (u.valp); > len = l; > - if (UNLIKELY (l > N / HOST_BITS_PER_WIDE_INT)) > + if (UNLIKELY (l > WIDE_INT_MAX_INL_ELTS)) > { > u.valp = XNEWVEC (HOST_WIDE_INT, l); > return u.valp; > @@ -1664,8 +1653,8 @@ inline void > widest_int_storage ::set_len (unsigned int l, bool) > { > gcc_checking_assert (l <= len); > - if (UNLIKELY (len > N / HOST_BITS_PER_WIDE_INT) > - && l <= N / HOST_BITS_PER_WIDE_INT) > + if (UNLIKELY (len > WIDE_INT_MAX_INL_ELTS) > + && l <= WIDE_INT_MAX_INL_ELTS) > { > HOST_WIDE_INT *valp = u.valp; > memcpy (u.val, valp, l * sizeof (u.val[0])); > @@ -1721,7 +1710,7 @@ inline unsigned int > wi::int_traits < widest_int_storage >:: > get_binary_precision (const T1 &, const T2 &) > { > - return N / WIDE_INT_MAX_INL_PRECISION * WIDEST_INT_MAX_PRECISION; > + return N; > } > > /* A reference to one element of a trailing_wide_ints structure. */ > @@ -2193,8 +2182,8 @@ template > inline unsigned int > wi::get_binary_precision (const T1 &x, const T2 &y) > { > - return wi::int_traits ::get_binary_precision (x, > - y); > + using res_traits = wi::int_traits ; > + return res_traits::get_binary_precision (x, y); > } > > /* Copy the contents of Y to X, but keeping X's current precision. */ > @@ -2683,8 +2672,8 @@ wi::bswap (const T &x) > WI_UNARY_RESULT_VAR (result, val, T, x); > unsigned int precision = get_precision (result); > WIDE_INT_REF_FOR (T) xi (x, precision); > - if (result.needs_write_val_arg) > - gcc_unreachable (); /* bswap on widest_int makes no sense. */ > + static_assert (!result.needs_write_val_arg, > + "bswap on widest_int makes no sense"); > result.set_len (bswap_large (val, xi.val, xi.len, precision)); > return result; > } > @@ -2697,8 +2686,8 @@ wi::bitreverse (const T &x) > WI_UNARY_RESULT_VAR (result, val, T, x); > unsigned int precision = get_precision (result); > WIDE_INT_REF_FOR (T) xi (x, precision); > - if (result.needs_write_val_arg) > - gcc_unreachable (); /* bitreverse on widest_int makes no sense. */ > + static_assert (!result.needs_write_val_arg, > + "bitreverse on widest_int makes no sense"); > result.set_len (bitreverse_large (val, xi.val, xi.len, precision)); > return result; > } > @@ -3127,8 +3116,8 @@ wi::mul_high (const T1 &x, const T2 &y, > unsigned int precision = get_precision (result); > WIDE_INT_REF_FOR (T1) xi (x, precision); > WIDE_INT_REF_FOR (T2) yi (y, precision); > - if (result.needs_write_val_arg) > - gcc_unreachable (); /* mul_high on widest_int doesn't make sense. */ > + static_assert (!result.needs_write_val_arg, > + "mul_high on widest_int doesn't make sense"); > result.set_len (mul_internal (val, xi.val, xi.len, > yi.val, yi.len, precision, > sgn, 0, true)); > --- gcc/tree.h.jj 2023-10-09 14:37:45.880940104 +0200 > +++ gcc/tree.h 2023-10-09 17:04:47.138376452 +0200 > @@ -6259,13 +6259,10 @@ namespace wi > struct int_traits > > { > static const enum precision_type precision_type > - = N == ADDR_MAX_PRECISION ? CONST_PRECISION : WIDEST_CONST_PRECISION; > + = N == ADDR_MAX_PRECISION ? INL_CONST_PRECISION : CONST_PRECISION; > static const bool host_dependent_precision = false; > static const bool is_sign_extended = true; > static const unsigned int precision = N; > - static const unsigned int inl_precision > - = N == ADDR_MAX_PRECISION ? 0 > - : N / WIDEST_INT_MAX_PRECISION * WIDE_INT_MAX_INL_PRECISION; > }; > > typedef extended_tree widest_extended_tree; > > Jakub