Hi Richard!

On 2026-07-29T08:47:19+0200, Richard Biener <[email protected]> wrote:
> On Tue, 28 Jul 2026, Mikael Morin wrote:
>> Le 28/07/2026 à 17:22, Richard Biener a écrit :
>> > On Tue, 28 Jul 2026, Richard Biener wrote:
>> >> On Tue, 28 Jul 2026, Richard Biener wrote:
>> >>> On Tue, 28 Jul 2026, Richard Biener wrote:
>> >>>> On Tue, 28 Jul 2026, Thomas Schwinge wrote:
>> >>>>> On 2026-07-28T08:11:46+0200, Richard Biener <[email protected]> wrote:
>> >>>>>> On Fri, 24 Jul 2026, Thomas Schwinge wrote:
>> >>>>>>> GCC supports targets without 'casesi'/'tablejump' instructions.  (For
>> >>>>>>> example, '--target=amdgcn-amdhsa', '--target=nvptx-none' -- could
>> >>>>>>> probably be implemented, but currently isn't.)  For x86_64, this can
>> >>>>>>> be
>> >>>>>>> "faked" by modifying 'gcc/config/i386/i386.md':
>> >>>>>>>
>> >>>>>>>       (define_expand "tablejump"
>> >>>>>>>         [(parallel [(set (pc) (match_operand 0
>> >>>>>>>         "indirect_branch_operand"))
>> >>>>>>>                    (use (label_ref (match_operand 1)))])]
>> >>>>>>>      -  ""
>> >>>>>>>      +  "false"
>> >>>>>>>
>> >>>>>>>       (define_insn "*tablejump_1"
>> >>>>>>>         [(set (pc) (match_operand:W 0 "indirect_branch_operand"
>> >>>>>>>         "rBw"))
>> >>>>>>>          (use (label_ref (match_operand 1)))]
>> >>>>>>>      -  ""
>> >>>>>>>      +  "false"
>> >>>>>>>
>> >>>>>>> For such GCC configurations, we've recently acquired a number of
>> >>>>>>> GCC/Fortran regressions à la:
>> >>>>>>>
>> >>>>>>>      during RTL pass: expand
>> >>>>>>>      
>> >>>>>>> [...]/source-gcc/gcc/testsuite/gfortran.dg/assumed_rank_bounds_3.f90:178:38:
>> >>>>>>>      internal compiler error: in emit_case_dispatch_table, at
>> >>>>>>>      stmt.cc:1199
>> >>>>>>>      0x1f2d35d internal_error(char const*, ...)
>> >>>>>>>              [...]/source-gcc/gcc/diagnostic-global-context.cc:787
>> >>>>>>>      0x8d368b fancy_abort(char const*, int, char const*)
>> >>>>>>>              [...]/source-gcc/gcc/diagnostics/context.cc:1813
>> >>>>>>>      0x773983 emit_case_dispatch_table
>> >>>>>>>              [...]/source-gcc/gcc/stmt.cc:1199
>> >>>>>>>      0x1078752 expand_case(gswitch*)
>> >>>>>>>              [...]/source-gcc/gcc/stmt.cc:1359
>> >>>>>>>
>> >>>>>>> That's 'gcc/cfgexpand.cc:expand_gimple_stmt_1', 'case GIMPLE_SWITCH:'
>> >>>>>>> calling 'gcc/stmt.cc:expand_case', which calls
>> >>>>>>> 'gcc/stmt.cc:emit_case_dispatch_table', which does:
>> >>>>>>>
>> >>>>>>>      [...] try "casesi".  If that
>> >>>>>>>      fails, try "tablejump".   A target *must* have one of them (or
>> >>>>>>>      both).
>> >>>>>>>
>> >>>>>>> That means, for targets providing neither 'casesi' nor 'tablejump',
>> >>>>>>> there
>> >>>>>>> must not be any 'GIMPLE_SWITCH'es anymore, when getting to
>> >>>>>>> 'gcc/cfgexpand.cc:expand_gimple_stmt_1' -- and usually there aren't,
>> >>>>>>> due
>> >>>>>>> to 'gcc/tree-switch-conversion.cc' doing what is appropriate; in
>> >>>>>>> particular, 'gcc/tree-switch-conversion.h':
>> >>>>>>>
>> >>>>>>>      /* Return whether jump table expansion is allowed.  */
>> >>>>>>>      bool jump_table_cluster::is_enabled (void)
>> >>>>>>>      {
>> >>>>>>>        /* If neither casesi or tablejump is available, or
>> >>>>>>>        flag_jump_tables
>> >>>>>>>           over-ruled us, we really have no choice.  */
>> >>>>>>>        if (!targetm.have_casesi () && !targetm.have_tablejump ())
>> >>>>>>>          return false;
>> >>>>>>>      [...]
>> >>>>>>>
>> >>>>>>> ... deciding whether to lower 'GIMPLE_SWITCH'es into other control
>> >>>>>>> flow
>> >>>>>>> constructs supported by the target.
>> >>>>>>>
>> >>>>>>> Enter recent commit 
>> >>>>>>> r17-2216-g3a8d9347f30b9d66ed6a3c7e0959c08e93ecb205
>> >>>>>>> "fortran: Create a dedicated type for ranks and array dimensions",
>> >>>>>>> which:
>> >>>>>>>
>> >>>>>>> | [...] adds a type to represent ranks and array dimension, using the
>> >>>>>>> | same base type as originally used for the rank in array descriptors
>> >>>>>>> | (signed char), but with the stricter bounds (0 to
>> >>>>>>> | GFC_MAX_DIMENSIONS)
>> >>>>>>> | brought to the knowledge of the middle-end.  [...]
>> >>>>>>>
>> >>>>>>>      --- a/gcc/fortran/trans-types.cc
>> >>>>>>>      +++ b/gcc/fortran/trans-types.cc
>> >>>>>>>      [...]
>> >>>>>>>      +tree gfc_array_dim_rank_type;
>> >>>>>>>      [...]
>> >>>>>>>      @@ -1226,6 +1227,12 @@ gfc_init_types (void)
>> >>>>>>>         gfc_charlen_int_kind = get_int_kind_from_node
>> >>>>>>>         (size_type_node);
>> >>>>>>>         gfc_charlen_type_node = gfc_get_int_type
>> >>>>>>>         (gfc_charlen_int_kind);
>> >>>>>>>
>> >>>>>>>      +  gfc_array_dim_rank_type
>> >>>>>>>      +                = build_range_type (signed_char_type_node,
>> > 
>> > And basing this on unsigned_char_type_node might be an easier fix?
>> 
>> But unsigned types have the same problem, don't they?
>> Say if the type is [20, 30] and we are trying to check that a value v is
>> within say [21, 24], a transformation to check that v - 21 is within [0, 3]
>> can't use the original type because the values are no longer in the range of
>> the original type.
>
> Sure, but we're using an unsigned type of the original types precision
> anyway.  And in the gfortran case the low bound is zero, so we
> have [0, MAX_DIMENSIONS], meaning an unsigned char base type sounds
> appropriate?

That might be appropriate here, but (a) per Mikael's
commit r17-2216-g3a8d9347f30b9d66ed6a3c7e0959c08e93ecb205
"fortran: Create a dedicated type for ranks and array dimensions":

| This patch adds a type to represent ranks and array dimension, using the
| same base type as originally used for the rank in array descriptors
| (signed char), but with the stricter bounds (0 to GFC_MAX_DIMENSIONS)
| brought to the knowledge of the middle-end.  [...]

..., so it was an intentional decision to keep this as
'signed_char_type_node'.

..., and (b) using 'unsigned_char_type_node' just here obviously won't
resolve the underlying more general issue of PR126392
"'GIMPLE_SWITCH' lowering for 'INTEGER_TYPE' subrange wrappers vs. targets 
without 'casesi'/'tablejump'",
assuming that such types may also come into existence elsewhere.

Otherwise:

>> >>>>>>>      +                                    build_zero_cst
>> >>>>>>>      (signed_char_type_node),
>> >>>>>>>      +                                    build_int_cst
>> >>>>>>>      (signed_char_type_node,
>> >>>>>>>      +
>> >>>>>>>      GFC_MAX_DIMENSIONS));
>> >>>>>>>      [...]
>> >>>>>>>      --- a/gcc/fortran/trans-types.h
>> >>>>>>>      +++ b/gcc/fortran/trans-types.h
>> >>>>>>>      [...]
>> >>>>>>>      +/* An integral type with bounds [0, GFC_MAX_DIMENSIONS] 
>> >>>>>>> suitable
>> >>>>>>>      to hold an
>> >>>>>>>      +   array rank, or an array dimension index.  */
>> >>>>>>>      +extern GTY(()) tree gfc_array_dim_rank_type;
>> >>>>>>>      [...]
>> >>>>>>>
>> >>>>>>> ..., that is, a "subrange wrapper"; 'gcc/tree.cc':
>> >>>>>>>
>> >>>>>>>      /* Wrapper around build_range_type_1 with SHARED set to true.  
>> >>>>>>> */
>> >>>>>>>
>> >>>>>>>      tree
>> >>>>>>>      build_range_type (tree type, tree lowval, tree highval)
>> >>>>>>>      {
>> >>>>>>>        return build_range_type_1 (type, lowval, highval, true);
>> >>>>>>>      }
>> >>>>>>>
>> >>>>>>>      /* Create a range of some discrete type TYPE (an INTEGER_TYPE,
>> >>>>>>>      ENUMERAL_TYPE
>> >>>>>>>         or BOOLEAN_TYPE) with low bound LOWVAL and high bound 
>> >>>>>>> HIGHVAL.
>> >>>>>>>         If SHARED
>> >>>>>>>         is true, reuse such a type that has already been constructed.
>> >>>>>>>         */
>> >>>>>>>
>> >>>>>>>      static tree
>> >>>>>>>      build_range_type_1 (tree type, tree lowval, tree highval, bool
>> >>>>>>>      shared)
>> >>>>>>>      {
>> >>>>>>>      [...]
>> >>>>>>>
>> >>>>>>> That one has its own 'TREE_TYPE' pointing to 'signed_char_type_node',
>> >>>>>>> and
>> >>>>>>> itself has 'precision:8', but 'min <[...] 0> max <[...] 15>', that 
>> >>>>>>> is,
>> >>>>>>> a
>> >>>>>>> restricted range compared to 'min <[...] -128> max <[...] 127>'.
>> >>>>>>> Nothing
>> >>>>>>> bad with that, as far as I can tell, and supposedly that enables
>> >>>>>>> certain
>> >>>>>>> code optimizations, due to the more restricted range.
>> >>>>>>>
>> >>>>>>> Now, 'gcc/tree-switch-conversion.cc':
>> >>>>>>>
>> >>>>>>>      /* Attempt to expand CLUSTERS as a decision tree.  Return true
>> >>>>>>>      when
>> >>>>>>>         expanded.  */
>> >>>>>>>
>> >>>>>>>      bool
>> >>>>>>>      switch_decision_tree::try_switch_expansion (vec<cluster *>
>> >>>>>>>      &clusters)
>> >>>>>>>      {
>> >>>>>>>        tree index_expr = gimple_switch_index (m_switch);
>> >>>>>>>        tree index_type = TREE_TYPE (index_expr);
>> >>>>>>>        basic_block bb = gimple_bb (m_switch);
>> >>>>>>>
>> >>>>>>>        if (gimple_switch_num_labels (m_switch) == 1
>> >>>>>>>            || range_check_type (index_type) == NULL_TREE)
>> >>>>>>>          return false;
>> >>>>>>>      [...]
>> >>>>>>>
>> >>>>>>> ... calls 'gcc/fold-const.cc:range_check_type':
>> >>>>>>>
>> >>>>>>>      /* Helper routine for build_range_check and match.pd.  Return 
>> >>>>>>> the
>> >>>>>>>      type to
>> >>>>>>>         perform the check or NULL if it shouldn't be optimized.  */
>> >>>>>>>
>> >>>>>>>      tree
>> >>>>>>>      range_check_type (tree etype)
>> >>>>>>>      {
>> >>>>>>>        /* First make sure that arithmetics in this type is valid, 
>> >>>>>>> then
>> >>>>>>>        make sure
>> >>>>>>>           that it wraps around.  */
>> >>>>>>>        [...]
>> >>>>>>>        if (TREE_CODE (etype) == INTEGER_TYPE && !TYPE_UNSIGNED
>> >>>>>>>        (etype))
>> >>>>>>>          {
>> >>>>>>>            tree utype, minv, maxv;
>> >>>>>>>
>> >>>>>>>            /* Check if (unsigned) INT_MAX + 1 == (unsigned) INT_MIN
>> >>>>>>>               for the type in question, as we rely on this here.  */
>> >>>>>>>            utype = unsigned_type_for (etype);
>> >>>>>>>            maxv = fold_convert (utype, TYPE_MAX_VALUE (etype));
>> >>>>>>>            maxv = range_binop (PLUS_EXPR, NULL_TREE, maxv, 1,
>> >>>>>>>                                build_int_cst (TREE_TYPE (maxv), 1),
>> >>>>>>>            1);
>> >>>>>>>            minv = fold_convert (utype, TYPE_MIN_VALUE (etype));
>> >>>>>>>
>> >>>>>>>            if (integer_zerop (range_binop (NE_EXPR, 
>> >>>>>>> integer_type_node,
>> >>>>>>>                                            minv, 1, maxv, 1)))
>> >>>>>>>              etype = utype;
>> >>>>>>>            else
>> >>>>>>>              return NULL_TREE;
>> >>>>>>>          }
>> >>>>>>>      [...]
>> >>>>>>>
>> >>>>>>> Here we realize that 'TYPE_MAX_VALUE (gfc_array_dim_rank_type)' 
>> >>>>>>> ('15')
>> >>>>>>> does *not* wrap around to 'TYPE_MIN_VALUE (gfc_array_dim_rank_type)'
>> >>>>>>> ('0') when adding '1' (contrary to '127 + 1 -> -128', for example),
>> >>>>>>> and
>> >>>>>>> therefore we 'return NULL_TREE;', and therefore block the
>> >>>>>>> 'GIMPLE_SWITCH'
>> >>>>>>> lowering.
>> >>>>>>>
>> >>>>>>> Per my understanding, that problem has been latent, just now exposed
>> >>>>>>> via
>> >>>>>>> this GCC/Fortran front end change.
>> >>>>>>>
>> >>>>>>> Resolve this by peeling off 'INTEGER_TYPE' subrange wrappers, similar
>> >>>>>>> to
>> >>>>>>> how that's already being done for 'ENUMERAL_TYPE's, 'BOOLEAN_TYPE's.
>> >>>>>>> With that, the regressions for GCN and nvptx (as well as "faked"
>> >>>>>>> x86_64)
>> >>>>>>> disappear, and there's no other change in test results, including
>> >>>>>>> x86_64-pc-linux-gnu as well as powerpc64le-unknown-linux-gnu
>> >>>>>>> bootstrap.
>> >>>>>>>
>> >>>>>>>  PR tree-optimization/126392
>> >>>>>>>  gcc/
>> >>>>>>>  * fold-const.cc (range_check_type): Peel off subrange wrappers.
>> >>>>>>> ---
>> >>>>>>>   gcc/fold-const.cc | 4 ++++
>> >>>>>>>   1 file changed, 4 insertions(+)
>> >>>>>>>
>> >>>>>>> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
>> >>>>>>> index 1764942f34a..5eb25b547fe 100644
>> >>>>>>> --- a/gcc/fold-const.cc
>> >>>>>>> +++ b/gcc/fold-const.cc
>> >>>>>>> @@ -5544,6 +5544,10 @@ range_check_type (tree etype)
>> >>>>>>>       etype = TREE_TYPE (etype);
>> >>>>>>>     else if (TREE_CODE (etype) == ENUMERAL_TYPE || TREE_CODE (etype)
>> >>>>>>>     == BOOLEAN_TYPE)
>> >>>>>>>       etype = lang_hooks.types.type_for_size (TYPE_PRECISION (etype),
>> >>>>>>> 1);
>> >>>>>>> +  else if (TREE_CODE (etype) == INTEGER_TYPE
>> >>>>>>> +       && TREE_TYPE (etype))
>> >>>>>>> +    /* Peel off subrange wrappers 
>> >>>>>>> ('gcc/tree.cc:build_range_type_1').
>> >>>>>>> */
>> >>>>>>> +    etype = TREE_TYPE (etype);
>> >>>>>>
>> >>>>>> In principle reasonable, but I wonder whether we can rely on this
>> >>>>>> being useful for the purpose.  TREE_TYPE on INTEGER_TYPE isn't
>> >>>>>> documented
>> >>>>>
>> >>>>> Quite a number of front end as well as generic GCC code calls
>> >>>>> 'build_range_type', 'build_nonshared_range_type', so this should become
>> >>>>> documented, I infer?
>> >>>>>
>> >>>>>>   but IIRC it is indeed set when the INTEGER_TYPE is a
>> >>>>>> "subtype" (but I don't think it has to).
>> >>>>>
>> >>>>> When you say "don't think it has to", is your worry that
>> >>>>> 'gcc/tree.cc:build_range_type_1' might not return a type with
>> >>>>> 'TREE_TYPE'
>> >>>>> in the 'type_hash_canon' code path (I shall run a test with a
>> >>>>> corresponding 'gcc_assert' added), or is it something else?
>> >>>>>
>> >>>>> Or, is your worry that other types may have 'TREE_TYPE' set, without
>> >>>>> being such "subrange wrappers"?  Then we should disambiguate that?
>> >>>>>
>> >>>>>> I'll note that using this type does not get you a wrapping type
>> >>>>>> in the original range either, so I'm not sure what the condition
>> >>>>>> above is testing.
>> >>>>>
>> >>>>> I don't understand "what the condition above is testing", please
>> >>>>> clarify?
>> >>>>> With: 'TREE_CODE (etype) == INTEGER_TYPE && TREE_TYPE (etype)' I intend
>> >>>>> to match (only) such "subrange wrappers", mirroring what
>> >>>>> 'gcc/tree.cc:build_range_type_1' is doing.
>> >>>>>
>> >>>>> Indeed the base type ('TREE_TYPE (etype)') need not be 'unsigned'.  For
>> >>>>> example, like in my example in the Git commit log cited above, where it
>> >>>>> was 'signed char'.  But note that we don't just return that base type,
>> >>>>> but instead assign it to 'etype' and then continue 'range_check_type'
>> >>>>> analysis with that one.  That is, in particular the "make sure that it
>> >>>>> wraps around" code gets executed:
>> >>>>>
>> >>>>>      if (TREE_CODE (etype) == INTEGER_TYPE && !TYPE_UNSIGNED (etype))
>> >>>>>        {
>> >>>>>          [...]
>> >>>>>          utype = unsigned_type_for (etype);
>> >>>>>          [...]
>> >>>>>
>> >>>>> ..., which then either returns this 'utype', or 'NULL'.
>> >>>>>
>> >>>>>> So you are simply by-passing the check, fixing the ICE but either
>> >>>>>> showing the check is pointless or simply ignoring the issue when
>> >>>>>> it doesn't pass.
>> >>>>>
>> >>>>> Sorry, again I don't understand what exactly this commen applies to.
>> >>>>>
>> >>>>> Is this about the 'range_check_type' usage in
>> >>>>> 'gcc/tree-switch-conversion.cc:switch_decision_tree::try_switch_expansion'?
>> >>>>> Indeed that one uses the 'range_check_type' return value only as a
>> >>>>> boolean flag (appropriate types exists: continue vs. doesn't exist:
>> >>>>> 'return false;'), but per my understanding the actual 'GIMPLE_SWITCH'
>> >>>>> transformation code then individually agian calls 'range_check_type'
>> >>>>> where necessary.
>> >>>>>
>> >>>>>
>> >>>>> But please let me know if the underlying problem should be solved in
>> >>>>> another way.  I'm certainly not an expert in 'GIMPLE_SWITCH'es, and
>> >>>>> neither do I know all the details about GCC's type system.
>> >>>>>
>> >>>>> I came up with this patch by tracing down what was happening, and
>> >>>>> noticing that 'range_check_type' already does similar fix-up for
>> >>>>> 'ENUMERAL_TYPE's, 'BOOLEAN_TYPE's, so why not similarly also for other
>> >>>>> "subrange" types.
>> >>>>
>> >>>> I wonder why range_check_type jumps through hoops here:
>> >>>>
>> >>>>    if (TREE_CODE (etype) == INTEGER_TYPE && !TYPE_UNSIGNED (etype))
>> >>>>      {
>> >>>>        tree utype, minv, maxv;
>> >>>>
>> >>>>        /* Check if (unsigned) INT_MAX + 1 == (unsigned) INT_MIN
>> >>>>           for the type in question, as we rely on this here.  */
>> >>>>        utype = unsigned_type_for (etype);
>> >>>>        maxv = fold_convert (utype, TYPE_MAX_VALUE (etype));
>> >>>>        maxv = range_binop (PLUS_EXPR, NULL_TREE, maxv, 1,
>> >>>>                            build_int_cst (TREE_TYPE (maxv), 1), 1);
>> >>>>        minv = fold_convert (utype, TYPE_MIN_VALUE (etype));
>> >>>>
>> >>>>        if (integer_zerop (range_binop (NE_EXPR, integer_type_node,
>> >>>>                                        minv, 1, maxv, 1)))
>> >>>>          etype = utype;
>> >>>>        else
>> >>>>          return NULL_TREE;
>> >>>>      }
>> >>>> ...
>> >>>>    return etype;
>> >>>>
>> >>>> when, for an unsigned subrange type it does not bother to verify
>> >>>> anything

My assumption is that for 'unsigned' types, it's fine to always assume
the wrap-around semantics?

>> >>>> and when it practically ignores subrange types from
>> >>>> ENUMERAL or BOOLEAN types by only looking at their "base" types?
>> >>>>
>> >>>> Why does it not just always return unsigned_type_for (etype)?!

Right, so that is indeed a good question.

>> >>>> That is, what is the wrong thing it thinks might do that this
>> >>>> guards?

I'll try to figure that out.

>> >>>> And why isn't that an issue for enumeral or booleans
>> >>>> or enumeral bitint types or for the case you are adding?
>> >>>
>> >>> That said, if the range type were [-2, 7] based on 'signed char'
>> >>> then we might eventually want to convert a [-2, 2] range check
>> >>> to an unsigned compare of (unsigned)val + 2 < 4.  I'm not sure
>> >>> how this breaks when 7 + 1 isn't -2 and why it's OK to check
>> >>> on the TREE_TYPE of the type when the switch () and the cases
>> >>> are based on the range type (IIRC the case values never use
>> >>> the range type but the underlying type).
>> >>>
>> >>> But I for sure must be missing something.
>> >>
>> >> This was added by r0-59059-ge1af8299421eb7 by jakub btw.

..., and here is the patch submission discussion thread
<https://inbox.sourceware.org/[email protected]>
"New fold_range_test optimizations".  (Have not yet digested.)


Grüße
 Thomas

Reply via email to