On Tue, 28 Jul 2026, Richard Biener wrote: > On Tue, 28 Jul 2026, Thomas Schwinge wrote: > > > Hi Richard! > > > > Thanks for looking into this! Regarding your review: > > > > 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, > > >> + 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 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)?! > > That is, what is the wrong thing it thinks might do that this > guards? 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. Richard. > Richard. > > > > > > > Grüße > > Thomas > > > > -- Richard Biener <[email protected]> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)
