From 7314b98b1bcd382c996a79bdcd0a1cb1e9231f55 Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Tue, 27 Sep 2022 10:16:52 +0200 Subject: unswitch most profitable condition first When doing the loop unswitching re-org we promised to followup with improvements on the cost modeling. The following makes sure we try to unswitch on the most profitable condition first. As most profitable we pick the condition leading to the edge with the highest profile count. Note the profile is only applied when picking the first unswitching opportunity since the profile counts are not updated with earlier unswitchings in mind. Further opportunities are picked in DFS order. * tree-ssa-loop-unswitch.cc (unswitch_predicate::count): New. (unswitch_predicate::unswitch_predicate): Initialize count. (init_loop_unswitch_info): First collect candidates and determine the outermost loop to unswitch. (tree_ssa_unswitch_loops): First perform all guard hoisting, then perform unswitching on innermost loop predicates. (find_unswitching_predicates_for_bb): Keep track of the most profitable predicate to unswitch on. (tree_unswitch_single_loop): Unswitch given predicate if not NULL. --- gcc/tree-ssa-loop-unswitch.cc | 66 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/gcc/tree-ssa-loop-unswitch.cc b/gcc/tree-ssa-loop-unswitch.cc index 7d6781d1505..dfe75f52f12 100644 --- a/gcc/tree-ssa-loop-unswitch.cc +++ b/gcc/tree-ssa-loop-unswitch.cc @@ -118,6 +118,7 @@ struct unswitch_predicate if (!false_range.varying_p () && !false_range.undefined_p ()) false_range.invert (); + count = e->count (); num = predicates->length (); predicates->safe_push (this); } @@ -126,7 +127,8 @@ struct unswitch_predicate unswitch_predicate (gcond *stmt) : switch_p (false) { - if (EDGE_SUCC (gimple_bb (stmt), 0)->flags & EDGE_TRUE_VALUE) + basic_block bb = gimple_bb (stmt); + if (EDGE_SUCC (bb, 0)->flags & EDGE_TRUE_VALUE) edge_index = 0; else edge_index = 1; @@ -134,6 +136,7 @@ struct unswitch_predicate tree rhs = gimple_cond_rhs (stmt); enum tree_code code = gimple_cond_code (stmt); condition = build2 (code, boolean_type_node, lhs, rhs); + count = EDGE_SUCC (bb, 0)->count ().max (EDGE_SUCC (bb, 1)->count ()); if (irange::supports_p (TREE_TYPE (lhs))) { auto range_op = range_op_handler (code, TREE_TYPE (lhs)); @@ -180,6 +183,9 @@ struct unswitch_predicate /* Index of the edge the predicate belongs to in the successor vector. */ int edge_index; + /* The profile count of this predicate. */ + profile_count count; + /* Whether the predicate was created from a switch statement. */ bool switch_p; @@ -206,10 +212,14 @@ static class loop *tree_unswitch_loop (class loop *, edge, tree); static bool tree_unswitch_single_loop (class loop *, dump_user_location_t, predicate_vector &predicate_path, unsigned loop_size, unsigned &budget, - int ignored_edge_flag, bitmap); + int ignored_edge_flag, bitmap, + unswitch_predicate * = NULL, + basic_block = NULL); static void find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, - vec &candidates); + vec &candidates, + unswitch_predicate *&hottest, + basic_block &hottest_bb); static bool tree_unswitch_outer_loop (class loop *); static edge find_loop_guard (class loop *, vec&); static bool empty_bb_without_guard_p (class loop *, basic_block, @@ -242,7 +252,8 @@ set_predicates_for_bb (basic_block bb, vec predicates) Return total number of instructions in the loop. */ static unsigned -init_loop_unswitch_info (class loop *loop) +init_loop_unswitch_info (class loop *loop, unswitch_predicate *&hottest, + basic_block &hottest_bb) { unsigned total_insns = 0; @@ -259,13 +270,16 @@ init_loop_unswitch_info (class loop *loop) total_insns += insns; } + hottest = NULL; + hottest_bb = NULL; /* Find all unswitching candidates. */ for (unsigned i = 0; i != loop->num_nodes; i++) { /* Find a bb to unswitch on. */ vec candidates; candidates.create (1); - find_unswitching_predicates_for_bb (bbs[i], loop, candidates); + find_unswitching_predicates_for_bb (bbs[i], loop, candidates, + hottest, hottest_bb); if (!candidates.is_empty ()) set_predicates_for_bb (bbs[i], candidates); else @@ -329,7 +343,10 @@ tree_ssa_unswitch_loops (function *fun) unswitch_predicate::predicates = new vec (); /* Unswitch innermost loop. */ - unsigned int loop_size = init_loop_unswitch_info (loop); + unswitch_predicate *hottest; + basic_block hottest_bb; + unsigned int loop_size = init_loop_unswitch_info (loop, hottest, + hottest_bb); unsigned int budget = loop_size + param_max_unswitch_insns; predicate_vector predicate_path; @@ -338,7 +355,8 @@ tree_ssa_unswitch_loops (function *fun) changed_unswitch |= tree_unswitch_single_loop (loop, loc, predicate_path, loop_size, budget, - ignored_edge_flag, handled); + ignored_edge_flag, handled, + hottest, hottest_bb); predicate_path.release (); for (auto predlist : bb_predicates) @@ -449,7 +467,9 @@ is_maybe_undefined (const tree name, gimple *stmt, class loop *loop) static void find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, - vec &candidates) + vec &candidates, + unswitch_predicate *&hottest, + basic_block &hottest_bb) { gimple *last, *def; tree use; @@ -489,6 +509,14 @@ find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, unswitch_predicate *predicate = new unswitch_predicate (stmt); candidates.safe_push (predicate); + /* If we unswitch on this predicate we isolate both paths, so + pick the highest count for updating of the hottest predicate + to unswitch on first. */ + if (!hottest || predicate->count > hottest->count) + { + hottest = predicate; + hottest_bb = bb; + } } else if (gswitch *stmt = safe_dyn_cast (last)) { @@ -561,6 +589,11 @@ find_unswitching_predicates_for_bb (basic_block bb, class loop *loop, edge_index, e, edge_range[edge_index]); candidates.safe_push (predicate); + if (!hottest || predicate->count > hottest->count) + { + hottest = predicate; + hottest_bb = bb; + } } } } @@ -888,13 +921,15 @@ evaluate_loop_insns_for_predicate (class loop *loop, /* Unswitch single LOOP. PREDICATE_PATH contains so far used predicates for unswitching. BUDGET is number of instruction for which we can increase - the loop and is updated when unswitching occurs. */ + the loop and is updated when unswitching occurs. If HOTTEST is not + NULL then pick this candidate as the one to unswitch on. */ static bool tree_unswitch_single_loop (class loop *loop, dump_user_location_t loc, predicate_vector &predicate_path, unsigned loop_size, unsigned &budget, - int ignored_edge_flag, bitmap handled) + int ignored_edge_flag, bitmap handled, + unswitch_predicate *hottest, basic_block hottest_bb) { class loop *nloop; bool changed = false; @@ -939,8 +974,15 @@ tree_unswitch_single_loop (class loop *loop, dump_user_location_t loc, } return false; }; - /* Check predicates of reachable blocks. */ - evaluate_bbs (loop, NULL, ignored_edge_flag, check_predicates); + + if (hottest) + { + predicate = hottest; + predicate_bb = hottest_bb; + } + else + /* Check predicates of reachable blocks. */ + evaluate_bbs (loop, NULL, ignored_edge_flag, check_predicates); if (predicate != NULL) { -- cgit v1.2.1 From 21cd625eb8f055a72ee056be32a958b786843e80 Mon Sep 17 00:00:00 2001 From: konglin1 Date: Mon, 7 Nov 2022 17:34:50 +0800 Subject: i386: Fix typo in sse-22.c pragma gcc/testsuite/ChangeLog: * gcc.target/i386/sse-22.c: Fix typo in pragma GCC target. --- gcc/testsuite/gcc.target/i386/sse-22.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcc/testsuite/gcc.target/i386/sse-22.c b/gcc/testsuite/gcc.target/i386/sse-22.c index f5808e4513b..f600bb544b2 100644 --- a/gcc/testsuite/gcc.target/i386/sse-22.c +++ b/gcc/testsuite/gcc.target/i386/sse-22.c @@ -103,7 +103,7 @@ #ifndef DIFFERENT_PRAGMAS -#pragma GCC target ("sse4a,3dnow,avx,avx2,fma4,xop,aes,pclmul,popcnt,abm,lzcnt,bmi,bmi2,tbm,lwp,fsgsbase,rdrnd,f16c,rtm,rdseed,prfchw,adx,fxsr,xsaveopt,avx512f,avx512er,avx512cd,avx512pf,sha,prefetchwt1,avx512vl,avx512bw,avx512dq,avx512vbmi,avx512vbmi2,avx512ifma,avx5124fmaps,avx5124vnniw,avx512vpopcntdq,gfni,avx512bitalg,avx512bf16,avx512vp2intersect,serialize,tsxldtrk,amx-tile,amx-int8,amx-bf16,kl,widekl,avxvnni,avx512fp16,avxifma,avxvnniint8,avxneconvert,amx-fp16.raoint") +#pragma GCC target ("sse4a,3dnow,avx,avx2,fma4,xop,aes,pclmul,popcnt,abm,lzcnt,bmi,bmi2,tbm,lwp,fsgsbase,rdrnd,f16c,rtm,rdseed,prfchw,adx,fxsr,xsaveopt,avx512f,avx512er,avx512cd,avx512pf,sha,prefetchwt1,avx512vl,avx512bw,avx512dq,avx512vbmi,avx512vbmi2,avx512ifma,avx5124fmaps,avx5124vnniw,avx512vpopcntdq,gfni,avx512bitalg,avx512bf16,avx512vp2intersect,serialize,tsxldtrk,amx-tile,amx-int8,amx-bf16,kl,widekl,avxvnni,avx512fp16,avxifma,avxvnniint8,avxneconvert,amx-fp16,raoint") #endif /* Following intrinsics require immediate arguments. They -- cgit v1.2.1 From 071d00e0faabbd45449d2e83f207fca0f8e8ef68 Mon Sep 17 00:00:00 2001 From: Tobias Burnus Date: Mon, 7 Nov 2022 11:32:33 +0100 Subject: Fortran: Fix reallocation on assignment for kind=4 strings [PR107508] The check whether reallocation on assignment was required did not handle kind=4 characters correctly such that there was always a reallocation, implying issues with pointer addresses and lower bounds. Additionally, with all deferred strings, the old memory was not freed on reallocation. And, finally, inside the block which was only executed if string lengths or bounds or dynamic types changed, was a subcheck of the same, which was effectively a no op but still confusing and at least added with -O0 extra instructions to the binary. PR fortran/107508 gcc/fortran/ChangeLog: * trans-array.cc (gfc_alloc_allocatable_for_assignment): Fix string-length check, plug memory leak, and avoid generation of effectively no-op code. * trans-expr.cc (alloc_scalar_allocatable_for_assignment): Extend comment; minor cleanup. gcc/testsuite/ChangeLog: * gfortran.dg/widechar_11.f90: New test. --- gcc/fortran/trans-array.cc | 57 ++++--------------------------- gcc/fortran/trans-expr.cc | 6 ++-- gcc/testsuite/gfortran.dg/widechar_11.f90 | 51 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 gcc/testsuite/gfortran.dg/widechar_11.f90 diff --git a/gcc/fortran/trans-array.cc b/gcc/fortran/trans-array.cc index 514cb057afb..b7d4c41b5fe 100644 --- a/gcc/fortran/trans-array.cc +++ b/gcc/fortran/trans-array.cc @@ -10527,7 +10527,6 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, tree offset; tree jump_label1; tree jump_label2; - tree neq_size; tree lbd; tree class_expr2 = NULL_TREE; int n; @@ -10607,6 +10606,11 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, elemsize1 = expr1->ts.u.cl->backend_decl; else elemsize1 = lss->info->string_length; + tree unit_size = TYPE_SIZE_UNIT (gfc_get_char_type (expr1->ts.kind)); + elemsize1 = fold_build2_loc (input_location, MULT_EXPR, + TREE_TYPE (elemsize1), elemsize1, + fold_convert (TREE_TYPE (elemsize1), unit_size)); + } else if (expr1->ts.type == BT_CLASS) { @@ -10699,19 +10703,7 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, /* Allocate if data is NULL. */ cond_null = fold_build2_loc (input_location, EQ_EXPR, logical_type_node, array1, build_int_cst (TREE_TYPE (array1), 0)); - - if (expr1->ts.type == BT_CHARACTER && expr1->ts.deferred) - { - tmp = fold_build2_loc (input_location, NE_EXPR, - logical_type_node, - lss->info->string_length, - rss->info->string_length); - cond_null = fold_build2_loc (input_location, TRUTH_OR_EXPR, - logical_type_node, tmp, cond_null); - cond_null= gfc_evaluate_now (cond_null, &fblock); - } - else - cond_null= gfc_evaluate_now (cond_null, &fblock); + cond_null= gfc_evaluate_now (cond_null, &fblock); tmp = build3_v (COND_EXPR, cond_null, build1_v (GOTO_EXPR, jump_label1), @@ -10778,19 +10770,6 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, tmp = build1_v (LABEL_EXPR, jump_label1); gfc_add_expr_to_block (&fblock, tmp); - /* If the lhs has not been allocated, its bounds will not have been - initialized and so its size is set to zero. */ - size1 = gfc_create_var (gfc_array_index_type, NULL); - gfc_init_block (&alloc_block); - gfc_add_modify (&alloc_block, size1, gfc_index_zero_node); - gfc_init_block (&realloc_block); - gfc_add_modify (&realloc_block, size1, - gfc_conv_descriptor_size (desc, expr1->rank)); - tmp = build3_v (COND_EXPR, cond_null, - gfc_finish_block (&alloc_block), - gfc_finish_block (&realloc_block)); - gfc_add_expr_to_block (&fblock, tmp); - /* Get the rhs size and fix it. */ size2 = gfc_index_one_node; for (n = 0; n < expr2->rank; n++) @@ -10807,16 +10786,6 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, } size2 = gfc_evaluate_now (size2, &fblock); - cond = fold_build2_loc (input_location, NE_EXPR, logical_type_node, - size1, size2); - - /* If the lhs is deferred length, assume that the element size - changes and force a reallocation. */ - if (expr1->ts.deferred) - neq_size = gfc_evaluate_now (logical_true_node, &fblock); - else - neq_size = gfc_evaluate_now (cond, &fblock); - /* Deallocation of allocatable components will have to occur on reallocation. Fix the old descriptor now. */ if ((expr1->ts.type == BT_DERIVED) @@ -11048,20 +11017,6 @@ gfc_alloc_allocatable_for_assignment (gfc_loopinfo *loop, gfc_add_block_to_block (&realloc_block, &caf_se.post); realloc_expr = gfc_finish_block (&realloc_block); - /* Reallocate if sizes or dynamic types are different. */ - if (elemsize1) - { - tmp = fold_build2_loc (input_location, NE_EXPR, logical_type_node, - elemsize1, elemsize2); - tmp = gfc_evaluate_now (tmp, &fblock); - neq_size = fold_build2_loc (input_location, TRUTH_OR_EXPR, - logical_type_node, neq_size, tmp); - } - tmp = build3_v (COND_EXPR, neq_size, realloc_expr, - build_empty_stmt (input_location)); - - realloc_expr = tmp; - /* Malloc expression. */ gfc_init_block (&alloc_block); if (!coarray) diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc index e7b9211f17e..f3fbb527157 100644 --- a/gcc/fortran/trans-expr.cc +++ b/gcc/fortran/trans-expr.cc @@ -11236,10 +11236,10 @@ alloc_scalar_allocatable_for_assignment (stmtblock_t *block, if (expr1->ts.type == BT_CHARACTER && expr1->ts.deferred) { - /* Use the rhs string length and the lhs element size. */ + /* Use the rhs string length and the lhs element size. Note that 'size' is + used below for the string-length comparison, only. */ size = string_length; - tmp = TREE_TYPE (gfc_typenode_for_spec (&expr1->ts)); - tmp = TYPE_SIZE_UNIT (tmp); + tmp = TYPE_SIZE_UNIT (gfc_get_char_type (expr1->ts.kind)); size_in_bytes = fold_build2_loc (input_location, MULT_EXPR, TREE_TYPE (tmp), tmp, fold_convert (TREE_TYPE (tmp), size)); diff --git a/gcc/testsuite/gfortran.dg/widechar_11.f90 b/gcc/testsuite/gfortran.dg/widechar_11.f90 new file mode 100644 index 00000000000..02530fb1730 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/widechar_11.f90 @@ -0,0 +1,51 @@ +! { dg-do run } +! { dg-additional-options "-fdump-tree-original" } +! +! PR fortran/107508 +! +use iso_c_binding +implicit none +character(len=:,kind=4), allocatable, target :: a4str(:), a4str2 +type(c_ptr) :: cptr, cptr2 + +allocate(character(len=7,kind=4) :: a4str(-2:3)) +allocate(character(len=9,kind=4) :: a4str2) + +cptr = c_loc(a4str) +cptr2 = c_loc(a4str2) + +if (len(a4str) /= 7) error stop +if (lbound(a4str,1) /= -2) error stop +if (ubound(a4str,1) /= 3) error stop +if (len(a4str2) /= 9) error stop + +a4str = [4_"sf456aq", 4_"3dtzu24", 4_"_4fh7sm", 4_"=ff85s7", 4_"j=8af4d", 4_".,A%Fsz"] +a4str2 = 4_"4f5g5f8a9" + +!print *, lbound(a4str), ubound(a4str) ! expected (-2:3) - actually: (1:6) + +if (len(a4str) /= 7) error stop +if (lbound(a4str,1) /= -2) error stop +if (ubound(a4str,1) /= 3) error stop +if (len(a4str2) /= 9) error stop +if (.not. c_associated (cptr, c_loc(a4str))) error stop +if (.not. c_associated (cptr2, c_loc(a4str2))) error stop +end + +! { dg-final { scan-tree-dump-times "__builtin_malloc" 4 "original" } } +! { dg-final { scan-tree-dump-times "__builtin_realloc" 2 "original" } } + +! { dg-final { scan-tree-dump-times "a4str.data = __builtin_malloc \\(168\\);" 2 "original" } } +! { dg-final { scan-tree-dump-times "a4str.data = __builtin_realloc \\(a4str.data, 168\\);" 1 "original" } } +! { dg-final { scan-tree-dump-times "a4str2 = \\(character\\(kind=4\\)\\\[1:.a4str2\\\] \\*\\) __builtin_malloc \\(36\\);" 2 "original" } } +! { dg-final { scan-tree-dump-times "a4str2 = \\(character\\(kind=4\\)\\\[1:.a4str2\\\] \\*\\) __builtin_realloc \\(\\(void \\*\\) a4str2, 36\\);" 1 "original" } } + +! Array: Assert, realloc-check assign string length (alloc + (realloc'ed) assignment): +! { dg-final { scan-tree-dump-times "if \\(\[^\\n\\r\]*\\.a4str != 7\\)" 2 "original" } } +! { dg-final { scan-tree-dump-times "if \\(D\\.\[0-9\]+ != 28\\) goto L\\." 1 "original" } } +! { dg-final { scan-tree-dump-times "\\.a4str = 7;" 2 "original" } } + +! Scalar: Assert, realloc-check assign string length (alloc + (realloc'ed) assignment): +! { dg-final { scan-tree-dump-times "if \\(\[^\\n\\r\]*\\.a4str2 != 9\\)" 2 "original" } } +! { dg-final { scan-tree-dump-times "if \\(\\.a4str2 == 9\\) goto L\\." 1 "original" } } +! { dg-final { scan-tree-dump-times "\\.a4str2 = 9;" 2 "original" } } -- cgit v1.2.1 From 3bff15c1c9fb3eb0bb042717e072476ec2d6d88c Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Mon, 7 Nov 2022 08:40:12 +0100 Subject: [range-op] Restrict division by power of 2 optimization to positive numbers. The problem here is that we are transforming a division by a power of 2 into a right shift, and using this to shift the maybe nonzero bits. This gives the wrong result when the number being divided is negative. In the testcase we are dividing this by 8: [irange] int [-256, -255] NONZERO 0xffffff01 and coming up with: [irange] int [-32, -31] NONZERO 0xffffffe0 The maybe nonzero bits are wrong as -31 has the LSB set (0xffffffe1) whereas the bitmask says the lower 4 bits are off. PR tree-optimization/107541 gcc/ChangeLog: * range-op.cc (operator_div::fold_range): Restrict power of 2 optimization to positive numbers. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr107541.c: New test. --- gcc/range-op.cc | 4 +++- gcc/testsuite/gcc.dg/tree-ssa/pr107541.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr107541.c diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 5e94c3d2282..2b5db0cac85 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -1953,7 +1953,9 @@ operator_div::fold_range (irange &r, tree type, return true; tree t; - if (rh.singleton_p (&t)) + if (code == TRUNC_DIV_EXPR + && rh.singleton_p (&t) + && !wi::neg_p (lh.lower_bound ())) { wide_int wi = wi::to_wide (t); int shift = wi::exact_log2 (wi); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr107541.c b/gcc/testsuite/gcc.dg/tree-ssa/pr107541.c new file mode 100644 index 00000000000..475142186b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr107541.c @@ -0,0 +1,16 @@ +// { dg-do run } +// { dg-options "-O1" } + +unsigned char a = 1; +char b, e; +long c; +short d; +int main() { + a = ~(1 && a); + c = ~((~a / 8 | -2) & 11007578330939886389LLU); + e = -c; + d = ~c / e; + if (d < 2000) + __builtin_abort(); + return 0; +} -- cgit v1.2.1 From 5d060d8b0477ff4911f41c816281daaa24b41a13 Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Mon, 7 Nov 2022 15:33:01 +0300 Subject: tree-ssa-sink: do not touch calls that return twice Avoid moving pure/const calls that may return twice in tree-ssa-sink: properly redirecting the associated abnormal edge is non-trivial. gcc/ChangeLog: PR tree-optimization/107505 * tree-ssa-sink.cc (statement_sink_location): Additionally reject ECF_RETURNS_TWICE calls. gcc/testsuite/ChangeLog: PR tree-optimization/107505 * gcc.dg/pr107505.c: New test. --- gcc/testsuite/gcc.dg/pr107505.c | 26 ++++++++++++++++++++++++++ gcc/tree-ssa-sink.cc | 5 +++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr107505.c diff --git a/gcc/testsuite/gcc.dg/pr107505.c b/gcc/testsuite/gcc.dg/pr107505.c new file mode 100644 index 00000000000..01270eac91c --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr107505.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-guess-branch-probability" } */ + +int n; + +void +bar (void); + +__attribute__ ((noinline, returns_twice)) int +zero (void) +{ + return 0; +} + +void +foo (void) +{ + int a = zero (); + + for (n = 0; n < 2; ++n) + { + } + + if (a) + bar (); +} diff --git a/gcc/tree-ssa-sink.cc b/gcc/tree-ssa-sink.cc index 9213052017a..eb7c2e6819c 100644 --- a/gcc/tree-ssa-sink.cc +++ b/gcc/tree-ssa-sink.cc @@ -263,12 +263,13 @@ statement_sink_location (gimple *stmt, basic_block frombb, *zero_uses_p = false; - /* We only can sink assignments and non-looping const/pure calls. */ + /* We only can sink assignments and const/pure calls that are guaranteed + to return exactly once. */ int cf; if (!is_gimple_assign (stmt) && (!is_gimple_call (stmt) || !((cf = gimple_call_flags (stmt)) & (ECF_CONST|ECF_PURE)) - || (cf & ECF_LOOPING_CONST_OR_PURE))) + || (cf & (ECF_LOOPING_CONST_OR_PURE|ECF_RETURNS_TWICE)))) return false; /* We only can sink stmts with a single definition. */ -- cgit v1.2.1 From e5bcbcd04cfcb2f8635ea8431f4e77065e44b0bd Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 7 Nov 2022 15:15:50 +0100 Subject: libstdc++: Add _Float128 to_chars/from_chars support for x86, ia64 and ppc64le with glibc The following patch adds std::{to,from}_chars support for std::float128_t on glibc 2.26+ for {i?86,x86_64,ia64,powerpc64le}-linux. When long double is already IEEE quad, previous changes already handle it by using long double overloads in _Float128 overloads. The powerpc64le case (with explicit or implicit -mabi=ibmlongdouble) is handled by using the __float128/__ieee128 entrypoints which are already in the library and used for -mabi=ieeelongdouble. For i?86, x86_64 and ia64 this patch adds new library entrypoints, mostly by enabling the code that was already there for powerpc64le-linux. Those use __float128 or __ieee128, the patch uses _Float128 for the exported overloads and internally as template parameter. While powerpc64le-linux uses __sprintfieee128 and __strtoieee128, for _Float128 the patch uses the glibc 2.26 strfromf128 and strtof128 APIs. So that one can build gcc against older glibc and then compile user programs on newer glibc, the patch uses weak references unless gcc is compiled against glibc 2.26+. strfromf128 unfortunately can't handle %.0Lf and %.*Le, %.*Lf, %.*Lg format strings sprintf/__sprintfieee128 use, we need to remove the L from those and replace * with actually directly printing the precision into the format string (i.e. it can handle %.0f and %.27f (floating point type is implied from the function name)). Unlike the std::{,b}float16_t support, this one actually exports APIs with std::float128_t aka _Float128 in the mangled name, because no standard format is superset of it. On the other side, e.g. on i?86/x86_64 it doesn't have restrictions like for _Float16/__bf16 which ISAs need to be enabled in order to use it. The denorm_min case in the testcase is temporarily commented out because of the ERANGE subnormal issue Patrick posted patch for. 2022-11-07 Jakub Jelinek * include/std/charconv (from_chars, to_chars): Add _Float128 overfloads if _GLIBCXX_HAVE_FLOAT128_MATH is defined. * config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export _ZSt8to_charsPcS_DF128_, _ZSt8to_charsPcS_DF128_St12chars_format, _ZSt8to_charsPcS_DF128_St12chars_formati and _ZSt10from_charsPKcS0_RDF128_St12chars_format. * src/c++17/floating_from_chars.cc (USE_STRTOF128_FOR_FROM_CHARS): Define if needed. (__strtof128): Declare. (from_chars_impl): Handle _Float128. (from_chars): New _Float128 overload if USE_STRTOF128_FOR_FROM_CHARS is define. * src/c++17/floating_to_chars.cc (__strfromf128): Declare. (FLOAT128_TO_CHARS): Define even when _Float128 is supported and wider than long double. (F128_type): Use _Float128 for that case. (floating_type_traits): Specialize for F128_type rather than __float128. (sprintf_ld): Add length argument. Handle _Float128. (__floating_to_chars_shortest, __floating_to_chars_precision): Pass length to sprintf_ld. (to_chars): Add _Float128 overloads for the F128_type being _Float128 cases. * testsuite/20_util/to_chars/float128_c++23.cc: New test. --- libstdc++-v3/config/abi/pre/gnu.ver | 4 + libstdc++-v3/include/std/charconv | 61 ++++++++++++ libstdc++-v3/src/c++17/floating_from_chars.cc | 26 +++++ libstdc++-v3/src/c++17/floating_to_chars.cc | 72 ++++++++++++-- .../testsuite/20_util/to_chars/float128_c++23.cc | 105 +++++++++++++++++++++ 5 files changed, 259 insertions(+), 9 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 1c714fba0ae..4d97ec37147 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -2450,6 +2450,10 @@ GLIBCXX_3.4.31 { _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format; _ZSt22__from_chars_float16_tPKcS0_RfSt12chars_format; _ZSt23__from_chars_bfloat16_tPKcS0_RfSt12chars_format; + _ZSt8to_charsPcS_DF128_; + _ZSt8to_charsPcS_DF128_St12chars_format; + _ZSt8to_charsPcS_DF128_St12chars_formati; + _ZSt10from_charsPKcS0_RDF128_St12chars_format; } GLIBCXX_3.4.30; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv index e9bf953cdcc..09163af7fc9 100644 --- a/libstdc++-v3/include/std/charconv +++ b/libstdc++-v3/include/std/charconv @@ -736,6 +736,27 @@ namespace __detail __value = __val; return __res; } +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH) +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT + __extension__ from_chars_result + from_chars(const char* __first, const char* __last, __ieee128& __value, + chars_format __fmt = chars_format::general) noexcept; + + inline from_chars_result + from_chars(const char* __first, const char* __last, _Float128& __value, + chars_format __fmt = chars_format::general) noexcept + { + __extension__ __ieee128 __val; + from_chars_result __res = from_chars(__first, __last, __val, __fmt); + if (__res.ec == errc{}) + __value = __val; + return __res; + } +#else + from_chars_result + from_chars(const char* __first, const char* __last, _Float128& __value, + chars_format __fmt = chars_format::general) noexcept; +#endif #endif #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) \ @@ -851,6 +872,46 @@ namespace __detail return to_chars(__first, __last, static_cast(__value), __fmt, __precision); } +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH) +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT + __extension__ to_chars_result + to_chars(char* __first, char* __last, __float128 __value) noexcept; + __extension__ to_chars_result + to_chars(char* __first, char* __last, __float128 __value, + chars_format __fmt) noexcept; + __extension__ to_chars_result + to_chars(char* __first, char* __last, __float128 __value, + chars_format __fmt, int __precision) noexcept; + + inline to_chars_result + to_chars(char* __first, char* __last, _Float128 __value) noexcept + { + __extension__ + return to_chars(__first, __last, static_cast<__float128>(__value)); + } + inline to_chars_result + to_chars(char* __first, char* __last, _Float128 __value, + chars_format __fmt) noexcept + { + __extension__ + return to_chars(__first, __last, static_cast<__float128>(__value), __fmt); + } + inline to_chars_result + to_chars(char* __first, char* __last, _Float128 __value, + chars_format __fmt, int __precision) noexcept + { + __extension__ + return to_chars(__first, __last, static_cast<__float128>(__value), __fmt, + __precision); + } +#else + to_chars_result to_chars(char* __first, char* __last, _Float128 __value) + noexcept; + to_chars_result to_chars(char* __first, char* __last, _Float128 __value, + chars_format __fmt) noexcept; + to_chars_result to_chars(char* __first, char* __last, _Float128 __value, + chars_format __fmt, int __precision) noexcept; +#endif #endif #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 939c751f861..11a9be68770 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -59,6 +59,14 @@ #endif // strtold for __ieee128 extern "C" __ieee128 __strtoieee128(const char*, char**); +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ + && defined(__GLIBC_PREREQ) +#define USE_STRTOF128_FOR_FROM_CHARS 1 +extern "C" _Float128 __strtof128(const char*, char**) +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH + __attribute__((__weak__)) +#endif + __asm ("strtof128"); #endif #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \ @@ -618,6 +626,16 @@ namespace # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT else if constexpr (is_same_v) tmpval = __strtoieee128(str, &endptr); +# elif defined(USE_STRTOF128_FOR_FROM_CHARS) + else if constexpr (is_same_v) + { +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH + if (&__strtof128 == nullptr) + tmpval = _Float128(std::strtold(str, &endptr); + else +#endif + tmpval = __strtof128(str, &endptr); + } # endif #else tmpval = std::strtod(str, &endptr); @@ -1239,6 +1257,14 @@ from_chars(const char* first, const char* last, __ieee128& value, // fast_float doesn't support IEEE binary128 format, but we can use strtold. return from_chars_strtod(first, last, value, fmt); } +#elif defined(USE_STRTOF128_FOR_FROM_CHARS) +from_chars_result +from_chars(const char* first, const char* last, _Float128& value, + chars_format fmt) noexcept +{ + // fast_float doesn't support IEEE binary128 format, but we can use strtold. + return from_chars_strtod(first, last, value, fmt); +} #endif #endif // USE_LIB_FAST_FLOAT || USE_STRTOD_FOR_FROM_CHARS diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc index a253ee42416..d6be6465b12 100644 --- a/libstdc++-v3/src/c++17/floating_to_chars.cc +++ b/libstdc++-v3/src/c++17/floating_to_chars.cc @@ -43,6 +43,13 @@ #endif // sprintf for __ieee128 extern "C" int __sprintfieee128(char*, const char*, ...); +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ + && defined(__GLIBC_PREREQ) +extern "C" int __strfromf128(char*, size_t, const char*, _Float128) +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH + __attribute__((__weak__)) +#endif + __asm ("strfromf128"); #endif // This implementation crucially assumes float/double have the @@ -77,10 +84,11 @@ extern "C" int __sprintfieee128(char*, const char*, ...); #if defined _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT && __FLT128_MANT_DIG__ == 113 // Define overloads of std::to_chars for __float128. # define FLOAT128_TO_CHARS 1 -#endif - -#ifdef FLOAT128_TO_CHARS using F128_type = __float128; +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ + && defined(__GLIBC_PREREQ) +# define FLOAT128_TO_CHARS 1 +using F128_type = _Float128; #else using F128_type = void; #endif @@ -252,7 +260,7 @@ namespace # ifdef FLOAT128_TO_CHARS template<> - struct floating_type_traits<__float128> : floating_type_traits_binary128 + struct floating_type_traits : floating_type_traits_binary128 { }; # endif #endif @@ -1034,7 +1042,8 @@ namespace #pragma GCC diagnostic ignored "-Wabi" template inline int - sprintf_ld(char* buffer, const char* format_string, T value, Extra... args) + sprintf_ld(char* buffer, size_t length __attribute__((unused)), + const char* format_string, T value, Extra... args) { int len; @@ -1044,10 +1053,31 @@ namespace fesetround(FE_TONEAREST); // We want round-to-nearest behavior. #endif +#ifdef FLOAT128_TO_CHARS #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT if constexpr (is_same_v) len = __sprintfieee128(buffer, format_string, args..., value); else +#else + if constexpr (is_same_v) + { +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH + if (&__strfromf128 == nullptr) + len = sprintf(buffer, format_string, args..., (long double)value); + else +#endif + if constexpr (sizeof...(args) == 0) + len = __strfromf128(buffer, length, "%.0f", value); + else + { + // strfromf128 unfortunately doesn't allow .* + char fmt[3 * sizeof(int) + 6]; + sprintf(fmt, "%%.%d%c", args..., int(format_string[4])); + len = __strfromf128(buffer, length, fmt, value); + } + } + else +#endif #endif len = sprintf(buffer, format_string, args..., value); @@ -1205,8 +1235,10 @@ template // can avoid this if we use sprintf to write all but the last // digit, and carefully compute and write the last digit // ourselves. - char buffer[expected_output_length+1]; - const int output_length = sprintf_ld(buffer, "%.0Lf", value); + char buffer[expected_output_length + 1]; + const int output_length = sprintf_ld(buffer, + expected_output_length + 1, + "%.0Lf", value); __glibcxx_assert(output_length == expected_output_length); memcpy(first, buffer, output_length); return {first + output_length, errc{}}; @@ -1396,9 +1428,10 @@ template __builtin_unreachable(); // Do the sprintf into the local buffer. - char buffer[output_length_upper_bound+1]; + char buffer[output_length_upper_bound + 1]; int output_length - = sprintf_ld(buffer, output_specifier, value, effective_precision); + = sprintf_ld(buffer, output_length_upper_bound + 1, output_specifier, + value, effective_precision); __glibcxx_assert(output_length <= output_length_upper_bound); if (effective_precision > 0) @@ -1798,6 +1831,7 @@ to_chars(char* first, char* last, long double value, chars_format fmt, } #ifdef FLOAT128_TO_CHARS +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT to_chars_result to_chars(char* first, char* last, __float128 value) noexcept { @@ -1816,6 +1850,26 @@ to_chars(char* first, char* last, __float128 value, chars_format fmt, { return __floating_to_chars_precision(first, last, value, fmt, precision); } +#else +to_chars_result +to_chars(char* first, char* last, _Float128 value) noexcept +{ + return __floating_to_chars_shortest(first, last, value, chars_format{}); +} + +to_chars_result +to_chars(char* first, char* last, _Float128 value, chars_format fmt) noexcept +{ + return __floating_to_chars_shortest(first, last, value, fmt); +} + +to_chars_result +to_chars(char* first, char* last, _Float128 value, chars_format fmt, + int precision) noexcept +{ + return __floating_to_chars_precision(first, last, value, fmt, precision); +} +#endif #endif // Entrypoints for 16-bit floats. diff --git a/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc new file mode 100644 index 00000000000..4c01458e6d9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc @@ -0,0 +1,105 @@ +// Copyright (C) 2022 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2b" } +// { dg-do run { target c++23 } } +// { dg-require-effective-target ieee_floats } +// { dg-require-effective-target size32plus } +// { dg-add-options ieee } + +#include +#include +#include +#include +#include + +#if defined(__STDCPP_FLOAT128_T__) \ + && (defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) \ + || defined(_GLIBCXX_HAVE_FLOAT128_MATH)) +void +test(std::chars_format fmt = std::chars_format{}) +{ + std::float128_t tests[] = { +// std::numeric_limits::denorm_min(), + std::numeric_limits::min(), + 0.0f128, + -42.0f128, + 1234.5678912345f128, + std::numbers::e_v, + std::numbers::log2e_v, + std::numbers::log10e_v, + std::numbers::pi_v, + std::numbers::inv_pi_v, + std::numbers::inv_sqrtpi_v, + std::numbers::ln2_v, + std::numbers::ln10_v, + std::numbers::sqrt2_v, + std::numbers::sqrt3_v, + std::numbers::inv_sqrt3_v, + std::numbers::egamma_v, + std::numbers::phi_v, + std::numeric_limits::max() + }; + char str1[10000], str2[10000]; + for (auto u : tests) + { + auto [ptr1, ec1] = std::to_chars(str1, str1 + sizeof(str1), u, fmt); + VERIFY( ec1 == std::errc() ); +// std::cout << i << ' ' << std::string_view (str1, ptr1) << '\n'; + if (fmt == std::chars_format::fixed) + { + auto [ptr2, ec2] = std::to_chars(str2, str2 + (ptr1 - str1), u, fmt); + VERIFY( ec2 == std::errc() && ptr2 - str2 == ptr1 - str1 ); + auto [ptr3, ec3] = std::to_chars(str2, str2 + (ptr1 - str1 - 1), u, fmt); + VERIFY( ec3 != std::errc() ); + } + std::float128_t v; + auto [ptr4, ec4] = std::from_chars(str1, ptr1, v, + fmt == std::chars_format{} + ? std::chars_format::general : fmt); + VERIFY( ec4 == std::errc() && ptr4 == ptr1 ); + VERIFY( u == v ); + + auto [ptr5, ec5] = std::to_chars(str1, str1 + sizeof(str1), u, fmt, 90); + VERIFY( ec5 == std::errc() ); +// std::cout << i << ' ' << std::string_view (str1, ptr5) << '\n'; + v = 4.0f128; + auto [ptr6, ec6] = std::from_chars(str1, ptr5, v, + fmt == std::chars_format{} + ? std::chars_format::general : fmt); + VERIFY( ec6 == std::errc() && ptr6 == ptr5 ); + if (fmt == std::chars_format::fixed && u > 0.0f128 && u < 0.000001f128) + VERIFY( v == 0.0 ); + else + VERIFY( u == v ); + } +} +#endif + +int +main() +{ +#if defined(__STDCPP_FLOAT128_T__) \ + && (defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) \ + || defined(_GLIBCXX_HAVE_FLOAT128_MATH)) + test(); + test(std::chars_format::fixed); + test(std::chars_format::scientific); + test(std::chars_format::general); + test(std::chars_format::hex); +#endif +} -- cgit v1.2.1 From cb0ceeaee9e041aaac3edd089b07b439621d0f29 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 7 Nov 2022 15:17:21 +0100 Subject: libstdc++: Update from latest fast_float [PR107468] The following patch updates from fast_float trunk. That way it grabs two of the 4 LOCAL_PATCHES, some smaller tweaks, to_extended cleanups and most importantly fix for the incorrect rounding case, PR107468 aka https://github.com/fastfloat/fast_float/issues/149 Using std::fegetround showed in benchmarks too slow, so instead of doing that the patch limits the fast path where it uses floating point multiplication rather than integral to cases where we can prove there will be no rounding (the multiplication will be exact, not just that the two multiplication or division operation arguments are exactly representable). 2022-11-07 Jakub Jelinek PR libstdc++/107468 * src/c++17/fast_float/MERGE: Adjust for merge from upstream. * src/c++17/fast_float/LOCAL_PATCHES: Remove commits that were upstreamed. * src/c++17/fast_float/README.md: Merge from fast_float 662497742fea7055f0e0ee27e5a7ddc382c2c38e commit. * src/c++17/fast_float/fast_float.h: Likewise. * testsuite/20_util/from_chars/pr107468.cc: New test. --- libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES | 2 - libstdc++-v3/src/c++17/fast_float/MERGE | 2 +- libstdc++-v3/src/c++17/fast_float/README.md | 91 +++++++---- libstdc++-v3/src/c++17/fast_float/fast_float.h | 173 +++++++++++++-------- .../testsuite/20_util/from_chars/pr107468.cc | 42 +++++ 5 files changed, 211 insertions(+), 99 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc diff --git a/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES b/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES index 5bb42933398..71495d6728b 100644 --- a/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES +++ b/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES @@ -1,4 +1,2 @@ r12-6647 r12-6648 -r12-6664 -r12-6665 diff --git a/libstdc++-v3/src/c++17/fast_float/MERGE b/libstdc++-v3/src/c++17/fast_float/MERGE index 43bdc3981c8..20eae9d710f 100644 --- a/libstdc++-v3/src/c++17/fast_float/MERGE +++ b/libstdc++-v3/src/c++17/fast_float/MERGE @@ -1,4 +1,4 @@ -d35368cae610b4edeec61cd41e4d2367a4d33f58 +662497742fea7055f0e0ee27e5a7ddc382c2c38e The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libstdc++-v3/src/c++17/fast_float/README.md b/libstdc++-v3/src/c++17/fast_float/README.md index 1e1c06d0a3e..d6ae279527c 100644 --- a/libstdc++-v3/src/c++17/fast_float/README.md +++ b/libstdc++-v3/src/c++17/fast_float/README.md @@ -1,12 +1,5 @@ ## fast_float number parsing library: 4x faster than strtod -![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg) -![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg) -![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg) -![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg) -![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg) -[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml) - The fast_float library provides fast header-only implementations for the C++ from_chars functions for `float` and `double` types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including @@ -28,8 +21,8 @@ struct from_chars_result { ``` It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting -a locale-independent format equivalent to the C++17 from_chars function. -The resulting floating-point value is the closest floating-point values (using either float or double), +a locale-independent format equivalent to the C++17 from_chars function. +The resulting floating-point value is the closest floating-point values (using either float or double), using the "round to even" convention for values that would otherwise fall right in-between two values. That is, we provide exact parsing according to the IEEE standard. @@ -47,7 +40,7 @@ Example: ``` C++ #include "fast_float/fast_float.h" #include - + int main() { const std::string input = "3.1416 xyz "; double result; @@ -60,15 +53,15 @@ int main() { Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of -the type `fast_float::chars_format`. It is a bitset value: we check whether +the type `fast_float::chars_format`. It is a bitset value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set to determine whether we allow the fixed point and scientific notation respectively. The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. -The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. +The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. * The `from_chars` function does not skip leading white-space characters. * [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden. -* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. +* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. Furthermore, we have the following restrictions: * We only support `float` and `double` types at this time. @@ -77,22 +70,22 @@ Furthermore, we have the following restrictions: We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. - +We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`). ## Using commas as decimal separator The C++ standard stipulate that `from_chars` has to be locale-independent. In -particular, the decimal separator has to be the period (`.`). However, -some users still want to use the `fast_float` library with in a locale-dependent +particular, the decimal separator has to be the period (`.`). However, +some users still want to use the `fast_float` library with in a locale-dependent manner. Using a separate function called `from_chars_advanced`, we allow the users -to pass a `parse_options` instance which contains a custom decimal separator (e.g., +to pass a `parse_options` instance which contains a custom decimal separator (e.g., the comma). You may use it as follows. ```C++ #include "fast_float/fast_float.h" #include - + int main() { const std::string input = "3,1416 xyz "; double result; @@ -104,25 +97,55 @@ int main() { } ``` +You can parse delimited numbers: +```C++ + const std::string input = "234532.3426362,7869234.9823,324562.645"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 234532.3426362. + if(answer.ptr[0] != ',') { + // unexpected delimiter + } + answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 7869234.9823. + if(answer.ptr[0] != ',') { + // unexpected delimiter + } + answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 324562.645. +``` ## Reference -- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021. +- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021. ## Other programming languages - [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. - [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. -- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. +- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core). - [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. ## Relation With Other Work -The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). +The fast_float library is part of GCC (as of version 12): the `from_chars` function in GCC relies on fast_float. + +The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). +There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS). + ## Users The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). @@ -135,14 +158,14 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems. ``` -$ ./build/benchmarks/benchmark +$ ./build/benchmarks/benchmark # parsing random integers in the range [0,1) -volume = 2.09808 MB -netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s -doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s -strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s -abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s -fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s +volume = 2.09808 MB +netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s +doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s +strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s +abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s +fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s ``` See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code. @@ -183,23 +206,23 @@ You should change the `GIT_TAG` line so that you recover the version you wish to ## Using as single header -The script `script/amalgamate.py` may be used to generate a single header +The script `script/amalgamate.py` may be used to generate a single header version of the library if so desired. -Just run the script from the root directory of this repository. +Just run the script from the root directory of this repository. You can customize the license type and output file if desired as described in the command line help. You may directly download automatically generated single-header files: -https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h +https://github.com/fastfloat/fast_float/releases/download/v3.4.0/fast_float.h ## Credit -Though this work is inspired by many different people, this work benefited especially from exchanges with -Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided +Though this work is inspired by many different people, this work benefited especially from exchanges with +Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits. -The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published +The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published under the Apache 2.0 license. ## License diff --git a/libstdc++-v3/src/c++17/fast_float/fast_float.h b/libstdc++-v3/src/c++17/fast_float/fast_float.h index 31fb88b8aba..5da55e2fe0a 100644 --- a/libstdc++-v3/src/c++17/fast_float/fast_float.h +++ b/libstdc++-v3/src/c++17/fast_float/fast_float.h @@ -74,7 +74,7 @@ struct parse_options { * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of * the type `fast_float::chars_format`. It is a bitset value: we check whether * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set - * to determine whether we allowe the fixed point and scientific notation respectively. + * to determine whether we allow the fixed point and scientific notation respectively. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. */ template @@ -98,12 +98,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last, || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ || defined(__MINGW64__) \ || defined(__s390x__) \ - || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ - || defined(__EMSCRIPTEN__)) + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) ) #define FASTFLOAT_64BIT #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ || defined(__arm__) || defined(_M_ARM) \ - || defined(__MINGW32__)) + || defined(__MINGW32__) || defined(__EMSCRIPTEN__)) #define FASTFLOAT_32BIT #else // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. @@ -128,7 +127,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last, #define FASTFLOAT_VISUAL_STUDIO 1 #endif -#ifdef __BYTE_ORDER__ +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ #define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #elif defined _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 @@ -271,8 +270,9 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline value128 full_multiplication(uint64_t a, uint64_t b) { value128 answer; -#ifdef _M_ARM64 +#if defined(_M_ARM64) && !defined(__MINGW32__) // ARM64 has native support for 64-bit multiplications, no need to emulate + // But MinGW on ARM64 doesn't have native support for 64-bit multiplications answer.high = __umulh(a, b); answer.low = a * b; #elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) @@ -307,21 +307,69 @@ constexpr static double powers_of_ten_double[] = { 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}; +// used for max_mantissa_double and max_mantissa_float +constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; +// Largest integer value v so that (5**index * v) <= 1<<53. +// 0x10000000000000 == 1 << 53 +constexpr static uint64_t max_mantissa_double[] = { + 0x10000000000000, + 0x10000000000000 / 5, + 0x10000000000000 / (5 * 5), + 0x10000000000000 / (5 * 5 * 5), + 0x10000000000000 / (5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555), + 0x10000000000000 / (constant_55555 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)}; + // Largest integer value v so that (5**index * v) <= 1<<24. + // 0x1000000 == 1<<24 + constexpr static uint64_t max_mantissa_float[] = { + 0x1000000, + 0x1000000 / 5, + 0x1000000 / (5 * 5), + 0x1000000 / (5 * 5 * 5), + 0x1000000 / (5 * 5 * 5 * 5), + 0x1000000 / (constant_55555), + 0x1000000 / (constant_55555 * 5), + 0x1000000 / (constant_55555 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * constant_55555), + 0x1000000 / (constant_55555 * constant_55555 * 5)}; template struct binary_format { + using equiv_uint = typename std::conditional::type; + static inline constexpr int mantissa_explicit_bits(); static inline constexpr int minimum_exponent(); static inline constexpr int infinite_power(); static inline constexpr int sign_index(); - static inline constexpr int min_exponent_fast_path(); static inline constexpr int max_exponent_fast_path(); static inline constexpr int max_exponent_round_to_even(); static inline constexpr int min_exponent_round_to_even(); - static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr uint64_t max_mantissa_fast_path(int64_t power); static inline constexpr int largest_power_of_ten(); static inline constexpr int smallest_power_of_ten(); static inline constexpr T exact_power_of_ten(int64_t power); static inline constexpr size_t max_digits(); + static inline constexpr equiv_uint exponent_mask(); + static inline constexpr equiv_uint mantissa_mask(); + static inline constexpr equiv_uint hidden_bit_mask(); }; template <> inline constexpr int binary_format::mantissa_explicit_bits() { @@ -364,21 +412,6 @@ template <> inline constexpr int binary_format::infinite_power() { template <> inline constexpr int binary_format::sign_index() { return 63; } template <> inline constexpr int binary_format::sign_index() { return 31; } -template <> inline constexpr int binary_format::min_exponent_fast_path() { -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - return 0; -#else - return -22; -#endif -} -template <> inline constexpr int binary_format::min_exponent_fast_path() { -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - return 0; -#else - return -10; -#endif -} - template <> inline constexpr int binary_format::max_exponent_fast_path() { return 22; } @@ -386,11 +419,17 @@ template <> inline constexpr int binary_format::max_exponent_fast_path() return 10; } -template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return uint64_t(2) << mantissa_explicit_bits(); +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 22 + // + return max_mantissa_double[power]; } -template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return uint64_t(2) << mantissa_explicit_bits(); +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 10 + // + return max_mantissa_float[power]; } template <> @@ -429,6 +468,33 @@ template <> inline constexpr size_t binary_format::max_digits() { return 114; } +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7F800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7FF0000000000000; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x007FFFFF; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x000FFFFFFFFFFFFF; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x00800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x0010000000000000; +} + template fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { uint64_t word = am.mantissa; @@ -2410,40 +2476,24 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n // this converts a native floating-point number to an extended-precision float. template fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + using equiv_uint = typename binary_format::equiv_uint; + constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); + constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); + constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); + adjusted_mantissa am; int32_t bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); - if (std::is_same::value) { - constexpr uint32_t exponent_mask = 0x7F800000; - constexpr uint32_t mantissa_mask = 0x007FFFFF; - constexpr uint64_t hidden_bit_mask = 0x00800000; - uint32_t bits; - ::memcpy(&bits, &value, sizeof(T)); - if ((bits & exponent_mask) == 0) { - // denormal - am.power2 = 1 - bias; - am.mantissa = bits & mantissa_mask; - } else { - // normal - am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); - am.power2 -= bias; - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; - } + equiv_uint bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; } else { - constexpr uint64_t exponent_mask = 0x7FF0000000000000; - constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; - constexpr uint64_t hidden_bit_mask = 0x0010000000000000; - uint64_t bits; - ::memcpy(&bits, &value, sizeof(T)); - if ((bits & exponent_mask) == 0) { - // denormal - am.power2 = 1 - bias; - am.mantissa = bits & mantissa_mask; - } else { - // normal - am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); - am.power2 -= bias; - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; - } + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; } return am; @@ -2869,11 +2919,10 @@ from_chars_result from_chars_advanced(const char *first, const char *last, } answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch; - // Next is Clinger's fast path. - if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path() && !pns.too_many_digits) { + // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal + if (pns.exponent >= 0 && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path(pns.exponent) && !pns.too_many_digits) { value = T(pns.mantissa); - if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } - else { value = value * binary_format::exact_power_of_ten(pns.exponent); } + value = value * binary_format::exact_power_of_ten(pns.exponent); if (pns.negative) { value = -value; } return answer; } diff --git a/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc b/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc new file mode 100644 index 00000000000..95bf669c945 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2022 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run { target c++17 } } +// { dg-add-options ieee } + +#include +#include +#include +#include + +int +main() +{ + // FP from_char not available otherwise. +#if __cpp_lib_to_chars >= 201611L \ + && _GLIBCXX_USE_C99_FENV_TR1 \ + && defined(FE_DOWNWARD) \ + && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + // PR libstdc++/107468 + float f; + char buf[] = "3.355447e+07"; + std::fesetround(FE_DOWNWARD); + auto [ptr, ec] = std::from_chars(buf, buf + sizeof(buf) - 1, f, std::chars_format::scientific); + VERIFY( ec == std::errc() && ptr == buf + sizeof(buf) - 1 ); + VERIFY( f == 33554472.0f ); +#endif +} -- cgit v1.2.1 From 9aa1b63a5554563b7a3081391358d6cedcabea88 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Mon, 7 Nov 2022 17:26:44 +0100 Subject: Fix NULL filename handling The previous commit introduced a regression as some Ada tests end up passing NULL as the filename to remap_filename. Handle this as before to fix them. gcc/ * file-prefix-map.cc (remap_filename): Handle NULL filenames. --- gcc/file-prefix-map.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcc/file-prefix-map.cc b/gcc/file-prefix-map.cc index 439586bd2b5..40b10edcf92 100644 --- a/gcc/file-prefix-map.cc +++ b/gcc/file-prefix-map.cc @@ -73,7 +73,7 @@ remap_filename (file_prefix_map *maps, const char *filename) char *realname; size_t name_len; - if (lbasename (filename) == filename) + if (!filename || lbasename (filename) == filename) return filename; realname = lrealpath (filename); -- cgit v1.2.1 From 42f42d70c6ab1dea0a9c2c3ed435890156759aef Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Mon, 7 Nov 2022 11:08:21 -0500 Subject: C++: Template lambda mangling testcases I found some additional cases when working on the demangler. May as well check their mangling. gcc/testsuite/ * g++.dg/abi/lambda-tpl1.h: Add more cases. * g++.dg/abi/lambda-tpl1-17.C: Add checks. * g++.dg/abi/lambda-tpl1-18.C: Likewise. * g++.dg/abi/lambda-tpl1-18vs17.C: Likewise. --- gcc/testsuite/g++.dg/abi/lambda-tpl1-17.C | 6 ++++++ gcc/testsuite/g++.dg/abi/lambda-tpl1-18.C | 6 ++++++ gcc/testsuite/g++.dg/abi/lambda-tpl1-18vs17.C | 5 +++++ gcc/testsuite/g++.dg/abi/lambda-tpl1.h | 11 +++++++++++ 4 files changed, 28 insertions(+) diff --git a/gcc/testsuite/g++.dg/abi/lambda-tpl1-17.C b/gcc/testsuite/g++.dg/abi/lambda-tpl1-17.C index b61aaf98cd0..010f6222151 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-tpl1-17.C +++ b/gcc/testsuite/g++.dg/abi/lambda-tpl1-17.C @@ -18,3 +18,9 @@ // { dg-final { scan-assembler {_ZNK6l_var3MUlRT_IJXspT0_EEEE_clI1XJLi1ELi2ELi3EEEEDaS1_:} } } // { dg-final { scan-assembler {_ZNK6l_var4MUlR1YIJDpT_EEE_clIJ1US6_EEEDaS3_:} } } // { dg-final { scan-assembler {_ZZ2FnILi1EEvvENKUlT_E_clIiEEDaS0_:} } } + +// { dg-final { scan-assembler {_ZZ1fvENKUlT_E_clIcLc0EEEDaS_:} } } +// { dg-final { scan-assembler {_ZZ1fvENKUlT_E_clIiLi0EEEDaS_:} } } +// { dg-final { scan-assembler {_ZZZ1fvENKUlT_E_clIcLc0EEEDaS_ENKUlcS_E_clIcEEDacS_:} } } +// { dg-final { scan-assembler {_ZZZ1fvENKUlT_E_clIiLi0EEEDaS_ENKUliS_E_clIiEEDaiS_:} } } +// { dg-final { scan-assembler {_ZZ1fvENKUlP1UIT_Lj0EEPS_IiLj0EEE0_clIcEEDaS2_S4_:} } } diff --git a/gcc/testsuite/g++.dg/abi/lambda-tpl1-18.C b/gcc/testsuite/g++.dg/abi/lambda-tpl1-18.C index dbeea407651..66cce9aa266 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-tpl1-18.C +++ b/gcc/testsuite/g++.dg/abi/lambda-tpl1-18.C @@ -23,3 +23,9 @@ // https://github.com/llvm/llvm-project/issues/58631 // { dg-final { scan-assembler {_ZZ2FnILi1EEvvENKUlTyT_E_clIiEEDaS0_:} } } + +// { dg-final { scan-assembler {_ZZ1fvENKUlTyTnT_S_E_clIcLc0EEEDaS_:} } } +// { dg-final { scan-assembler {_ZZ1fvENKUlTyTnT_S_E_clIiLi0EEEDaS_:} } } +// { dg-final { scan-assembler {_ZZZ1fvENKUlTyTnT_S_E_clIcLc0EEEDaS_ENKUlTycS_E_clIcEEDacS_:} } } +// { dg-final { scan-assembler {_ZZZ1fvENKUlTyTnT_S_E_clIiLi0EEEDaS_ENKUlTyiS_E_clIiEEDaiS_:} } } +// { dg-final { scan-assembler {_ZZ1fvENKUlTyP1UIT_Lj0EEPS_IiLj0EEE_clIcEEDaS2_S4_:} } } diff --git a/gcc/testsuite/g++.dg/abi/lambda-tpl1-18vs17.C b/gcc/testsuite/g++.dg/abi/lambda-tpl1-18vs17.C index 8bead7393c7..6489db9e442 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-tpl1-18vs17.C +++ b/gcc/testsuite/g++.dg/abi/lambda-tpl1-18vs17.C @@ -14,3 +14,8 @@ // { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK10l_tpl_autoMUlT_T0_E_clIiiEEDaS_S0_'\) and '-fabi-version=18' \('_ZNK10l_tpl_autoMUlTyT_T0_E_clIiiEEDaS0_S1_'\) [^\n]*\n} } // { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK5l_tplMUlT_E_clIiEEDaS_'\) and '-fabi-version=18' \('_ZNK5l_tplMUlTyT_E_clIiEEDaS0_'\) [^\n]*\n} } // { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZNK6l_autoMUlT_E_clIiEEDaS_'\) and '-fabi-version=18' \('_ZNK6l_autoMUlT_E_clIiEEDaS0_'\) [^\n]*\n} } +// { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZZ1fvENKUlT_E_clIiLi0EEEDaS_'\) and '-fabi-version=18' \('_ZZ1fvENKUlTyTnT_S_E_clIiLi0EEEDaS_'\) [^\n]*\n} } +// { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZZZ1fvENKUlT_E_clIiLi0EEEDaS_ENKUliS_E_clIiEEDaiS_'\) and '-fabi-version=18' \('_ZZZ1fvENKUlTyTnT_S_E_clIiLi0EEEDaS_ENKUlTyiS_E_clIiEEDaiS_'\) [^\n]*\n} } +// { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZZ1fvENKUlT_E_clIcLc0EEEDaS_'\) and '-fabi-version=18' \('_ZZ1fvENKUlTyTnT_S_E_clIcLc0EEEDaS_'\) [^\n]*\n} } +// { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZZZ1fvENKUlT_E_clIcLc0EEEDaS_ENKUlcS_E_clIcEEDacS_'\) and '-fabi-version=18' \('_ZZZ1fvENKUlTyTnT_S_E_clIcLc0EEEDaS_ENKUlTycS_E_clIcEEDacS_'\) [^\n]*\n} } +// { dg-regexp {[^\n]*lambda-tpl1.h:[:0-9]* warning: the mangled name [^\n]* \('_ZZ1fvENKUlP1UIT_Lj0EEPS_IiLj0EEE0_clIcEEDaS2_S4_'\) and '-fabi-version=18' \('_ZZ1fvENKUlTyP1UIT_Lj0EEPS_IiLj0EEE_clIcEEDaS2_S4_'\) [^\n]*\n} } diff --git a/gcc/testsuite/g++.dg/abi/lambda-tpl1.h b/gcc/testsuite/g++.dg/abi/lambda-tpl1.h index 5d6fe5e1d0a..376c3f6a2f4 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-tpl1.h +++ b/gcc/testsuite/g++.dg/abi/lambda-tpl1.h @@ -56,4 +56,15 @@ void f () l_var4 (y); Fn<1> (); + + auto l1 = [] (T a) { + auto l2 = [] (T a, U b) {}; + + l2 (a, v); + }; + auto l3 = [](U *, U *) {}; + + l1 (1); + l1 ('1'); + l3 ((U *)nullptr, nullptr); } -- cgit v1.2.1 From f471cb71c86c1e7a3dead324142bdf880f00a3da Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 7 Nov 2022 13:29:30 -0500 Subject: libstdc++: Implement ranges::cartesian_product_view from P2374R4 This also implements the proposed resolutions of the tentatively ready LWG issues 3760, 3761 and 3801 for cartesian_product_view. I'm not sure how/if we should implement the recommended practice of: iterator::difference_type should be the smallest signed-integer-like type that is sufficiently wide to store the product of the maximum sizes of all underlying ranges if such a type exists because for e.g. extern std::vector x, y; auto v = views::cartesian_product(x, y); IIUC it'd mean difference_type should be __int128 (on 64-bit systems), which seems quite wasteful: in practice the size of any cartesian product probably won't exceed the precision of say ptrdiff_t, and using anything larger will just incur unnecessary space/time overhead. It's also probably not worth the complexity to use less precision than ptrdiff_t (when possible) either. So this patch defines difference_type as common_type_t, range_difference_t<_Vs>...> which should mean it's least as large as the difference_type of each underlying range, and at least as large as ptrdiff_t. This patch also adds assertions to catch any overflow that occurs due to this choice of difference_type. libstdc++-v3/ChangeLog: * include/std/ranges (__maybe_const_t): New alias for __detail::__maybe_const_t. (__detail::__cartesian_product_is_random_access): Define. (__detail::__cartesian_product_common_arg): Define. (__detail::__cartesian_product_is_bidirectional): Define. (__detail::__cartesian_product_is_common): Define. (__detail::__cartesian_product_is_sized): Define. (__detail::__cartesian_is_sized_sentinel): Define. (__detail::__cartesian_common_arg_end): Define. (cartesian_product_view): Define. (cartesian_product_view::_Iterator): Define. (views::__detail::__can_cartesian_product_view): Define. (views::_CartesianProduct, views::cartesian_product): Define. * testsuite/std/ranges/cartesian_product/1.cc: New test. --- libstdc++-v3/include/std/ranges | 513 +++++++++++++++++++++ .../testsuite/std/ranges/cartesian_product/1.cc | 186 ++++++++ 2 files changed, 699 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index a55e9e7f760..959886a1a55 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -829,6 +829,9 @@ namespace __detail } // namespace __detail +// Shorthand for __detail::__maybe_const_t. +using __detail::__maybe_const_t; + namespace views::__adaptor { // True if the range adaptor _Adaptor can be applied with _Args. @@ -7973,6 +7976,516 @@ namespace views::__adaptor inline constexpr _Stride stride; } + + namespace __detail + { + template + concept __cartesian_product_is_random_access + = (random_access_range<__maybe_const_t<_Const, _First>> + && ... + && (random_access_range<__maybe_const_t<_Const, _Vs>> + && sized_range<__maybe_const_t<_Const, _Vs>>)); + + template + concept __cartesian_product_common_arg + = common_range<_Range> || (sized_range<_Range> && random_access_range<_Range>); + + template + concept __cartesian_product_is_bidirectional + = (bidirectional_range<__maybe_const_t<_Const, _First>> + && ... + && (bidirectional_range<__maybe_const_t<_Const, _Vs>> + && __cartesian_product_common_arg<__maybe_const_t<_Const, _Vs>>)); + + template + concept __cartesian_product_is_common = __cartesian_product_common_arg<_First>; + + template + concept __cartesian_product_is_sized = (sized_range<_Vs> && ...); + + template class FirstSent, typename _First, typename... _Vs> + concept __cartesian_is_sized_sentinel + = (sized_sentinel_for>, + iterator_t<__maybe_const_t<_Const, _First>>> + && ... + && (sized_range<__maybe_const_t<_Const, _Vs>> + && sized_sentinel_for>, + iterator_t<__maybe_const_t<_Const, _Vs>>>)); + + template<__cartesian_product_common_arg _Range> + constexpr auto + __cartesian_common_arg_end(_Range& __r) + { + if constexpr (common_range<_Range>) + return ranges::end(__r); + else + return ranges::begin(__r) + ranges::distance(__r); + } + } // namespace __detail + + template + requires (view<_First> && ... && view<_Vs>) + class cartesian_product_view : public view_interface> + { + tuple<_First, _Vs...> _M_bases; + + template class _Iterator; + + static auto + _S_difference_type() + { + // TODO: Implement the recommended practice of using the smallest + // sufficiently wide type according to the maximum sizes of the + // underlying ranges? + return common_type_t, + range_difference_t<_Vs>...>{}; + } + + public: + cartesian_product_view() = default; + + constexpr explicit + cartesian_product_view(_First __first, _Vs... __rest) + : _M_bases(std::move(__first), std::move(__rest)...) + { } + + constexpr _Iterator + begin() requires (!__detail::__simple_view<_First> || ... || !__detail::__simple_view<_Vs>) + { return _Iterator(*this, __detail::__tuple_transform(ranges::begin, _M_bases)); } + + constexpr _Iterator + begin() const requires (range && ... && range) + { return _Iterator(*this, __detail::__tuple_transform(ranges::begin, _M_bases)); } + + constexpr _Iterator + end() requires ((!__detail::__simple_view<_First> || ... || !__detail::__simple_view<_Vs>) + && __detail::__cartesian_product_is_common<_First, _Vs...>) + { + bool __empty_tail = [this](index_sequence<_Is...>) { + return (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); + }(make_index_sequence{}); + + auto __it = __detail::__tuple_transform(ranges::begin, _M_bases); + if (!__empty_tail) + std::get<0>(__it) = __detail::__cartesian_common_arg_end(std::get<0>(_M_bases)); + return _Iterator{*this, std::move(__it)}; + } + + constexpr _Iterator + end() const requires __detail::__cartesian_product_is_common + { + bool __empty_tail = [this](index_sequence<_Is...>) { + return (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); + }(make_index_sequence{}); + + auto __it = __detail::__tuple_transform(ranges::begin, _M_bases); + if (!__empty_tail) + std::get<0>(__it) = __detail::__cartesian_common_arg_end(std::get<0>(_M_bases)); + return _Iterator{*this, std::move(__it)}; + } + + constexpr default_sentinel_t + end() const noexcept + { return default_sentinel; } + + constexpr auto + size() requires __detail::__cartesian_product_is_sized<_First, _Vs...> + { + using _ST = __detail::__make_unsigned_like_t; + return [&](index_sequence<_Is...>) { + auto __size = static_cast<_ST>(1); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral<_ST>) + { + bool __overflow + = (__builtin_mul_overflow(__size, + static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))), + &__size) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __size = (static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))) * ...); + return __size; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + constexpr auto + size() const requires __detail::__cartesian_product_is_sized + { + using _ST = __detail::__make_unsigned_like_t; + return [&](index_sequence<_Is...>) { + auto __size = static_cast<_ST>(1); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral<_ST>) + { + bool __overflow + = (__builtin_mul_overflow(__size, + static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))), + &__size) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __size = (static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))) * ...); + return __size; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + }; + + template + cartesian_product_view(_Vs&&...) -> cartesian_product_view...>; + + template + requires (view<_First> && ... && view<_Vs>) + template + class cartesian_product_view<_First, _Vs...>::_Iterator + { + using _Parent = __maybe_const_t<_Const, cartesian_product_view>; + _Parent* _M_parent = nullptr; + __detail::__tuple_or_pair_t>, + iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current; + + constexpr + _Iterator(_Parent& __parent, decltype(_M_current) __current) + : _M_parent(std::__addressof(__parent)), + _M_current(std::move(__current)) + { } + + static auto + _S_iter_concept() + { + if constexpr (__detail::__cartesian_product_is_random_access<_Const, _First, _Vs...>) + return random_access_iterator_tag{}; + else if constexpr (__detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...>) + return bidirectional_iterator_tag{}; + else if constexpr (forward_range<__maybe_const_t<_Const, _First>>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + friend cartesian_product_view; + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(_S_iter_concept()); + using value_type + = __detail::__tuple_or_pair_t>, + range_value_t<__maybe_const_t<_Const, _Vs>>...>; + using reference + = __detail::__tuple_or_pair_t>, + range_reference_t<__maybe_const_t<_Const, _Vs>>...>; + using difference_type = decltype(cartesian_product_view::_S_difference_type()); + + _Iterator() requires forward_range<__maybe_const_t<_Const, _First>> = default; + + constexpr + _Iterator(_Iterator __i) + requires _Const + && (convertible_to, iterator_t> + && ... && convertible_to, iterator_t>) + : _M_parent(std::__addressof(__i._M_parent)), + _M_current(std::move(__i._M_current)) + { } + + constexpr auto + operator*() const + { + auto __f = [](auto& __i) -> decltype(auto) { + return *__i; + }; + return __detail::__tuple_transform(__f, _M_current); + } + + constexpr _Iterator& + operator++() + { + _M_next(); + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + constexpr _Iterator + operator++(int) requires forward_range<__maybe_const_t<_Const, _First>> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() + requires __detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...> + { + _M_prev(); + return *this; + } + + constexpr _Iterator + operator--(int) + requires __detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { + _M_advance(__x); + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return *this += -__x; } + + constexpr reference + operator[](difference_type __n) const + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return *((*this) + __n); } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires equality_comparable>> + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _Iterator& __x, default_sentinel_t) + { + return [&](index_sequence<_Is...>) { + return ((std::get<_Is>(__x._M_current) + == ranges::end(std::get<_Is>(__x._M_parent->_M_bases))) + || ...); + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + friend constexpr auto + operator<=>(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _First, _Vs...> + { return __x._M_current <=> __y._M_current; } + + friend constexpr _Iterator + operator+(_Iterator __x, difference_type __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __x += __y; } + + friend constexpr _Iterator + operator+(difference_type __x, _Iterator __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __y += __x; } + + friend constexpr _Iterator + operator-(_Iterator __x, difference_type __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __x -= __y; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires __detail::__cartesian_is_sized_sentinel<_Const, iterator_t, _First, _Vs...> + { return __x._M_distance_from(__y._M_current); } + + friend constexpr difference_type + operator-(const _Iterator& __i, default_sentinel_t) + requires __detail::__cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Vs...> + { + tuple __end_tuple = [&](index_sequence<_Is...>) { + return tuple{ranges::end(std::get<0>(__i._M_parent->_M_bases)), + ranges::begin(std::get<1 + _Is>(__i._M_parent->_M_bases))...}; + }(make_index_sequence{}); + return __i._M_distance_from(__end_tuple); + } + + friend constexpr difference_type + operator-(default_sentinel_t, const _Iterator& __i) + requires __detail::__cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Vs...> + { return -(__i - default_sentinel); } + + friend constexpr auto + iter_move(const _Iterator& __i) + { return __detail::__tuple_transform(ranges::iter_move, __i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __l, const _Iterator& __r) + requires (indirectly_swappable>> + && ... + && indirectly_swappable>>) + { + [&](index_sequence<_Is...>) { + (ranges::iter_swap(std::get<_Is>(__l._M_current), std::get<_Is>(__r._M_current)), ...); + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + private: + template + constexpr void + _M_next() + { + auto& __it = std::get<_Nm>(_M_current); + ++__it; + if constexpr (_Nm > 0) + if (__it == ranges::end(std::get<_Nm>(_M_parent->_M_bases))) + { + __it = ranges::begin(std::get<_Nm>(_M_parent->_M_bases)); + _M_next<_Nm - 1>(); + } + } + + template + constexpr void + _M_prev() + { + auto& __it = std::get<_Nm>(_M_current); + if (__it == ranges::begin(std::get<_Nm>(_M_parent->_M_bases))) + { + __it = __detail::__cartesian_common_arg_end(std::get<_Nm>(_M_parent->_M_bases)); + if constexpr (_Nm > 0) + _M_prev<_Nm - 1>(); + } + --__it; + } + + template + constexpr void + _M_advance(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { + if (__x == 1) + _M_next<_Nm>(); + else if (__x == -1) + _M_prev<_Nm>(); + else if (__x != 0) + { + // Constant time iterator advancement. + auto& __r = std::get<_Nm>(_M_parent->_M_bases); + auto& __it = std::get<_Nm>(_M_current); + if constexpr (_Nm == 0) + { +#ifdef _GLIBCXX_ASSERTIONS + auto __size = ranges::ssize(__r); + auto __begin = ranges::begin(__r); + auto __offset = __it - __begin; + __glibcxx_assert(__offset + __x >= 0 && __offset + __x <= __size); +#endif + __it += __x; + } + else + { + auto __size = ranges::ssize(__r); + auto __begin = ranges::begin(__r); + auto __offset = __it - __begin; + __offset += __x; + __x = __offset / __size; + __offset %= __size; + if (__offset < 0) + { + __offset = __size + __offset; + --__x; + } + __it = __begin + __offset; + _M_advance<_Nm - 1>(__x); + } + } + } + + template + constexpr difference_type + _M_distance_from(const _Tuple& __t) const + { + return [&](index_sequence<_Is...>) { + auto __sum = static_cast(0); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow + = (__builtin_add_overflow(__sum, _M_scaled_distance<_Is>(__t), &__sum) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __sum = (_M_scaled_distance<_Is>(__t) + ...); + return __sum; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + template + constexpr difference_type + _M_scaled_distance(const _Tuple& __t) const + { + auto __dist = static_cast(std::get<_Nm>(_M_current) + - std::get<_Nm>(__t)); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow = __builtin_mul_overflow(__dist, _M_scaled_size<_Nm+1>(), &__dist); + __glibcxx_assert(!__overflow); + } + else +#endif + __dist *= _M_scaled_size<_Nm+1>(); + return __dist; + } + + template + constexpr difference_type + _M_scaled_size() const + { + if constexpr (_Nm <= sizeof...(_Vs)) + { + auto __size = static_cast(ranges::size + (std::get<_Nm>(_M_parent->_M_bases))); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow = __builtin_mul_overflow(__size, _M_scaled_size<_Nm+1>(), &__size); + __glibcxx_assert(!__overflow); + } + else +#endif + __size *= _M_scaled_size<_Nm+1>(); + return __size; + } + else + return static_cast(1); + } + }; + + namespace views + { + namespace __detail + { + template + concept __can_cartesian_product_view + = requires { cartesian_product_view...>(std::declval<_Ts>()...); }; + } + + struct _CartesianProduct + { + template + requires (sizeof...(_Ts) == 0 || __detail::__can_cartesian_product_view<_Ts...>) + constexpr auto + operator() [[nodiscard]] (_Ts&&... __ts) const + { + if constexpr (sizeof...(_Ts) == 0) + return views::empty>; + else + return cartesian_product_view...>(std::forward<_Ts>(__ts)...); + } + }; + + inline constexpr _CartesianProduct cartesian_product; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc b/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc new file mode 100644 index 00000000000..d6e4b538b20 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc @@ -0,0 +1,186 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + int x[] = {1, 2, 3}; + int y[] = {4, 5, 6}; + int z[] = {7, 8}; + int w[] = {9}; + + auto v0 = views::cartesian_product(); + VERIFY( ranges::end(v0) - ranges::begin(v0) == 0 ); + VERIFY( ranges::size(v0) == 0 ); + VERIFY( ranges::empty(v0) ); + + auto v1 = views::cartesian_product(x); + VERIFY( ranges::end(v1) - ranges::begin(v1) == 3 ); + VERIFY( ranges::size(v1) == 3 ); + VERIFY( ranges::equal(v1 | views::keys, x) ); + VERIFY( std::get<0>(v1[0]) == 1 ); + VERIFY( std::get<0>(v1[1]) == 2 ); + VERIFY( std::get<0>(v1[2]) == 3 ); + VERIFY( ranges::equal(v1 | views::reverse | views::keys, x | views::reverse)); + + auto v2 = views::cartesian_product(x, y); + VERIFY( ranges::size(v2) == 9 ); + VERIFY( ranges::end(v2) - ranges::begin(v2) == 9 ); + VERIFY( ranges::equal(v2 | views::keys, (int[]){1, 1, 1, 2, 2, 2, 3, 3, 3})); + VERIFY( ranges::equal(v2 | views::values, (int[]){4, 5, 6, 4, 5, 6, 4, 5, 6})); + VERIFY( ranges::equal(v2 | views::reverse | views::keys, (int[]){3, 3, 3, 2, 2, 2, 1, 1, 1}) ); + VERIFY( ranges::equal(v2 | views::reverse | views::values, (int[]){6, 5, 4, 6, 5, 4, 6, 5, 4}) ); + + auto v3 = views::cartesian_product(x, y, z); + VERIFY( ranges::size(v3) == 18 ); + VERIFY( ranges::equal(v3, (std::tuple[]){{1,4,7}, {1,4,8}, {1,5,7}, {1,5,8}, + {1,6,7}, {1,6,8}, {2,4,7}, {2,4,8}, + {2,5,7}, {2,5,8}, {2,6,7}, {2,6,8}, + {3,4,7}, {3,4,8}, {3,5,7}, {3,5,8}, + {3,6,7}, {3,6,8}}) ); + + auto v4 = views::cartesian_product(x, y, z, w); + VERIFY( ranges::size(v4) == 18 ); + VERIFY( ranges::equal(v4 | views::elements<3>, views::repeat(9, 18)) ); + + auto i4 = v4.begin(), j4 = i4 + 1; + VERIFY( j4 > i4 ); + VERIFY( i4[0] == std::tuple(1, 4, 7, 9) ); + VERIFY( i4 + 18 == v4.end() ); + i4 += 5; + VERIFY( i4 != v4.begin() ); + VERIFY( i4 - 5 == v4.begin() ); + VERIFY( *i4 == std::tuple(1, 6, 8, 9) ); + VERIFY( i4 - 5 != i4 ); + i4 -= 3; + VERIFY( *i4 == std::tuple(1, 5, 7, 9) ); + VERIFY( j4 + 1 == i4 ); + ranges::iter_swap(i4, j4); + VERIFY( *j4 == std::tuple(1, 5, 7, 9) ); + VERIFY( *i4 == std::tuple(1, 4, 8, 9) ); + + return true; +} + +void +test02() +{ + int x[] = {1, 2}; + __gnu_test::test_input_range rx(x); + auto v = views::cartesian_product(rx, x); + auto i = v.begin(); + std::default_sentinel_t s = v.end(); + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 1 && std::get<1>(*i) == 1 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 1 && std::get<1>(*i) == 2 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 2 && std::get<1>(*i) == 1 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 2 && std::get<1>(*i) == 2 ); + ++i; + VERIFY( i == s ); +} + +void +test03() +{ + int x[2]; + __gnu_test::test_input_range rx(x); + auto v = views::cartesian_product(views::counted(rx.begin(), 2), x); + VERIFY( v.size() == 4 ); + auto i = v.begin(); + std::default_sentinel_t s = v.end(); + VERIFY( i - s == -4 ); + VERIFY( s - i == 4 ); + ++i; + VERIFY( i - s == -3 ); + VERIFY( s - i == 3 ); + ++i; + VERIFY( i - s == -2 ); + VERIFY( s - i == 2 ); + ++i; + VERIFY( i - s == -1 ); + VERIFY( s - i == 1 ); + ++i; + VERIFY( i - s == 0 ); + VERIFY( s - i == 0 ); +} + +void +test04() +{ + // Exhaustively verify correctness of our iterator addition implementation + // (which runs in constant time) for this 24-element cartesian_product_view. + int x[4], y[3], z[2], w[1]; + auto v = views::cartesian_product(x, y, z, w); + + auto n = ranges::ssize(v); + for (int i = 0; i <= n; i++) + for (int j = 0; i + j <= n; j++) + { + auto b1 = v.begin(); + for (int k = 0; k < i + j; k++) + ++b1; + VERIFY( b1 - v.begin() == i + j ); + auto b2 = (v.begin() + i) + j; + auto b3 = v.begin() + (i + j); + VERIFY( b1 == b2 && b2 == b3 ); + + auto e1 = v.end(); + for (int k = 0; k < i + j; k++) + --e1; + VERIFY( v.end() - e1 == i + j ); + auto e2 = (v.end() - i) - j; + auto e3 = v.end() - (i + j); + VERIFY( e1 == e2 && e2 == e3 ); + } +} + +void +test05() +{ +#if __SIZEOF_INT128__ + auto r = views::iota(__int128(0), __int128(5)); +#else + auto r = views::iota(0ll, 5ll); +#endif + auto v = views::cartesian_product(r, r); + VERIFY( ranges::size(v) == 25 ); + VERIFY( v.end() - v.begin() == 25 ); + VERIFY( v.begin() + ranges::ssize(v) - v.begin() == 25 ); +} + +constexpr bool +test06() +{ + int x[] = {1, 2, 3}; + auto v = views::cartesian_product(x, views::empty, x); + VERIFY( ranges::size(v) == 0 ); + VERIFY( ranges::begin(v) == ranges::end(v) ); + VERIFY( ranges::begin(v) - ranges::begin(v) == 0 ); + + return true; +} + +int +main() +{ + static_assert(test01()); + test02(); + test03(); + test04(); + test05(); + static_assert(test06()); +} -- cgit v1.2.1 From 2ee0165f72be96083deaa8fd315bcfed011acd52 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 7 Nov 2022 13:29:42 -0500 Subject: libstdc++: Implement ranges::as_rvalue_view from P2446R2 libstdc++-v3/ChangeLog: * include/std/ranges (as_rvalue_view): Define. (enable_borrowed_range): Define. (views::__detail::__can_as_rvalue_view): Define. (views::_AsRvalue, views::as_rvalue): Define. * testsuite/std/ranges/adaptors/as_rvalue/1.cc: New test. --- libstdc++-v3/include/std/ranges | 90 ++++++++++++++++++++++ .../testsuite/std/ranges/adaptors/as_rvalue/1.cc | 47 +++++++++++ 2 files changed, 137 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 959886a1a55..ba544e116e1 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -8486,6 +8486,96 @@ namespace views::__adaptor inline constexpr _CartesianProduct cartesian_product; } + + template + requires view<_Vp> + class as_rvalue_view : public view_interface> + { + _Vp _M_base = _Vp(); + + public: + as_rvalue_view() requires default_initializable<_Vp> = default; + + constexpr explicit + as_rvalue_view(_Vp __base) + : _M_base(std::move(__base)) + { } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return move_iterator(ranges::begin(_M_base)); } + + constexpr auto + begin() const requires range + { return move_iterator(ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { + if constexpr (common_range<_Vp>) + return move_iterator(ranges::end(_M_base)); + else + return move_sentinel(ranges::end(_M_base)); + } + + constexpr auto + end() const requires range + { + if constexpr (common_range) + return move_iterator(ranges::end(_M_base)); + else + return move_sentinel(ranges::end(_M_base)); + } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + }; + + template + as_rvalue_view(_Range&&) -> as_rvalue_view>; + + template + inline constexpr bool enable_borrowed_range> + = enable_borrowed_range<_Tp>; + + namespace views + { + namespace __detail + { + template + concept __can_as_rvalue_view = requires { as_rvalue_view(std::declval<_Tp>()); }; + } + + struct _AsRvalue : __adaptor::_RangeAdaptorClosure + { + template + requires __detail::__can_as_rvalue_view<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { + if constexpr (same_as, + range_reference_t<_Range>>) + return views::all(std::forward<_Range>(__r)); + else + return as_rvalue_view(std::forward<_Range>(__r)); + } + }; + + inline constexpr _AsRvalue as_rvalue; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc new file mode 100644 index 00000000000..8ca4f50e9d2 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc @@ -0,0 +1,47 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + + std::unique_ptr a[3] = { std::make_unique(1), + std::make_unique(2), + std::make_unique(3) }; + std::unique_ptr b[3]; + auto v = a | views::as_rvalue; + ranges::copy(v, b); + VERIFY( ranges::all_of(a, [](auto& p) { return p.get() == nullptr; }) ); + VERIFY( ranges::equal(b | views::transform([](auto& p) { return *p; }), (int[]){1, 2, 3}) ); + + return true; +} + +void +test02() +{ + std::unique_ptr x = std::make_unique(42); + std::unique_ptr y; + __gnu_test::test_input_range rx(&x, &x+1); + auto v = rx | views::as_rvalue; + static_assert(!ranges::common_range); + ranges::copy(v, &y); + VERIFY( x.get() == nullptr ); + VERIFY( *y == 42 ); +} + +int +main() +{ + static_assert(test01()); + test02(); +} -- cgit v1.2.1 From 03ed4e57e3d46a61513b3d1ab1720997aec8cf71 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Tue, 1 Nov 2022 09:49:18 -0700 Subject: Extend optimization for integer bit test on __atomic_fetch_[or|and]_* Extend optimization for _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3); _5 = (signed int) _1; _4 = _5 >= 0; to _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3); _5 = (signed int) _1; if (_5 >= 0) gcc/ PR middle-end/102566 * tree-ssa-ccp.cc (optimize_atomic_bit_test_and): Also handle if (_5 < 0) and if (_5 >= 0). gcc/testsuite/ PR middle-end/102566 * g++.target/i386/pr102566-7.C --- gcc/testsuite/g++.target/i386/pr102566-7.C | 22 ++++++++ gcc/tree-ssa-ccp.cc | 84 ++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/g++.target/i386/pr102566-7.C diff --git a/gcc/testsuite/g++.target/i386/pr102566-7.C b/gcc/testsuite/g++.target/i386/pr102566-7.C new file mode 100644 index 00000000000..ce90214f33d --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr102566-7.C @@ -0,0 +1,22 @@ +/* { dg-do compile { target c++11 } } */ +/* { dg-options "-O2" } */ + +#include + +template +void lock_bts(std::atomic &a) { while (!(a.fetch_or(b) & b)); } +template +void lock_btr(std::atomic &a) { while (a.fetch_and(~b) & b); } +template +void lock_btc(std::atomic &a) { while (a.fetch_xor(b) & b); } +template void lock_bts<1U<<30>(std::atomic &a); +template void lock_btr<1U<<30>(std::atomic &a); +template void lock_btc<1U<<30>(std::atomic &a); +template void lock_bts<1U<<31>(std::atomic &a); +template void lock_btr<1U<<31>(std::atomic &a); +template void lock_btc<1U<<31>(std::atomic &a); + +/* { dg-final { scan-assembler-times "lock;?\[ \t\]*btsl" 2 } } */ +/* { dg-final { scan-assembler-times "lock;?\[ \t\]*btrl" 2 } } */ +/* { dg-final { scan-assembler-times "lock;?\[ \t\]*btcl" 2 } } */ +/* { dg-final { scan-assembler-not "cmpxchg" } } */ diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc index 9778e776cf2..3a4b6bc1118 100644 --- a/gcc/tree-ssa-ccp.cc +++ b/gcc/tree-ssa-ccp.cc @@ -3471,17 +3471,35 @@ optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip, { gimple *use_nop_stmt; if (!single_imm_use (use_lhs, &use_p, &use_nop_stmt) - || !is_gimple_assign (use_nop_stmt)) + || (!is_gimple_assign (use_nop_stmt) + && gimple_code (use_nop_stmt) != GIMPLE_COND)) return false; - tree use_nop_lhs = gimple_assign_lhs (use_nop_stmt); - rhs_code = gimple_assign_rhs_code (use_nop_stmt); - if (rhs_code != BIT_AND_EXPR) + /* Handle both + _4 = _5 < 0; + and + if (_5 < 0) + */ + tree use_nop_lhs = nullptr; + rhs_code = ERROR_MARK; + if (is_gimple_assign (use_nop_stmt)) { - if (TREE_CODE (use_nop_lhs) == SSA_NAME + use_nop_lhs = gimple_assign_lhs (use_nop_stmt); + rhs_code = gimple_assign_rhs_code (use_nop_stmt); + } + if (!use_nop_lhs || rhs_code != BIT_AND_EXPR) + { + /* Also handle + if (_5 < 0) + */ + if (use_nop_lhs + && TREE_CODE (use_nop_lhs) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_nop_lhs)) return false; - if (rhs_code == BIT_NOT_EXPR) + if (use_nop_lhs && rhs_code == BIT_NOT_EXPR) { + /* Handle + _7 = ~_2; + */ g = convert_atomic_bit_not (fn, use_nop_stmt, lhs, mask); if (!g) @@ -3512,14 +3530,31 @@ optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip, } else { - if (TREE_CODE (TREE_TYPE (use_nop_lhs)) != BOOLEAN_TYPE) - return false; + tree cmp_rhs1, cmp_rhs2; + if (use_nop_lhs) + { + /* Handle + _4 = _5 < 0; + */ + if (TREE_CODE (TREE_TYPE (use_nop_lhs)) + != BOOLEAN_TYPE) + return false; + cmp_rhs1 = gimple_assign_rhs1 (use_nop_stmt); + cmp_rhs2 = gimple_assign_rhs2 (use_nop_stmt); + } + else + { + /* Handle + if (_5 < 0) + */ + rhs_code = gimple_cond_code (use_nop_stmt); + cmp_rhs1 = gimple_cond_lhs (use_nop_stmt); + cmp_rhs2 = gimple_cond_rhs (use_nop_stmt); + } if (rhs_code != GE_EXPR && rhs_code != LT_EXPR) return false; - tree cmp_rhs1 = gimple_assign_rhs1 (use_nop_stmt); if (use_lhs != cmp_rhs1) return false; - tree cmp_rhs2 = gimple_assign_rhs2 (use_nop_stmt); if (!integer_zerop (cmp_rhs2)) return false; @@ -3547,6 +3582,14 @@ optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip, _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3); _6 = _1 & 0x80000000; _4 = _6 != 0 or _6 == 0; + and convert + _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3); + _5 = (signed int) _1; + if (_5 < 0 or _5 >= 0) + to + _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3); + _6 = _1 & 0x80000000; + if (_6 != 0 or _6 == 0) */ and_mask = build_int_cst (TREE_TYPE (use_rhs), highest); @@ -3567,6 +3610,14 @@ optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip, _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3); _6 = _1 & 0x80000000; _4 = _6 != 0 or _6 == 0; + and convert + _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3); + _5 = (signed int) _1; + if (_5 < 0 or _5 >= 0) + to + _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3); + _6 = _1 & 0x80000000; + if (_6 != 0 or _6 == 0) */ } var = make_ssa_name (TREE_TYPE (use_rhs)); @@ -3577,11 +3628,14 @@ optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip, gsi = gsi_for_stmt (use_nop_stmt); gsi_insert_before (&gsi, g, GSI_NEW_STMT); use_stmt = g; - g = gimple_build_assign (use_nop_lhs, - (rhs_code == GE_EXPR - ? EQ_EXPR : NE_EXPR), - var, - build_zero_cst (TREE_TYPE (use_rhs))); + rhs_code = rhs_code == GE_EXPR ? EQ_EXPR : NE_EXPR; + tree const_zero = build_zero_cst (TREE_TYPE (use_rhs)); + if (use_nop_lhs) + g = gimple_build_assign (use_nop_lhs, rhs_code, + var, const_zero); + else + g = gimple_build_cond (rhs_code, var, const_zero, + nullptr, nullptr); gsi_insert_after (&gsi, g, GSI_NEW_STMT); gsi = gsi_for_stmt (use_nop_stmt); gsi_remove (&gsi, true); -- cgit v1.2.1 From a239a63f868e29e9276088e7c0fb00804c2903ba Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Fri, 4 Nov 2022 22:24:42 +0100 Subject: Improve multiplication by powers of 2 in range-ops. For unsigned numbers, multiplication by X, where X is a power of 2 is [0,0][X,+INF]. This patch causes a regression to g++.dg/pr71488.C where -Wstringop-overflow gets the same IL as before, but better ranges cause it to issue a bogus warning. I will create a PR with some notes. No discernible changes in performance. Tested on x86-64 Linux. PR tree-optimization/55157 gcc/ChangeLog: * range-op.cc (operator_mult::wi_fold): Optimize multiplications by powers of 2. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr55157.c: New test. --- gcc/range-op.cc | 16 ++++++++++++++-- gcc/testsuite/gcc.dg/tree-ssa/pr55157.c | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr55157.c diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 2b5db0cac85..a13e88840a6 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -1911,8 +1911,20 @@ operator_mult::wi_fold (irange &r, tree type, // diff = max - min prod2 = prod3 - prod0; if (wi::geu_p (prod2, sizem1)) - // The range covers all values. - r.set_varying (type); + { + // Multiplying by X, where X is a power of 2 is [0,0][X,+INF]. + if (TYPE_UNSIGNED (type) && rh_lb == rh_ub + && wi::exact_log2 (rh_lb) != -1 && prec > 1) + { + r.set (type, rh_lb, wi::max_value (prec, sign)); + int_range<2> zero; + zero.set_zero (type); + r.union_ (zero); + } + else + // The range covers all values. + r.set_varying (type); + } else { wide_int new_lb = wide_int::from (prod0, prec, sign); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr55157.c b/gcc/testsuite/gcc.dg/tree-ssa/pr55157.c new file mode 100644 index 00000000000..bbdda45bd64 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr55157.c @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-evrp" } + +void gg(void); +int f(unsigned t) +{ + unsigned g = t*16; + if (g==0) return 1; + gg(); + gg(); + gg(); + gg(); + gg(); + gg(); + if (g<=4) return 1; + return 0; +} + +// { dg-final { scan-tree-dump-times " if " 1 "evrp" } } -- cgit v1.2.1 From 93ab7d03dfbdd096c1d3c83e92d794bf4eed18df Mon Sep 17 00:00:00 2001 From: David Faust Date: Mon, 7 Nov 2022 10:30:52 -0800 Subject: bpf: cleanup missed refactor Commit 068baae1864 "bpf: add preserve_field_info builtin" factored out some repeated code to a new function maybe_make_core_relo (), but missed using it in one place. Clean that up. gcc/ * config/bpf/bpf.cc (handle_attr_preserve): Use maybe_make_core_relo(). --- gcc/config/bpf/bpf.cc | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc index ea8ca64d1d6..fd4003c2bfc 100644 --- a/gcc/config/bpf/bpf.cc +++ b/gcc/config/bpf/bpf.cc @@ -1731,7 +1731,6 @@ handle_attr_preserve (function *fn) { basic_block bb; rtx_insn *insn; - rtx_code_label *label; FOR_EACH_BB_FN (bb, fn) { FOR_BB_INSNS (bb, insn) @@ -1762,28 +1761,7 @@ handle_attr_preserve (function *fn) } if (is_attr_preserve_access (expr)) - { - auto_vec accessors; - tree container = bpf_core_compute (expr, &accessors); - if (accessors.length () < 1) - continue; - accessors.reverse (); - - container = TREE_TYPE (container); - const char * section_name; - if (DECL_SECTION_NAME (fn->decl)) - section_name = DECL_SECTION_NAME (fn->decl); - else - section_name = ".text"; - - label = gen_label_rtx (); - LABEL_PRESERVE_P (label) = 1; - emit_label (label); - - /* Add the CO-RE relocation information to the BTF container. */ - bpf_core_reloc_add (container, section_name, &accessors, label, - BPF_RELO_FIELD_BYTE_OFFSET); - } + maybe_make_core_relo (expr, BPF_RELO_FIELD_BYTE_OFFSET); } } rtx_insn *seq = get_insns (); -- cgit v1.2.1 From b457b779427b0f7b3fbac447811c9c52db5bc79e Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 8 Nov 2022 00:35:09 +0100 Subject: libstdc++: Fix up libstdc++ build against glibc 2.25 or older [PR107562] On Mon, Nov 07, 2022 at 05:48:42PM +0000, Jonathan Wakely wrote: > On Mon, 7 Nov 2022 at 16:11, Joseph Myers wrote: > > > > On Wed, 2 Nov 2022, Jakub Jelinek via Gcc-patches wrote: > > > > > APIs. So that one can build gcc against older glibc and then compile > > > user programs on newer glibc, the patch uses weak references unless > > > gcc is compiled against glibc 2.26+. strfromf128 unfortunately can't > > > > This support for older glibc doesn't actually seem to be working, on an > > older system with glibc 2.19 I'm seeing > > > > /scratch/jmyers/fsf/gcc-mainline/libstdc++-v3/src/c++17/floating_to_chars.cc:52:3: error: expected initializer before '__asm' > > 52 | __asm ("strfromf128"); > > | ^~~~~ > > > > and a series of subsequent errors. > > This seems to "fix" it (not sure if it's right though): > > #ifndef _GLIBCXX_HAVE_FLOAT128_MATH > extern "C" _Float128 __strtof128(const char*, char**) > __attribute__((__weak__)); > #endif > extern "C" _Float128 __strtof128(const char*, char**) > __asm ("strtof128"); It is, but floating_from_chars.cc has the same problem, and I think we can avoid the duplication, like this: 2022-11-08 Jakub Jelinek PR libstdc++/107562 * src/c++17/floating_from_chars.cc (__strtof128): Put __asm before __attribute__. * src/c++17/floating_to_chars.cc (__strfromf128): Likewise. --- libstdc++-v3/src/c++17/floating_from_chars.cc | 3 ++- libstdc++-v3/src/c++17/floating_to_chars.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 11a9be68770..29eb4634e9d 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -63,10 +63,11 @@ extern "C" __ieee128 __strtoieee128(const char*, char**); && defined(__GLIBC_PREREQ) #define USE_STRTOF128_FOR_FROM_CHARS 1 extern "C" _Float128 __strtof128(const char*, char**) + __asm ("strtof128") #ifndef _GLIBCXX_HAVE_FLOAT128_MATH __attribute__((__weak__)) #endif - __asm ("strtof128"); + ; #endif #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \ diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc index d6be6465b12..afce8d74c12 100644 --- a/libstdc++-v3/src/c++17/floating_to_chars.cc +++ b/libstdc++-v3/src/c++17/floating_to_chars.cc @@ -46,10 +46,11 @@ extern "C" int __sprintfieee128(char*, const char*, ...); #elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ && defined(__GLIBC_PREREQ) extern "C" int __strfromf128(char*, size_t, const char*, _Float128) + __asm ("strfromf128") #ifndef _GLIBCXX_HAVE_FLOAT128_MATH __attribute__((__weak__)) #endif - __asm ("strfromf128"); + ; #endif // This implementation crucially assumes float/double have the -- cgit v1.2.1 From f8d901d00e94e5a03c3321b37303eddd7c321ecb Mon Sep 17 00:00:00 2001 From: GCC Administrator Date: Tue, 8 Nov 2022 00:17:53 +0000 Subject: Daily bump. --- gcc/ChangeLog | 190 +++++++++++++++++++++++++++++++++++++++++ gcc/DATESTAMP | 2 +- gcc/ada/ChangeLog | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ gcc/fortran/ChangeLog | 9 ++ gcc/testsuite/ChangeLog | 99 ++++++++++++++++++++++ libstdc++-v3/ChangeLog | 70 ++++++++++++++++ 6 files changed, 588 insertions(+), 1 deletion(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9d3b61e9690..35a83852b08 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,193 @@ +2022-11-07 David Faust + + * config/bpf/bpf.cc (handle_attr_preserve): Use maybe_make_core_relo(). + +2022-11-07 Aldy Hernandez + + PR tree-optimization/55157 + * range-op.cc (operator_mult::wi_fold): Optimize multiplications + by powers of 2. + +2022-11-07 H.J. Lu + + PR middle-end/102566 + * tree-ssa-ccp.cc (optimize_atomic_bit_test_and): Also handle + if (_5 < 0) and if (_5 >= 0). + +2022-11-07 Richard Purdie + + * file-prefix-map.cc (remap_filename): Handle NULL filenames. + +2022-11-07 Alexander Monakov + + PR tree-optimization/107505 + * tree-ssa-sink.cc (statement_sink_location): Additionally + reject ECF_RETURNS_TWICE calls. + +2022-11-07 Aldy Hernandez + + PR tree-optimization/107541 + * range-op.cc (operator_div::fold_range): Restrict power of 2 + optimization to positive numbers. + +2022-11-07 Richard Biener + + * tree-ssa-loop-unswitch.cc (unswitch_predicate::count): New. + (unswitch_predicate::unswitch_predicate): Initialize count. + (init_loop_unswitch_info): First collect candidates and + determine the outermost loop to unswitch. + (tree_ssa_unswitch_loops): First perform all guard hoisting, + then perform unswitching on innermost loop predicates. + (find_unswitching_predicates_for_bb): Keep track of the + most profitable predicate to unswitch on. + (tree_unswitch_single_loop): Unswitch given predicate if + not NULL. + +2022-11-07 Martin Liska + Gerald Pfeifer + + * doc/invoke.texi: Improve wording. + +2022-11-07 Martin Liska + + * range-op.cc: Add final override keywords. + +2022-11-07 Kewen Lin + + PR tree-optimization/107412 + * gimple-fold.cc (gimple_fold_mask_load_store_mem_ref): Rename to ... + (gimple_fold_partial_load_store_mem_ref): ... this, add one parameter + mask_p indicating it's for mask or length, and add some handlings for + IFN LEN_{LOAD,STORE}. + (gimple_fold_mask_load): Rename to ... + (gimple_fold_partial_load): ... this, add one parameter mask_p. + (gimple_fold_mask_store): Rename to ... + (gimple_fold_partial_store): ... this, add one parameter mask_p. + (gimple_fold_call): Add the handlings for IFN LEN_{LOAD,STORE}, + and adjust calls on gimple_fold_mask_load_store_mem_ref to + gimple_fold_partial_load_store_mem_ref. + +2022-11-07 Hu, Lin1 + + * common/config/i386/cpuinfo.h + (get_intel_cpu): Handle Grand Ridge. + * common/config/i386/i386-common.cc + (processor_names): Add grandridge. + (processor_alias_table): Ditto. + * common/config/i386/i386-cpuinfo.h: + (enum processor_types): Add INTEL_GRANDRIDGE. + * config.gcc: Add -march=grandridge. + * config/i386/driver-i386.cc (host_detect_local_cpu): + Handle grandridge. + * config/i386/i386-c.cc (ix86_target_macros_internal): + Ditto. + * config/i386/i386-options.cc (m_GRANDRIDGE): New define. + (processor_cost_table): Add grandridge. + * config/i386/i386.h (enum processor_type): + Add PROCESSOR_GRANDRIDGE. + (PTA_GRANDRIDGE): Ditto. + * doc/extend.texi: Add grandridge. + * doc/invoke.texi: Ditto. + +2022-11-07 konglin1 + + * config/i386/i386.opt:Add -mprefer-remote-atomic. + * config/i386/sync.md (atomic_): + New define_expand. + (atomic_add): Rename to below one. + (atomic_add_1): To this. + (atomic_): Ditto. + (atomic__1): Ditto. + * doc/invoke.texi: Add -mprefer-remote-atomic. + +2022-11-07 konglin1 + + * common/config/i386/cpuinfo.h (get_available_features): + Detect raoint. + * common/config/i386/i386-common.cc (OPTION_MASK_ISA2_RAOINT_SET, + OPTION_MASK_ISA2_RAOINT_UNSET): New. + (ix86_handle_option): Handle -mraoint. + * common/config/i386/i386-cpuinfo.h (enum processor_features): + Add FEATURE_RAOINT. + * common/config/i386/i386-isas.h: Add ISA_NAME_TABLE_ENTRY for + raoint. + * config.gcc: Add raointintrin.h + * config/i386/cpuid.h (bit_RAOINT): New. + * config/i386/i386-builtin.def (BDESC): Add new builtins. + * config/i386/i386-c.cc (ix86_target_macros_internal): Define + __RAOINT__. + * config/i386/i386-isa.def (RAOINT): Add DEF_PTA(RAOINT). + * config/i386/i386-options.cc (ix86_valid_target_attribute_inner_p): + Add -mraoint. + * config/i386/sync.md (rao_a): New define insn. + * config/i386/i386.opt: Add option -mraoint. + * config/i386/x86gprintrin.h: Include raointintrin.h. + * doc/extend.texi: Document raoint. + * doc/invoke.texi: Document -mraoint. + * doc/sourcebuild.texi: Document target raoint. + * config/i386/raointintrin.h: New file. + +2022-11-07 Haochen Jiang + + * common/config/i386/cpuinfo.h + (get_intel_cpu): Handle Granite Rapids. + * common/config/i386/i386-common.cc: + (processor_names): Add graniterapids. + (processor_alias_table): Ditto. + * common/config/i386/i386-cpuinfo.h + (enum processor_subtypes): Add INTEL_GRANTIERAPIDS. + * config.gcc: Add -march=graniterapids. + * config/i386/driver-i386.cc (host_detect_local_cpu): + Handle graniterapids. + * config/i386/i386-c.cc (ix86_target_macros_internal): + Ditto. + * config/i386/i386-options.cc (m_GRANITERAPIDS): New. + (processor_cost_table): Add graniterapids. + * config/i386/i386.h (enum processor_type): + Add PROCESSOR_GRANITERAPIDS. + (PTA_GRANITERAPIDS): Ditto. + * doc/extend.texi: Add graniterapids. + * doc/invoke.texi: Ditto. + +2022-11-07 Haochen Jiang + Hongtao Liu + + * common/config/i386/cpuinfo.h (get_available_features): + Detect PREFETCHI. + * common/config/i386/i386-common.cc + (OPTION_MASK_ISA2_PREFETCHI_SET, + OPTION_MASK_ISA2_PREFETCHI_UNSET): New. + (ix86_handle_option): Handle -mprefetchi. + * common/config/i386/i386-cpuinfo.h + (enum processor_features): Add FEATURE_PREFETCHI. + * common/config/i386/i386-isas.h: Add ISA_NAME_TABLE_ENTRY + for prefetchi. + * config.gcc: Add prfchiintrin.h. + * config/i386/cpuid.h (bit_PREFETCHI): New. + * config/i386/i386-builtin-types.def: + Add DEF_FUNCTION_TYPE (VOID, PCVOID, INT) + and DEF_FUNCTION_TYPE (VOID, PCVOID, INT, INT, INT). + * config/i386/i386-builtin.def (BDESC): Add new builtins. + * config/i386/i386-c.cc (ix86_target_macros_internal): + Define __PREFETCHI__. + * config/i386/i386-expand.cc: Handle new builtins. + * config/i386/i386-isa.def (PREFETCHI): + Add DEF_PTA(PREFETCHI). + * config/i386/i386-options.cc + (ix86_valid_target_attribute_inner_p): Handle prefetchi. + * config/i386/i386.md (prefetchi): New define_insn. + * config/i386/i386.opt: Add option -mprefetchi. + * config/i386/predicates.md (local_func_symbolic_operand): + New predicates. + * config/i386/x86gprintrin.h: Include prfchiintrin.h. + * config/i386/xmmintrin.h (enum _mm_hint): New enum for + prefetchi. + (_mm_prefetch): Handle the highest bit of enum. + * doc/extend.texi: Document prefetchi. + * doc/invoke.texi: Document -mprefetchi. + * doc/sourcebuild.texi: Document target prefetchi. + * config/i386/prfchiintrin.h: New file. + 2022-11-06 Uroš Bizjak * optabs.cc (can_vec_set_var_idx_p): Use operand[2] diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index 0503ed97af3..f47121e1a89 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20221107 +20221108 diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index 4b0877b2de5..f74024705c0 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,222 @@ +2022-11-07 Cedric Landet + + * doc/gnat_ugn/gnat_and_program_execution.rst: Mention the needed + -no-pie for windows to use gprof. + * gnat_ugn.texi: Regenerate. + +2022-11-07 Piotr Trojanek + + * lib-xref.adb (Hash): Tune hash function. + +2022-11-07 Piotr Trojanek + + * sem_prag.adb (Non_Significant_Pragma_Reference): Detect + references with aggregates; only assign local variables Id and C + when necessary. + +2022-11-07 Bob Duff + + * exp_ch4.adb + (Component_Equality, Expand_Array_Equality) + (Expand_Record_Equality): Use named notation. + +2022-11-07 Bob Duff + + * exp_ch4.adb + (Expand_Array_Equality): Do not test Ltyp = Rtyp here, because + that is necessarily true. Move assertion thereof to more general + place. + (Expand_Composite_Equality): Pass in Outer_Type, for use in + warnings. Rename Typ to be Comp_Type, to more clearly distinguish + it from Outer_Type. Print warning when appropriate. + * exp_ch4.ads: Minor comment fix. + * errout.ads: There is no such pragma as Warning_As_Pragma -- + Warning_As_Error must have been intended. Improve comment for ?x?. + * exp_ch3.adb + (Build_Untagged_Equality): Update comment to be accurate for more + recent versions of Ada. + * sem_case.adb + (Choice_Analysis): Declare user-defined "=" functions as abstract. + * sem_util.ads + (Is_Bounded_String): Give RM reference in comment. + * warnsw.ads, warnsw.adb + (Warn_On_Ignored_Equality): Implement new warning switch -gnatw_q. + * doc/gnat_ugn/building_executable_programs_with_gnat.rst: + Document new warning switch. + * gnat_ugn.texi: Regenerate. + +2022-11-07 Piotr Trojanek + + * sem_aux.ads (Is_Body): Annotate with Inline. + * sem_util.ads (Is_Body_Or_Package_Declaration): Likewise. + +2022-11-07 Bob Duff + + * freeze.adb + (Build_Inherited_Condition_Pragmas): Do nothing if A_Post is + empty. + +2022-11-07 Quentin Ochem + Steve Baird + + * bindgen.adb: fixed the way the device init and final symbols are + computed, re-using the normal way these symbols would be computed + with a __device_ prefix. Also fixed the "is null;" procedure on + the host side which are not Ada 95, replaced with a procedure + raising an exception as it should never be called. Remove the + unused function Device_Ada_Final_Link_Name. + +2022-11-07 Steve Baird + + * opt.ads: Declare new string pointer variable, CUDA_Device_Library_Name. + Modify comments for existing Boolean variable Enable_CUDA_Device_Expansion. + * switch-b.adb: When "-d_c" switch is encountered, check that the next + character is an "'='; use the remaining characters to initialize + Opt.CUDA_Device_Library_Name. + * bindgen.adb: Remove (for now) most support for host-side invocation of + device-side finalization. Make use of the new CUDA_Device_Library_Name + in determining the string used to refer (on the host side) to the + device-side initialization procedure. Declare the placeholder routine + that is named in the CUDA_Execute pragma (and the CUDA_Register_Function + call) as an exported null procedure, rather than as an imported procedure. + It is not clear whether it is really necessary to specify the link-name + for this should-never-be-called subprogram on the host side, but for now it + shouldn't hurt to do so. + +2022-11-07 Piotr Trojanek + + * exp_ch6.adb (Expand_Protected_Subprogram_Call): Examine scope + tree and not the scope stack. + +2022-11-07 Piotr Trojanek + + * mdll.ads (Build_Import_Library): Fix grammar in comment. + * mdll.adb (Build_Import_Library): Directly execute code of a + nested routine; rename No_Lib_Prefix to Strip_Lib_Prefix. + +2022-11-07 Bob Duff + + * sem_warn.adb + (Check_For_Warnings): Remove unnecessary exception handler. + (Warn_On_Known_Condition): Suppress warning when we detect a True + or False that has been turned into a more complex expression + because True is represented as "nonzero". (Note that the complex + expression will subsequently be constant-folded to a Boolean True + or False). Also simplify to always print "condition is always ..." + instead of special-casing object names. The special case was + unhelpful, and indeed wrong when the expression is a literal. + +2022-11-07 Piotr Trojanek + + * checks.adb (Safe_To_Capture_In_Parameter_Value): Remove. + * sem_util.adb (Safe_To_Capture_Value): Stop search at the current + body. + +2022-11-07 Piotr Trojanek + + * sem_warn.adb (Warn_On_In_Out): Remove No_ prefix; flip return + values between True and False; adapt caller. + +2022-11-07 Piotr Trojanek + + * sem_ch13.adb (Add_Call): Just look at Instantiation_Depth. + * sem_ch3.adb (Derive_Subprograms): Likewise. + * sem_warn.adb (Check_References): Remove redundant filtering with + Instantiation_Depth that follows filtering with + Instantiation_Location. + * sinput.adb (Instantiation_Depth): Reuse Instantiation_Location. + +2022-11-07 Piotr Trojanek + + * sem_warn.adb + (No_Warn_On_In_Out): For subprograms we can simply call + Warnings_Off. + (Output_Non_Modified_In_Out_Warnings): Remove repeated + suppression. + +2022-11-07 Piotr Trojanek + + * sem_aggr.adb (Resolve_Delta_Array_Aggregate): Reject boxes in + delta array aggregates. + +2022-11-07 Piotr Trojanek + + * sem_res.adb (Enclosing_Declaration_Or_Statement): Moved to + Sem_Util. + * sem_util.ads (Enclosing_Declaration_Or_Statement): Moved from + Sem_Res. + * sem_util.adb (Enclosing_Declaration_Or_Statement): Likewise. + +2022-11-07 Piotr Trojanek + + * sem_res.adb (Resolve): Only call Comes_From_Predefined_Lib_Unit + when its result might be needed. + +2022-11-07 Piotr Trojanek + + * sem_ch12.adb (Check_Generic_Actuals): Remove redundant parens; + refactor an excessive if-statement; remove repeated call to Node. + +2022-11-07 Piotr Trojanek + + * inline.adb (Establish_Actual_Mapping_For_Inlined_Call): Move + comment next to a condition that it describes. + +2022-11-07 Steve Baird + + * exp_put_image.adb + (Image_Should_Call_Put_Image): Correctly handle the case of an + inherited Put_Image aspect specification for a scalar type. + +2022-11-07 Piotr Trojanek + + * libgnarl/s-interr.adb: Tune whitespace. + +2022-11-07 Piotr Trojanek + + * libgnarl/s-interr.adb: Reorder context items and pragmas. + +2022-11-07 Piotr Trojanek + + * exp_ch4.adb + (Expand_Modular_Addition): Rewrite using Make_XXX calls. + (Expand_Modular_Op): Likewise. + (Expand_Modular_Subtraction): Likewise. + * exp_imgv.adb + (Expand_User_Defined_Enumeration_Image): Likewise. + +2022-11-07 Piotr Trojanek + + * checks.adb (Apply_Arithmetic_Overflow_Strict): Rewrite using a + newly created operator node. + * exp_ch4.adb (Expand_Array_Comparison): Likewise. + * exp_ch6.adb (Add_Call_By_Copy_Code): Rewriting actual parameter + using its own location and not the location of the subprogram + call. + * sem_warn.adb (Check_References): Looping with Original_Node is + no longer needed. + +2022-11-07 Piotr Trojanek + + * sem_prag.adb (Is_In_Context_Clause): Rewrite without negations + and inequalities. + +2022-11-07 Piotr Trojanek + + * sem_prag.adb (Analyze_Pragma [Pragma_Obsolescent]): Reject + misplaced pragma. + +2022-11-07 Piotr Trojanek + + * sem_warn.adb (Output_Obsolescent_Entity_Warnings): Tag warnings + about obsolescent functions just like we tag similar warnings for + packages and procedures. + +2022-11-07 Piotr Trojanek + + * exp_attr.adb (Expand_N_Attribute_Reference): Remove useless + skipping for attribute Input. + 2022-11-04 Justin Squirek * exp_attr.adb (Expand_N_Attribute_Reference): Skip operand diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 12002d9798f..69755e261a7 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,12 @@ +2022-11-07 Tobias Burnus + + PR fortran/107508 + * trans-array.cc (gfc_alloc_allocatable_for_assignment): Fix + string-length check, plug memory leak, and avoid generation of + effectively no-op code. + * trans-expr.cc (alloc_scalar_allocatable_for_assignment): Extend + comment; minor cleanup. + 2022-11-03 Tobias Burnus * openmp.cc (gfc_match_omp_clauses): Permit derived types for diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index abf1d8f648e..e2232f687ba 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,102 @@ +2022-11-07 Aldy Hernandez + + PR tree-optimization/55157 + * gcc.dg/tree-ssa/pr55157.c: New test. + +2022-11-07 H.J. Lu + + PR middle-end/102566 + * g++.target/i386/pr102566-7.C + +2022-11-07 Nathan Sidwell + + * g++.dg/abi/lambda-tpl1.h: Add more cases. + * g++.dg/abi/lambda-tpl1-17.C: Add checks. + * g++.dg/abi/lambda-tpl1-18.C: Likewise. + * g++.dg/abi/lambda-tpl1-18vs17.C: Likewise. + +2022-11-07 Alexander Monakov + + PR tree-optimization/107505 + * gcc.dg/pr107505.c: New test. + +2022-11-07 Aldy Hernandez + + PR tree-optimization/107541 + * gcc.dg/tree-ssa/pr107541.c: New test. + +2022-11-07 Tobias Burnus + + PR fortran/107508 + * gfortran.dg/widechar_11.f90: New test. + +2022-11-07 konglin1 + + * gcc.target/i386/sse-22.c: Fix typo in pragma GCC target. + +2022-11-07 Kewen Lin + + PR tree-optimization/107412 + * gcc.target/powerpc/pr107412.c: New test. + * gcc.target/powerpc/p9-vec-length-epil-8.c: Adjust scan times for + folded LEN_LOAD. + +2022-11-07 Hu, Lin1 + + * g++.target/i386/mv16.C: Add grandridge. + * gcc.target/i386/funcspec-56.inc: Handle new march. + +2022-11-07 konglin1 + + * gcc.target/i386/raoint-atomic-fetch.c: New test. + +2022-11-07 konglin1 + + * g++.dg/other/i386-2.C: Add -mraoint. + * g++.dg/other/i386-3.C: Ditto. + * gcc.target/i386/funcspec-56.inc: Add new target attribute. + * gcc.target/i386/sse-12.c: Add -mraoint. + * gcc.target/i386/sse-13.c: Ditto. + * gcc.target/i386/sse-14.c: Ditto. + * gcc.target/i386/sse-22.c: Add raoint target. + * gcc.target/i386/sse-23.c: Ditto. + * lib/target-supports.exp: Add check_effective_target_raoint. + * gcc.target/i386/rao-helper.h: New test. + * gcc.target/i386/raoint-1.c: Ditto. + * gcc.target/i386/raoint-aadd-2.c: Ditto. + * gcc.target/i386/raoint-aand-2.c: Ditto. + * gcc.target/i386/raoint-aor-2.c: Ditto. + * gcc.target/i386/raoint-axor-2.c: Ditto. + * gcc.target/i386/x86gprintrin-1.c: Ditto. + * gcc.target/i386/x86gprintrin-2.c: Ditto. + * gcc.target/i386/x86gprintrin-3.c: Ditto. + * gcc.target/i386/x86gprintrin-4.c: Ditto. + * gcc.target/i386/x86gprintrin-5.c: Ditto. + +2022-11-07 Haochen Jiang + + * g++.target/i386/mv16.C: Add graniterapids. + * gcc.target/i386/funcspec-56.inc: Handle new march. + +2022-11-07 Haochen Jiang + Hongtao Liu + + * g++.dg/other/i386-2.C: Add -mprefetchi. + * g++.dg/other/i386-3.C: Ditto. + * gcc.target/i386/avx-1.c: Ditto. + * gcc.target/i386/funcspec-56.inc: Add new target attribute. + * gcc.target/i386/sse-13.c: Add -mprefetchi. + * gcc.target/i386/sse-23.c: Ditto. + * gcc.target/i386/x86gprintrin-1.c: Ditto. + * gcc.target/i386/x86gprintrin-2.c: Ditto. + * gcc.target/i386/x86gprintrin-3.c: Ditto. + * gcc.target/i386/x86gprintrin-4.c: Ditto. + * gcc.target/i386/x86gprintrin-5.c: Ditto. + * gcc.target/i386/prefetchi-1.c: New test. + * gcc.target/i386/prefetchi-2.c: Ditto. + * gcc.target/i386/prefetchi-3.c: Ditto. + * gcc.target/i386/prefetchi-4.c: Ditto. + 2022-11-06 Patrick Palka * g++.dg/special/initpri3.C: New test. diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index fe270579d4a..5874c62a943 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,73 @@ +2022-11-07 Jakub Jelinek + + PR libstdc++/107562 + * src/c++17/floating_from_chars.cc (__strtof128): Put __asm before + __attribute__. + * src/c++17/floating_to_chars.cc (__strfromf128): Likewise. + +2022-11-07 Patrick Palka + + * include/std/ranges (as_rvalue_view): Define. + (enable_borrowed_range): Define. + (views::__detail::__can_as_rvalue_view): Define. + (views::_AsRvalue, views::as_rvalue): Define. + * testsuite/std/ranges/adaptors/as_rvalue/1.cc: New test. + +2022-11-07 Patrick Palka + + * include/std/ranges (__maybe_const_t): New alias for + __detail::__maybe_const_t. + (__detail::__cartesian_product_is_random_access): Define. + (__detail::__cartesian_product_common_arg): Define. + (__detail::__cartesian_product_is_bidirectional): Define. + (__detail::__cartesian_product_is_common): Define. + (__detail::__cartesian_product_is_sized): Define. + (__detail::__cartesian_is_sized_sentinel): Define. + (__detail::__cartesian_common_arg_end): Define. + (cartesian_product_view): Define. + (cartesian_product_view::_Iterator): Define. + (views::__detail::__can_cartesian_product_view): Define. + (views::_CartesianProduct, views::cartesian_product): Define. + * testsuite/std/ranges/cartesian_product/1.cc: New test. + +2022-11-07 Jakub Jelinek + + PR libstdc++/107468 + * src/c++17/fast_float/MERGE: Adjust for merge from upstream. + * src/c++17/fast_float/LOCAL_PATCHES: Remove commits that were + upstreamed. + * src/c++17/fast_float/README.md: Merge from fast_float + 662497742fea7055f0e0ee27e5a7ddc382c2c38e commit. + * src/c++17/fast_float/fast_float.h: Likewise. + * testsuite/20_util/from_chars/pr107468.cc: New test. + +2022-11-07 Jakub Jelinek + + * include/std/charconv (from_chars, to_chars): Add _Float128 + overfloads if _GLIBCXX_HAVE_FLOAT128_MATH is defined. + * config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export + _ZSt8to_charsPcS_DF128_, _ZSt8to_charsPcS_DF128_St12chars_format, + _ZSt8to_charsPcS_DF128_St12chars_formati and + _ZSt10from_charsPKcS0_RDF128_St12chars_format. + * src/c++17/floating_from_chars.cc (USE_STRTOF128_FOR_FROM_CHARS): + Define if needed. + (__strtof128): Declare. + (from_chars_impl): Handle _Float128. + (from_chars): New _Float128 overload if USE_STRTOF128_FOR_FROM_CHARS + is define. + * src/c++17/floating_to_chars.cc (__strfromf128): Declare. + (FLOAT128_TO_CHARS): Define even when _Float128 is supported and + wider than long double. + (F128_type): Use _Float128 for that case. + (floating_type_traits): Specialize for F128_type rather than + __float128. + (sprintf_ld): Add length argument. Handle _Float128. + (__floating_to_chars_shortest, __floating_to_chars_precision): + Pass length to sprintf_ld. + (to_chars): Add _Float128 overloads for the F128_type being + _Float128 cases. + * testsuite/20_util/to_chars/float128_c++23.cc: New test. + 2022-11-06 Patrick Palka * include/bits/atomic_wait.h (_detail::__platform_wait_alignment): -- cgit v1.2.1 From c838119946c9f75f1e42f4320275355822cc86fc Mon Sep 17 00:00:00 2001 From: Andrew MacLeod Date: Mon, 7 Nov 2022 15:07:35 -0500 Subject: Add transitive inferred range processing. Rewalk statements at the end of a block to see if any inferred ranges affect earlier calculations and register those as inferred ranges. gcc/ PR tree-optimization/104530 * gimple-range-cache.cc (ranger_cache::register_inferred_value): New. Split from: (ranger_cache::apply_inferred_ranges): Move setting cache to separate function. * gimple-range-cache.h (register_inferred_value): New prototype. * gimple-range-infer.cc (infer_range_manager::has_range_p): New. * gimple-range-infer.h (has_range_p): New prototype. * gimple-range.cc (register_transitive_inferred_ranges): New. * gimple-range.h (register_transitive_inferred_ranges): New proto. * tree-vrp.cc (rvrp_folder::fold_stmt): Check for transitive inferred ranges at the end of the block before folding final stmt. gcc/testsuite/ * gcc.dg/pr104530.c: New. --- gcc/gimple-range-cache.cc | 36 +++++++++++++++++++------------ gcc/gimple-range-cache.h | 1 + gcc/gimple-range-infer.cc | 11 ++++++++++ gcc/gimple-range-infer.h | 1 + gcc/gimple-range.cc | 48 +++++++++++++++++++++++++++++++++++++++++ gcc/gimple-range.h | 1 + gcc/testsuite/gcc.dg/pr104530.c | 19 ++++++++++++++++ gcc/tree-vrp.cc | 9 ++++++++ 8 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr104530.c diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index 89e2403acce..ce5a0c8155e 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -1544,8 +1544,27 @@ ranger_cache::range_from_dom (vrange &r, tree name, basic_block start_bb, return true; } -// This routine is used during a block walk to move the state of non-null for -// any operands on stmt S to nonnull. +// This routine will register an inferred value in block BB, and possibly +// update the on-entry cache if appropriate. + +void +ranger_cache::register_inferred_value (const vrange &ir, tree name, + basic_block bb) +{ + Value_Range r (TREE_TYPE (name)); + if (!m_on_entry.get_bb_range (r, name, bb)) + exit_range (r, name, bb, RFD_READ_ONLY); + if (r.intersect (ir)) + { + m_on_entry.set_bb_range (name, bb, r); + // If this range was invariant before, remove invariance. + if (!m_gori.has_edge_range_p (name)) + m_gori.set_range_invariant (name, false); + } +} + +// This routine is used during a block walk to adjust any inferred ranges +// of operands on stmt S. void ranger_cache::apply_inferred_ranges (gimple *s) @@ -1574,17 +1593,6 @@ ranger_cache::apply_inferred_ranges (gimple *s) tree name = infer.name (x); m_exit.add_range (name, bb, infer.range (x)); if (update) - { - Value_Range r (TREE_TYPE (name)); - if (!m_on_entry.get_bb_range (r, name, bb)) - exit_range (r, name, bb, RFD_READ_ONLY); - if (r.intersect (infer.range (x))) - { - m_on_entry.set_bb_range (name, bb, r); - // If this range was invariant before, remove invariance. - if (!m_gori.has_edge_range_p (name)) - m_gori.set_range_invariant (name, false); - } - } + register_inferred_value (infer.range (x), name, bb); } } diff --git a/gcc/gimple-range-cache.h b/gcc/gimple-range-cache.h index 45053b5873a..8e3ae8f58c6 100644 --- a/gcc/gimple-range-cache.h +++ b/gcc/gimple-range-cache.h @@ -87,6 +87,7 @@ public: void propagate_updated_value (tree name, basic_block bb); + void register_inferred_value (const vrange &r, tree name, basic_block bb); void apply_inferred_ranges (gimple *s); gori_compute m_gori; infer_range_manager m_exit; diff --git a/gcc/gimple-range-infer.cc b/gcc/gimple-range-infer.cc index 010b34a6bde..8714ef2ed41 100644 --- a/gcc/gimple-range-infer.cc +++ b/gcc/gimple-range-infer.cc @@ -252,6 +252,17 @@ infer_range_manager::get_nonzero (tree name) return *(m_nonzero[v]); } +// Return TRUE if there are any range inferences in block BB. + +bool +infer_range_manager::has_range_p (basic_block bb) +{ + if (bb->index >= (int)m_on_exit.length ()) + return false; + bitmap b = m_on_exit[bb->index].m_names; + return b && !bitmap_empty_p (b); +} + // Return TRUE if NAME has a range inference in block BB. bool diff --git a/gcc/gimple-range-infer.h b/gcc/gimple-range-infer.h index adfe1fd8c69..10705e046d3 100644 --- a/gcc/gimple-range-infer.h +++ b/gcc/gimple-range-infer.h @@ -62,6 +62,7 @@ public: void add_range (tree name, basic_block bb, const vrange &r); void add_nonzero (tree name, basic_block bb); bool has_range_p (tree name, basic_block bb); + bool has_range_p (basic_block bb); bool maybe_adjust_range (vrange &r, tree name, basic_block bb); private: class exit_range_head diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index 806386918bd..2885d0fa21e 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -482,6 +482,54 @@ gimple_ranger::register_inferred_ranges (gimple *s) m_cache.apply_inferred_ranges (s); } +// This function will walk the statements in BB to determine if any +// discovered inferred ranges in the block have any transitive effects, +// and if so, register those effects in BB. + +void +gimple_ranger::register_transitive_inferred_ranges (basic_block bb) +{ + // Return if there are no inferred ranges in BB. + infer_range_manager &infer = m_cache.m_exit; + if (!infer.has_range_p (bb)) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Checking for transitive inferred ranges in BB %d\n", + bb->index); + + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); + gsi_next (&si)) + { + gimple *s = gsi_stmt (si); + tree lhs = gimple_get_lhs (s); + // If the LHS alreayd has an inferred effect, leave it be. + if (!gimple_range_ssa_p (lhs) || infer.has_range_p (lhs, bb)) + continue; + // Pick up global value. + Value_Range g (TREE_TYPE (lhs)); + range_of_expr (g, lhs); + + // If either dependency has an inferred range, check if recalculating + // the LHS is different than the global value. If so, register it as + // an inferred range as well. + Value_Range r (TREE_TYPE (lhs)); + r.set_undefined (); + tree name1 = gori ().depend1 (lhs); + tree name2 = gori ().depend2 (lhs); + if ((name1 && infer.has_range_p (name1, bb)) + || (name2 && infer.has_range_p (name2, bb))) + { + // Check if folding S produces a different result. + if (fold_range (r, s, this) && g != r) + { + infer.add_range (lhs, bb, r); + m_cache.register_inferred_value (r, lhs, bb); + } + } + } +} + // When a statement S has changed since the result was cached, re-evaluate // and update the global cache. diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h index 22e05f645f8..dfe8199b8b0 100644 --- a/gcc/gimple-range.h +++ b/gcc/gimple-range.h @@ -62,6 +62,7 @@ public: auto_edge_flag non_executable_edge_flag; bool fold_stmt (gimple_stmt_iterator *gsi, tree (*) (tree)); void register_inferred_ranges (gimple *s); + void register_transitive_inferred_ranges (basic_block bb); protected: bool fold_range_internal (vrange &r, gimple *s, tree name); void prefill_name (vrange &r, tree name); diff --git a/gcc/testsuite/gcc.dg/pr104530.c b/gcc/testsuite/gcc.dg/pr104530.c new file mode 100644 index 00000000000..1ec10154e1b --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr104530.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-evrp" } */ + +void foo(void); + +static int a, *b = &a, c, d = 1; + +int main() { + c = 0 == b; + a = *b; + if (c % d) + for (; d; --d) + foo(); + b = 0; +} + + +/* { dg-final { scan-tree-dump-not "foo" "evrp" } } */ + diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc index 39f7eb7a75e..3393c73a7db 100644 --- a/gcc/tree-vrp.cc +++ b/gcc/tree-vrp.cc @@ -4501,6 +4501,15 @@ public: bool fold_stmt (gimple_stmt_iterator *gsi) override { + gimple *s = gsi_stmt (*gsi); + // If this is a block ending condition, and there are inferred ranges, + // reparse the block to see if there are any transitive inferred ranges. + if (is_a (s)) + { + basic_block bb = gimple_bb (s); + if (bb && s == gimple_outgoing_range_stmt_p (bb)) + m_ranger->register_transitive_inferred_ranges (bb); + } bool ret = m_simplifier.simplify (gsi); if (!ret) ret = m_ranger->fold_stmt (gsi, follow_single_use_edges); -- cgit v1.2.1 From 431be04b8b6e31d950ddab340ed866d197d23d4d Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 4 Nov 2022 15:22:45 -0400 Subject: c++: implement P2468R2, the equality operator you are looking for This paper is resolving the problem of well-formed C++17 code becoming ambiguous in C++20 due to asymmetrical operator== being compared with itself in reverse. I had previously implemented a tiebreaker such that if the two candidates were functions with the same parameter types, we would prefer the non-reversed candidate. But the committee went with a different approach: if there's an operator!= with the same parameter types as the operator==, don't consider the reversed form of the ==. So this patch implements that, and changes my old tiebreaker to give a pedwarn if it is used. I also noticed that we were giving duplicate errors for some testcases, and fixed the tourney logic to avoid that. As a result, a lot of tests of the form struct A { bool operator==(const A&); }; need to be fixed to add a const function-cv-qualifier, e.g. struct A { bool operator==(const A&) const; }; The committee thought such code ought to be fixed, so breaking it was fine. 18_support/comparisons/algorithms/fallback.cc also breaks with this patch, because of the similarly asymmetrical bool operator==(const S&, S&) { return true; } As a result, some of the asserts need to be reversed. The H test in spaceship-eq15.C is specified in the standard to be well-formed because the op!= in the inline namespace is not found by the search, but that seems wrong to me. I've implemented that behavior, but disabled it for now; if we decide that is the way we want to go, we can just remove the "0 &&" in add_candidates to enable it. Co-authored-by: Jakub Jelinek gcc/cp/ChangeLog: * cp-tree.h (fns_correspond): Declare. * decl.cc (fns_correspond): New. * call.cc (add_candidates): Look for op!= matching op==. (joust): Complain about non-standard reversed tiebreaker. (tourney): Fix champ_compared_to_predecessor logic. (build_new_op): Don't complain about error_mark_node not having 'bool' type. * pt.cc (tsubst_copy_and_build): Don't try to be permissive when seen_error(). gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-eq15.C: New test. * g++.dg/cpp0x/defaulted3.C: Add const. * g++.dg/cpp2a/bit-cast7.C: Add const. * g++.dg/cpp2a/spaceship-rewrite1.C: Expect error. * g++.dg/cpp2a/spaceship-rewrite5.C: Expect error. * g++.old-deja/g++.jason/byval2.C: Expect error. * g++.old-deja/g++.other/overload13.C: Add const. libstdc++-v3/ChangeLog: * testsuite/18_support/comparisons/algorithms/fallback.cc: Adjust asserts. --- gcc/cp/call.cc | 109 ++++++++++- gcc/cp/cp-tree.h | 1 + gcc/cp/decl.cc | 66 +++++++ gcc/cp/pt.cc | 5 +- gcc/testsuite/g++.dg/cpp0x/defaulted3.C | 2 +- gcc/testsuite/g++.dg/cpp2a/bit-cast7.C | 4 +- gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C | 208 +++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C | 2 +- gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C | 2 +- gcc/testsuite/g++.old-deja/g++.jason/byval2.C | 2 +- gcc/testsuite/g++.old-deja/g++.other/overload13.C | 2 +- .../18_support/comparisons/algorithms/fallback.cc | 6 +- 12 files changed, 389 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2c0fa37f53a..492db9b59ad 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -6232,6 +6232,7 @@ add_candidates (tree fns, tree first_arg, const vec *args, bool check_list_ctor = false; bool check_converting = false; unification_kind_t strict; + tree ne_fns = NULL_TREE; if (!fns) return; @@ -6269,6 +6270,32 @@ add_candidates (tree fns, tree first_arg, const vec *args, ctype = conversion_path ? BINFO_TYPE (conversion_path) : NULL_TREE; } + /* P2468: Check if operator== is a rewrite target with first operand + (*args)[0]; for now just do the lookups. */ + if ((flags & (LOOKUP_REWRITTEN | LOOKUP_REVERSED)) + && DECL_OVERLOADED_OPERATOR_IS (fn, EQ_EXPR)) + { + tree ne_name = ovl_op_identifier (false, NE_EXPR); + if (DECL_CLASS_SCOPE_P (fn)) + { + ne_fns = lookup_fnfields (TREE_TYPE ((*args)[0]), ne_name, + 1, tf_none); + if (ne_fns == error_mark_node || ne_fns == NULL_TREE) + ne_fns = NULL_TREE; + else + ne_fns = BASELINK_FUNCTIONS (ne_fns); + } + else + { + tree context = decl_namespace_context (fn); + ne_fns = lookup_qualified_name (context, ne_name, LOOK_want::NORMAL, + /*complain*/false); + if (ne_fns == error_mark_node + || !is_overloaded_fn (ne_fns)) + ne_fns = NULL_TREE; + } + } + if (first_arg) non_static_args = args; else @@ -6345,6 +6372,27 @@ add_candidates (tree fns, tree first_arg, const vec *args, continue; } + /* When considering reversed operator==, if there's a corresponding + operator!= in the same scope, it's not a rewrite target. */ + if (ne_fns) + { + bool found = false; + for (lkp_iterator ne (ne_fns); !found && ne; ++ne) + if (0 && !ne.using_p () + && DECL_NAMESPACE_SCOPE_P (fn) + && DECL_CONTEXT (*ne) != DECL_CONTEXT (fn)) + /* ??? This kludge excludes inline namespace members for the H + test in spaceship-eq15.C, but I don't see why we would want + that behavior. Asked Core 2022-11-04. Disabling for now. */; + else if (fns_correspond (fn, *ne)) + { + found = true; + break; + } + if (found) + continue; + } + if (TREE_CODE (fn) == TEMPLATE_DECL) { if (!add_template_candidate (candidates, @@ -6917,10 +6965,12 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, gcc_checking_assert (cand->reversed ()); gcc_fallthrough (); case NE_EXPR: + if (result == error_mark_node) + ; /* If a rewritten operator== candidate is selected by overload resolution for an operator @, its return type shall be cv bool.... */ - if (TREE_CODE (TREE_TYPE (result)) != BOOLEAN_TYPE) + else if (TREE_CODE (TREE_TYPE (result)) != BOOLEAN_TYPE) { if (complain & tf_error) { @@ -12488,10 +12538,53 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, if (winner && comp != winner) { /* Ambiguity between normal and reversed comparison operators - with the same parameter types; prefer the normal one. */ - if ((cand1->reversed () != cand2->reversed ()) + with the same parameter types. P2468 decided not to go with + this approach to resolving the ambiguity, so pedwarn. */ + if ((complain & tf_warning_or_error) + && (cand1->reversed () != cand2->reversed ()) && cand_parms_match (cand1, cand2)) - return cand1->reversed () ? -1 : 1; + { + struct z_candidate *w, *l; + if (cand2->reversed ()) + winner = 1, w = cand1, l = cand2; + else + winner = -1, w = cand2, l = cand1; + if (warn) + { + auto_diagnostic_group d; + if (pedwarn (input_location, 0, + "C++20 says that these are ambiguous, " + "even though the second is reversed:")) + { + print_z_candidate (input_location, + N_("candidate 1:"), w); + print_z_candidate (input_location, + N_("candidate 2:"), l); + if (w->fn == l->fn + && DECL_NONSTATIC_MEMBER_FUNCTION_P (w->fn) + && (type_memfn_quals (TREE_TYPE (w->fn)) + & TYPE_QUAL_CONST) == 0) + { + /* Suggest adding const to + struct A { bool operator==(const A&); }; */ + tree parmtype + = FUNCTION_FIRST_USER_PARMTYPE (w->fn); + parmtype = TREE_VALUE (parmtype); + if (TYPE_REF_P (parmtype) + && TYPE_READONLY (TREE_TYPE (parmtype)) + && (same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (parmtype), + DECL_CONTEXT (w->fn)))) + inform (DECL_SOURCE_LOCATION (w->fn), + "try making the operator a % " + "member function"); + } + } + } + else + add_warning (w, l); + return winner; + } winner = 0; goto tweak; @@ -12880,7 +12973,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) { struct z_candidate *champ = candidates, *challenger; int fate; - int champ_compared_to_predecessor = 0; + struct z_candidate *champ_compared_to_predecessor = nullptr; /* Walk through the list once, comparing each current champ to the next candidate, knocking out a candidate or two with each comparison. */ @@ -12897,12 +12990,12 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) champ = challenger->next; if (champ == 0) return NULL; - champ_compared_to_predecessor = 0; + champ_compared_to_predecessor = nullptr; } else { + champ_compared_to_predecessor = champ; champ = challenger; - champ_compared_to_predecessor = 1; } challenger = champ->next; @@ -12914,7 +13007,7 @@ tourney (struct z_candidate *candidates, tsubst_flags_t complain) for (challenger = candidates; challenger != champ - && !(champ_compared_to_predecessor && challenger->next == champ); + && challenger != champ_compared_to_predecessor; challenger = challenger->next) { fate = joust (champ, challenger, 0, complain); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d13bb3d4c0e..bbc8be21900 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6820,6 +6820,7 @@ extern void note_break_stmt (void); extern bool note_iteration_stmt_body_start (void); extern void note_iteration_stmt_body_end (bool); extern void determine_local_discriminator (tree); +extern bool fns_correspond (tree, tree); extern int decls_match (tree, tree, bool = true); extern bool maybe_version_functions (tree, tree, bool); extern bool merge_default_template_args (tree, tree, bool); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 6e98ea35a39..890cfcabd35 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -980,6 +980,72 @@ function_requirements_equivalent_p (tree newfn, tree oldfn) return cp_tree_equal (reqs1, reqs2); } +/* Two functions of the same name correspond [basic.scope.scope] if + + + both declare functions with the same non-object-parameter-type-list, + equivalent ([temp.over.link]) trailing requires-clauses (if any, except as + specified in [temp.friend]), and, if both are non-static members, they have + corresponding object parameters, or + + + both declare function templates with equivalent + non-object-parameter-type-lists, return types (if any), template-heads, and + trailing requires-clauses (if any), and, if both are non-static members, + they have corresponding object parameters. + + This is a subset of decls_match: it identifies declarations that cannot be + overloaded with one another. This function does not consider DECL_NAME. */ + +bool +fns_correspond (tree newdecl, tree olddecl) +{ + if (TREE_CODE (newdecl) != TREE_CODE (olddecl)) + return false; + + if (TREE_CODE (newdecl) == TEMPLATE_DECL) + { + if (!template_heads_equivalent_p (newdecl, olddecl)) + return 0; + newdecl = DECL_TEMPLATE_RESULT (newdecl); + olddecl = DECL_TEMPLATE_RESULT (olddecl); + } + + tree f1 = TREE_TYPE (newdecl); + tree f2 = TREE_TYPE (olddecl); + + int rq1 = type_memfn_rqual (f1); + int rq2 = type_memfn_rqual (f2); + + /* If only one is a non-static member function, ignore ref-quals. */ + if (TREE_CODE (f1) != TREE_CODE (f2)) + rq1 = rq2; + /* Two non-static member functions have corresponding object parameters if: + + exactly one is an implicit object member function with no ref-qualifier + and the types of their object parameters ([dcl.fct]), after removing + top-level references, are the same, or + + their object parameters have the same type. */ + /* ??? We treat member functions of different classes as corresponding even + though that means the object parameters have different types. */ + else if ((rq1 == REF_QUAL_NONE) != (rq2 == REF_QUAL_NONE)) + rq1 = rq2; + + bool types_match = rq1 == rq2; + + if (types_match) + { + tree p1 = FUNCTION_FIRST_USER_PARMTYPE (newdecl); + tree p2 = FUNCTION_FIRST_USER_PARMTYPE (olddecl); + types_match = compparms (p1, p2); + } + + /* Two function declarations match if either has a requires-clause + then both have a requires-clause and their constraints-expressions + are equivalent. */ + if (types_match && flag_concepts) + types_match = function_requirements_equivalent_p (newdecl, olddecl); + + return types_match; +} + /* Subroutine of duplicate_decls: return truthvalue of whether or not types of these decls match. diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c3fc56a13ff..57917de321f 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -20937,8 +20937,9 @@ tsubst_copy_and_build (tree t, /* In a lambda fn, we have to be careful to not introduce new this captures. Legacy code can't be using lambdas anyway, so it's ok to be - stricter. Be strict with C++20 template-id ADL too. */ - bool strict = in_lambda || template_id_p; + stricter. Be strict with C++20 template-id ADL too. + And be strict if we're already failing anyway. */ + bool strict = in_lambda || template_id_p || seen_error(); bool diag = true; if (strict) error_at (cp_expr_loc_or_input_loc (t), diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted3.C b/gcc/testsuite/g++.dg/cpp0x/defaulted3.C index 75e89c8ff0c..33de973a1fa 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted3.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted3.C @@ -4,7 +4,7 @@ template struct A { template - bool operator==(const A&) = delete; // { dg-message "declared" } + bool operator==(const A&) const = delete; // { dg-message "declared" } operator bool () { return true; } }; diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C index 4a3c6820070..6927db3c961 100644 --- a/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast7.C @@ -16,7 +16,7 @@ struct J struct K { long int a, b : 11, c; - constexpr bool operator == (const K &x) + constexpr bool operator == (const K &x) const { return a == x.a && b == x.b && c == x.c; } @@ -29,7 +29,7 @@ struct L struct M { long long int a, b : 11, c; - constexpr bool operator == (const M &x) + constexpr bool operator == (const M &x) const { return a == x.a && b == x.b && c == x.c; } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C new file mode 100644 index 00000000000..dc509563140 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq15.C @@ -0,0 +1,208 @@ +// P2468R2 - The Equality Operator You Are Looking For +// { dg-do compile { target c++20 } } + +struct A { + bool operator== (const A &) { return true; } + bool operator!= (const A &) { return false; } +}; +bool a = A{} != A{}; + +template +struct B { + bool operator== (const T &) const; + bool operator!= (const T &) const; +}; +struct B1 : B { }; +bool b1 = B1{} == B1{}; +bool b2 = B1{} != B1{}; + +template +struct C { + using C1 = C; + using C2 = C; + C () = default; + C (const C2 &); + bool operator== (C1) const; + bool operator!= (C1) const; +}; +using C3 = C; +bool c = C3{} == C3{}; + +struct D { + D (); + D (int *); + bool operator== (const D &) const; // { dg-message "candidate: 'bool D::operator==\\\(const D&\\\) const' \\\(reversed\\\)" } + operator int * () const; +}; +bool d = nullptr != D{}; // { dg-error "ambiguous overload for 'operator!=' in 'nullptr != D\\\(\\\)' \\\(operand types are 'std::nullptr_t' and 'D'\\\)" } + // { dg-message "candidate: 'operator!=\\\(int\\\*, int\\\*\\\)' \\\(built-in\\\)" "" { target *-*-* } .-1 } + +using ubool = unsigned char; + +struct E { + operator bool () const; +}; +unsigned char operator== (E, E);// { dg-message "candidate: 'unsigned char operator==\\\(E, E\\\)'" } + // { dg-message "no known conversion for argument 1 from 'int' to 'E'" "" { target *-*-* } .-1 } +unsigned char e = E{} != E{}; // { dg-error "return type of 'unsigned char operator==\\\(E, E\\\)' is not 'bool'" } + // { dg-message "used as rewritten candidate for comparison of 'E' and 'E'" "" { target *-*-* } .-1 } + +// F-H are the testcase from [over.match.oper] +struct F {}; +template +bool operator== (F, T); // { dg-message "candidate: 'template bool operator==\\\(F, T\\\)'" } + // { dg-message "template argument deduction/substitution failed:" "" { target *-*-* } .-1 } +bool f1 = 0 == F (); // OK, calls reversed == +template +bool operator!= (F, T); +bool f2 = 0 == F (); // { dg-error "no match for 'operator==' in '0 == F\\\(\\\)' \\\(operand types are 'int' and 'F'\\\)" } + // { dg-message "cannot convert '0' \\\(type 'int'\\\) to type 'F'" "" { target *-*-* } .-1 } + +struct G { + bool operator== (const G &); +}; +struct G1 : G { + G1 (); + G1 (G); + bool operator!= (const G &); +}; +bool g1 = G () == G1 (); // OK, != prevents rewrite +bool g2 = G1 () == G (); // { dg-error "ambiguous, even though the second is reversed" } + +struct H {}; +template +bool operator== (H, T); +inline namespace H1 { + template + bool operator!= (H, T); +} +// [over.match.oper] currently says that this is OK because the inline +// namespace isn't searched, but that seems wrong to me, so I'm going to go +// ahead and search it for now. Remove the "0 &&" in add_candidates to +// change this to the currently specified behavior. +// { dg-error "no match" "" { target *-*-* } .+1 } +bool h = 0 == H (); // OK, calls reversed == + +template +struct I { + int operator== (const double &) const; + friend inline int operator== (const double &, const T &) { return 1; } +}; +struct I1 : I { }; +bool i = I1{} == 0.; // { dg-error "return type of 'int operator==\\\(const double&, const I1&\\\)' is not 'bool'" } + // { dg-message "used as rewritten candidate for comparison of 'I1' and 'double'" "" { target *-*-* } .-1 } + +struct J { + bool operator== (const J &) const; + bool operator!= (const J &) const; +}; +struct J1 : J { + J1 (const J &); + bool operator== (const J1 &x) const { + return static_cast (*this) == x; // { dg-error "ambiguous overload for 'operator==' in '\\\*\\\(const J\\\*\\\)\\\(\\\(const J1\\\*\\\)this\\\) == x' \\\(operand types are 'const J' and 'const J1'\\\)" } + } +}; + +struct K { + bool operator== (const K &); +}; +bool k = K{} == K{}; // { dg-error "ambiguous, even though the second is reversed" } + +struct L { + bool operator== (const L &) const; +}; +bool l = L{} == L{}; + +struct M { + bool operator== (M); +}; +bool m = M () == M (); + +struct N { + virtual bool operator== (const N &) const; +}; +struct N1 : N { + bool operator== (const N &) const override; +}; +bool n = N1 () == N1 (); // { dg-error "ambiguous, even though the second is reversed" } + +struct O { + virtual signed char operator== (const O &) const; + signed char operator!= (const O &x) const { return !operator== (x); } +}; +struct O1 : O { + signed char operator== (const O &) const override; +}; +bool o = O1 () != O1 (); + +template +bool +foo (T x, T y) +requires requires { x == y; } +{ + return x == y; +} +bool b3 = foo (B1 (), B1 ()); + +struct P {}; +template +bool operator== (P, T); +template +bool operator!= (P, T); +bool p = 0 == P (); + +struct Q {}; +template +bool operator== (Q, T); +template +bool operator!= (Q, U); +bool q = 0 == Q (); // { dg-error "" } + +struct R { + template + bool operator== (const T &); +}; +bool r = R () == R (); // { dg-error "ambiguous, even though the second is reversed" } + +struct S { + template + bool operator== (const T &) const; + bool operator!= (const S &); +}; +bool s = S () == S (); + +struct T {}; +template +bool operator== (T, int); +bool operator!= (T, int); +bool t = 0 == T (); + +struct U {}; +bool operator== (U, int); +bool u1 = 0 == U (); +namespace U1 { bool operator!= (U, int); } +bool u2 = 0 == U (); +using U1::operator!=; +bool u3 = 0 == U (); // { dg-error "" } + +struct V {}; +template +bool operator== (V, T); +bool v1 = 0 == V (); +namespace V1 { template bool operator!= (V, T); } +bool v2 = 0 == V (); +using V1::operator!=; +bool v3 = 0 == V (); // { dg-error "" } + +template +struct W { +bool operator== (int) requires (N == 1); +bool operator!= (int) requires (N == 2); +}; +int w = 0 == W<1> (); + +struct X { + bool operator== (X const &); + static bool operator!= (X const &, X const &); // { dg-error "'static bool X::operator!=\\\(const X&, const X&\\\)' must be either a non-static member function or a non-member function" } +}; +bool x = X () == X (); // { dg-error "ambiguous, even though the second is reversed" } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C index c4030cd2f4d..ebe81e4b5e9 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite1.C @@ -11,5 +11,5 @@ int main() { A a1; A a2; - return a1 == a2; + return a1 == a2; // { dg-error "ambiguous, even though the second is reversed" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C index d0424377e8e..460f6332938 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-rewrite5.C @@ -12,4 +12,4 @@ struct A { A a; A b; -auto c = (a == b); +auto c = (a == b); // { dg-error "ambiguous, even though the second is reversed" "" { target c++20 } } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/byval2.C b/gcc/testsuite/g++.old-deja/g++.jason/byval2.C index 40bf2a36528..0575109ed1a 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/byval2.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/byval2.C @@ -18,6 +18,6 @@ inline char operator == (const Char a, const char b) { return 0; } char mystrcmp(Char s[31], Char t[31]) { - for (; *s == *t; ++s, ++t) if (*s == '\0') return 0; + for (; *s == *t; ++s, ++t) if (*s == '\0') return 0; // { dg-error "reversed" "" { target c++20 } } return char(*s - *t); } diff --git a/gcc/testsuite/g++.old-deja/g++.other/overload13.C b/gcc/testsuite/g++.old-deja/g++.other/overload13.C index 54ab404af11..f59bd4a49c3 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/overload13.C +++ b/gcc/testsuite/g++.old-deja/g++.other/overload13.C @@ -2,7 +2,7 @@ // Origin: Nathan Sidwell struct A { - bool operator== (A const &); + bool operator== (A const &) const; operator bool () const; operator int * () const; }; diff --git a/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc b/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc index 05e1bf7775e..8bf78faf232 100644 --- a/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc +++ b/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc @@ -31,12 +31,12 @@ template using adl::S; -static_assert( has_strong_order_fallback ); +static_assert( ! has_strong_order_fallback ); static_assert( has_strong_order_fallback ); static_assert( ! has_strong_order_fallback ); -static_assert( has_weak_order_fallback ); +static_assert( ! has_weak_order_fallback ); static_assert( has_weak_order_fallback ); static_assert( ! has_weak_order_fallback ); -static_assert( has_partial_order_fallback ); +static_assert( ! has_partial_order_fallback ); static_assert( ! has_partial_order_fallback ); // LWG 3465 static_assert( ! has_partial_order_fallback ); -- cgit v1.2.1 From 8d0326943ee4eb87309faca28ee0ed13346dd70a Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Tue, 8 Nov 2022 01:39:52 +0000 Subject: libstdc++: Fix syntax error in old-glibc case in floating_from_chars.cc [PR107562] PR libstdc++/107562 * src/c++17/floating_from_chars.cc (from_chars_impl): Fix syntax error. --- libstdc++-v3/src/c++17/floating_from_chars.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 29eb4634e9d..be1e1051b5c 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -632,7 +632,7 @@ namespace { #ifndef _GLIBCXX_HAVE_FLOAT128_MATH if (&__strtof128 == nullptr) - tmpval = _Float128(std::strtold(str, &endptr); + tmpval = _Float128(std::strtold(str, &endptr)); else #endif tmpval = __strtof128(str, &endptr); -- cgit v1.2.1 From 55e042407ef307764cb9d5a4a06954518e2112b4 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 7 Nov 2022 21:52:30 -0500 Subject: analyzer: fix "when 'strchr' returns non-NULL" message Tweak analyzer handling of strchr, so that we show the when 'strchr' returns non-NULL message for that execution path. gcc/analyzer/ChangeLog: * region-model-impl-calls.cc (region_model::impl_call_strchr): Move to on_call_post. Handle both outcomes using bifurcation, rather than just the "not found" case. * region-model.cc (region_model::on_call_pre): Move BUILT_IN_STRCHR and "strchr" to... (region_model::on_call_post): ...here. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/strchr-1.c (test_literal): Detect writing to a string literal. Verify that we emit the "when '__builtin_strchr' returns non-NULL" message. Signed-off-by: David Malcolm --- gcc/analyzer/region-model-impl-calls.cc | 14 +++++++------- gcc/analyzer/region-model.cc | 14 ++++++++++++-- gcc/testsuite/gcc.dg/analyzer/strchr-1.c | 3 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 30fa765c4b4..46dbbb53bdc 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -1013,7 +1013,7 @@ region_model::impl_call_realloc (const call_details &cd) } } -/* Handle the on_call_pre part of "strchr" and "__builtin_strchr". */ +/* Handle the on_call_post part of "strchr" and "__builtin_strchr". */ void region_model::impl_call_strchr (const call_details &cd) @@ -1075,13 +1075,13 @@ region_model::impl_call_strchr (const call_details &cd) bool m_found; }; - /* Bifurcate state, creating a "not found" out-edge. */ + /* Body of region_model::impl_call_strchr. */ if (cd.get_ctxt ()) - cd.get_ctxt ()->bifurcate (make_unique (cd, false)); - - /* The "unbifurcated" state is the "found" case. */ - strchr_call_info found (cd, true); - found.update_model (this, NULL, cd.get_ctxt ()); + { + cd.get_ctxt ()->bifurcate (make_unique (cd, false)); + cd.get_ctxt ()->bifurcate (make_unique (cd, true)); + cd.get_ctxt ()->terminate_path (); + } } /* Handle the on_call_pre part of "strcpy" and "__builtin_strcpy_chk". */ diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index edf3412a817..e182d2e0e1a 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2223,7 +2223,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt, case BUILT_IN_REALLOC: return false; case BUILT_IN_STRCHR: - impl_call_strchr (cd); + /* Handle in "on_call_post". */ return false; case BUILT_IN_STRCPY: case BUILT_IN_STRCPY_CHK: @@ -2341,7 +2341,7 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt, else if (is_named_call_p (callee_fndecl, "strchr", call, 2) && POINTER_TYPE_P (cd.get_arg_type (0))) { - impl_call_strchr (cd); + /* Handle in "on_call_post". */ return false; } else if (is_named_call_p (callee_fndecl, "strlen", call, 1) @@ -2418,6 +2418,12 @@ region_model::on_call_post (const gcall *call, impl_call_pipe (cd); return; } + else if (is_named_call_p (callee_fndecl, "strchr", call, 2) + && POINTER_TYPE_P (cd.get_arg_type (0))) + { + impl_call_strchr (cd); + return; + } /* Was this fndecl referenced by __attribute__((malloc(FOO)))? */ if (lookup_attribute ("*dealloc", DECL_ATTRIBUTES (callee_fndecl))) @@ -2435,6 +2441,10 @@ region_model::on_call_post (const gcall *call, impl_call_realloc (cd); return; + case BUILT_IN_STRCHR: + impl_call_strchr (cd); + return; + case BUILT_IN_VA_END: impl_call_va_end (cd); return; diff --git a/gcc/testsuite/gcc.dg/analyzer/strchr-1.c b/gcc/testsuite/gcc.dg/analyzer/strchr-1.c index dfe1bc9ea1a..bfa48916ca2 100644 --- a/gcc/testsuite/gcc.dg/analyzer/strchr-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/strchr-1.c @@ -3,12 +3,13 @@ const char* test_literal (int x) { - char *p = __builtin_strchr ("123", x); + char *p = __builtin_strchr ("123", x); /* { dg-message "when '__builtin_strchr' returns non-NULL" } */ if (p) { __analyzer_eval (*p == x); /* { dg-message "UNKNOWN" } */ /* TODO: this ought to be TRUE, but it's unclear that it's worth stashing this constraint. */ + *p = 'A'; /* { dg-warning "write to string literal" } */ } return p; } -- cgit v1.2.1 From be9fdbda1cbcd6a35b05424679c6c059605b61cb Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 7 Nov 2022 21:52:40 -0500 Subject: analyzer: introduce succeed_or_fail_call_info This makes some followup code much cleaner. gcc/analyzer/ChangeLog: * call-info.cc (success_call_info::get_desc): Delete. (failed_call_info::get_desc): Likewise. (succeed_or_fail_call_info::get_desc): New. * call-info.h (class succeed_or_fail_call_info): New. (class success_call_info): Convert to a subclass of succeed_or_fail_call_info. (class failed_call_info): Likewise. Signed-off-by: David Malcolm --- gcc/analyzer/call-info.cc | 21 ++++++--------------- gcc/analyzer/call-info.h | 34 ++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/gcc/analyzer/call-info.cc b/gcc/analyzer/call-info.cc index ffdab73b165..7a1c4edfcae 100644 --- a/gcc/analyzer/call-info.cc +++ b/gcc/analyzer/call-info.cc @@ -141,24 +141,15 @@ call_info::call_info (const call_details &cd) gcc_assert (m_fndecl); } -/* class success_call_info : public call_info. */ - -/* Implementation of call_info::get_desc vfunc for success_call_info. */ - -label_text -success_call_info::get_desc (bool can_colorize) const -{ - return make_label_text (can_colorize, "when %qE succeeds", get_fndecl ()); -} - -/* class failed_call_info : public call_info. */ - -/* Implementation of call_info::get_desc vfunc for failed_call_info. */ +/* class succeed_or_fail_call_info : public call_info. */ label_text -failed_call_info::get_desc (bool can_colorize) const +succeed_or_fail_call_info::get_desc (bool can_colorize) const { - return make_label_text (can_colorize, "when %qE fails", get_fndecl ()); + if (m_success) + return make_label_text (can_colorize, "when %qE succeeds", get_fndecl ()); + else + return make_label_text (can_colorize, "when %qE fails", get_fndecl ()); } } // namespace ana diff --git a/gcc/analyzer/call-info.h b/gcc/analyzer/call-info.h index 4bb7dd7e198..2fd50776f0a 100644 --- a/gcc/analyzer/call-info.h +++ b/gcc/analyzer/call-info.h @@ -51,17 +51,36 @@ private: }; /* Subclass of call_info for a "success" outcome of a call, - adding a "when `FNDECL' succeeds" message. + adding either a + "when `FNDECL' succeeds" message (when 'success' is true) + or a + "when `FNDECL' fails" message (when 'success' is false). This is still abstract: the custom_edge_info::update_model vfunc must be implemented. */ -class success_call_info : public call_info +class succeed_or_fail_call_info : public call_info { public: label_text get_desc (bool can_colorize) const final override; protected: - success_call_info (const call_details &cd) : call_info (cd) {} + succeed_or_fail_call_info (const call_details &cd, bool success) + : call_info (cd), m_success (success) {} + + bool m_success; +}; + +/* Subclass of call_info for a "success" outcome of a call, + adding a "when `FNDECL' succeeds" message. + This is still abstract: the custom_edge_info::update_model vfunc + must be implemented. */ + +class success_call_info : public succeed_or_fail_call_info +{ +protected: + success_call_info (const call_details &cd) + : succeed_or_fail_call_info (cd, true) + {} }; /* Subclass of call_info for a "failure" outcome of a call, @@ -69,13 +88,12 @@ protected: This is still abstract: the custom_edge_info::update_model vfunc must be implemented. */ -class failed_call_info : public call_info +class failed_call_info : public succeed_or_fail_call_info { -public: - label_text get_desc (bool can_colorize) const final override; - protected: - failed_call_info (const call_details &cd) : call_info (cd) {} + failed_call_info (const call_details &cd) + : succeed_or_fail_call_info (cd, false) + {} }; } // namespace ana -- cgit v1.2.1 From 3d2d04cda493fb55ff47b042010943ce2e04cab2 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 7 Nov 2022 21:52:46 -0500 Subject: analyzer: start adding support for errno gcc/analyzer/ChangeLog: * region-model-impl-calls.cc (region_model::impl_call_errno_location): New. * region-model-manager.cc (region_model_manager::region_model_manager): Initialize m_thread_local_region and m_errno_region. * region-model-manager.h (region_model_manager::get_errno_region): New accessor. (region_model_manager::m_thread_local_region): New. (region_model_manager::m_errno_region): New. * region-model.cc (region_model::on_call_pre): Special-case "__errno_location". (region_model::set_errno): New. * region-model.h (impl_call_errno_location): New decl. (region_model::set_errno): New decl. * region.cc (thread_local_region::dump_to_pp): New. (errno_region::dump_to_pp): New. * region.h (enum memory_space): Add MEMSPACE_THREAD_LOCAL. (enum region_kind): Add RK_THREAD_LOCAL and RK_ERRNO. (class thread_local_region): New. (is_a_helper ::test): New. (class errno_region): New. (is_a_helper ::test): New. * store.cc (binding_cluster::escaped_p): New. (store::escaped_p): Treat errno as always having escaped. (store::replay_call_summary_cluster): Handle RK_THREAD_LOCAL and RK_ERRNO. * store.h (binding_cluster::escaped_p): Remove definition. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/errno-1.c: New test. Signed-off-by: David Malcolm --- gcc/analyzer/region-model-impl-calls.cc | 14 ++++++++ gcc/analyzer/region-model-manager.cc | 2 ++ gcc/analyzer/region-model-manager.h | 4 +++ gcc/analyzer/region-model.cc | 22 ++++++++++++ gcc/analyzer/region-model.h | 3 ++ gcc/analyzer/region.cc | 22 ++++++++++++ gcc/analyzer/region.h | 60 ++++++++++++++++++++++++++++++++- gcc/analyzer/store.cc | 17 ++++++++++ gcc/analyzer/store.h | 2 +- gcc/testsuite/gcc.dg/analyzer/errno-1.c | 23 +++++++++++++ 10 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/errno-1.c diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 46dbbb53bdc..bc644f8f3ad 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -413,6 +413,20 @@ region_model::impl_call_calloc (const call_details &cd) } } +/* Handle the on_call_pre part of "__errno_location". */ + +void +region_model::impl_call_errno_location (const call_details &cd) +{ + if (cd.get_lhs_region ()) + { + const region *errno_reg = m_mgr->get_errno_region (); + const svalue *errno_ptr = m_mgr->get_ptr_svalue (cd.get_lhs_type (), + errno_reg); + cd.maybe_set_lhs (errno_ptr); + } +} + /* Handle the on_call_pre part of "error" and "error_at_line" from GNU's non-standard . MIN_ARGS identifies the minimum number of expected arguments diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index de01627bdb6..08bf5d2e758 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -74,6 +74,8 @@ region_model_manager::region_model_manager (logger *logger) m_fndecls_map (), m_labels_map (), m_globals_region (alloc_region_id (), &m_root_region), m_globals_map (), + m_thread_local_region (alloc_region_id (), &m_root_region), + m_errno_region (alloc_region_id (), &m_thread_local_region), m_store_mgr (this), m_range_mgr (new bounded_ranges_manager ()), m_known_fn_mgr (logger) diff --git a/gcc/analyzer/region-model-manager.h b/gcc/analyzer/region-model-manager.h index 3d8f76ee24c..0ff253b624c 100644 --- a/gcc/analyzer/region-model-manager.h +++ b/gcc/analyzer/region-model-manager.h @@ -107,6 +107,7 @@ public: { return &m_globals_region; } + const errno_region *get_errno_region () const { return &m_errno_region; } const function_region *get_region_for_fndecl (tree fndecl); const label_region *get_region_for_label (tree label); const decl_region *get_region_for_global (tree expr); @@ -287,6 +288,9 @@ private: typedef globals_map_t::iterator globals_iterator_t; globals_map_t m_globals_map; + thread_local_region m_thread_local_region; + errno_region m_errno_region; + consolidation_map m_field_regions; consolidation_map m_element_regions; consolidation_map m_offset_regions; diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index e182d2e0e1a..0ca454a0f9c 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2288,6 +2288,11 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt, impl_call_realloc (cd); return false; } + else if (is_named_call_p (callee_fndecl, "__errno_location", call, 0)) + { + impl_call_errno_location (cd); + return false; + } else if (is_named_call_p (callee_fndecl, "error")) { if (impl_call_error (cd, 3, out_terminate_path)) @@ -6418,6 +6423,23 @@ region_model::maybe_complain_about_infoleak (const region *dst_reg, copied_sval)); } +/* Set errno to a positive symbolic int, as if some error has occurred. */ + +void +region_model::set_errno (const call_details &cd) +{ + const region *errno_reg = m_mgr->get_errno_region (); + conjured_purge p (this, cd.get_ctxt ()); + const svalue *new_errno_sval + = m_mgr->get_or_create_conjured_svalue (integer_type_node, + cd.get_call_stmt (), + errno_reg, p); + const svalue *zero + = m_mgr->get_or_create_int_cst (integer_type_node, 0); + add_constraint (new_errno_sval, GT_EXPR, zero, cd.get_ctxt ()); + set_value (errno_reg, new_errno_sval, cd.get_ctxt ()); +} + /* class noop_region_model_context : public region_model_context. */ void diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 5c0bc44a57a..0caaf82936b 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -349,6 +349,7 @@ class region_model void impl_call_analyzer_get_unknown_ptr (const call_details &cd); void impl_call_builtin_expect (const call_details &cd); void impl_call_calloc (const call_details &cd); + void impl_call_errno_location (const call_details &cd); bool impl_call_error (const call_details &cd, unsigned min_args, bool *out_terminate_path); void impl_call_fgets (const call_details &cd); @@ -544,6 +545,8 @@ class region_model const region *src_reg, region_model_context *ctxt); + void set_errno (const call_details &cd); + /* Implemented in sm-fd.cc */ void mark_as_valid_fd (const svalue *sval, region_model_context *ctxt); diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 4bc191848a4..6d97590a83a 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -1050,6 +1050,17 @@ root_region::dump_to_pp (pretty_printer *pp, bool simple) const pp_string (pp, "root_region()"); } +/* class thread_local_region : public space_region. */ + +void +thread_local_region::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + pp_string (pp, "thread_local_region"); + else + pp_string (pp, "thread_local_region()"); +} + /* class symbolic_region : public map_region. */ /* symbolic_region's ctor. */ @@ -1811,6 +1822,17 @@ var_arg_region::get_frame_region () const return as_a (get_parent_region ()); } +/* class errno_region : public region. */ + +void +errno_region::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + pp_string (pp, "errno_region"); + else + pp_string (pp, "errno_region()"); +} + /* class unknown_region : public region. */ /* Implementation of region::dump_to_pp vfunc for unknown_region. */ diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index 6315fac62e5..ecae887edaf 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -34,7 +34,8 @@ enum memory_space MEMSPACE_GLOBALS, MEMSPACE_STACK, MEMSPACE_HEAP, - MEMSPACE_READONLY_DATA + MEMSPACE_READONLY_DATA, + MEMSPACE_THREAD_LOCAL }; /* An enum for discriminating between the different concrete subclasses @@ -49,6 +50,7 @@ enum region_kind RK_LABEL, RK_STACK, RK_HEAP, + RK_THREAD_LOCAL, RK_ROOT, RK_SYMBOLIC, RK_DECL, @@ -62,6 +64,7 @@ enum region_kind RK_STRING, RK_BIT_RANGE, RK_VAR_ARG, + RK_ERRNO, RK_UNKNOWN, }; @@ -77,6 +80,8 @@ enum region_kind code_region (RK_CODE): represents the code segment, containing functions stack_region (RK_STACK): a stack, containing all stack frames heap_region (RK_HEAP): the heap, containing heap_allocated_regions + thread_local_region (RK_THREAD_LOCAL): thread-local data for the thread + being analyzed root_region (RK_ROOT): the top-level region function_region (RK_FUNCTION): the code for a particular function label_region (RK_LABEL): a particular label within a function @@ -102,6 +107,7 @@ enum region_kind within another region var_arg_region (RK_VAR_ARG): a region for the N-th vararg within a frame_region for a variadic call + errno_region (RK_ERRNO): a region for holding "errno" unknown_region (RK_UNKNOWN): for handling unimplemented tree codes. */ /* Abstract base class for representing ways of accessing chunks of memory. @@ -555,6 +561,32 @@ is_a_helper ::test (const region *reg) namespace ana { +/* Concrete space_region subclass: thread-local data for the thread + being analyzed. */ + +class thread_local_region : public space_region +{ +public: + thread_local_region (unsigned id, region *parent) + : space_region (id, parent) + {} + + enum region_kind get_kind () const final override { return RK_THREAD_LOCAL; } + void dump_to_pp (pretty_printer *pp, bool simple) const final override; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper ::test (const region *reg) +{ + return reg->get_kind () == RK_THREAD_LOCAL; +} + +namespace ana { + /* Concrete region subclass. The root region, containing all regions (either directly, or as descendents). Unique within a region_model_manager. */ @@ -1362,6 +1394,32 @@ template <> struct default_hash_traits namespace ana { +/* A region for errno for the current thread. */ + +class errno_region : public region +{ +public: + errno_region (unsigned id, const thread_local_region *parent) + : region (complexity (parent), id, parent, integer_type_node) + {} + + enum region_kind get_kind () const final override { return RK_ERRNO; } + + void dump_to_pp (pretty_printer *pp, bool simple) const final override; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper ::test (const region *reg) +{ + return reg->get_kind () == RK_ERRNO; +} + +namespace ana { + /* An unknown region, for handling unimplemented tree codes. */ class unknown_region : public region diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index c0f5ed104e1..636d4aa52da 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -2036,6 +2036,17 @@ binding_cluster::on_asm (const gasm *stmt, m_touched = true; } +/* Return true if this cluster has escaped. */ + +bool +binding_cluster::escaped_p () const +{ + /* Consider the "errno" region to always have escaped. */ + if (m_base_region->get_kind () == RK_ERRNO) + return true; + return m_escaped; +} + /* Return true if this binding_cluster has no information i.e. if there are no bindings, and it hasn't been marked as having escaped, or touched symbolically. */ @@ -2946,6 +2957,10 @@ store::escaped_p (const region *base_reg) const gcc_assert (base_reg); gcc_assert (base_reg->get_base_region () == base_reg); + /* "errno" can always be modified by external code. */ + if (base_reg->get_kind () == RK_ERRNO) + return true; + if (binding_cluster **cluster_slot = const_cast (m_cluster_map).get (base_reg)) return (*cluster_slot)->escaped_p (); @@ -3192,6 +3207,7 @@ store::replay_call_summary_cluster (call_summary_replay &r, case RK_CODE: case RK_STACK: case RK_HEAP: + case RK_THREAD_LOCAL: case RK_ROOT: /* Child regions. */ case RK_FIELD: @@ -3242,6 +3258,7 @@ store::replay_call_summary_cluster (call_summary_replay &r, case RK_HEAP_ALLOCATED: case RK_DECL: + case RK_ERRNO: { const region *caller_dest_reg = r.convert_region_from_summary (summary_base_reg); diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index 1087782a326..6243ec65ea1 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -644,7 +644,7 @@ public: void on_asm (const gasm *stmt, store_manager *mgr, const conjured_purge &p); - bool escaped_p () const { return m_escaped; } + bool escaped_p () const; bool touched_p () const { return m_touched; } bool redundant_p () const; diff --git a/gcc/testsuite/gcc.dg/analyzer/errno-1.c b/gcc/testsuite/gcc.dg/analyzer/errno-1.c new file mode 100644 index 00000000000..6b9d28c1079 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/errno-1.c @@ -0,0 +1,23 @@ +#include +#include "analyzer-decls.h" + +extern void external_fn (void); + +int test_reading_errno (void) +{ + return errno; +} + +void test_setting_errno (int val) +{ + errno = val; +} + +void test_storing_to_errno (int val) +{ + __analyzer_eval (errno == val); /* { dg-warning "UNKNOWN" } */ + errno = val; + __analyzer_eval (errno == val); /* { dg-warning "TRUE" } */ + external_fn (); + __analyzer_eval (errno == val); /* { dg-warning "UNKNOWN" } */ +} -- cgit v1.2.1 From a14598bf86f6950012e3d68cff14fcceec566ef7 Mon Sep 17 00:00:00 2001 From: Haochen Jiang Date: Tue, 8 Nov 2022 10:58:36 +0800 Subject: Add m_CORE_ATOM for atom cores gcc/ChangeLog: * config/i386/i386-options.cc (m_CORE_ATOM): New. * config/i386/x86-tune.def (X86_TUNE_SCHEDULE): Initial tune for CORE_ATOM. (X86_TUNE_PARTIAL_REG_DEPENDENCY): Ditto. (X86_TUNE_SSE_PARTIAL_REG_DEPENDENCY): Ditto. (X86_TUNE_SSE_PARTIAL_REG_FP_CONVERTS_DEPENDENCY): Ditto. (X86_TUNE_SSE_PARTIAL_REG_CONVERTS_DEPENDENCY): Ditto. (X86_TUNE_DEST_FALSE_DEP_FOR_GLC): Ditto. (X86_TUNE_MEMORY_MISMATCH_STALL): Ditto. (X86_TUNE_USE_LEAVE): Ditto. (X86_TUNE_PUSH_MEMORY): Ditto. (X86_TUNE_USE_INCDEC): Ditto. (X86_TUNE_INTEGER_DFMODE_MOVES): Ditto. (X86_TUNE_PREFER_KNOWN_REP_MOVSB_STOSB): Ditto. (X86_TUNE_MISALIGNED_MOVE_STRING_PRO_EPILOGUES): Ditto. (X86_TUNE_USE_SAHF): Ditto. (X86_TUNE_USE_BT): Ditto. (X86_TUNE_AVOID_FALSE_DEP_FOR_BMI): Ditto. (X86_TUNE_ONE_IF_CONV_INSN): Ditto. (X86_TUNE_AVOID_MFENCE): Ditto. (X86_TUNE_USE_SIMODE_FIOP): Ditto. (X86_TUNE_EXT_80387_CONSTANTS): Ditto. (X86_TUNE_SSE_UNALIGNED_LOAD_OPTIMAL): Ditto. (X86_TUNE_SSE_UNALIGNED_STORE_OPTIMAL): Ditto. (X86_TUNE_SSE_TYPELESS_STORES): Ditto. (X86_TUNE_SSE_LOAD0_BY_PXOR): Ditto. (X86_TUNE_AVOID_4BYTE_PREFIXES): Ditto. (X86_TUNE_USE_GATHER_2PARTS): Ditto. (X86_TUNE_USE_GATHER_4PARTS): Ditto. (X86_TUNE_USE_GATHER): Ditto. --- gcc/config/i386/i386-options.cc | 1 + gcc/config/i386/x86-tune.def | 71 +++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc index 23ab1f867d0..e5c77f3a84d 100644 --- a/gcc/config/i386/i386-options.cc +++ b/gcc/config/i386/i386-options.cc @@ -139,6 +139,7 @@ along with GCC; see the file COPYING3. If not see #define m_TREMONT (HOST_WIDE_INT_1U<> (W-1) ^ x) - @@ -372,7 +379,7 @@ DEF_TUNE (X86_TUNE_USE_SIMODE_FIOP, "use_simode_fiop", ~(m_PENT | m_LAKEMONT | m_PPRO | m_CORE_ALL | m_BONNELL | m_SILVERMONT | m_KNL | m_KNM | m_INTEL | m_AMD_MULTIPLE | m_LUJIAZUI | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT - | m_ALDERLAKE | m_GENERIC)) + | m_ALDERLAKE | m_CORE_ATOM | m_GENERIC)) /* X86_TUNE_USE_FFREEP: Use freep instruction instead of fstp. */ DEF_TUNE (X86_TUNE_USE_FFREEP, "use_ffreep", m_AMD_MULTIPLE | m_LUJIAZUI) @@ -381,7 +388,8 @@ DEF_TUNE (X86_TUNE_USE_FFREEP, "use_ffreep", m_AMD_MULTIPLE | m_LUJIAZUI) DEF_TUNE (X86_TUNE_EXT_80387_CONSTANTS, "ext_80387_constants", m_PPRO | m_P4_NOCONA | m_CORE_ALL | m_BONNELL | m_SILVERMONT | m_KNL | m_KNM | m_INTEL | m_K6_GEODE | m_ATHLON_K8 | m_LUJIAZUI - | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT | m_ALDERLAKE | m_GENERIC) + | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT | m_ALDERLAKE | m_CORE_ATOM + | m_GENERIC) /*****************************************************************************/ /* SSE instruction selection tuning */ @@ -397,14 +405,15 @@ DEF_TUNE (X86_TUNE_GENERAL_REGS_SSE_SPILL, "general_regs_sse_spill", DEF_TUNE (X86_TUNE_SSE_UNALIGNED_LOAD_OPTIMAL, "sse_unaligned_load_optimal", m_NEHALEM | m_SANDYBRIDGE | m_CORE_AVX2 | m_SILVERMONT | m_KNL | m_KNM | m_INTEL | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT | m_ALDERLAKE - | m_AMDFAM10 | m_BDVER | m_BTVER | m_ZNVER | m_LUJIAZUI | m_GENERIC) + | m_CORE_ATOM | m_AMDFAM10 | m_BDVER | m_BTVER | m_ZNVER | m_LUJIAZUI + | m_GENERIC) /* X86_TUNE_SSE_UNALIGNED_STORE_OPTIMAL: Use movups for misaligned stores instead of a sequence loading registers by parts. */ DEF_TUNE (X86_TUNE_SSE_UNALIGNED_STORE_OPTIMAL, "sse_unaligned_store_optimal", m_NEHALEM | m_SANDYBRIDGE | m_CORE_AVX2 | m_SILVERMONT | m_KNL | m_KNM | m_INTEL | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT | m_ALDERLAKE - | m_BDVER | m_ZNVER | m_LUJIAZUI | m_GENERIC) + | m_CORE_ATOM | m_BDVER | m_ZNVER | m_LUJIAZUI | m_GENERIC) /* X86_TUNE_SSE_PACKED_SINGLE_INSN_OPTIMAL: Use packed single precision 128bit instructions instead of double where possible. */ @@ -414,13 +423,13 @@ DEF_TUNE (X86_TUNE_SSE_PACKED_SINGLE_INSN_OPTIMAL, "sse_packed_single_insn_optim /* X86_TUNE_SSE_TYPELESS_STORES: Always movaps/movups for 128bit stores. */ DEF_TUNE (X86_TUNE_SSE_TYPELESS_STORES, "sse_typeless_stores", m_AMD_MULTIPLE | m_LUJIAZUI | m_CORE_ALL | m_TREMONT | m_ALDERLAKE - | m_GENERIC) + | m_CORE_ATOM | m_GENERIC) /* X86_TUNE_SSE_LOAD0_BY_PXOR: Always use pxor to load0 as opposed to xorps/xorpd and other variants. */ DEF_TUNE (X86_TUNE_SSE_LOAD0_BY_PXOR, "sse_load0_by_pxor", m_PPRO | m_P4_NOCONA | m_CORE_ALL | m_BDVER | m_BTVER | m_ZNVER - | m_LUJIAZUI | m_TREMONT | m_ALDERLAKE | m_GENERIC) + | m_LUJIAZUI | m_TREMONT | m_ALDERLAKE | m_CORE_ATOM | m_GENERIC) /* X86_TUNE_INTER_UNIT_MOVES_TO_VEC: Enable moves in from integer to SSE registers. If disabled, the moves will be done by storing @@ -467,22 +476,22 @@ DEF_TUNE (X86_TUNE_SLOW_PSHUFB, "slow_pshufb", /* X86_TUNE_AVOID_4BYTE_PREFIXES: Avoid instructions requiring 4+ bytes of prefixes. */ DEF_TUNE (X86_TUNE_AVOID_4BYTE_PREFIXES, "avoid_4byte_prefixes", m_SILVERMONT | m_GOLDMONT | m_GOLDMONT_PLUS | m_TREMONT | m_ALDERLAKE - | m_INTEL) + | m_CORE_ATOM | m_INTEL) /* X86_TUNE_USE_GATHER_2PARTS: Use gather instructions for vectors with 2 elements. */ DEF_TUNE (X86_TUNE_USE_GATHER_2PARTS, "use_gather_2parts", - ~(m_ZNVER1 | m_ZNVER2 | m_ZNVER3 | m_ALDERLAKE | m_GENERIC)) + ~(m_ZNVER1 | m_ZNVER2 | m_ZNVER3 | m_ALDERLAKE | m_CORE_ATOM | m_GENERIC)) /* X86_TUNE_USE_GATHER_4PARTS: Use gather instructions for vectors with 4 elements. */ DEF_TUNE (X86_TUNE_USE_GATHER_4PARTS, "use_gather_4parts", - ~(m_ZNVER1 | m_ZNVER2 | m_ZNVER3 | m_ALDERLAKE | m_GENERIC)) + ~(m_ZNVER1 | m_ZNVER2 | m_ZNVER3 | m_ALDERLAKE | m_CORE_ATOM | m_GENERIC)) /* X86_TUNE_USE_GATHER: Use gather instructions for vectors with 8 or more elements. */ DEF_TUNE (X86_TUNE_USE_GATHER, "use_gather", - ~(m_ZNVER1 | m_ZNVER2 | m_ALDERLAKE | m_GENERIC)) + ~(m_ZNVER1 | m_ZNVER2 | m_ALDERLAKE | m_CORE_ATOM | m_GENERIC)) /* X86_TUNE_AVOID_128FMA_CHAINS: Avoid creating loops with tight 128bit or smaller FMA chain. */ -- cgit v1.2.1 From 1f7b13005040a5ad1ea0670dcd6a7b0842248978 Mon Sep 17 00:00:00 2001 From: konglin1 Date: Tue, 8 Nov 2022 10:58:36 +0800 Subject: Revert "i386: Prefer remote atomic insn for atomic_fetch{add, and, or, xor}" This reverts commit 48fa4131e419942efc9dd762694fdc7e819de392. --- gcc/config/i386/i386.opt | 4 --- gcc/config/i386/sync.md | 27 +++----------------- gcc/doc/invoke.texi | 6 +---- .../gcc.target/i386/raoint-atomic-fetch.c | 29 ---------------------- 4 files changed, 4 insertions(+), 62 deletions(-) delete mode 100644 gcc/testsuite/gcc.target/i386/raoint-atomic-fetch.c diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt index abb1e5ecbdc..415c52e1bb4 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -1246,7 +1246,3 @@ Support PREFETCHI built-in functions and code generation. mraoint Target Mask(ISA2_RAOINT) Var(ix86_isa_flags2) Save Support RAOINT built-in functions and code generation. - -mprefer-remote-atomic -Target Var(flag_prefer_remote_atomic) Init(0) -Prefer use remote atomic insn for atomic operations. diff --git a/gcc/config/i386/sync.md b/gcc/config/i386/sync.md index 250899160c3..e6543a5efb0 100644 --- a/gcc/config/i386/sync.md +++ b/gcc/config/i386/sync.md @@ -791,28 +791,7 @@ (define_code_iterator any_plus_logic [and ior xor plus]) (define_code_attr plus_logic [(and "and") (ior "or") (xor "xor") (plus "add")]) -(define_expand "atomic_" - [(match_operand:SWI 0 "memory_operand") - (any_plus_logic:SWI (match_dup 0) - (match_operand:SWI 1 "nonmemory_operand")) - (match_operand:SI 2 "const_int_operand")] - "" -{ - if (flag_prefer_remote_atomic - && TARGET_RAOINT && operands[2] == const0_rtx - && (mode == SImode || mode == DImode)) - { - if (CONST_INT_P (operands[1])) - operands[1] = force_reg (mode, operands[1]); - emit_insn (maybe_gen_rao_a (, mode, operands[0], operands[1])); - } - else - emit_insn (gen_atomic__1 (operands[0], operands[1], - operands[2])); - DONE; -}) - -(define_insn "@rao_a" +(define_insn "rao_a" [(set (match_operand:SWI48 0 "memory_operand" "+m") (unspec_volatile:SWI48 [(any_plus_logic:SWI48 (match_dup 0) @@ -822,7 +801,7 @@ "TARGET_RAOINT" "a\t{%1, %0|%0, %1}") -(define_insn "atomic_add_1" +(define_insn "atomic_add" [(set (match_operand:SWI 0 "memory_operand" "+m") (unspec_volatile:SWI [(plus:SWI (match_dup 0) @@ -876,7 +855,7 @@ return "lock{%;} %K2sub{}\t{%1, %0|%0, %1}"; }) -(define_insn "atomic__1" +(define_insn "atomic_" [(set (match_operand:SWI 0 "memory_operand" "+m") (unspec_volatile:SWI [(any_logic:SWI (match_dup 0) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 94a2e20cfc1..975ee64103f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1438,7 +1438,7 @@ See RS/6000 and PowerPC Options. -mrdseed -msgx -mavx512vp2intersect -mserialize -mtsxldtrk@gol -mamx-tile -mamx-int8 -mamx-bf16 -muintr -mhreset -mavxvnni@gol -mavx512fp16 -mavxifma -mavxvnniint8 -mavxneconvert -mcmpccxadd -mamx-fp16 @gol --mprefetchi -mraoint -mprefer-remote-atomic@gol +-mprefetchi -mraoint @gol -mcldemote -mms-bitfields -mno-align-stringops -minline-all-stringops @gol -minline-stringops-dynamically -mstringop-strategy=@var{alg} @gol -mkl -mwidekl @gol @@ -33676,10 +33676,6 @@ execute pause if load value is not expected. This reduces excessive cachline bouncing when and works for all atomic logic fetch builtins that generates compare and swap loop. -@item -mprefer-remote-atomic -@opindex mprefer-remote-atomic -Prefer use remote atomic insn for atomic operations. - @item -mindirect-branch=@var{choice} @opindex mindirect-branch Convert indirect call and jump with @var{choice}. The default is diff --git a/gcc/testsuite/gcc.target/i386/raoint-atomic-fetch.c b/gcc/testsuite/gcc.target/i386/raoint-atomic-fetch.c deleted file mode 100644 index ac4099d888e..00000000000 --- a/gcc/testsuite/gcc.target/i386/raoint-atomic-fetch.c +++ /dev/null @@ -1,29 +0,0 @@ -/* { dg-do compile } */ -/* { dg-options "-mraoint -O2 -mprefer-remote-atomic" } */ -/* { dg-final { scan-assembler-times "aadd" 2 { target {! ia32 } } } } */ -/* { dg-final { scan-assembler-times "aand" 2 { target {! ia32 } } } } */ -/* { dg-final { scan-assembler-times "aor" 2 { target {! ia32 } } } } */ -/* { dg-final { scan-assembler-times "axor" 2 { target {! ia32 } } } } */ -/* { dg-final { scan-assembler-times "aadd" 1 { target ia32 } } } */ -/* { dg-final { scan-assembler-times "aand" 1 { target ia32 } } } */ -/* { dg-final { scan-assembler-times "aor" 1 { target ia32 } } } */ -/* { dg-final { scan-assembler-times "axor" 1 { target ia32 } } } */ -volatile int x; -volatile long long y; -int *a; -long long *b; - -void extern -rao_int_test (void) -{ - __atomic_add_fetch (a, x, __ATOMIC_RELAXED); - __atomic_and_fetch (a, x, __ATOMIC_RELAXED); - __atomic_or_fetch (a, x, __ATOMIC_RELAXED); - __atomic_xor_fetch (a, x, __ATOMIC_RELAXED); -#ifdef __x86_64__ - __atomic_add_fetch (b, y, __ATOMIC_RELAXED); - __atomic_and_fetch (b, y, __ATOMIC_RELAXED); - __atomic_or_fetch (b, y, __ATOMIC_RELAXED); - __atomic_xor_fetch (b, y, __ATOMIC_RELAXED); -#endif -} -- cgit v1.2.1 From 4596339d9fabdcbd66b5a7430fa56544f75ecef1 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 7 Nov 2022 22:27:08 +0000 Subject: libstdc++: Remove empty elements in manual This fixes a spurious comma before the list of authors in the PDF version of the libstdc++ manual. Also fix the commented-out examples which should show not . libstdc++-v3/ChangeLog: * doc/xml/authors.xml: Remove empty author element. * doc/xml/manual/spine.xml: Likewise. * doc/html/manual/index.html: Regenerate. --- libstdc++-v3/doc/html/manual/index.html | 2 +- libstdc++-v3/doc/xml/authors.xml | 9 ++------- libstdc++-v3/doc/xml/manual/spine.xml | 9 ++------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/libstdc++-v3/doc/html/manual/index.html b/libstdc++-v3/doc/html/manual/index.html index 292afde6051..e5441528890 100644 --- a/libstdc++-v3/doc/html/manual/index.html +++ b/libstdc++-v3/doc/html/manual/index.html @@ -1,5 +1,5 @@ -The GNU C++ Library Manual

The GNU C++ Library Manual

Paolo Carlini

Phil Edwards

Doug Gregor

Benjamin Kosnik

Dhruv Matani

Jason Merrill

Mark Mitchell

Nathan Myers

Felix Natter

Stefan Olsson

Johannes Singler

Ami Tavory

Jonathan Wakely

The GNU C++ Library Manual

Paolo Carlini

Phil Edwards

Doug Gregor

Benjamin Kosnik

Dhruv Matani

Jason Merrill

Mark Mitchell

Nathan Myers

Felix Natter

Stefan Olsson

Johannes Singler

Ami Tavory

Jonathan Wakely


Table of Contents

I. Introduction diff --git a/libstdc++-v3/doc/xml/authors.xml b/libstdc++-v3/doc/xml/authors.xml index 0a120b508e9..95626d46cba 100644 --- a/libstdc++-v3/doc/xml/authors.xml +++ b/libstdc++-v3/doc/xml/authors.xml @@ -13,18 +13,13 @@ - + - + --> - - - - - PaoloCarlini TR1, LWG Active, Closed, Defects lists. diff --git a/libstdc++-v3/doc/xml/manual/spine.xml b/libstdc++-v3/doc/xml/manual/spine.xml index 34daa33344c..eff72bc13d7 100644 --- a/libstdc++-v3/doc/xml/manual/spine.xml +++ b/libstdc++-v3/doc/xml/manual/spine.xml @@ -48,18 +48,13 @@ - + - + --> - - - - - PaoloCarlini TR1, LWG Active, Closed, Defects lists. -- cgit v1.2.1 From acbfa2bc6006dacda86e3fa53d545e89b66a9533 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 7 Nov 2022 22:52:31 +0000 Subject: libstdc++: Update my author blurb in the manual libstdc++-v3/ChangeLog: * doc/xml/authors.xml: Update the blurb listing my doc contributions. --- libstdc++-v3/doc/xml/authors.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libstdc++-v3/doc/xml/authors.xml b/libstdc++-v3/doc/xml/authors.xml index 95626d46cba..ee8e835f692 100644 --- a/libstdc++-v3/doc/xml/authors.xml +++ b/libstdc++-v3/doc/xml/authors.xml @@ -102,7 +102,10 @@ JonathanWakely - shared_ptr, markup editing and styling + Allocators, ABI History, API Evolution and Deprecation History, + Experimental Library Extensions, Implementation Status, + Running the Testsuite, Shared Library Versioning, Using Macros, + Using Exceptions, shared_ptr, markup editing and styling. -- cgit v1.2.1 From ae5de5a32798995bcc61099f3021ac28e8614bea Mon Sep 17 00:00:00 2001 From: Bob Duff Date: Tue, 18 Oct 2022 08:33:47 -0400 Subject: ada: Add new -gnatw_q switch to usage message ...along with -gnatw_Q. gcc/ada/ * usage.adb: Add -gnatw_q and -gnatw_Q. --- gcc/ada/usage.adb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gcc/ada/usage.adb b/gcc/ada/usage.adb index 7d11ae53696..642cfda1b27 100644 --- a/gcc/ada/usage.adb +++ b/gcc/ada/usage.adb @@ -567,6 +567,10 @@ begin "record types"); Write_Line (" .Q* turn off warnings for questionable layout of " & "record types"); + Write_Line (" _q turn on warnings for ignored " & + "equality operators"); + Write_Line (" _Q* turn off warnings for ignored " & + "equality operators"); Write_Line (" r+ turn on warnings for redundant construct"); Write_Line (" R* turn off warnings for redundant construct"); Write_Line (" .r+ turn on warnings for object renaming function"); -- cgit v1.2.1 From 59dd07ef2534c00f0144431bf54d8219ebb91218 Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Tue, 18 Oct 2022 14:31:00 +0200 Subject: ada: Raise Tag_Error when Ada.Tags operations are called with No_Tag Implement missing behavior of RM 13.9 (25.1/3): Tag_Error is raised by a call of Interface_Ancestor_Tags and Is_Descendant_At_Same_Level, if any tag passed is No_Tag. This change also fixes Descendant_Tag, which relies on Is_Descendant_At_Same_Level. The remaining operations already worked properly. gcc/ada/ * libgnat/a-tags.adb (Interface_Ancestor_Tags): Raise Tag_Error on No_Tag. (Is_Descendant_At_Same_Level): Likewise. --- gcc/ada/libgnat/a-tags.adb | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/gcc/ada/libgnat/a-tags.adb b/gcc/ada/libgnat/a-tags.adb index d663a41f5a8..a9af942fc64 100644 --- a/gcc/ada/libgnat/a-tags.adb +++ b/gcc/ada/libgnat/a-tags.adb @@ -554,13 +554,18 @@ package body Ada.Tags is ----------------------------- function Interface_Ancestor_Tags (T : Tag) return Tag_Array is - TSD_Ptr : constant Addr_Ptr := - To_Addr_Ptr (To_Address (T) - DT_Typeinfo_Ptr_Size); - TSD : constant Type_Specific_Data_Ptr := - To_Type_Specific_Data_Ptr (TSD_Ptr.all); - Iface_Table : constant Interface_Data_Ptr := TSD.Interfaces_Table; - + TSD_Ptr : Addr_Ptr; + TSD : Type_Specific_Data_Ptr; + Iface_Table : Interface_Data_Ptr; begin + if T = No_Tag then + raise Tag_Error; + end if; + + TSD_Ptr := To_Addr_Ptr (To_Address (T) - DT_Typeinfo_Ptr_Size); + TSD := To_Type_Specific_Data_Ptr (TSD_Ptr.all); + Iface_Table := TSD.Interfaces_Table; + if Iface_Table = null then declare Table : Tag_Array (1 .. 0); @@ -731,7 +736,10 @@ package body Ada.Tags is Ancestor : Tag) return Boolean is begin - if Descendant = Ancestor then + if Descendant = No_Tag or else Ancestor = No_Tag then + raise Tag_Error; + + elsif Descendant = Ancestor then return True; else -- cgit v1.2.1 From b9d8ad7175359f7a6dfd803fcc3b3f619301e734 Mon Sep 17 00:00:00 2001 From: Javier Miranda Date: Sat, 15 Oct 2022 16:29:38 +0000 Subject: ada: Missing master of task causing assertion failure gcc/ada/ * exp_ch9.adb (Build_Master_Entity): Handle missing case: when the context of the master is a BIP function whose result type has tasks. --- gcc/ada/exp_ch9.adb | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/gcc/ada/exp_ch9.adb b/gcc/ada/exp_ch9.adb index decf61782af..70ede15901e 100644 --- a/gcc/ada/exp_ch9.adb +++ b/gcc/ada/exp_ch9.adb @@ -3207,10 +3207,45 @@ package body Exp_Ch9 is Find_Enclosing_Context (Par, Context, Context_Id, Decls); end if; + -- When the enclosing context is a BIP function whose result type has + -- tasks, the function has an extra formal that is the master of the + -- tasks to be created by its returned object (that is, when its + -- enclosing context is a return statement). However, if the body of + -- the function creates tasks before its return statements, such tasks + -- need their own master. + + if Has_Master_Entity (Context_Id) + and then Ekind (Context_Id) = E_Function + and then Is_Build_In_Place_Function (Context_Id) + and then Needs_BIP_Task_Actuals (Context_Id) + then + -- No need to add it again if previously added + + declare + Master_Present : Boolean; + + begin + -- Handle transient scopes + + if Context_Id /= Current_Scope then + Push_Scope (Context_Id); + Master_Present := + Present (Current_Entity_In_Scope (Name_uMaster)); + Pop_Scope; + else + Master_Present := + Present (Current_Entity_In_Scope (Name_uMaster)); + end if; + + if Master_Present then + return; + end if; + end; + -- Nothing to do if the context already has a master; internally built -- finalizers don't need a master. - if Has_Master_Entity (Context_Id) + elsif Has_Master_Entity (Context_Id) or else Is_Finalizer (Context_Id) then return; -- cgit v1.2.1 From 4a22fdac0f6e79478768e6792c9804816ecb3f4a Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Thu, 14 Oct 2021 12:48:12 +0200 Subject: ada: Reject record delta aggregates with limited expressions Implement a missing check related to record delta aggregates. gcc/ada/ * sem_aggr.adb (Resolve_Delta_Record_Aggregate): Reject expressions of a limited types. --- gcc/ada/sem_aggr.adb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gcc/ada/sem_aggr.adb b/gcc/ada/sem_aggr.adb index 31ce9cadd94..383f18f7301 100644 --- a/gcc/ada/sem_aggr.adb +++ b/gcc/ada/sem_aggr.adb @@ -3746,7 +3746,17 @@ package body Sem_Aggr is ("'<'> in record delta aggregate is not allowed", Assoc); else Analyze_And_Resolve (Expression (Assoc), Comp_Type); + + -- The expression must not be of a limited type; RM 4.3.1(17.4/5) + + if Is_Limited_Type (Etype (Expression (Assoc))) then + Error_Msg_N + ("expression of a limited type in record delta aggregate " & + "is not allowed", + Expression (Assoc)); + end if; end if; + Next (Assoc); end loop; end Resolve_Delta_Record_Aggregate; -- cgit v1.2.1 From 2ff87e21c2053fe3a48ced3e2cc9ab1b900aceaa Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Thu, 14 Oct 2021 23:24:54 +0200 Subject: ada: Allow initialization of limited objects with delta aggregates Objects of a limited type can be initialized with "aggregates", which is a collective term for ordinary aggregates (i.e. record aggregates and array aggregates), extension aggregates and finally for delta aggregates (introduced by Ada 2022). gcc/ada/ * sem_ch3.adb (OK_For_Limited_Init_In_05): Handle delta aggregates just like other aggregates. --- gcc/ada/sem_ch3.adb | 1 + 1 file changed, 1 insertion(+) diff --git a/gcc/ada/sem_ch3.adb b/gcc/ada/sem_ch3.adb index 76dc6325060..f6b852051dc 100644 --- a/gcc/ada/sem_ch3.adb +++ b/gcc/ada/sem_ch3.adb @@ -20145,6 +20145,7 @@ package body Sem_Ch3 is case Nkind (Original_Node (Exp)) is when N_Aggregate + | N_Delta_Aggregate | N_Extension_Aggregate | N_Function_Call | N_Op -- cgit v1.2.1 From 79e02673e97d1a359ca1fc2dc3f6d51d0debe7d8 Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Thu, 14 Oct 2021 23:31:21 +0200 Subject: ada: Reject limited objects in array and record delta aggregates For array delta aggregates the base expression cannot be limited; for record delta aggregates the base expression can only be limited if it is a newly constructed object. gcc/ada/ * sem_aggr.adb (Resolve_Delta_Aggregate): Implement rules related to limited objects appearing as the base expression. --- gcc/ada/sem_aggr.adb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gcc/ada/sem_aggr.adb b/gcc/ada/sem_aggr.adb index 383f18f7301..4da05dd7317 100644 --- a/gcc/ada/sem_aggr.adb +++ b/gcc/ada/sem_aggr.adb @@ -3421,6 +3421,18 @@ package body Sem_Aggr is Analyze_And_Resolve (Base, Typ); if Is_Array_Type (Typ) then + -- For an array_delta_aggregate, the base_expression and each + -- expression in every array_component_association shall be of a + -- nonlimited type; RM 4.3.4(13/5). However, to prevent repeated + -- errors we only check the base expression and not array component + -- associations. + + if Is_Limited_Type (Etype (Base)) then + Error_Msg_N + ("array delta aggregate shall be of a nonlimited type", Base); + Explain_Limited_Type (Etype (Base), Base); + end if; + Resolve_Delta_Array_Aggregate (N, Typ); else @@ -3432,6 +3444,11 @@ package body Sem_Aggr is ("delta aggregates for record types must use (), not '[']", N); end if; + -- The base_expression of a record_delta_aggregate can be of a + -- limited type only if it is newly constructed; RM 7.5(2.1/5). + + Check_Expr_OK_In_Limited_Aggregate (Base); + Resolve_Delta_Record_Aggregate (N, Typ); end if; -- cgit v1.2.1 From 83ebb97db77ec475668d81ba3e717b2ebbeaffb7 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Wed, 19 Oct 2022 00:04:31 +0200 Subject: ada: Remove obsolete code in Resolve_If_Expression gcc/ada/ * sem_res.adb (Resolve_If_Expression): Remove obsolete special case. --- gcc/ada/sem_res.adb | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/gcc/ada/sem_res.adb b/gcc/ada/sem_res.adb index 402da430b2f..e5b3612d186 100644 --- a/gcc/ada/sem_res.adb +++ b/gcc/ada/sem_res.adb @@ -9393,21 +9393,10 @@ package body Sem_Res is Apply_Check (Then_Expr); -- If ELSE expression present, just resolve using the determined type - -- If type is universal, resolve to any member of the class. if Present (Else_Expr) then - if Typ = Universal_Integer then - Resolve (Else_Expr, Any_Integer); - - elsif Typ = Universal_Real then - Resolve (Else_Expr, Any_Real); - - else - Resolve (Else_Expr, Result_Type); - end if; - + Resolve (Else_Expr, Result_Type); Check_Unset_Reference (Else_Expr); - Apply_Check (Else_Expr); -- Apply RM 4.5.7 (17/3): whether the expression is statically or -- cgit v1.2.1 From 5c0722cb40ac1be1e5c40b3aff6fc7b58e3e4cba Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Tue, 6 Sep 2022 23:28:26 +0200 Subject: ada: Cleanup local variable that is only set as an out parameter Minor improvements; found experimenting with improved detection of unreferenced objects. gcc/ada/ * exp_spark.adb (SPARK_Freeze_Type): Refine type of a local object. * sem_ch3.adb (Derive_Subprograms): Remove initial value for New_Subp, which is in only written as an out parameter and never read. --- gcc/ada/exp_spark.adb | 2 +- gcc/ada/sem_ch3.adb | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/gcc/ada/exp_spark.adb b/gcc/ada/exp_spark.adb index c89d604aa80..ba7bd7fe5d2 100644 --- a/gcc/ada/exp_spark.adb +++ b/gcc/ada/exp_spark.adb @@ -895,7 +895,7 @@ package body Exp_SPARK is procedure SPARK_Freeze_Type (N : Entity_Id) is Typ : constant Entity_Id := Entity (N); - Renamed_Eq : Node_Id; + Renamed_Eq : Entity_Id; -- Defining unit name for the predefined equality function in the case -- where the type has a primitive operation that is a renaming of -- predefined equality (but only if there is also an overriding diff --git a/gcc/ada/sem_ch3.adb b/gcc/ada/sem_ch3.adb index f6b852051dc..8f4e9f80eb8 100644 --- a/gcc/ada/sem_ch3.adb +++ b/gcc/ada/sem_ch3.adb @@ -16497,15 +16497,15 @@ package body Sem_Ch3 is -- Local variables - Alias_Subp : Entity_Id; - Act_List : Elist_Id; - Act_Elmt : Elmt_Id; - Act_Subp : Entity_Id := Empty; - Elmt : Elmt_Id; - Need_Search : Boolean := False; - New_Subp : Entity_Id := Empty; - Parent_Base : Entity_Id; - Subp : Entity_Id; + Alias_Subp : Entity_Id; + Act_List : Elist_Id; + Act_Elmt : Elmt_Id; + Act_Subp : Entity_Id := Empty; + Elmt : Elmt_Id; + Need_Search : Boolean := False; + New_Subp : Entity_Id; + Parent_Base : Entity_Id; + Subp : Entity_Id; -- Start of processing for Derive_Subprograms -- cgit v1.2.1 From d96a20bf2ef939c0dd9d2bb32c59267842279c29 Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Tue, 6 Sep 2022 23:20:47 +0200 Subject: ada: Remove unneeded code in handling formal type defaults Unneeded code found while experimenting with improved detection of unreferenced objects. gcc/ada/ * sem_ch12.adb (Validate_Formal_Type_Default): Remove call to Collect_Interfaces, which had no effect apart from populating a list that was not used; fix style. --- gcc/ada/sem_ch12.adb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gcc/ada/sem_ch12.adb b/gcc/ada/sem_ch12.adb index 2b7833dfdcd..ca0f4913e36 100644 --- a/gcc/ada/sem_ch12.adb +++ b/gcc/ada/sem_ch12.adb @@ -17303,13 +17303,11 @@ package body Sem_Ch12 is else declare - Act_Iface_List : Elist_Id; - Iface : Node_Id; - Iface_Ent : Entity_Id; + Iface : Node_Id; + Iface_Ent : Entity_Id; begin Iface := First (Abstract_Interface_List (Formal)); - Collect_Interfaces (Def_Sub, Act_Iface_List); while Present (Iface) loop Iface_Ent := Entity (Iface); -- cgit v1.2.1 From 7857d873293d2333e1cc8e67ff9bb963fc8b4d1f Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Wed, 7 Sep 2022 21:36:04 +0200 Subject: ada: Fix inconsistent whitespace in Ada.Numerics.Generic_Complex_Arrays Cleanup only. gcc/ada/ * libgnat/a-ngcoar.ads, libgnat/a-ngcoar.adb: Remove extra spaces. --- gcc/ada/libgnat/a-ngcoar.adb | 4 ++-- gcc/ada/libgnat/a-ngcoar.ads | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gcc/ada/libgnat/a-ngcoar.adb b/gcc/ada/libgnat/a-ngcoar.adb index 8dfbc3b174a..1b9118cd3b2 100644 --- a/gcc/ada/libgnat/a-ngcoar.adb +++ b/gcc/ada/libgnat/a-ngcoar.adb @@ -902,7 +902,7 @@ package body Ada.Numerics.Generic_Complex_Arrays is function "-" (Left : Real_Vector; Right : Complex_Vector) return Complex_Vector - renames Instantiations."-"; + renames Instantiations."-"; function "-" (Left : Complex_Vector; @@ -956,7 +956,7 @@ package body Ada.Numerics.Generic_Complex_Arrays is ----------- function "abs" (Right : Complex_Vector) return Real'Base - renames Instantiations."abs"; + renames Instantiations."abs"; -------------- -- Argument -- diff --git a/gcc/ada/libgnat/a-ngcoar.ads b/gcc/ada/libgnat/a-ngcoar.ads index 8f8f37a7906..05295caa655 100644 --- a/gcc/ada/libgnat/a-ngcoar.ads +++ b/gcc/ada/libgnat/a-ngcoar.ads @@ -135,7 +135,7 @@ package Ada.Numerics.Generic_Complex_Arrays is function Compose_From_Cartesian (Re : Real_Matrix) return Complex_Matrix; function Compose_From_Cartesian - (Re, Im : Real_Matrix) return Complex_Matrix; + (Re, Im : Real_Matrix) return Complex_Matrix; function Modulus (X : Complex_Matrix) return Real_Matrix; function "abs" (Right : Complex_Matrix) return Real_Matrix renames Modulus; @@ -229,7 +229,7 @@ package Ada.Numerics.Generic_Complex_Arrays is function "*" (Left : Complex; - Right : Complex_Matrix) return Complex_Matrix; + Right : Complex_Matrix) return Complex_Matrix; function "*" (Left : Complex_Matrix; -- cgit v1.2.1 From c2596d4533389b8898516b444f8464a1720711af Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Thu, 17 Jun 2021 19:44:40 +0200 Subject: ada: Fix expansion of 'Wide_Image and 'Wide_Wide_Image on composite types Attributes Wide_Image and Wide_Wide_Image applied to composite types are now expanded just like attribute Image. gcc/ada/ * exp_imgv.adb (Expand_Wide_Image_Attribute): Handle just like attribute Image. (Expand_Wide_Wide_Image_Attribute): Likewise. * exp_put_image.adb (Build_Image_Call): Adapt to also work for Wide and Wide_Wide attributes. * exp_put_image.ads (Build_Image_Call): Update comment. * rtsfind.ads (RE_Id): Support wide variants of Get. (RE_Unit_Table): Likewise. --- gcc/ada/exp_imgv.adb | 19 +++++++++++++++++++ gcc/ada/exp_put_image.adb | 29 +++++++++++++++++++++++++---- gcc/ada/exp_put_image.ads | 6 +++--- gcc/ada/rtsfind.ads | 4 ++++ 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/gcc/ada/exp_imgv.adb b/gcc/ada/exp_imgv.adb index f2043f525d5..398e4771c14 100644 --- a/gcc/ada/exp_imgv.adb +++ b/gcc/ada/exp_imgv.adb @@ -1842,6 +1842,15 @@ package body Exp_Imgv is return; end if; + -- If Image should be transformed using Put_Image, then do so. See + -- Exp_Put_Image for details. + + if Exp_Put_Image.Image_Should_Call_Put_Image (N) then + Rewrite (N, Exp_Put_Image.Build_Image_Call (N)); + Analyze_And_Resolve (N, Standard_Wide_String, Suppress => All_Checks); + return; + end if; + Rtyp := Root_Type (Entity (Pref)); Insert_Actions (N, New_List ( @@ -1942,6 +1951,16 @@ package body Exp_Imgv is return; end if; + -- If Image should be transformed using Put_Image, then do so. See + -- Exp_Put_Image for details. + + if Exp_Put_Image.Image_Should_Call_Put_Image (N) then + Rewrite (N, Exp_Put_Image.Build_Image_Call (N)); + Analyze_And_Resolve + (N, Standard_Wide_Wide_String, Suppress => All_Checks); + return; + end if; + Rtyp := Root_Type (Entity (Pref)); Insert_Actions (N, New_List ( diff --git a/gcc/ada/exp_put_image.adb b/gcc/ada/exp_put_image.adb index c489ad41fd1..f90f0206f27 100644 --- a/gcc/ada/exp_put_image.adb +++ b/gcc/ada/exp_put_image.adb @@ -1058,12 +1058,14 @@ package body Exp_Put_Image is ---------------------- function Build_Image_Call (N : Node_Id) return Node_Id is - -- For T'Image (X) Generate an Expression_With_Actions node: + -- For T'[[Wide_]Wide_]Image (X) Generate an Expression_With_Actions + -- node: -- -- do -- S : Buffer; -- U_Type'Put_Image (S, X); - -- Result : constant String := Get (S); + -- Result : constant [[Wide_]Wide_]String := + -- [[Wide_[Wide_]]Get (S); -- Destroy (S); -- in Result end -- @@ -1091,14 +1093,33 @@ package body Exp_Put_Image is Image_Prefix)); Result_Entity : constant Entity_Id := Make_Temporary (Loc, 'R'); + + subtype Image_Name_Id is Name_Id with Static_Predicate => + Image_Name_Id in Name_Image | Name_Wide_Image | Name_Wide_Wide_Image; + -- Attribute names that will be mapped to the corresponding result types + -- and functions. + + Attribute_Name_Id : constant Name_Id := Attribute_Name (N); + + Result_Typ : constant Entity_Id := + (case Image_Name_Id'(Attribute_Name_Id) is + when Name_Image => Stand.Standard_String, + when Name_Wide_Image => Stand.Standard_Wide_String, + when Name_Wide_Wide_Image => Stand.Standard_Wide_Wide_String); + Get_Func_Id : constant RE_Id := + (case Image_Name_Id'(Attribute_Name_Id) is + when Name_Image => RE_Get, + when Name_Wide_Image => RE_Wide_Get, + when Name_Wide_Wide_Image => RE_Wide_Wide_Get); + Result_Decl : constant Node_Id := Make_Object_Declaration (Loc, Defining_Identifier => Result_Entity, Object_Definition => - New_Occurrence_Of (Stand.Standard_String, Loc), + New_Occurrence_Of (Result_Typ, Loc), Expression => Make_Function_Call (Loc, - Name => New_Occurrence_Of (RTE (RE_Get), Loc), + Name => New_Occurrence_Of (RTE (Get_Func_Id), Loc), Parameter_Associations => New_List ( New_Occurrence_Of (Sink_Entity, Loc)))); Actions : List_Id; diff --git a/gcc/ada/exp_put_image.ads b/gcc/ada/exp_put_image.ads index b2b65aa2374..d4055d10b96 100644 --- a/gcc/ada/exp_put_image.ads +++ b/gcc/ada/exp_put_image.ads @@ -91,9 +91,9 @@ package Exp_Put_Image is -- T'Image. function Build_Image_Call (N : Node_Id) return Node_Id; - -- N is a call to T'Image, and this translates it into the appropriate code - -- to call T'Put_Image into a buffer and then extract the string from the - -- buffer. + -- N is a call to T'[[Wide_]Wide_]Image, and this translates it into the + -- appropriate code to call T'Put_Image into a buffer and then extract the + -- [[wide] wide] string from the buffer. procedure Preload_Root_Buffer_Type (Compilation_Unit : Node_Id); -- Call RTE (RE_Root_Buffer_Type) if necessary, to load the packages diff --git a/gcc/ada/rtsfind.ads b/gcc/ada/rtsfind.ads index 24aca2cf6b6..ce49e2df149 100644 --- a/gcc/ada/rtsfind.ads +++ b/gcc/ada/rtsfind.ads @@ -609,6 +609,8 @@ package Rtsfind is RE_Buffer_Type, -- Ada.Strings.Text_Buffers.Unbounded RE_Get, -- Ada.Strings.Text_Buffers.Unbounded + RE_Wide_Get, -- Ada.Strings.Text_Buffers.Unbounded + RE_Wide_Wide_Get, -- Ada.Strings.Text_Buffers.Unbounded RE_Wait_For_Release, -- Ada.Synchronous_Barriers @@ -2245,6 +2247,8 @@ package Rtsfind is RE_Buffer_Type => Ada_Strings_Text_Buffers_Unbounded, RE_Get => Ada_Strings_Text_Buffers_Unbounded, + RE_Wide_Get => Ada_Strings_Text_Buffers_Unbounded, + RE_Wide_Wide_Get => Ada_Strings_Text_Buffers_Unbounded, RE_Wait_For_Release => Ada_Synchronous_Barriers, -- cgit v1.2.1 From 788e5f06d4e804dcc9e255fa448ba0c3db1586c4 Mon Sep 17 00:00:00 2001 From: Ronan Desplanques Date: Mon, 17 Oct 2022 12:00:09 +0200 Subject: ada: Preanalyze classwide contracts as spec expressions Classwide contracts are "spec expressions" as defined in the documentation in sem.ads. Before this patch, the instances of classwide contracts that are destined to class conditions merging were not preanalyzed as spec expressions. That caused preanalysis to emit spurious errors in some cases. gcc/ada/ * contracts.adb (Preanalyze_Condition): Use Preanalyze_Spec_Expression. --- gcc/ada/contracts.adb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gcc/ada/contracts.adb b/gcc/ada/contracts.adb index a300d739eff..21f438f90f3 100644 --- a/gcc/ada/contracts.adb +++ b/gcc/ada/contracts.adb @@ -42,13 +42,13 @@ with Nmake; use Nmake; with Opt; use Opt; with Sem; use Sem; with Sem_Aux; use Sem_Aux; +with Sem_Ch3; use Sem_Ch3; with Sem_Ch6; use Sem_Ch6; with Sem_Ch8; use Sem_Ch8; with Sem_Ch12; use Sem_Ch12; with Sem_Ch13; use Sem_Ch13; with Sem_Disp; use Sem_Disp; with Sem_Prag; use Sem_Prag; -with Sem_Res; use Sem_Res; with Sem_Type; use Sem_Type; with Sem_Util; use Sem_Util; with Sinfo; use Sinfo; @@ -4755,7 +4755,7 @@ package body Contracts is Install_Formals (Subp); Inside_Class_Condition_Preanalysis := True; - Preanalyze_And_Resolve (Expr, Standard_Boolean); + Preanalyze_Spec_Expression (Expr, Standard_Boolean); Inside_Class_Condition_Preanalysis := False; Remove_Formals (Subp); -- cgit v1.2.1 From 786c6ba5a51f54c404dd491523788f7359a29a91 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Wed, 19 Oct 2022 12:39:42 +0200 Subject: ada: Remove redundant line in Analyze_Qualified_Expression The same statement is present a few lines above. gcc/ada/ * sem_ch4.adb (Analyze_Qualified_Expression): Remove redundant line. --- gcc/ada/sem_ch4.adb | 2 -- 1 file changed, 2 deletions(-) diff --git a/gcc/ada/sem_ch4.adb b/gcc/ada/sem_ch4.adb index f136e9715d7..489fb47247a 100644 --- a/gcc/ada/sem_ch4.adb +++ b/gcc/ada/sem_ch4.adb @@ -4389,8 +4389,6 @@ package body Sem_Ch4 is end loop; end if; end if; - - Set_Etype (N, T); end Analyze_Qualified_Expression; ----------------------------------- -- cgit v1.2.1 From 270713d3f65243a089f4cdee04cd689422a95530 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Thu, 20 Oct 2022 11:05:16 +0200 Subject: ada: Minor consistency tweaks in Sem_Ch4 This ensures that, during the analysis of the qualified expressions, type conversions and unchecked type conversions, the determination of the type of the node and the analysis of its expression are done in the same order. No functional changes. gcc/ada/ * sem_ch4.adb (Analyze_Qualified_Expression): Analyze the expression only after setting the type. (Analyze_Unchecked_Type_Conversion): Likewise. (Analyze_Short_Circuit): Likewise for the operands. (Analyze_Type_Conversion): Minor tweaks. (Analyze_Unchecked_Expression): Likewise. --- gcc/ada/sem_ch4.adb | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/gcc/ada/sem_ch4.adb b/gcc/ada/sem_ch4.adb index 489fb47247a..0c02fd80675 100644 --- a/gcc/ada/sem_ch4.adb +++ b/gcc/ada/sem_ch4.adb @@ -4323,16 +4323,14 @@ package body Sem_Ch4 is ---------------------------------- procedure Analyze_Qualified_Expression (N : Node_Id) is - Mark : constant Entity_Id := Subtype_Mark (N); Expr : constant Node_Id := Expression (N); + Mark : constant Entity_Id := Subtype_Mark (N); + I : Interp_Index; It : Interp; T : Entity_Id; begin - Analyze_Expression (Expr); - - Set_Etype (N, Any_Type); Find_Type (Mark); T := Entity (Mark); @@ -4353,6 +4351,8 @@ package body Sem_Ch4 is Set_Etype (N, T); + Analyze_Expression (Expr); + if T = Any_Type then return; end if; @@ -5948,9 +5948,9 @@ package body Sem_Ch4 is It : Interp; begin + Set_Etype (N, Any_Type); Analyze_Expression (L); Analyze_Expression (R); - Set_Etype (N, Any_Type); if not Is_Overloaded (L) then if Root_Type (Etype (L)) = Standard_Boolean @@ -6083,7 +6083,9 @@ package body Sem_Ch4 is ----------------------------- procedure Analyze_Type_Conversion (N : Node_Id) is - Expr : constant Node_Id := Expression (N); + Expr : constant Node_Id := Expression (N); + Mark : constant Entity_Id := Subtype_Mark (N); + Typ : Entity_Id; begin @@ -6100,11 +6102,13 @@ package body Sem_Ch4 is -- Otherwise full type analysis is required, as well as some semantic -- checks to make sure the argument of the conversion is appropriate. - Find_Type (Subtype_Mark (N)); - Typ := Entity (Subtype_Mark (N)); + Find_Type (Mark); + Typ := Entity (Mark); Set_Etype (N, Typ); - Check_Fully_Declared (Typ, N); + Analyze_Expression (Expr); + + Check_Fully_Declared (Typ, N); Validate_Remote_Type_Type_Conversion (N); -- Only remaining step is validity checks on the argument. These @@ -6227,10 +6231,12 @@ package body Sem_Ch4 is ---------------------------------- procedure Analyze_Unchecked_Expression (N : Node_Id) is + Expr : constant Node_Id := Expression (N); + begin - Analyze (Expression (N), Suppress => All_Checks); - Set_Etype (N, Etype (Expression (N))); - Save_Interps (Expression (N), N); + Analyze (Expr, Suppress => All_Checks); + Set_Etype (N, Etype (Expr)); + Save_Interps (Expr, N); end Analyze_Unchecked_Expression; --------------------------------------- @@ -6238,10 +6244,13 @@ package body Sem_Ch4 is --------------------------------------- procedure Analyze_Unchecked_Type_Conversion (N : Node_Id) is + Expr : constant Node_Id := Expression (N); + Mark : constant Entity_Id := Subtype_Mark (N); + begin - Find_Type (Subtype_Mark (N)); - Analyze_Expression (Expression (N)); - Set_Etype (N, Entity (Subtype_Mark (N))); + Find_Type (Mark); + Set_Etype (N, Entity (Mark)); + Analyze_Expression (Expr); end Analyze_Unchecked_Type_Conversion; ------------------------------------ -- cgit v1.2.1 From f2fa41b442ae6b6ab1fbde82d31abcd986b9f1f3 Mon Sep 17 00:00:00 2001 From: Steve Baird Date: Wed, 19 Oct 2022 12:42:55 -0700 Subject: ada: Improve handling of declare expressions in deferred-freezing contexts In some cases where a declare expression occurs in a deferred-freezing context (e.g., within the default value for a discriminant or for a formal parameter, or within the expression of an expression function), the compiler generates a bugbox. gcc/ada/ * sem_ch3.adb (Analyze_Object_Declaration): Do not perform expansion actions if In_Spec_Expression is true. --- gcc/ada/sem_ch3.adb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/gcc/ada/sem_ch3.adb b/gcc/ada/sem_ch3.adb index 8f4e9f80eb8..95ffbe00ba4 100644 --- a/gcc/ada/sem_ch3.adb +++ b/gcc/ada/sem_ch3.adb @@ -4721,6 +4721,26 @@ package body Sem_Ch3 is Expand_Sliding_Conversion (E, T); end if; + if In_Spec_Expression and then In_Declare_Expr > 0 then + -- It is too early to be doing expansion-ish things, + -- so exit early. But we have to set Ekind (Id) now so + -- that subsequent uses of this entity are not rejected + -- via the same mechanism that (correctly) rejects + -- "X : Integer := X;". + + if Constant_Present (N) then + Mutate_Ekind (Id, E_Constant); + Set_Is_True_Constant (Id); + else + Mutate_Ekind (Id, E_Variable); + if Present (E) then + Set_Has_Initial_Value (Id); + end if; + end if; + + goto Leave; + end if; + Expand_Subtype_From_Expr (N => N, Unc_Type => T, -- cgit v1.2.1 From c523e3f1ea9b451fddc4c535009b3eecffc4ac4a Mon Sep 17 00:00:00 2001 From: Ronan Desplanques Date: Fri, 14 Oct 2022 16:28:58 +0200 Subject: ada: Align -gnatwc's documentation with its behavior Shortly after the -gnatwc flag was introduced, its behavior was tweaked, but its documentation was not updated accordingly. gcc/ada/ * doc/gnat_ugn/building_executable_programs_with_gnat.rst (-gnatwc): Fix flag documentation. * gnat_ugn.texi: Regenerate. --- gcc/ada/doc/gnat_ugn/building_executable_programs_with_gnat.rst | 2 +- gcc/ada/gnat_ugn.texi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gcc/ada/doc/gnat_ugn/building_executable_programs_with_gnat.rst b/gcc/ada/doc/gnat_ugn/building_executable_programs_with_gnat.rst index 31e2e31421e..87fb1087e42 100644 --- a/gcc/ada/doc/gnat_ugn/building_executable_programs_with_gnat.rst +++ b/gcc/ada/doc/gnat_ugn/building_executable_programs_with_gnat.rst @@ -2938,7 +2938,7 @@ of the pragma in the :title:`GNAT_Reference_manual`). tests that are known to be True or False at compile time. The default is that such warnings are not generated. Note that this warning does - not get issued for the use of boolean variables or constants whose + not get issued for the use of boolean constants whose values are known at compile time, since this is a standard technique for conditional compilation in Ada, and this would generate too many false positive warnings. diff --git a/gcc/ada/gnat_ugn.texi b/gcc/ada/gnat_ugn.texi index 385f1d3deb5..7b1aaeba954 100644 --- a/gcc/ada/gnat_ugn.texi +++ b/gcc/ada/gnat_ugn.texi @@ -10925,7 +10925,7 @@ This switch activates warnings for conditional expressions used in tests that are known to be True or False at compile time. The default is that such warnings are not generated. Note that this warning does -not get issued for the use of boolean variables or constants whose +not get issued for the use of boolean constants whose values are known at compile time, since this is a standard technique for conditional compilation in Ada, and this would generate too many false positive warnings. -- cgit v1.2.1 From 7a08b9393c7b36c4dca8fca9a5cda261594f61d6 Mon Sep 17 00:00:00 2001 From: Bob Duff Date: Fri, 21 Oct 2022 11:09:49 -0400 Subject: ada: Move warnings switches -- initial work This patch prepares to move warning switches from Opt into Warnsw. gcc/ada/ * warnsw.ads, warnsw.adb, fe.h, err_vars.ads, errout.ads: Move Warning_Doc_Switch from Err_Vars to Warnsw. Access Warn_On_Questionable_Layout on the C side via a function rather than a variable, because we plan to turn the variables into renamings, and you can't Export renamings. * erroutc.adb, switch-c.adb, errout.adb: Likewise. * gcc-interface/decl.cc: Use Get_Warn_On_Questionable_Layout instead of Warn_On_Questionable_Layout. * gcc-interface/Makefile.in (GNATMAKE_OBJS): Add warnsw.o, because it is indirectly imported via Errout. * gcc-interface/Make-lang.in (GNATBIND_OBJS): Likewise and remove restrict.o (not needed). --- gcc/ada/err_vars.ads | 6 ------ gcc/ada/errout.adb | 2 ++ gcc/ada/errout.ads | 9 --------- gcc/ada/erroutc.adb | 1 + gcc/ada/fe.h | 4 ++-- gcc/ada/gcc-interface/Make-lang.in | 2 +- gcc/ada/gcc-interface/Makefile.in | 2 +- gcc/ada/gcc-interface/decl.cc | 2 +- gcc/ada/switch-c.adb | 1 - gcc/ada/warnsw.adb | 1 - gcc/ada/warnsw.ads | 13 ++++++++++++- 11 files changed, 20 insertions(+), 23 deletions(-) diff --git a/gcc/ada/err_vars.ads b/gcc/ada/err_vars.ads index 05329dc6f21..79d5f319f59 100644 --- a/gcc/ada/err_vars.ads +++ b/gcc/ada/err_vars.ads @@ -81,12 +81,6 @@ package Err_Vars is -- Source_Reference line, then this is initialized to No_Source_File, -- to force an initial reference to the real source file name. - Warning_Doc_Switch : Boolean := True; - -- If this is set True, then the ??/?x?/?x? sequences in error messages - -- are active (see errout.ads for details). If this switch is False, then - -- these sequences are ignored (i.e. simply equivalent to a single ?). The - -- -gnatw.d switch sets this flag True, -gnatw.D sets this flag False. - ---------------------------------------- -- Error Message Insertion Parameters -- ---------------------------------------- diff --git a/gcc/ada/errout.adb b/gcc/ada/errout.adb index 5730a543ee1..19ea1553260 100644 --- a/gcc/ada/errout.adb +++ b/gcc/ada/errout.adb @@ -53,6 +53,8 @@ with Stand; use Stand; with Stylesw; use Stylesw; with System.OS_Lib; with Uname; use Uname; +with Warnsw; pragma Unreferenced (Warnsw); +-- Will be referenced when various flags are moved to Warnsw. package body Errout is diff --git a/gcc/ada/errout.ads b/gcc/ada/errout.ads index 846a4a6c07b..aeb9a2fb912 100644 --- a/gcc/ada/errout.ads +++ b/gcc/ada/errout.ads @@ -59,15 +59,6 @@ package Errout is Error_Msg_Exception : exception renames Err_Vars.Error_Msg_Exception; -- Exception raised if Raise_Exception_On_Error is true - Warning_Doc_Switch : Boolean renames Err_Vars.Warning_Doc_Switch; - -- If this is set True, then the ??/?*?/?$?/?x?/?.x?/?_x? insertion - -- sequences in error messages generate appropriate tags for the output - -- error messages. If this switch is False, then these sequences are still - -- recognized (for the purposes of implementing the pattern matching in - -- pragmas Warnings (Off,..) and Warning_As_Error(...) but do not result - -- in adding the error message tag. The -gnatw.d switch sets this flag - -- True, -gnatw.D sets this flag False. - Current_Node : Node_Id := Empty; -- Used by Error_Msg as a default Node_Id. -- Relevant only when Opt.Include_Subprogram_In_Messages is set. diff --git a/gcc/ada/erroutc.adb b/gcc/ada/erroutc.adb index d0cbe9fdff1..9ecc97fb46d 100644 --- a/gcc/ada/erroutc.adb +++ b/gcc/ada/erroutc.adb @@ -44,6 +44,7 @@ with Stringt; use Stringt; with Targparm; with Uintp; use Uintp; with Widechar; use Widechar; +with Warnsw; use Warnsw; package body Erroutc is diff --git a/gcc/ada/fe.h b/gcc/ada/fe.h index 79a1b58836e..8102c6d5ec4 100644 --- a/gcc/ada/fe.h +++ b/gcc/ada/fe.h @@ -366,9 +366,9 @@ extern Boolean Stack_Check_Probes_On_Target; /* warnsw: */ -#define Warn_On_Questionable_Layout warnsw__warn_on_questionable_layout +#define Get_Warn_On_Questionable_Layout warnsw__get_warn_on_questionable_layout -extern Boolean Warn_On_Questionable_Layout; +extern Boolean Get_Warn_On_Questionable_Layout (); // The following corresponds to Ada code in Einfo.Utils. diff --git a/gcc/ada/gcc-interface/Make-lang.in b/gcc/ada/gcc-interface/Make-lang.in index 02b2d1c6f2a..45a4168e890 100644 --- a/gcc/ada/gcc-interface/Make-lang.in +++ b/gcc/ada/gcc-interface/Make-lang.in @@ -601,7 +601,6 @@ GNATBIND_OBJS = \ ada/osint-b.o \ ada/osint.o \ ada/output.o \ - ada/restrict.o \ ada/rident.o \ ada/scans.o \ ada/scil_ll.o \ @@ -629,6 +628,7 @@ GNATBIND_OBJS = \ ada/uintp.o \ ada/uname.o \ ada/urealp.o \ + ada/warnsw.o \ ada/widechar.o \ ada/gnat.o \ ada/g-dynhta.o \ diff --git a/gcc/ada/gcc-interface/Makefile.in b/gcc/ada/gcc-interface/Makefile.in index 6b19b8be58e..5137ebaa0c6 100644 --- a/gcc/ada/gcc-interface/Makefile.in +++ b/gcc/ada/gcc-interface/Makefile.in @@ -333,7 +333,7 @@ GNATMAKE_OBJS = a-except.o ali.o ali-util.o aspects.o s-casuti.o alloc.o \ s-purexc.o s-htable.o scil_ll.o sem_aux.o sinfo.o sinput.o sinput-c.o \ snames.o stand.o stringt.o styleg.o stylesw.o system.o validsw.o \ switch.o switch-m.o table.o targparm.o tempdir.o types.o uintp.o \ - uname.o urealp.o usage.o widechar.o \ + uname.o urealp.o usage.o widechar.o warnsw.o \ seinfo.o einfo-entities.o einfo-utils.o sinfo-nodes.o sinfo-utils.o \ $(EXTRA_GNATMAKE_OBJS) diff --git a/gcc/ada/gcc-interface/decl.cc b/gcc/ada/gcc-interface/decl.cc index f8c76982de5..e25ce498f2c 100644 --- a/gcc/ada/gcc-interface/decl.cc +++ b/gcc/ada/gcc-interface/decl.cc @@ -8363,7 +8363,7 @@ components_to_record (Node_Id gnat_component_list, Entity_Id gnat_record_type, && !Debug_Flag_Dot_R); const bool w_reorder = (Convention (gnat_record_type) == Convention_Ada - && Warn_On_Questionable_Layout + && Get_Warn_On_Questionable_Layout () && !(No_Reordering (gnat_record_type) && GNAT_Mode)); tree gnu_zero_list = NULL_TREE; tree gnu_self_list = NULL_TREE; diff --git a/gcc/ada/switch-c.adb b/gcc/ada/switch-c.adb index a1a877716f0..364ff55a4a6 100644 --- a/gcc/ada/switch-c.adb +++ b/gcc/ada/switch-c.adb @@ -28,7 +28,6 @@ -- circularities, especially for back ends using Adabkend. with Debug; use Debug; -with Errout; use Errout; with Lib; use Lib; with Osint; use Osint; with Opt; use Opt; diff --git a/gcc/ada/warnsw.adb b/gcc/ada/warnsw.adb index 733c9620631..dd40c8844c3 100644 --- a/gcc/ada/warnsw.adb +++ b/gcc/ada/warnsw.adb @@ -23,7 +23,6 @@ -- -- ------------------------------------------------------------------------------ -with Err_Vars; use Err_Vars; with Opt; use Opt; with Output; use Output; diff --git a/gcc/ada/warnsw.ads b/gcc/ada/warnsw.ads index 9edd6bea37e..645094509f4 100644 --- a/gcc/ada/warnsw.ads +++ b/gcc/ada/warnsw.ads @@ -38,6 +38,15 @@ package Warnsw is -- here as time goes by. And in fact a really nice idea would be to put -- them all in a Warn_Record so that they would be easy to save/restore. + Warning_Doc_Switch : Boolean := True; + -- If this is set True, then the ??/?*?/?$?/?x?/?.x?/?_x? insertion + -- sequences in error messages generate appropriate tags for the output + -- error messages. If this switch is False, then these sequences are still + -- recognized (for the purposes of implementing the pattern matching in + -- pragmas Warnings (Off,..) and Warning_As_Error(...) but do not result + -- in adding the error message tag. The -gnatw.d switch sets this flag + -- True, -gnatw.D sets this flag False. + Warn_On_Anonymous_Allocators : Boolean := False; -- Warn when allocators for anonymous access types are present, which, -- although not illegal in Ada, may be confusing to users due to how @@ -71,7 +80,9 @@ package Warnsw is -- efficiency reasons and would be improved by reordering the components. -- Off by default, modified by use of -gnatw.q/.Q (but not -gnatwa). - -- WARNING: There is a matching C declaration of this variable in fe.h + function Get_Warn_On_Questionable_Layout return Boolean is + (Warn_On_Questionable_Layout); + -- WARNING: There is a matching C declaration of this function in fe.h Warn_On_Record_Holes : Boolean := False; -- Warn when explicit record component clauses leave uncovered holes (gaps) -- cgit v1.2.1 From f1668c3d35b1031fa3ee266b6c3292e53344d315 Mon Sep 17 00:00:00 2001 From: Javier Miranda Date: Sun, 16 Oct 2022 19:48:53 +0000 Subject: ada: Enforce matching of extra formals This patch enforces matching of extra formals in overridden subprograms, subprogram renamings, and subprograms to which attributes 'Access, 'Unchecked_Access, or 'Unrestricted_Access is applied (for these access cases the subprogram is checked against its corresponding subprogram type). This enforcement is an internal consistency check, not an implementation of some language legality rule. gcc/ada/ * debug.adb (Debug_Flag_Underscore_XX): Switch -gnatd_X used temporarily to allow disabling extra formal checks. * exp_attr.adb (Expand_N_Attribute_Reference [access types]): Add extra formals to the subprogram referenced in the prefix of 'Unchecked_Access, 'Unrestricted_Access or 'Access; required to check that its extra formals match the extra formals of the corresponding subprogram type. * exp_ch3.adb (Stream_Operation_OK): Declaration moved to the public part of the package. (Validate_Tagged_Type_Extra_Formals): New subprogram. (Expand_Freeze_Record_Type): Improve the code that takes care of adding the extra formals of dispatching primitives; extended to add also the extra formals to renamings of dispatching primitives. * exp_ch3.ads (Stream_Operation_OK): Declaration moved from the package body. * exp_ch6.adb (Check_BIP_Actuals): Complete documentation. (Has_BIP_Extra_Formal): Subprogram declaration moved to the public part of the package. In addition, a parameter has been added to disable an assertion that requires its use with frozen entities. (Duplicate_Params_Without_Extra_Actuals): New subprogram. (Check_Subprogram_Variant): Emit the call without duplicating the extra formals since they will be added when the call is analyzed. (Expand_Call_Helper): Ensure that the called subprogram has all its extra formals, enforce assertion checking extra formals on thunks, and mark calls from thunks as processed-BIP-calls to avoid adding their extra formals twice. (Is_Build_In_Place_Function): Return False for entities with foreign convention. (Is_Build_In_Place_Function_Call): Return True also for not BIP functions that have BIP formals since the extra actuals are required. (Make_Build_In_Place_Call_In_Object_Declaration): Occurrences of Is_Return_Object replaced by the local variable Is_OK_Return_Object that evaluates to False for scopes with foreign convention. (Might_Have_Tasks): Fix check of class-wide limited record types. (Needs_BIP_Task_Actuals): Remove assertion to allow calling this function in more contexts; in addition it returns False for functions returning objects with foreign convention. (Needs_BIP_Finalization_Master): Likewise. (Needs_BIP_Alloc_Form): Likewise. (Validate_Subprogram_Calls): Check that the number of actuals (including extra actuals) of calls in the subtree N match their corresponding formals. * exp_ch6.ads (Has_BIP_Extra_Formal): Subprogram declaration moved to the public part of the package. In addition, a parameter has been added to disable an assertion that requires its use with frozen entities. (Is_Build_In_Place_Function_Call): Complete documentation. (Validate_Subprogram_Calls): Check that the number of actuals (including extra actuals) of calls in the subtree N match their corresponding formals. * freeze.adb (Check_Itype): Add extra formals to anonymous access subprogram itypes. (Freeze_Expression): Improve code that disables the addition of extra formals to functions with foreign convention. (Check_Extra_Formals): Moved to package Sem_Ch6 as Extra_Formals_OK. (Freeze_Subprogram): Add extra formals to non-dispatching subprograms. * frontend.adb (Frontend): Validate all the subprogram calls; it can be disabled using switch -gnatd_X * sem_ch3.adb (Access_Subprogram_Declaration): Defer the addition of extra formals to the freezing point so that we know the convention. (Check_Anonymous_Access_Component): Likewise. (Derive_Subprogram): Fix documentation. * sem_ch6.adb (Has_Reliable_Extra_Formals): New subprogram. (Check_Anonymous_Return): Fix check of access to class-wide limited record types. (Check_Untagged_Equality): Placed in alphabetical order. (Extra_Formals_OK): Subprogram moved from freeze.adb. (Extra_Formals_Match_OK): New subprogram. (Has_BIP_Formals): New subprogram. (Has_Extra_Formals): New subprograms. (Needs_Accessibility_Check_Extra): New subprogram. (Parent_Subprogram): New subprogram. (Add_Extra_Formal): Minor code cleanup. (Create_Extra_Formals): Enforce matching extra formals on overridden and aliased entities. * sem_ch6.ads (Extra_Formals_Match_OK): New subprogram. (Extra_Formals_OK): Subprogram moved from freeze.adb. * sem_eval.adb (Compile_Time_Known_Value): Improve predicate to avoid assertion failure; found working on this ticket; this change does not affect the behavior of the compiler because this subprogram has an exception handler that returns False when the assertion fails. * sem_util.adb (Needs_Result_Accessibility_Level): Do not return False for dispatching operations compiled with Ada_Version < 2012 since they they may be overridden by primitives compiled with Ada_Version >= Ada_2012. --- gcc/ada/debug.adb | 6 +- gcc/ada/exp_attr.adb | 41 +- gcc/ada/exp_ch3.adb | 136 +++++- gcc/ada/exp_ch3.ads | 16 +- gcc/ada/exp_ch6.adb | 314 +++++++++++-- gcc/ada/exp_ch6.ads | 20 +- gcc/ada/freeze.adb | 115 +---- gcc/ada/frontend.adb | 11 + gcc/ada/sem_ch3.adb | 25 +- gcc/ada/sem_ch6.adb | 1186 ++++++++++++++++++++++++++++++++++++++------------ gcc/ada/sem_ch6.ads | 19 + gcc/ada/sem_eval.adb | 1 + gcc/ada/sem_util.adb | 7 +- 13 files changed, 1438 insertions(+), 459 deletions(-) diff --git a/gcc/ada/debug.adb b/gcc/ada/debug.adb index 94e729e9bcc..d84d114bef1 100644 --- a/gcc/ada/debug.adb +++ b/gcc/ada/debug.adb @@ -189,7 +189,7 @@ package body Debug is -- d_U Disable prepending messages with "error:". -- d_V Enable verifications on the expanded tree -- d_W - -- d_X + -- d_X Disable assertions to check matching of extra formals -- d_Y -- d_Z @@ -1044,6 +1044,10 @@ package body Debug is -- d_V Enable verification of the expanded code before calling the backend -- and generate error messages on each inconsistency found. + -- d_X Disable assertions to check matching of extra formals; switch added + -- temporarily to disable these checks until this work is complete if + -- they cause unexpected assertion failures. + -- d1 Error messages have node numbers where possible. Normally error -- messages have only source locations. This option is useful when -- debugging errors caused by expanded code, where the source location diff --git a/gcc/ada/exp_attr.adb b/gcc/ada/exp_attr.adb index 25f16276c5e..9c8d80ffe25 100644 --- a/gcc/ada/exp_attr.adb +++ b/gcc/ada/exp_attr.adb @@ -2316,19 +2316,40 @@ package body Exp_Attr is if Is_Access_Protected_Subprogram_Type (Btyp) then Expand_Access_To_Protected_Op (N, Pref, Typ); - -- If prefix is a subprogram that has class-wide preconditions and - -- an indirect-call wrapper (ICW) of such subprogram is available - -- then replace the prefix by the ICW. - elsif Is_Access_Subprogram_Type (Btyp) and then Is_Entity_Name (Pref) - and then Present (Class_Preconditions (Entity (Pref))) - and then Present (Indirect_Call_Wrapper (Entity (Pref))) then - Rewrite (Pref, - New_Occurrence_Of - (Indirect_Call_Wrapper (Entity (Pref)), Loc)); - Analyze_And_Resolve (N, Typ); + -- If prefix is a subprogram that has class-wide preconditions + -- and an indirect-call wrapper (ICW) of the subprogram is + -- available then replace the prefix by the ICW. + + if Present (Class_Preconditions (Entity (Pref))) + and then Present (Indirect_Call_Wrapper (Entity (Pref))) + then + Rewrite (Pref, + New_Occurrence_Of + (Indirect_Call_Wrapper (Entity (Pref)), Loc)); + Analyze_And_Resolve (N, Typ); + end if; + + -- Ensure the availability of the extra formals to check that + -- they match. + + if not Is_Frozen (Entity (Pref)) + or else From_Limited_With (Etype (Entity (Pref))) + then + Create_Extra_Formals (Entity (Pref)); + end if; + + if not Is_Frozen (Btyp_DDT) + or else From_Limited_With (Etype (Btyp_DDT)) + then + Create_Extra_Formals (Btyp_DDT); + end if; + + pragma Assert + (Extra_Formals_Match_OK + (E => Entity (Pref), Ref_E => Btyp_DDT)); -- If prefix is a type name, this is a reference to the current -- instance of the type, within its initialization procedure. diff --git a/gcc/ada/exp_ch3.adb b/gcc/ada/exp_ch3.adb index 1e70b584f22..90f01ca2747 100644 --- a/gcc/ada/exp_ch3.adb +++ b/gcc/ada/exp_ch3.adb @@ -44,7 +44,6 @@ with Exp_Dist; use Exp_Dist; with Exp_Put_Image; with Exp_Smem; use Exp_Smem; with Exp_Strm; use Exp_Strm; -with Exp_Tss; use Exp_Tss; with Exp_Util; use Exp_Util; with Freeze; use Freeze; with Ghost; use Ghost; @@ -408,15 +407,6 @@ package body Exp_Ch3 is -- Freeze entities of all predefined primitive operations. This is needed -- because the bodies of these operations do not normally do any freezing. - function Stream_Operation_OK - (Typ : Entity_Id; - Operation : TSS_Name_Type) return Boolean; - -- Check whether the named stream operation must be emitted for a given - -- type. The rules for inheritance of stream attributes by type extensions - -- are enforced by this function. Furthermore, various restrictions prevent - -- the generation of these operations, as a useful optimization or for - -- certification purposes and to save unnecessary generated code. - -------------------------- -- Adjust_Discriminants -- -------------------------- @@ -5380,6 +5370,10 @@ package body Exp_Ch3 is procedure Register_Dispatch_Table_Wrappers (Typ : Entity_Id); -- Register dispatch-table wrappers in the dispatch table of Typ + procedure Validate_Tagged_Type_Extra_Formals (Typ : Entity_Id); + -- Check extra formals of dispatching primitives of tagged type Typ. + -- Used in pragma Debug. + --------------------------------------- -- Build_Class_Condition_Subprograms -- --------------------------------------- @@ -5509,6 +5503,78 @@ package body Exp_Ch3 is end loop; end Register_Dispatch_Table_Wrappers; + ---------------------------------------- + -- Validate_Tagged_Type_Extra_Formals -- + ---------------------------------------- + + procedure Validate_Tagged_Type_Extra_Formals (Typ : Entity_Id) is + Ovr_Subp : Entity_Id; + Elmt : Elmt_Id; + Subp : Entity_Id; + + begin + pragma Assert (not Is_Class_Wide_Type (Typ)); + + -- No check required if expansion is not active since we never + -- generate extra formals in such case. + + if not Expander_Active then + return; + end if; + + Elmt := First_Elmt (Primitive_Operations (Typ)); + while Present (Elmt) loop + Subp := Node (Elmt); + + -- Extra formals of a dispatching primitive must match: + + -- 1) The extra formals of its covered interface primitive + + if Present (Interface_Alias (Subp)) then + pragma Assert + (Extra_Formals_Match_OK + (E => Interface_Alias (Subp), + Ref_E => Alias (Subp))); + end if; + + -- 2) The extra formals of its renamed primitive + + if Present (Alias (Subp)) then + pragma Assert + (Extra_Formals_Match_OK + (E => Subp, + Ref_E => Ultimate_Alias (Subp))); + end if; + + -- 3) The extra formals of its overridden primitive + + if Present (Overridden_Operation (Subp)) then + Ovr_Subp := Overridden_Operation (Subp); + + -- Handle controlling function wrapper + + if Is_Wrapper (Subp) + and then Ultimate_Alias (Ovr_Subp) = Subp + then + if Present (Overridden_Operation (Ovr_Subp)) then + pragma Assert + (Extra_Formals_Match_OK + (E => Subp, + Ref_E => Overridden_Operation (Ovr_Subp))); + end if; + + else + pragma Assert + (Extra_Formals_Match_OK + (E => Subp, + Ref_E => Ovr_Subp)); + end if; + end if; + + Next_Elmt (Elmt); + end loop; + end Validate_Tagged_Type_Extra_Formals; + -- Local variables Typ : constant Node_Id := Entity (N); @@ -5897,28 +5963,58 @@ package body Exp_Ch3 is -- inherited functions, then add their bodies to the freeze actions. Append_Freeze_Actions (Typ, Wrapper_Body_List); + end if; - -- Create extra formals for the primitive operations of the type. - -- This must be done before analyzing the body of the initialization - -- procedure, because a self-referential type might call one of these - -- primitives in the body of the init_proc itself. + -- Create extra formals for the primitive operations of the type. + -- This must be done before analyzing the body of the initialization + -- procedure, because a self-referential type might call one of these + -- primitives in the body of the init_proc itself. + -- + -- This is not needed: + -- 1) If expansion is disabled, because extra formals are only added + -- when we are generating code. + -- + -- 2) For types with foreign convention since primitives with foreign + -- convention don't have extra formals and AI95-117 requires that + -- all primitives of a tagged type inherit the convention. + if Expander_Active + and then Is_Tagged_Type (Typ) + and then not Has_Foreign_Convention (Typ) + then declare Elmt : Elmt_Id; - Subp : Entity_Id; + E : Entity_Id; begin + -- Add extra formals to primitive operations + Elmt := First_Elmt (Primitive_Operations (Typ)); while Present (Elmt) loop - Subp := Node (Elmt); - if not Has_Foreign_Convention (Subp) - and then not Is_Predefined_Dispatching_Operation (Subp) + Create_Extra_Formals (Node (Elmt)); + Next_Elmt (Elmt); + end loop; + + -- Add extra formals to renamings of primitive operations. The + -- addition of extra formals is done in two steps to minimize + -- the compile time required for this action; the evaluation of + -- Find_Dispatching_Type() and Contains() is only done here for + -- renamings that are not primitive operations. + + E := First_Entity (Scope (Typ)); + while Present (E) loop + if Is_Dispatching_Operation (E) + and then Present (Alias (E)) + and then Find_Dispatching_Type (E) = Typ + and then not Contains (Primitive_Operations (Typ), E) then - Create_Extra_Formals (Subp); + Create_Extra_Formals (E); end if; - Next_Elmt (Elmt); + Next_Entity (E); end loop; + + pragma Debug (Validate_Tagged_Type_Extra_Formals (Typ)); end; end if; diff --git a/gcc/ada/exp_ch3.ads b/gcc/ada/exp_ch3.ads index f7d43c4aa7e..24e2263296d 100644 --- a/gcc/ada/exp_ch3.ads +++ b/gcc/ada/exp_ch3.ads @@ -25,9 +25,10 @@ -- Expand routines for chapter 3 constructs -with Types; use Types; -with Elists; use Elists; -with Uintp; use Uintp; +with Types; use Types; +with Elists; use Elists; +with Exp_Tss; use Exp_Tss; +with Uintp; use Uintp; package Exp_Ch3 is @@ -207,4 +208,13 @@ package Exp_Ch3 is -- Make_Predefined_Primitive_Eq_Spec; see there for description of -- the Renamed_Eq parameter. + function Stream_Operation_OK + (Typ : Entity_Id; + Operation : TSS_Name_Type) return Boolean; + -- Check whether the named stream operation must be emitted for a given + -- type. The rules for inheritance of stream attributes by type extensions + -- are enforced by this function. Furthermore, various restrictions prevent + -- the generation of these operations, as a useful optimization or for + -- certification purposes and to save unnecessary generated code. + end Exp_Ch3; diff --git a/gcc/ada/exp_ch6.adb b/gcc/ada/exp_ch6.adb index 0fa97688c5b..fce7a7cebf5 100644 --- a/gcc/ada/exp_ch6.adb +++ b/gcc/ada/exp_ch6.adb @@ -214,7 +214,8 @@ package body Exp_Ch6 is (Subp_Call : Node_Id; Subp_Id : Entity_Id) return Boolean; -- Given a subprogram call to the given subprogram return True if the - -- names of BIP extra actual and formal parameters match. + -- names of BIP extra actual and formal parameters match, and the number + -- of actuals (including extra actuals) matches the number of formals. function Check_Number_Of_Actuals (Subp_Call : Node_Id; @@ -314,15 +315,6 @@ package body Exp_Ch6 is -- Expand simple return from function. In the case where we are returning -- from a function body this is called by Expand_N_Simple_Return_Statement. - function Has_BIP_Extra_Formal - (E : Entity_Id; - Kind : BIP_Formal_Kind) return Boolean; - -- Given a frozen subprogram, subprogram type, entry or entry family, - -- return True if E has the BIP extra formal associated with Kind. It must - -- be invoked with a frozen entity or a subprogram type of a dispatching - -- call since we can only rely on the availability of the extra formals - -- on these entities. - procedure Insert_Post_Call_Actions (N : Node_Id; Post_Call : List_Id); -- Insert the Post_Call list previously produced by routine Expand_Actuals -- or Expand_Call_Helper into the tree. @@ -3342,9 +3334,53 @@ package body Exp_Ch6 is ------------------------------ procedure Check_Subprogram_Variant is + + function Duplicate_Params_Without_Extra_Actuals + (Call_Node : Node_Id) return List_Id; + -- Duplicate actual parameters of Call_Node into New_Call without + -- extra actuals. + + -------------------------------------------- + -- Duplicate_Params_Without_Extra_Actuals -- + -------------------------------------------- + + function Duplicate_Params_Without_Extra_Actuals + (Call_Node : Node_Id) return List_Id + is + Proc_Id : constant Entity_Id := Entity (Name (Call_Node)); + Actuals : constant List_Id := Parameter_Associations (Call_Node); + NL : List_Id; + Actual : Node_Or_Entity_Id; + Formal : Entity_Id; + + begin + if Actuals = No_List then + return No_List; + + else + NL := New_List; + Actual := First (Actuals); + Formal := First_Formal (Proc_Id); + + while Present (Formal) + and then Formal /= Extra_Formals (Proc_Id) + loop + Append (New_Copy (Actual), NL); + Next (Actual); + + Next_Formal (Formal); + end loop; + + return NL; + end if; + end Duplicate_Params_Without_Extra_Actuals; + + -- Local variables + Variant_Prag : constant Node_Id := Get_Pragma (Current_Scope, Pragma_Subprogram_Variant); + New_Call : Node_Id; Pragma_Arg1 : Node_Id; Variant_Proc : Entity_Id; @@ -3373,12 +3409,17 @@ package body Exp_Ch6 is Variant_Proc := Entity (Pragma_Arg1); - Insert_Action (Call_Node, + New_Call := Make_Procedure_Call_Statement (Loc, Name => New_Occurrence_Of (Variant_Proc, Loc), Parameter_Associations => - New_Copy_List (Parameter_Associations (Call_Node)))); + Duplicate_Params_Without_Extra_Actuals (Call_Node)); + + Insert_Action (Call_Node, New_Call); + + pragma Assert (Etype (New_Call) /= Any_Type + or else Serious_Errors_Detected > 0); end if; end Check_Subprogram_Variant; @@ -3679,6 +3720,12 @@ package body Exp_Ch6 is end if; end if; + -- Ensure that the called subprogram has all its formals + + if not Is_Frozen (Subp) then + Create_Extra_Formals (Subp); + end if; + -- Ada 2005 (AI-345): We have a procedure call as a triggering -- alternative in an asynchronous select or as an entry call in -- a conditional or timed select. Check whether the procedure call @@ -3817,7 +3864,7 @@ package body Exp_Ch6 is and then Thunk_Entity (Current_Scope) = Subp and then Present (Extra_Formals (Subp)) then - pragma Assert (Present (Extra_Formals (Current_Scope))); + pragma Assert (Extra_Formals_Match_OK (Current_Scope, Subp)); declare Target_Formal : Entity_Id; @@ -3839,6 +3886,13 @@ package body Exp_Ch6 is Add_Actual_Parameter (Remove_Head (Extra_Actuals)); end loop; + -- Mark the call as processed build-in-place call; required + -- to avoid adding the extra formals twice. + + if Nkind (Call_Node) = N_Function_Call then + Set_Is_Expanded_Build_In_Place_Call (Call_Node); + end if; + Expand_Actuals (Call_Node, Subp, Post_Call); pragma Assert (Is_Empty_List (Post_Call)); pragma Assert (Check_Number_Of_Actuals (Call_Node, Subp)); @@ -6401,8 +6455,13 @@ package body Exp_Ch6 is if Nkind (Exp) = N_Function_Call then pragma Assert (Ekind (Scope_Id) = E_Function); + + -- This assertion works fine because Is_Build_In_Place_Function_Call + -- returns True for BIP function calls but also for function calls + -- that have BIP formals. + pragma Assert - (Is_Build_In_Place_Function (Scope_Id) = + (Has_BIP_Formals (Scope_Id) = Is_Build_In_Place_Function_Call (Exp)); null; end if; @@ -6440,7 +6499,7 @@ package body Exp_Ch6 is pragma Assert (Comes_From_Extended_Return_Statement (N) or else not Is_Build_In_Place_Function_Call (Exp) - or else Is_Build_In_Place_Function (Scope_Id)); + or else Has_BIP_Formals (Scope_Id)); if not Comes_From_Extended_Return_Statement (N) and then Is_Build_In_Place_Function (Scope_Id) @@ -7044,8 +7103,9 @@ package body Exp_Ch6 is -------------------------- function Has_BIP_Extra_Formal - (E : Entity_Id; - Kind : BIP_Formal_Kind) return Boolean + (E : Entity_Id; + Kind : BIP_Formal_Kind; + Must_Be_Frozen : Boolean := True) return Boolean is Extra_Formal : Entity_Id := Extra_Formals (E); @@ -7055,7 +7115,7 @@ package body Exp_Ch6 is -- extra formals are added when the target subprogram is frozen; see -- Expand_Dispatching_Call). - pragma Assert (Is_Frozen (E) + pragma Assert ((Is_Frozen (E) or else not Must_Be_Frozen) or else (Ekind (E) = E_Subprogram_Type and then Is_Dispatch_Table_Entity (E)) or else (Is_Dispatching_Operation (E) @@ -7684,7 +7744,7 @@ package body Exp_Ch6 is or else (Kind = E_Subprogram_Type and then Typ /= Standard_Void_Type)) and then Is_Build_In_Place_Result_Type (Typ) - and then not (Is_Imported (E) and then Has_Foreign_Convention (E)); + and then not Has_Foreign_Convention (E); end Is_Build_In_Place_Function; ------------------------------------- @@ -7739,12 +7799,29 @@ package body Exp_Ch6 is raise Program_Error; end if; - declare - Result : constant Boolean := Is_Build_In_Place_Function (Function_Id); - -- So we can stop here in the debugger - begin - return Result; - end; + if Is_Build_In_Place_Function (Function_Id) then + return True; + + -- True also if the function has BIP Formals + + else + declare + Kind : constant Entity_Kind := Ekind (Function_Id); + + begin + if (Kind in E_Function | E_Generic_Function + or else (Kind = E_Subprogram_Type + and then + Etype (Function_Id) /= Standard_Void_Type)) + and then Has_BIP_Formals (Function_Id) + then + -- So we can stop here in the debugger + return True; + else + return False; + end if; + end; + end if; end Is_Build_In_Place_Function_Call; ----------------------------------- @@ -8413,6 +8490,11 @@ package body Exp_Ch6 is -- initialization expression of the object to Empty, which would be -- illegal Ada, and would cause gigi to misallocate X. + Is_OK_Return_Object : constant Boolean := + Is_Return_Object (Obj_Def_Id) + and then + not Has_Foreign_Convention (Return_Applies_To (Scope (Obj_Def_Id))); + -- Start of processing for Make_Build_In_Place_Call_In_Object_Declaration begin @@ -8465,7 +8547,7 @@ package body Exp_Ch6 is -- the result object is in a different (transient) scope, so won't cause -- freezing. - if Definite and then not Is_Return_Object (Obj_Def_Id) then + if Definite and then not Is_OK_Return_Object then -- The presence of an address clause complicates the build-in-place -- expansion because the indicated address must be processed before @@ -8548,7 +8630,7 @@ package body Exp_Ch6 is -- really be directly built in place in the aggregate and not in a -- temporary. ???) - if Is_Return_Object (Obj_Def_Id) then + if Is_OK_Return_Object then Pass_Caller_Acc := True; -- When the enclosing function has a BIP_Alloc_Form formal then we @@ -8733,7 +8815,7 @@ package body Exp_Ch6 is -- itself the return expression of an enclosing BIP function, then mark -- the object as having no initialization. - if Definite and then not Is_Return_Object (Obj_Def_Id) then + if Definite and then not Is_OK_Return_Object then -- The related object declaration is encased in a transient block -- because the build-in-place function call contains at least one @@ -9090,7 +9172,7 @@ package body Exp_Ch6 is and then not No_Run_Time_Mode and then (Has_Task (Typ) or else (Is_Class_Wide_Type (Typ) - and then Is_Limited_Record (Typ) + and then Is_Limited_Record (Etype (Typ)) and then not Has_Aspect (Etype (Typ), Aspect_No_Task_Parts))); end Might_Have_Tasks; @@ -9100,7 +9182,6 @@ package body Exp_Ch6 is ---------------------------- function Needs_BIP_Task_Actuals (Func_Id : Entity_Id) return Boolean is - pragma Assert (Is_Build_In_Place_Function (Func_Id)); Subp_Id : Entity_Id; Func_Typ : Entity_Id; @@ -9125,6 +9206,12 @@ package body Exp_Ch6 is Func_Typ := Underlying_Type (Etype (Subp_Id)); + -- Functions returning types with foreign convention don't have extra + -- formals. + + if Has_Foreign_Convention (Func_Typ) then + return False; + -- At first sight, for all the following cases, we could add assertions -- to ensure that if Func_Id is frozen then the computed result matches -- with the availability of the task master extra formal; unfortunately @@ -9132,7 +9219,7 @@ package body Exp_Ch6 is -- (that is, Is_Frozen has been set by Freeze_Entity but it has not -- completed its work). - if Has_Task (Func_Typ) then + elsif Has_Task (Func_Typ) then return True; elsif Ekind (Func_Id) = E_Function then @@ -9164,8 +9251,6 @@ package body Exp_Ch6 is Typ : constant Entity_Id := Underlying_Type (Etype (Func_Id)); begin - pragma Assert (Is_Build_In_Place_Function (Func_Id)); - -- A formal giving the finalization master is needed for build-in-place -- functions whose result type needs finalization or is a tagged type. -- Tagged primitive build-in-place functions need such a formal because @@ -9177,7 +9262,8 @@ package body Exp_Ch6 is -- such build-in-place functions, primitive or not. return not Restriction_Active (No_Finalization) - and then (Needs_Finalization (Typ) or else Is_Tagged_Type (Typ)); + and then (Needs_Finalization (Typ) or else Is_Tagged_Type (Typ)) + and then not Has_Foreign_Convention (Typ); end Needs_BIP_Finalization_Master; -------------------------- @@ -9188,8 +9274,6 @@ package body Exp_Ch6 is Typ : constant Entity_Id := Underlying_Type (Etype (Func_Id)); begin - pragma Assert (Is_Build_In_Place_Function (Func_Id)); - -- A formal giving the allocation method is needed for build-in-place -- functions whose result type is returned on the secondary stack or -- is a tagged type. Tagged primitive build-in-place functions need @@ -9201,7 +9285,8 @@ package body Exp_Ch6 is -- to be passed to all such build-in-place functions, primitive or not. return not Restriction_Active (No_Secondary_Stack) - and then (Needs_Secondary_Stack (Typ) or else Is_Tagged_Type (Typ)); + and then (Needs_Secondary_Stack (Typ) or else Is_Tagged_Type (Typ)) + and then not Has_Foreign_Convention (Typ); end Needs_BIP_Alloc_Form; ------------------------------------- @@ -9496,6 +9581,161 @@ package body Exp_Ch6 is return Unqual_BIP_Function_Call (Expr); end Unqual_BIP_Iface_Function_Call; + ------------------------------- + -- Validate_Subprogram_Calls -- + ------------------------------- + + procedure Validate_Subprogram_Calls (N : Node_Id) is + + function Process_Node (Nod : Node_Id) return Traverse_Result; + -- Function to traverse the subtree of N using Traverse_Proc. + + ------------------ + -- Process_Node -- + ------------------ + + function Process_Node (Nod : Node_Id) return Traverse_Result is + begin + case Nkind (Nod) is + when N_Entry_Call_Statement + | N_Procedure_Call_Statement + | N_Function_Call + => + declare + Call_Node : Node_Id renames Nod; + Subp : Entity_Id; + + begin + -- Call using access to subprogram with explicit dereference + + if Nkind (Name (Call_Node)) = N_Explicit_Dereference then + Subp := Etype (Name (Call_Node)); + + -- Prefix notation calls + + elsif Nkind (Name (Call_Node)) = N_Selected_Component then + Subp := Entity (Selector_Name (Name (Call_Node))); + + -- Call to member of entry family, where Name is an indexed + -- component, with the prefix being a selected component + -- giving the task and entry family name, and the index + -- being the entry index. + + elsif Nkind (Name (Call_Node)) = N_Indexed_Component then + Subp := + Entity (Selector_Name (Prefix (Name (Call_Node)))); + + -- Normal case + + else + Subp := Entity (Name (Call_Node)); + end if; + + pragma Assert (Check_BIP_Actuals (Call_Node, Subp)); + end; + + -- Skip generic bodies + + when N_Package_Body => + if Ekind (Unique_Defining_Entity (Nod)) = E_Generic_Package then + return Skip; + end if; + + when N_Subprogram_Body => + if Ekind (Unique_Defining_Entity (Nod)) in E_Generic_Function + | E_Generic_Procedure + then + return Skip; + end if; + + -- Nodes we want to ignore + + -- Skip calls placed in the full declaration of record types since + -- the call will be performed by their Init Proc; for example, + -- calls initializing default values of discriminants or calls + -- providing the initial value of record type components. Other + -- full type declarations are processed because they may have + -- calls that must be checked. For example: + + -- type T is array (1 .. Some_Function_Call (...)) of Some_Type; + + -- ??? More work needed here to handle the following case: + + -- type Rec is record + -- F : String (1 .. ); + -- end record; + + when N_Full_Type_Declaration => + if Is_Record_Type (Defining_Entity (Nod)) then + return Skip; + end if; + + -- Skip calls placed in subprogram specifications since function + -- calls initializing default parameter values will be processed + -- when the call to the subprogram is found (if the default actual + -- parameter is required), and calls found in aspects will be + -- processed when their corresponding pragma is found, or in the + -- specific case of class-wide pre-/postconditions, when their + -- helpers are found. + + when N_Procedure_Specification + | N_Function_Specification + => + return Skip; + + when N_Abstract_Subprogram_Declaration + | N_At_Clause + | N_Call_Marker + | N_Empty + | N_Enumeration_Representation_Clause + | N_Enumeration_Type_Definition + | N_Function_Instantiation + | N_Freeze_Generic_Entity + | N_Generic_Function_Renaming_Declaration + | N_Generic_Package_Renaming_Declaration + | N_Generic_Procedure_Renaming_Declaration + | N_Generic_Package_Declaration + | N_Generic_Subprogram_Declaration + | N_Itype_Reference + | N_Number_Declaration + | N_Package_Instantiation + | N_Package_Renaming_Declaration + | N_Pragma + | N_Procedure_Instantiation + | N_Protected_Type_Declaration + | N_Record_Representation_Clause + | N_Validate_Unchecked_Conversion + | N_Variable_Reference_Marker + | N_Use_Package_Clause + | N_Use_Type_Clause + | N_With_Clause + => + return Skip; + + when others => + null; + end case; + + return OK; + end Process_Node; + + procedure Check_Calls is new Traverse_Proc (Process_Node); + + -- Start of processing for Validate_Subprogram_Calls + + begin + -- No action required if we are not generating code or compiling sources + -- that have errors. + + if Serious_Errors_Detected > 0 + or else Operating_Mode /= Generate_Code + then + return; + end if; + + Check_Calls (N); + end Validate_Subprogram_Calls; + -------------- -- Warn_BIP -- -------------- diff --git a/gcc/ada/exp_ch6.ads b/gcc/ada/exp_ch6.ads index 19d0bc3ff69..66888c51a07 100644 --- a/gcc/ada/exp_ch6.ads +++ b/gcc/ada/exp_ch6.ads @@ -121,6 +121,18 @@ package Exp_Ch6 is -- The returned node is the root of the procedure body which will replace -- the original function body, which is not needed for the C program. + function Has_BIP_Extra_Formal + (E : Entity_Id; + Kind : BIP_Formal_Kind; + Must_Be_Frozen : Boolean := True) return Boolean; + -- Given a subprogram, subprogram type, entry or entry family, return True + -- if E has the BIP extra formal associated with Kind. In general this + -- subprogram must be invoked with a frozen entity or a subprogram type of + -- a dispatching call since we can only rely on the availability of extra + -- formals on these entities; this requirement can be relaxed using the + -- formal Must_Be_Frozen in scenarios where we know that the entity has + -- the extra formals. + procedure Install_Class_Preconditions_Check (Call_Node : Node_Id); -- Install check of class-wide preconditions on the caller. @@ -137,7 +149,8 @@ package Exp_Ch6 is function Is_Build_In_Place_Function_Call (N : Node_Id) return Boolean; -- Ada 2005 (AI-318-02): Returns True if N denotes a call to a function -- that requires handling as a build-in-place call (possibly qualified or - -- converted). + -- converted); that is, BIP function calls, and calls to functions with + -- inherited BIP formals. function Is_Build_In_Place_Result_Type (Typ : Entity_Id) return Boolean; -- Ada 2005 (AI-318-02): Returns True if functions returning the type use @@ -265,6 +278,11 @@ package Exp_Ch6 is -- to reference the secondary dispatch table of an interface; otherwise -- return Empty. + procedure Validate_Subprogram_Calls (N : Node_Id); + -- Check that the number of actuals (including extra actuals) of calls in + -- the subtree N match their corresponding formals; check also that the + -- names of BIP extra actuals and formals match. + private pragma Inline (Is_Build_In_Place_Return_Object); diff --git a/gcc/ada/freeze.adb b/gcc/ada/freeze.adb index 1fdc9d0d60e..032c73d3dfb 100644 --- a/gcc/ada/freeze.adb +++ b/gcc/ada/freeze.adb @@ -4984,6 +4984,7 @@ package body Freeze is and then Convention (Desig) /= Convention_Protected then Set_Is_Frozen (Desig); + Create_Extra_Formals (Desig); end if; end Check_Itype; @@ -7131,11 +7132,11 @@ package body Freeze is Check_Debug_Info_Needed (E); - -- AI-117 requires that the convention of a partial view be the - -- same as the convention of the full view. Note that this is a - -- recognized breach of privacy, but it's essential for logical - -- consistency of representation, and the lack of a rule in - -- RM95 was an oversight. + -- AI95-117 requires that the convention of a partial view be + -- the same as the convention of the full view. Note that this + -- is a recognized breach of privacy, but it's essential for + -- logical consistency of representation, and the lack of a + -- rule in RM95 was an oversight. Set_Convention (E, Convention (Full_View (E))); @@ -7360,7 +7361,7 @@ package body Freeze is if Is_Composite_Type (E) then - -- AI-117 requires that all new primitives of a tagged type must + -- AI95-117 requires that all new primitives of a tagged type must -- inherit the convention of the full view of the type. Inherited -- and overriding operations are defined to inherit the convention -- of their parent or overridden subprogram (also specified in @@ -8268,7 +8269,7 @@ package body Freeze is if Present (Nam) and then Ekind (Nam) = E_Function and then Nkind (Parent (N)) = N_Function_Call - and then Convention (Nam) = Convention_Ada + and then not Has_Foreign_Convention (Nam) then Create_Extra_Formals (Nam); end if; @@ -9875,77 +9876,11 @@ package body Freeze is ----------------------- procedure Freeze_Subprogram (E : Entity_Id) is - function Check_Extra_Formals (E : Entity_Id) return Boolean; - -- Return True if the decoration of the attributes associated with extra - -- formals are properly set. procedure Set_Profile_Convention (Subp_Id : Entity_Id); -- Set the conventions of all anonymous access-to-subprogram formals and -- result subtype of subprogram Subp_Id to the convention of Subp_Id. - ------------------------- - -- Check_Extra_Formals -- - ------------------------- - - function Check_Extra_Formals (E : Entity_Id) return Boolean is - Last_Formal : Entity_Id := Empty; - Formal : Entity_Id; - Has_Extra_Formals : Boolean := False; - - begin - -- No check required if expansion is disabled because extra - -- formals are only generated when we are generating code. - -- See Create_Extra_Formals. - - if not Expander_Active then - return True; - end if; - - -- Check attribute Extra_Formal: If available, it must be set only - -- on the last formal of E. - - Formal := First_Formal (E); - while Present (Formal) loop - if Present (Extra_Formal (Formal)) then - if Has_Extra_Formals then - return False; - end if; - - Has_Extra_Formals := True; - end if; - - Last_Formal := Formal; - Next_Formal (Formal); - end loop; - - -- Check attribute Extra_Accessibility_Of_Result - - if Ekind (E) in E_Function | E_Subprogram_Type - and then Needs_Result_Accessibility_Level (E) - and then No (Extra_Accessibility_Of_Result (E)) - then - return False; - end if; - - -- Check attribute Extra_Formals: If E has extra formals, then this - -- attribute must point to the first extra formal of E. - - if Has_Extra_Formals then - return Present (Extra_Formals (E)) - and then Present (Extra_Formal (Last_Formal)) - and then Extra_Formal (Last_Formal) = Extra_Formals (E); - - -- When E has no formals, the first extra formal is available through - -- the Extra_Formals attribute. - - elsif Present (Extra_Formals (E)) then - return No (First_Formal (E)); - - else - return True; - end if; - end Check_Extra_Formals; - ---------------------------- -- Set_Profile_Convention -- ---------------------------- @@ -10084,30 +10019,26 @@ package body Freeze is -- that we know the convention. if not Has_Foreign_Convention (E) then - if No (Extra_Formals (E)) then - -- Extra formals are shared by derived subprograms; therefore, if - -- the ultimate alias of E has been frozen before E then the extra - -- formals have been added, but the attribute Extra_Formals is - -- still unset (and must be set now). + -- Extra formals of dispatching operations are added later by + -- Expand_Freeze_Record_Type, which also adds extra formals to + -- internal entities built to handle interface types. - if Present (Alias (E)) - and then Is_Frozen (Ultimate_Alias (E)) - and then Present (Extra_Formals (Ultimate_Alias (E))) - and then Last_Formal (Ultimate_Alias (E)) = Last_Formal (E) - then - Set_Extra_Formals (E, Extra_Formals (Ultimate_Alias (E))); + if not Is_Dispatching_Operation (E) then + Create_Extra_Formals (E); - if Ekind (E) = E_Function then - Set_Extra_Accessibility_Of_Result (E, - Extra_Accessibility_Of_Result (Ultimate_Alias (E))); - end if; - else - Create_Extra_Formals (E); - end if; + pragma Assert + ((Ekind (E) = E_Subprogram_Type + and then Extra_Formals_OK (E)) + or else + (Is_Subprogram (E) + and then Extra_Formals_OK (E) + and then + (No (Overridden_Operation (E)) + or else Extra_Formals_Match_OK (E, + Ultimate_Alias (Overridden_Operation (E)))))); end if; - pragma Assert (Check_Extra_Formals (E)); Set_Mechanisms (E); -- If this is convention Ada and a Valued_Procedure, that's odd diff --git a/gcc/ada/frontend.adb b/gcc/ada/frontend.adb index 12c91b11d9a..cdca67bf397 100644 --- a/gcc/ada/frontend.adb +++ b/gcc/ada/frontend.adb @@ -30,6 +30,7 @@ with Checks; with CStand; with Debug; use Debug; with Elists; +with Exp_Ch6; with Exp_Dbug; with Exp_Unst; with Fmap; @@ -523,6 +524,16 @@ begin VAST.Check_Tree (Cunit (Main_Unit)); end if; + -- Validate all the subprogram calls; this work will be done by VAST; in + -- the meantime it is done to check extra formals and it can be disabled + -- using -gnatd_X (which also disables all the other assertions on extra + -- formals). It is invoked using pragma Debug to avoid adding any cost + -- when the compiler is built with assertions disabled. + + if not Debug_Flag_Underscore_XX then + pragma Debug (Exp_Ch6.Validate_Subprogram_Calls (Cunit (Main_Unit))); + end if; + -- Dump the source now. Note that we do this as soon as the analysis -- of the tree is complete, because it is not just a dump in the case -- of -gnatD, where it rewrites all source locations in the tree. diff --git a/gcc/ada/sem_ch3.adb b/gcc/ada/sem_ch3.adb index 95ffbe00ba4..dbe4d72626e 100644 --- a/gcc/ada/sem_ch3.adb +++ b/gcc/ada/sem_ch3.adb @@ -1318,7 +1318,8 @@ package body Sem_Ch3 is Check_Restriction (No_Access_Subprograms, T_Def); - Create_Extra_Formals (Desig_Type); + -- Addition of extra formals must be delayed till the freeze point so + -- that we know the convention. end Access_Subprogram_Declaration; ---------------------------- @@ -11788,11 +11789,9 @@ package body Sem_Ch3 is Insert_Before (Typ_Decl, Decl); Analyze (Decl); - -- If an access to subprogram, create the extra formals - - if Present (Acc_Def) then - Create_Extra_Formals (Designated_Type (Anon_Access)); - end if; + -- At first sight we could add here the extra formals of an access to + -- subprogram; however, it must delayed till the freeze point so that + -- we know the convention. if Nkind (Comp_Def) = N_Component_Definition then Rewrite (Comp_Def, @@ -16053,12 +16052,12 @@ package body Sem_Ch3 is Next_Formal (Formal); end loop; - -- Extra formals are shared between the parent subprogram and the - -- derived subprogram (implicit in the above copy of formals), unless - -- the parent type is a limited interface type; hence we must inherit - -- also the reference to the first extra formal. When the parent type is - -- an interface the extra formals will be added when the subprogram is - -- frozen (see Freeze.Freeze_Subprogram). + -- Extra formals are shared between the parent subprogram and this + -- internal entity built by Derive_Subprogram (implicit in the above + -- copy of formals), unless the parent type is a limited interface type; + -- hence we must inherit also the reference to the first extra formal. + -- When the parent type is an interface, the extra formals will be added + -- when the tagged type is frozen (see Expand_Freeze_Record_Type). if not Is_Limited_Interface (Parent_Type) then Set_Extra_Formals (New_Subp, Extra_Formals (Parent_Subp)); @@ -16099,7 +16098,7 @@ package body Sem_Ch3 is Copy_Strub_Mode (New_Subp, Alias (New_Subp)); -- Derived subprograms of a tagged type must inherit the convention - -- of the parent subprogram (a requirement of AI-117). Derived + -- of the parent subprogram (a requirement of AI95-117). Derived -- subprograms of untagged types simply get convention Ada by default. -- If the derived type is a tagged generic formal type with unknown diff --git a/gcc/ada/sem_ch6.adb b/gcc/ada/sem_ch6.adb index d28de10d3d6..454db66dd2c 100644 --- a/gcc/ada/sem_ch6.adb +++ b/gcc/ada/sem_ch6.adb @@ -34,6 +34,7 @@ with Einfo.Utils; use Einfo.Utils; with Elists; use Elists; with Errout; use Errout; with Expander; use Expander; +with Exp_Ch3; use Exp_Ch3; with Exp_Ch6; use Exp_Ch6; with Exp_Ch9; use Exp_Ch9; with Exp_Dbug; use Exp_Dbug; @@ -200,6 +201,13 @@ package body Sem_Ch6 is -- This procedure makes S, a new overloaded entity, into the first visible -- entity with that name. + function Has_Reliable_Extra_Formals (E : Entity_Id) return Boolean; + -- E is the entity for a subprogram spec. Returns False for abstract + -- predefined dispatching primitives of Root_Controlled since they + -- cannot have extra formals (this is required to build the runtime); + -- it also returns False for predefined stream dispatching operations + -- not emitted by the frontend. Otherwise returns True. + function Is_Non_Overriding_Operation (Prev_E : Entity_Id; New_E : Entity_Id) return Boolean; @@ -3352,7 +3360,8 @@ package body Sem_Ch6 is or else (Is_Class_Wide_Type (Designated_Type (Etype (Scop))) and then - Is_Limited_Record (Designated_Type (Etype (Scop))))) + Is_Limited_Record + (Etype (Designated_Type (Etype (Scop)))))) and then Expander_Active then Decl := Build_Master_Declaration (Loc); @@ -8471,6 +8480,253 @@ package body Sem_Ch6 is (New_Id, Old_Id, Type_Conformant, True, Result, Err_Loc); end Check_Type_Conformant; + ----------------------------- + -- Check_Untagged_Equality -- + ----------------------------- + + procedure Check_Untagged_Equality (Eq_Op : Entity_Id) is + Eq_Decl : constant Node_Id := Unit_Declaration_Node (Eq_Op); + Typ : constant Entity_Id := Etype (First_Formal (Eq_Op)); + + procedure Freezing_Point_Warning (N : Node_Id; S : String); + -- Output a warning about the freezing point N of Typ + + function Is_Actual_Of_Instantiation + (E : Entity_Id; + Inst : Node_Id) return Boolean; + -- Return True if E is an actual parameter of instantiation Inst + + ----------------------------------- + -- Output_Freezing_Point_Warning -- + ----------------------------------- + + procedure Freezing_Point_Warning (N : Node_Id; S : String) is + begin + Error_Msg_String (1 .. S'Length) := S; + Error_Msg_Strlen := S'Length; + + if Ada_Version >= Ada_2012 then + Error_Msg_NE ("type& is frozen by ~??", N, Typ); + Error_Msg_N + ("\an equality operator cannot be declared after this point??", + N); + + else + Error_Msg_NE ("type& is frozen by ~ (Ada 2012)?y?", N, Typ); + Error_Msg_N + ("\an equality operator cannot be declared after this point" + & " (Ada 2012)?y?", N); + end if; + end Freezing_Point_Warning; + + -------------------------------- + -- Is_Actual_Of_Instantiation -- + -------------------------------- + + function Is_Actual_Of_Instantiation + (E : Entity_Id; + Inst : Node_Id) return Boolean + is + Assoc : Node_Id; + + begin + if Present (Generic_Associations (Inst)) then + Assoc := First (Generic_Associations (Inst)); + + while Present (Assoc) loop + if Present (Explicit_Generic_Actual_Parameter (Assoc)) + and then + Is_Entity_Name (Explicit_Generic_Actual_Parameter (Assoc)) + and then + Entity (Explicit_Generic_Actual_Parameter (Assoc)) = E + then + return True; + end if; + + Next (Assoc); + end loop; + end if; + + return False; + end Is_Actual_Of_Instantiation; + + -- Local variable + + Decl : Node_Id; + + -- Start of processing for Check_Untagged_Equality + + begin + -- This check applies only if we have a subprogram declaration or a + -- subprogram body that is not a completion, for an untagged record + -- type, and that is conformant with the predefined operator. + + if (Nkind (Eq_Decl) /= N_Subprogram_Declaration + and then not (Nkind (Eq_Decl) = N_Subprogram_Body + and then Acts_As_Spec (Eq_Decl))) + or else not Is_Record_Type (Typ) + or else Is_Tagged_Type (Typ) + or else not Is_User_Defined_Equality (Eq_Op) + then + return; + end if; + + -- In Ada 2012 case, we will output errors or warnings depending on + -- the setting of debug flag -gnatd.E. + + if Ada_Version >= Ada_2012 then + Error_Msg_Warn := Debug_Flag_Dot_EE; + + -- In earlier versions of Ada, nothing to do unless we are warning on + -- Ada 2012 incompatibilities (Warn_On_Ada_2012_Incompatibility set). + + else + if not Warn_On_Ada_2012_Compatibility then + return; + end if; + end if; + + -- Cases where the type has already been frozen + + if Is_Frozen (Typ) then + + -- The check applies to a primitive operation, so check that type + -- and equality operation are in the same scope. + + if Scope (Typ) /= Current_Scope then + return; + + -- If the type is a generic actual (sub)type, the operation is not + -- primitive either because the base type is declared elsewhere. + + elsif Is_Generic_Actual_Type (Typ) then + return; + + -- Here we may have an error of declaration after freezing, but we + -- must make sure not to flag the equality operator itself causing + -- the freezing when it is a subprogram body. + + else + Decl := Next (Declaration_Node (Typ)); + + while Present (Decl) and then Decl /= Eq_Decl loop + + -- The declaration of an object of the type + + if Nkind (Decl) = N_Object_Declaration + and then Etype (Defining_Identifier (Decl)) = Typ + then + Freezing_Point_Warning (Decl, "declaration"); + exit; + + -- The instantiation of a generic on the type + + elsif Nkind (Decl) in N_Generic_Instantiation + and then Is_Actual_Of_Instantiation (Typ, Decl) + then + Freezing_Point_Warning (Decl, "instantiation"); + exit; + + -- A noninstance proper body, body stub or entry body + + elsif Nkind (Decl) in N_Proper_Body + | N_Body_Stub + | N_Entry_Body + and then not Is_Generic_Instance (Defining_Entity (Decl)) + then + Freezing_Point_Warning (Decl, "body"); + exit; + + -- If we have reached the freeze node and immediately after we + -- have the body or generated code for the body, then it is the + -- body that caused the freezing and this is legal. + + elsif Nkind (Decl) = N_Freeze_Entity + and then Entity (Decl) = Typ + and then (Next (Decl) = Eq_Decl + or else + Sloc (Next (Decl)) = Sloc (Eq_Decl)) + then + return; + end if; + + Next (Decl); + end loop; + + -- Here we have a definite error of declaration after freezing + + if Ada_Version >= Ada_2012 then + Error_Msg_NE + ("equality operator must be declared before type & is " + & "frozen (RM 4.5.2 (9.8)) (Ada 2012)<<", Eq_Op, Typ); + + -- In Ada 2012 mode with error turned to warning, output one + -- more warning to warn that the equality operation may not + -- compose. This is the consequence of ignoring the error. + + if Error_Msg_Warn then + Error_Msg_N ("\equality operation may not compose??", Eq_Op); + end if; + + else + Error_Msg_NE + ("equality operator must be declared before type& is " + & "frozen (RM 4.5.2 (9.8)) (Ada 2012)?y?", Eq_Op, Typ); + end if; + + -- If we have found no freezing point and the declaration of the + -- operator could not be reached from that of the type and we are + -- in a package body, this must be because the type is declared + -- in the spec of the package. Add a message tailored to this. + + if No (Decl) and then In_Package_Body (Scope (Typ)) then + if Ada_Version >= Ada_2012 then + if Nkind (Eq_Decl) = N_Subprogram_Body then + Error_Msg_N + ("\put declaration in package spec<<", Eq_Op); + else + Error_Msg_N + ("\move declaration to package spec<<", Eq_Op); + end if; + + else + if Nkind (Eq_Decl) = N_Subprogram_Body then + Error_Msg_N + ("\put declaration in package spec (Ada 2012)?y?", + Eq_Op); + else + Error_Msg_N + ("\move declaration to package spec (Ada 2012)?y?", + Eq_Op); + end if; + end if; + end if; + end if; + + -- Now check for AI12-0352: the declaration of a user-defined primitive + -- equality operation for a record type T is illegal if it occurs after + -- a type has been derived from T. + + else + Decl := Next (Declaration_Node (Typ)); + + while Present (Decl) and then Decl /= Eq_Decl loop + if Nkind (Decl) = N_Full_Type_Declaration + and then Etype (Defining_Identifier (Decl)) = Typ + then + Error_Msg_N + ("equality operator cannot appear after derivation", Eq_Op); + Error_Msg_NE + ("an equality operator for& cannot be declared after " + & "this point??", + Decl, Typ); + end if; + + Next (Decl); + end loop; + end if; + end Check_Untagged_Equality; + --------------------------- -- Can_Override_Operator -- --------------------------- @@ -8950,6 +9206,26 @@ package body Sem_Ch6 is -- BIP_xxx denotes an extra formal for a build-in-place function. See -- the full list in exp_ch6.BIP_Formal_Kind. + function Has_Extra_Formals (E : Entity_Id) return Boolean; + -- Determines if E has its extra formals + + function Needs_Accessibility_Check_Extra + (E : Entity_Id; + Formal : Node_Id) return Boolean; + -- Determines whether the given formal of E needs an extra formal for + -- supporting accessibility checking. Returns True for both anonymous + -- access formals and formals of named access types that are marked as + -- controlling formals. The latter case can occur when the subprogram + -- Expand_Dispatching_Call creates a subprogram-type and substitutes + -- the types of access-to-class-wide actuals for the anonymous access- + -- to-specific-type of controlling formals. + + function Parent_Subprogram (Subp_Id : Entity_Id) return Entity_Id; + -- Subp_Id is a subprogram of a derived type; return its parent + -- subprogram if Subp_Id overrides a parent primitive or derives + -- from a parent primitive, and such parent primitive can have extra + -- formals. Otherwise return Empty. + ---------------------- -- Add_Extra_Formal -- ---------------------- @@ -8960,10 +9236,7 @@ package body Sem_Ch6 is Scope : Entity_Id; Suffix : String) return Entity_Id is - EF : constant Entity_Id := - Make_Defining_Identifier (Sloc (Assoc_Entity), - Chars => New_External_Name (Chars (Assoc_Entity), - Suffix => Suffix)); + EF : Entity_Id; begin -- A little optimization. Never generate an extra formal for the @@ -8974,6 +9247,10 @@ package body Sem_Ch6 is return Empty; end if; + EF := Make_Defining_Identifier (Sloc (Assoc_Entity), + Chars => New_External_Name (Chars (Assoc_Entity), + Suffix => Suffix)); + Mutate_Ekind (EF, E_In_Parameter); Set_Actual_Subtype (EF, Typ); Set_Etype (EF, Typ); @@ -8995,49 +9272,266 @@ package body Sem_Ch6 is return EF; end Add_Extra_Formal; - -- Local variables + ----------------------- + -- Has_Extra_Formals -- + ----------------------- - Formal_Type : Entity_Id; - P_Formal : Entity_Id; + function Has_Extra_Formals (E : Entity_Id) return Boolean is + begin + return Present (Extra_Formals (E)) + or else + (Ekind (E) = E_Function + and then Present (Extra_Accessibility_Of_Result (E))); + end Has_Extra_Formals; + + ------------------------------------- + -- Needs_Accessibility_Check_Extra -- + ------------------------------------- + + function Needs_Accessibility_Check_Extra + (E : Entity_Id; + Formal : Node_Id) return Boolean is + + begin + -- For dispatching operations this extra formal is not suppressed + -- since all the derivations must have matching formals. + + -- For nondispatching operations it is suppressed if we specifically + -- suppress accessibility checks at the package level for either the + -- subprogram, or the package in which it resides. However, we do + -- not suppress it simply if the scope has accessibility checks + -- suppressed, since this could cause trouble when clients are + -- compiled with a different suppression setting. The explicit checks + -- at the package level are safe from this point of view. + + if not Is_Dispatching_Operation (E) + and then + (Explicit_Suppress (E, Accessibility_Check) + or else Explicit_Suppress (Scope (E), Accessibility_Check)) + then + return False; + end if; + + -- Base_Type is applied to handle cases where there is a null + -- exclusion the formal may have an access subtype. + + return + Ekind (Base_Type (Etype (Formal))) = E_Anonymous_Access_Type + or else + (Is_Controlling_Formal (Formal) + and then Is_Access_Type (Base_Type (Etype (Formal)))); + end Needs_Accessibility_Check_Extra; + + ----------------------- + -- Parent_Subprogram -- + ----------------------- + + function Parent_Subprogram (Subp_Id : Entity_Id) return Entity_Id is + pragma Assert (not Is_Thunk (Subp_Id)); + Ovr_E : Entity_Id := Overridden_Operation (Subp_Id); + Ovr_Alias : Entity_Id; + + begin + if Present (Ovr_E) then + Ovr_Alias := Ultimate_Alias (Ovr_E); + + -- There is no real overridden subprogram if there is a mutual + -- reference between the E and its overridden operation. This + -- weird scenery occurs in the following cases: + + -- 1) Controlling function wrappers internally built by + -- Make_Controlling_Function_Wrappers. + + -- 2) Hidden overridden primitives of type extensions or private + -- extensions (cf. Find_Hidden_Overridden_Primitive). These + -- hidden primitives have suffix 'P'. + + -- 3) Overriding primitives of stub types (see the subprogram + -- Add_RACW_Primitive_Declarations_And_Bodies). + + if Ovr_Alias = Subp_Id then + pragma Assert + ((Is_Wrapper (Subp_Id) + and then Has_Controlling_Result (Subp_Id)) + or else Has_Suffix (Ovr_E, 'P') + or else Is_RACW_Stub_Type + (Find_Dispatching_Type (Subp_Id))); + + if Present (Overridden_Operation (Ovr_E)) then + Ovr_E := Overridden_Operation (Ovr_E); + + -- Ovr_E is an internal entity built by Derive_Subprogram and + -- we have no direct way to climb to the corresponding parent + -- subprogram but this internal entity has the extra formals + -- (if any) required for the purpose of checking the extra + -- formals of Subp_Id. + + else + pragma Assert (not Comes_From_Source (Ovr_E)); + end if; + + -- Use as our reference entity the ultimate renaming of the + -- overridden subprogram. + + elsif Present (Alias (Ovr_E)) then + pragma Assert (No (Overridden_Operation (Ovr_Alias)) + or else Overridden_Operation (Ovr_Alias) /= Ovr_E); + + Ovr_E := Ovr_Alias; + end if; + end if; + + if Present (Ovr_E) and then Has_Reliable_Extra_Formals (Ovr_E) then + return Ovr_E; + else + return Empty; + end if; + end Parent_Subprogram; + + -- Local variables + + Formal_Type : Entity_Id; + May_Have_Alias : Boolean; + Alias_Formal : Entity_Id := Empty; + Alias_Subp : Entity_Id := Empty; + Parent_Formal : Entity_Id := Empty; + Parent_Subp : Entity_Id := Empty; + Ref_E : Entity_Id; -- Start of processing for Create_Extra_Formals begin + pragma Assert (Is_Subprogram_Or_Entry (E) + or else Ekind (E) in E_Subprogram_Type); + -- We never generate extra formals if expansion is not active because we -- don't need them unless we are generating code. if not Expander_Active then return; - end if; + + -- Enumeration literals have no extra formal; this case occurs when + -- a function renames it. + + elsif Ekind (E) = E_Function + and then Ekind (Ultimate_Alias (E)) = E_Enumeration_Literal + then + return; + + -- Initialization procedures don't have extra formals + + elsif Is_Init_Proc (E) then + return; -- No need to generate extra formals in thunks whose target has no extra -- formals, but we can have two of them chained (interface and stack). - if Is_Thunk (E) and then No (Extra_Formals (Thunk_Target (E))) then + elsif Is_Thunk (E) and then No (Extra_Formals (Thunk_Target (E))) then return; - end if; - -- If this is a derived subprogram then the subtypes of the parent - -- subprogram's formal parameters will be used to determine the need - -- for extra formals. + -- If Extra_Formals were already created, don't do it again. This + -- situation may arise for subprogram types created as part of + -- dispatching calls (see Expand_Dispatching_Call). - if Is_Overloadable (E) and then Present (Alias (E)) then - P_Formal := First_Formal (Alias (E)); - else - P_Formal := Empty; + elsif Has_Extra_Formals (E) then + return; + + -- Extra formals of renamings of generic actual subprograms and + -- renamings of instances of generic subprograms are shared. The + -- check performed on the last formal is required to ensure that + -- this is the renaming built by Analyze_Instance_And_Renamings + -- (which shares all the formals); otherwise this would be wrong. + + elsif Ekind (E) in E_Function | E_Procedure + and then Is_Generic_Instance (E) + and then Present (Alias (E)) + and then Last_Formal (Ultimate_Alias (E)) = Last_Formal (E) + then + pragma Assert (Is_Generic_Instance (E) + = Is_Generic_Instance (Ultimate_Alias (E))); + + Create_Extra_Formals (Ultimate_Alias (E)); + + -- Share the extra formals + + Set_Extra_Formals (E, Extra_Formals (Ultimate_Alias (E))); + + if Ekind (E) = E_Function then + Set_Extra_Accessibility_Of_Result (E, + Extra_Accessibility_Of_Result (Ultimate_Alias (E))); + end if; + + pragma Assert (Extra_Formals_OK (E)); + return; end if; + -- Locate the last formal; required by Add_Extra_Formal. + Formal := First_Formal (E); while Present (Formal) loop Last_Extra := Formal; Next_Formal (Formal); end loop; - -- If Extra_Formals were already created, don't do it again. This - -- situation may arise for subprogram types created as part of - -- dispatching calls (see Expand_Dispatching_Call). + -- We rely on three entities to ensure consistency of extra formals of + -- entity E: + -- + -- 1. A reference entity (Ref_E). For thunks it is their target + -- primitive since this ensures that they have exactly the + -- same extra formals; otherwise it is the identity. + -- + -- 2. The parent subprogram; only for derived types and references + -- either the overridden subprogram or the internal entity built + -- by Derive_Subprogram that has the extra formals of the parent + -- subprogram; otherwise it is Empty. This entity ensures matching + -- extra formals in derived types. + -- + -- 3. For renamings, their ultimate alias; this ensures taking the + -- same decision in all the renamings (independently of the Ada + -- mode on which they are compiled). For example: + -- + -- pragma Ada_2012; + -- function Id_A (I : access Integer) return access Integer; + -- + -- pragma Ada_2005; + -- function Id_B (I : access Integer) return access Integer + -- renames Id_A; - if Present (Last_Extra) and then Present (Extra_Formal (Last_Extra)) then + if Is_Thunk (E) then + Ref_E := Thunk_Target (E); + else + Ref_E := E; + end if; + + if Is_Subprogram (Ref_E) then + Parent_Subp := Parent_Subprogram (Ref_E); + end if; + + May_Have_Alias := + (Is_Subprogram (Ref_E) or else Ekind (Ref_E) = E_Subprogram_Type); + + -- If the parent subprogram is available then its ultimate alias of + -- Ref_E is not needed since it will not be used to check its extra + -- formals. + + if No (Parent_Subp) + and then May_Have_Alias + and then Present (Alias (Ref_E)) + and then Has_Reliable_Extra_Formals (Ultimate_Alias (Ref_E)) + then + Alias_Subp := Ultimate_Alias (Ref_E); + end if; + + -- Cannot add extra formals to subprograms and access types that have + -- foreign convention nor to subprograms overriding primitives that + -- have foreign convention since the foreign language does not know + -- how to handle these extra formals; same for renamings of entities + -- with foreign convention. + + if Has_Foreign_Convention (Ref_E) + or else (Present (Alias_Subp) + and then Has_Foreign_Convention (Alias_Subp)) + then return; end if; @@ -9052,20 +9546,74 @@ package body Sem_Ch6 is goto Test_For_Func_Result_Extras; end if; + -- Process the formals relying on the formals of our reference entities: + -- Parent_Formal, Alias_Formal and Formal. Notice that we don't use the + -- formal of Ref_E; we must use the formal of E which is the entity to + -- which we are adding the extra formals. + + -- If this is a derived subprogram then the subtypes of the parent + -- subprogram's formal parameters will be used to determine the need + -- for extra formals. + + if Present (Parent_Subp) then + Parent_Formal := First_Formal (Parent_Subp); + + -- For concurrent types, the controlling argument of a dispatching + -- primitive implementing an interface primitive is implicit. For + -- example: + -- + -- type Iface is protected interface; + -- function Prim + -- (Obj : Iface; + -- Value : Integer) return Natural is abstract; + -- + -- protected type PO is new Iface with + -- function Prim (Value : Integer) return Natural; + -- end PO; + + if Convention (Ref_E) = Convention_Protected + and then Is_Abstract_Subprogram (Parent_Subp) + and then Is_Interface (Find_Dispatching_Type (Parent_Subp)) + then + Parent_Formal := Next_Formal (Parent_Formal); + + -- This is the nondispatching subprogram of a concurrent type + -- that overrides the interface primitive; the expander will + -- create the dispatching primitive (without Convention_Protected) + -- with all the matching formals (see exp_ch9.Build_Wrapper_Specs) + + pragma Assert (not Is_Dispatching_Operation (Ref_E)); + end if; + + -- Ensure that the ultimate alias has all its extra formals + + elsif Present (Alias_Subp) then + Create_Extra_Formals (Alias_Subp); + Alias_Formal := First_Formal (Alias_Subp); + end if; + Formal := First_Formal (E); while Present (Formal) loop + -- Here we establish our priority for deciding on the extra + -- formals: 1) Parent primitive 2) Aliased primitive 3) Identity + + if Present (Parent_Formal) then + Formal_Type := Etype (Parent_Formal); + + elsif Present (Alias_Formal) then + Formal_Type := Etype (Alias_Formal); + + else + Formal_Type := Etype (Formal); + end if; + -- Create extra formal for supporting the attribute 'Constrained. -- The case of a private type view without discriminants also -- requires the extra formal if the underlying type has defaulted -- discriminants. if Ekind (Formal) /= E_In_Parameter then - if Present (P_Formal) then - Formal_Type := Etype (P_Formal); - else - Formal_Type := Etype (Formal); - end if; -- Do not produce extra formals for Unchecked_Union parameters. -- Jump directly to the end of the loop. @@ -9110,36 +9658,22 @@ package body Sem_Ch6 is end if; end if; - -- Create extra formal for supporting accessibility checking. This - -- is done for both anonymous access formals and formals of named - -- access types that are marked as controlling formals. The latter - -- case can occur when Expand_Dispatching_Call creates a subprogram - -- type and substitutes the types of access-to-class-wide actuals - -- for the anonymous access-to-specific-type of controlling formals. - -- Base_Type is applied because in cases where there is a null - -- exclusion the formal may have an access subtype. + -- Extra formal for supporting accessibility checking + + if Needs_Accessibility_Check_Extra (Ref_E, Formal) then + pragma Assert (No (Parent_Formal) + or else Present (Extra_Accessibility (Parent_Formal))); + pragma Assert (No (Alias_Formal) + or else Present (Extra_Accessibility (Alias_Formal))); - -- This is suppressed if we specifically suppress accessibility - -- checks at the package level for either the subprogram, or the - -- package in which it resides. However, we do not suppress it - -- simply if the scope has accessibility checks suppressed, since - -- this could cause trouble when clients are compiled with a - -- different suppression setting. The explicit checks at the - -- package level are safe from this point of view. - - if (Ekind (Base_Type (Etype (Formal))) = E_Anonymous_Access_Type - or else (Is_Controlling_Formal (Formal) - and then Is_Access_Type (Base_Type (Etype (Formal))))) - and then not - (Explicit_Suppress (E, Accessibility_Check) - or else - Explicit_Suppress (Scope (E), Accessibility_Check)) - and then - (No (P_Formal) - or else Present (Extra_Accessibility (P_Formal))) - then Set_Extra_Accessibility (Formal, Add_Extra_Formal (Formal, Standard_Natural, E, "L")); + + else + pragma Assert (No (Parent_Formal) + or else No (Extra_Accessibility (Parent_Formal))); + pragma Assert (No (Alias_Formal) + or else No (Extra_Accessibility (Alias_Formal))); end if; -- This label is required when skipping extra formal generation for @@ -9147,8 +9681,12 @@ package body Sem_Ch6 is <> - if Present (P_Formal) then - Next_Formal (P_Formal); + if Present (Parent_Formal) then + Next_Formal (Parent_Formal); + end if; + + if Present (Alias_Formal) then + Next_Formal (Alias_Formal); end if; Next_Formal (Formal); @@ -9156,20 +9694,47 @@ package body Sem_Ch6 is <> - -- Ada 2012 (AI05-234): "the accessibility level of the result of a - -- function call is ... determined by the point of call ...". + -- Assume the worst case (Ada 2022) to evaluate this extra formal; + -- required to ensure matching of extra formals between subprograms + -- and access-to-subprogram types in projects with mixed Ada dialects. - if Needs_Result_Accessibility_Level (E) then - Set_Extra_Accessibility_Of_Result - (E, Add_Extra_Formal (E, Standard_Natural, E, "L")); - end if; + declare + Save_Ada_Version : constant Ada_Version_Type := Ada_Version; + + begin + Ada_Version := Ada_2022; + + if Needs_Result_Accessibility_Level (Ref_E) then + pragma Assert (No (Parent_Subp) + or else Needs_Result_Accessibility_Level (Parent_Subp)); + pragma Assert (No (Alias_Subp) + or else Needs_Result_Accessibility_Level (Alias_Subp)); + + Set_Extra_Accessibility_Of_Result (E, + Add_Extra_Formal (E, Standard_Natural, E, "L")); + + else + pragma Assert (No (Parent_Subp) + or else not Needs_Result_Accessibility_Level (Parent_Subp)); + pragma Assert (No (Alias_Subp) + or else not Needs_Result_Accessibility_Level (Alias_Subp)); + end if; + + Ada_Version := Save_Ada_Version; + end; -- Ada 2005 (AI-318-02): In the case of build-in-place functions, add -- appropriate extra formals. See type Exp_Ch6.BIP_Formal_Kind. - if Is_Build_In_Place_Function (E) then + if (Present (Parent_Subp) and then Has_BIP_Formals (Parent_Subp)) + or else + (Present (Alias_Subp) and then Has_BIP_Formals (Alias_Subp)) + or else + (Is_Build_In_Place_Function (Ref_E) + and then Has_Reliable_Extra_Formals (Ref_E)) + then declare - Result_Subt : constant Entity_Id := Etype (E); + Result_Subt : constant Entity_Id := Etype (Ref_E); Formal_Typ : Entity_Id; Subp_Decl : Node_Id; Discard : Entity_Id; @@ -9187,7 +9752,14 @@ package body Sem_Ch6 is -- dispatching context and such calls must be handled like calls -- to a class-wide function. - if Needs_BIP_Alloc_Form (E) then + if Needs_BIP_Alloc_Form (Ref_E) then + pragma Assert (No (Parent_Subp) + or else Has_BIP_Extra_Formal (Parent_Subp, BIP_Alloc_Form, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else Has_BIP_Extra_Formal (Alias_Subp, BIP_Alloc_Form, + Must_Be_Frozen => False)); + Discard := Add_Extra_Formal (E, Standard_Natural, @@ -9203,23 +9775,66 @@ package body Sem_Ch6 is (E, RTE (RE_Root_Storage_Pool_Ptr), E, BIP_Formal_Suffix (BIP_Storage_Pool)); end if; + + else + pragma Assert (No (Parent_Subp) + or else not + Has_BIP_Extra_Formal (Parent_Subp, BIP_Alloc_Form, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else not + Has_BIP_Extra_Formal (Alias_Subp, BIP_Alloc_Form, + Must_Be_Frozen => False)); end if; -- In the case of functions whose result type needs finalization, -- add an extra formal which represents the finalization master. - if Needs_BIP_Finalization_Master (E) then + if Needs_BIP_Finalization_Master (Ref_E) then + pragma Assert (No (Parent_Subp) + or else Has_BIP_Extra_Formal (Parent_Subp, + Kind => BIP_Finalization_Master, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else Has_BIP_Extra_Formal (Alias_Subp, + Kind => BIP_Finalization_Master, + Must_Be_Frozen => False)); + Discard := Add_Extra_Formal (E, RTE (RE_Finalization_Master_Ptr), E, BIP_Formal_Suffix (BIP_Finalization_Master)); + + else + pragma Assert (No (Parent_Subp) + or else not + Has_BIP_Extra_Formal (Parent_Subp, + Kind => BIP_Finalization_Master, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else not + Has_BIP_Extra_Formal (Alias_Subp, + Kind => BIP_Finalization_Master, + Must_Be_Frozen => False)); end if; -- When the result type contains tasks, add two extra formals: the -- master of the tasks to be created, and the caller's activation -- chain. - if Needs_BIP_Task_Actuals (E) then + if Needs_BIP_Task_Actuals (Ref_E) then + pragma Assert (No (Parent_Subp) + or else Has_BIP_Extra_Formal (Parent_Subp, BIP_Task_Master, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else Has_BIP_Extra_Formal (Alias_Subp, BIP_Task_Master, + Must_Be_Frozen => False) + or else + (Is_Abstract_Subprogram (Ref_E) + and then Is_Predefined_Dispatching_Operation (Ref_E) + and then Is_Interface + (Find_Dispatching_Type (Alias_Subp)))); + Discard := Add_Extra_Formal (E, Standard_Integer, @@ -9231,6 +9846,16 @@ package body Sem_Ch6 is Add_Extra_Formal (E, RTE (RE_Activation_Chain_Access), E, BIP_Formal_Suffix (BIP_Activation_Chain)); + + else + pragma Assert (No (Parent_Subp) + or else not + Has_BIP_Extra_Formal (Parent_Subp, BIP_Task_Master, + Must_Be_Frozen => False)); + pragma Assert (No (Alias_Subp) + or else not + Has_BIP_Extra_Formal (Alias_Subp, BIP_Task_Master, + Must_Be_Frozen => False)); end if; -- All build-in-place functions get an extra formal that will be @@ -9296,6 +9921,14 @@ package body Sem_Ch6 is if Is_Generic_Instance (E) and then Present (Alias (E)) then Set_Extra_Formals (Alias (E), Extra_Formals (E)); end if; + + pragma Assert (No (Alias_Subp) + or else Extra_Formals_Match_OK (E, Alias_Subp)); + + pragma Assert (No (Parent_Subp) + or else Extra_Formals_Match_OK (E, Parent_Subp)); + + pragma Assert (Extra_Formals_OK (E)); end Create_Extra_Formals; ----------------------------- @@ -9526,252 +10159,162 @@ package body Sem_Ch6 is end if; end Enter_Overloaded_Entity; - ----------------------------- - -- Check_Untagged_Equality -- - ----------------------------- - - procedure Check_Untagged_Equality (Eq_Op : Entity_Id) is - Eq_Decl : constant Node_Id := Unit_Declaration_Node (Eq_Op); - Typ : constant Entity_Id := Etype (First_Formal (Eq_Op)); - - procedure Freezing_Point_Warning (N : Node_Id; S : String); - -- Output a warning about the freezing point N of Typ - - function Is_Actual_Of_Instantiation - (E : Entity_Id; - Inst : Node_Id) return Boolean; - -- Return True if E is an actual parameter of instantiation Inst - - ----------------------------------- - -- Output_Freezing_Point_Warning -- - ----------------------------------- - - procedure Freezing_Point_Warning (N : Node_Id; S : String) is - begin - Error_Msg_String (1 .. S'Length) := S; - Error_Msg_Strlen := S'Length; - - if Ada_Version >= Ada_2012 then - Error_Msg_NE ("type& is frozen by ~??", N, Typ); - Error_Msg_N - ("\an equality operator cannot be declared after this point??", - N); - - else - Error_Msg_NE ("type& is frozen by ~ (Ada 2012)?y?", N, Typ); - Error_Msg_N - ("\an equality operator cannot be declared after this point" - & " (Ada 2012)?y?", N); - end if; - end Freezing_Point_Warning; - - -------------------------------- - -- Is_Actual_Of_Instantiation -- - -------------------------------- - - function Is_Actual_Of_Instantiation - (E : Entity_Id; - Inst : Node_Id) return Boolean - is - Assoc : Node_Id; - - begin - if Present (Generic_Associations (Inst)) then - Assoc := First (Generic_Associations (Inst)); - - while Present (Assoc) loop - if Present (Explicit_Generic_Actual_Parameter (Assoc)) - and then - Is_Entity_Name (Explicit_Generic_Actual_Parameter (Assoc)) - and then - Entity (Explicit_Generic_Actual_Parameter (Assoc)) = E - then - return True; - end if; - - Next (Assoc); - end loop; - end if; - - return False; - end Is_Actual_Of_Instantiation; - - -- Local variable - - Decl : Node_Id; - - -- Start of processing for Check_Untagged_Equality + ---------------------------- + -- Extra_Formals_Match_OK -- + ---------------------------- + function Extra_Formals_Match_OK + (E : Entity_Id; + Ref_E : Entity_Id) return Boolean is begin - -- This check applies only if we have a subprogram declaration or a - -- subprogram body that is not a completion, for an untagged record - -- type, and that is conformant with the predefined operator. + pragma Assert (Is_Subprogram (E)); + + -- Cases where no check can be performed: + -- 1) When expansion is not active (since we never generate extra + -- formals if expansion is not active because we don't need them + -- unless we are generating code). + -- 2) On abstract predefined dispatching operations of Root_Controlled + -- and predefined stream operations not emitted by the frontend. + -- 3) On renamings of abstract predefined dispatching operations of + -- interface types (since limitedness is not inherited in such + -- case (AI-419)). + -- 4) The controlling formal of the nondispatching subprogram of + -- a concurrent type that overrides an interface primitive is + -- implicit and hence we cannot check here if all its extra + -- formals match; the expander will create the dispatching + -- primitive (without Convention_Protected) with the matching + -- formals (see exp_ch9.Build_Wrapper_Specs) which will be + -- checked later. + + if Debug_Flag_Underscore_XX + or else not Expander_Active + or else + (Is_Predefined_Dispatching_Operation (E) + and then (not Has_Reliable_Extra_Formals (E) + or else not Has_Reliable_Extra_Formals (Ref_E))) + or else + (Is_Predefined_Dispatching_Operation (E) + and then Is_Abstract_Subprogram (E) + and then Is_Interface (Find_Dispatching_Type (Ref_E))) + then + return True; - if (Nkind (Eq_Decl) /= N_Subprogram_Declaration - and then not (Nkind (Eq_Decl) = N_Subprogram_Body - and then Acts_As_Spec (Eq_Decl))) - or else not Is_Record_Type (Typ) - or else Is_Tagged_Type (Typ) - or else not Is_User_Defined_Equality (Eq_Op) + elsif Convention (E) = Convention_Protected + and then not Is_Dispatching_Operation (E) + and then Is_Abstract_Subprogram (Ref_E) + and then Is_Interface (Find_Dispatching_Type (Ref_E)) then - return; + return True; end if; - -- In Ada 2012 case, we will output errors or warnings depending on - -- the setting of debug flag -gnatd.E. - - if Ada_Version >= Ada_2012 then - Error_Msg_Warn := Debug_Flag_Dot_EE; + -- Perform the checks - -- In earlier versions of Ada, nothing to do unless we are warning on - -- Ada 2012 incompatibilities (Warn_On_Ada_2012_Incompatibility set). - - else - if not Warn_On_Ada_2012_Compatibility then - return; - end if; + if No (Extra_Formals (E)) then + return No (Extra_Formals (Ref_E)); end if; - -- Cases where the type has already been frozen - - if Is_Frozen (Typ) then - - -- The check applies to a primitive operation, so check that type - -- and equality operation are in the same scope. - - if Scope (Typ) /= Current_Scope then - return; - - -- If the type is a generic actual (sub)type, the operation is not - -- primitive either because the base type is declared elsewhere. + if Ekind (E) in E_Function | E_Subprogram_Type + and then Present (Extra_Accessibility_Of_Result (E)) + /= Present (Extra_Accessibility_Of_Result (Ref_E)) + then + return False; + end if; - elsif Is_Generic_Actual_Type (Typ) then - return; + declare + Formal_1 : Entity_Id := Extra_Formals (E); + Formal_2 : Entity_Id := Extra_Formals (Ref_E); - -- Here we may have an error of declaration after freezing, but we - -- must make sure not to flag the equality operator itself causing - -- the freezing when it is a subprogram body. + begin + while Present (Formal_1) and then Present (Formal_2) loop + if Has_Suffix (Formal_1, 'L') then + if not Has_Suffix (Formal_2, 'L') then + return False; + end if; - else - Decl := Next (Declaration_Node (Typ)); + elsif Has_Suffix (Formal_1, 'O') then + if not Has_Suffix (Formal_2, 'O') then + return False; + end if; - while Present (Decl) and then Decl /= Eq_Decl loop + elsif BIP_Suffix_Kind (Formal_1) /= BIP_Suffix_Kind (Formal_2) then + return False; + end if; - -- The declaration of an object of the type + Formal_1 := Next_Formal_With_Extras (Formal_1); + Formal_2 := Next_Formal_With_Extras (Formal_2); + end loop; - if Nkind (Decl) = N_Object_Declaration - and then Etype (Defining_Identifier (Decl)) = Typ - then - Freezing_Point_Warning (Decl, "declaration"); - exit; + return No (Formal_1) and then No (Formal_2); + end; + end Extra_Formals_Match_OK; - -- The instantiation of a generic on the type + ---------------------- + -- Extra_Formals_OK -- + ---------------------- - elsif Nkind (Decl) in N_Generic_Instantiation - and then Is_Actual_Of_Instantiation (Typ, Decl) - then - Freezing_Point_Warning (Decl, "instantiation"); - exit; + function Extra_Formals_OK (E : Entity_Id) return Boolean is + Last_Formal : Entity_Id := Empty; + Formal : Entity_Id; + Has_Extra_Formals : Boolean := False; - -- A noninstance proper body, body stub or entry body + begin + -- No check required if explicitly disabled - elsif Nkind (Decl) in N_Proper_Body - | N_Body_Stub - | N_Entry_Body - and then not Is_Generic_Instance (Defining_Entity (Decl)) - then - Freezing_Point_Warning (Decl, "body"); - exit; + if Debug_Flag_Underscore_XX then + return True; - -- If we have reached the freeze node and immediately after we - -- have the body or generated code for the body, then it is the - -- body that caused the freezing and this is legal. + -- No check required if expansion is disabled because extra + -- formals are only generated when we are generating code. + -- See Create_Extra_Formals. - elsif Nkind (Decl) = N_Freeze_Entity - and then Entity (Decl) = Typ - and then (Next (Decl) = Eq_Decl - or else - Sloc (Next (Decl)) = Sloc (Eq_Decl)) - then - return; - end if; + elsif not Expander_Active then + return True; + end if; - Next (Decl); - end loop; + -- Check attribute Extra_Formal: If available, it must be set only + -- on the last formal of E. - -- Here we have a definite error of declaration after freezing + Formal := First_Formal (E); + while Present (Formal) loop + if Present (Extra_Formal (Formal)) then + if Has_Extra_Formals then + return False; + end if; - if Ada_Version >= Ada_2012 then - Error_Msg_NE - ("equality operator must be declared before type & is " - & "frozen (RM 4.5.2 (9.8)) (Ada 2012)<<", Eq_Op, Typ); + Has_Extra_Formals := True; + end if; - -- In Ada 2012 mode with error turned to warning, output one - -- more warning to warn that the equality operation may not - -- compose. This is the consequence of ignoring the error. + Last_Formal := Formal; + Next_Formal (Formal); + end loop; - if Error_Msg_Warn then - Error_Msg_N ("\equality operation may not compose??", Eq_Op); - end if; + -- Check attribute Extra_Accessibility_Of_Result - else - Error_Msg_NE - ("equality operator must be declared before type& is " - & "frozen (RM 4.5.2 (9.8)) (Ada 2012)?y?", Eq_Op, Typ); - end if; + if Ekind (E) in E_Function | E_Subprogram_Type + and then Needs_Result_Accessibility_Level (E) + and then No (Extra_Accessibility_Of_Result (E)) + then + return False; + end if; - -- If we have found no freezing point and the declaration of the - -- operator could not be reached from that of the type and we are - -- in a package body, this must be because the type is declared - -- in the spec of the package. Add a message tailored to this. + -- Check attribute Extra_Formals: If E has extra formals, then this + -- attribute must point to the first extra formal of E. - if No (Decl) and then In_Package_Body (Scope (Typ)) then - if Ada_Version >= Ada_2012 then - if Nkind (Eq_Decl) = N_Subprogram_Body then - Error_Msg_N - ("\put declaration in package spec<<", Eq_Op); - else - Error_Msg_N - ("\move declaration to package spec<<", Eq_Op); - end if; + if Has_Extra_Formals then + return Present (Extra_Formals (E)) + and then Present (Extra_Formal (Last_Formal)) + and then Extra_Formal (Last_Formal) = Extra_Formals (E); - else - if Nkind (Eq_Decl) = N_Subprogram_Body then - Error_Msg_N - ("\put declaration in package spec (Ada 2012)?y?", - Eq_Op); - else - Error_Msg_N - ("\move declaration to package spec (Ada 2012)?y?", - Eq_Op); - end if; - end if; - end if; - end if; + -- When E has no formals, the first extra formal is available through + -- the Extra_Formals attribute. - -- Now check for AI12-0352: the declaration of a user-defined primitive - -- equality operation for a record type T is illegal if it occurs after - -- a type has been derived from T. + elsif Present (Extra_Formals (E)) then + return No (First_Formal (E)); else - Decl := Next (Declaration_Node (Typ)); - - while Present (Decl) and then Decl /= Eq_Decl loop - if Nkind (Decl) = N_Full_Type_Declaration - and then Etype (Defining_Identifier (Decl)) = Typ - then - Error_Msg_N - ("equality operator cannot appear after derivation", Eq_Op); - Error_Msg_NE - ("an equality operator for& cannot be declared after " - & "this point??", - Decl, Typ); - end if; - - Next (Decl); - end loop; + return True; end if; - end Check_Untagged_Equality; + end Extra_Formals_OK; ----------------------------- -- Find_Corresponding_Spec -- @@ -10656,6 +11199,89 @@ package body Sem_Ch6 is end if; end Fully_Conformant_Discrete_Subtypes; + --------------------- + -- Has_BIP_Formals -- + --------------------- + + function Has_BIP_Formals (E : Entity_Id) return Boolean is + Formal : Entity_Id := First_Formal_With_Extras (E); + + begin + while Present (Formal) loop + if Is_Build_In_Place_Entity (Formal) then + return True; + end if; + + Next_Formal_With_Extras (Formal); + end loop; + + return False; + end Has_BIP_Formals; + + -------------------------------- + -- Has_Reliable_Extra_Formals -- + -------------------------------- + + function Has_Reliable_Extra_Formals (E : Entity_Id) return Boolean is + Alias_E : Entity_Id; + + begin + -- Extra formals are not added if expansion is not active (and hence if + -- available they are not reliable for extra formals check). + + if not Expander_Active then + return False; + + -- Currently the unique cases where extra formals are not reliable + -- are associated with predefined dispatching operations; otherwise + -- they are properly added when required. + + elsif not Is_Predefined_Dispatching_Operation (E) then + return True; + end if; + + Alias_E := Ultimate_Alias (E); + + -- Abstract predefined primitives of Root_Controlled don't have + -- extra formals; this is required to build the runtime. + + if Ekind (Alias_E) = E_Function + and then Is_Abstract_Subprogram (Alias_E) + and then Is_RTE (Underlying_Type (Etype (Alias_E)), + RE_Root_Controlled) + then + return False; + + -- Predefined stream dispatching operations that are not emitted by + -- the frontend; they have a renaming of the corresponding primitive + -- of their parent type and hence they don't have extra formals. + + else + declare + Typ : constant Entity_Id := + Underlying_Type (Find_Dispatching_Type (Alias_E)); + + begin + if (Get_TSS_Name (E) = TSS_Stream_Input + and then not Stream_Operation_OK (Typ, TSS_Stream_Input)) + or else + (Get_TSS_Name (E) = TSS_Stream_Output + and then not Stream_Operation_OK (Typ, TSS_Stream_Output)) + or else + (Get_TSS_Name (E) = TSS_Stream_Read + and then not Stream_Operation_OK (Typ, TSS_Stream_Read)) + or else + (Get_TSS_Name (E) = TSS_Stream_Write + and then not Stream_Operation_OK (Typ, TSS_Stream_Write)) + then + return False; + end if; + end; + end if; + + return True; + end Has_Reliable_Extra_Formals; + -------------------- -- Install_Entity -- -------------------- @@ -12527,7 +13153,7 @@ package body Sem_Ch6 is if Is_Dispatching_Operation (E) then -- An overriding dispatching subprogram inherits the - -- convention of the overridden subprogram (AI-117). + -- convention of the overridden subprogram (AI95-117). Set_Convention (S, Convention (E)); Check_Dispatching_Operation (S, E); diff --git a/gcc/ada/sem_ch6.ads b/gcc/ada/sem_ch6.ads index da56ce6ab72..5f0e1baa4f9 100644 --- a/gcc/ada/sem_ch6.ads +++ b/gcc/ada/sem_ch6.ads @@ -174,6 +174,22 @@ package Sem_Ch6 is -- the end of Subp's parameter list (with each subsequent extra formal -- being attached to the preceding extra formal). + function Extra_Formals_Match_OK + (E : Entity_Id; + Ref_E : Entity_Id) return Boolean; + -- Return True if the extra formals of the given entities match. E is a + -- subprogram, and Ref_E is the reference entity that will be used to check + -- the extra formals of E: a subprogram type or another subprogram. For + -- example, if E is a dispatching primitive of a tagged type then Ref_E + -- may be the overridden primitive of its parent type or its ultimate + -- renamed entity; however, if E is a subprogram to which 'Access is + -- applied then Ref_E is its corresponding subprogram type. Used in + -- assertions. + + function Extra_Formals_OK (E : Entity_Id) return Boolean; + -- Return True if the decoration of the attributes associated with extra + -- formals are properly set. Used in assertions. + function Find_Corresponding_Spec (N : Node_Id; Post_Error : Boolean := True) return Entity_Id; @@ -197,6 +213,9 @@ package Sem_Ch6 is -- Determines if two subtype definitions are fully conformant. Used -- for entry family conformance checks (RM 6.3.1 (24)). + function Has_BIP_Formals (E : Entity_Id) return Boolean; + -- Determines if a given entity has build-in-place formals + procedure Install_Entity (E : Entity_Id); -- Place a single entity on the visibility chain diff --git a/gcc/ada/sem_eval.adb b/gcc/ada/sem_eval.adb index 195f27e14d6..5d9b2d0484d 100644 --- a/gcc/ada/sem_eval.adb +++ b/gcc/ada/sem_eval.adb @@ -1823,6 +1823,7 @@ package body Sem_Eval is return False; elsif Op = Error + or else Nkind (Op) not in N_Has_Etype or else Etype (Op) = Any_Type or else Raises_Constraint_Error (Op) then diff --git a/gcc/ada/sem_util.adb b/gcc/ada/sem_util.adb index c00490cf55e..71548dcca17 100644 --- a/gcc/ada/sem_util.adb +++ b/gcc/ada/sem_util.adb @@ -23312,9 +23312,12 @@ package body Sem_Util is return Present (Extra_Accessibility_Of_Result (Alias (Func_Id))); - -- Remaining cases require Ada 2012 mode + -- Remaining cases require Ada 2012 mode, unless they are dispatching + -- operations, since they may be overridden by Ada_2012 primitives. - elsif Ada_Version < Ada_2012 then + elsif Ada_Version < Ada_2012 + and then not Is_Dispatching_Operation (Func_Id) + then return False; -- Handle the situation where a result is an anonymous access type -- cgit v1.2.1 From 59ad8b684dd67e171141761f520c6a2ec70e5d6c Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Thu, 20 Oct 2022 20:41:08 +0200 Subject: ada: Implement RM 4.5.7(10/3) name resolution rule This rule deals with the specific case of a conditional expression that is the operand of a type conversion and effectively distributes the conversion to the dependent expressions with the help of the dynamic semantics. gcc/ada/ * sem_ch4.adb (Analyze_Case_Expression): Compute the interpretations of the expression only at the end of the analysis, but skip doing it if it is the operand of a type conversion. (Analyze_If_Expression): Likewise. * sem_res.adb (Resolve): Deal specially with conditional expression that is the operand of a type conversion. (Resolve_Dependent_Expression): New procedure. (Resolve_Case_Expression): Call Resolve_Dependent_Expression. (Resolve_If_Expression): Likewise. (Resolve_If_Expression.Apply_Check): Take result type as parameter. (Resolve_Type_Conversion): Do not warn about a redundant conversion when the operand is a conditional expression. --- gcc/ada/sem_ch4.adb | 129 +++++++++++++++++++++++++++++----------------------- gcc/ada/sem_res.adb | 109 +++++++++++++++++++++++++++++++++----------- 2 files changed, 156 insertions(+), 82 deletions(-) diff --git a/gcc/ada/sem_ch4.adb b/gcc/ada/sem_ch4.adb index 0c02fd80675..23040d7033b 100644 --- a/gcc/ada/sem_ch4.adb +++ b/gcc/ada/sem_ch4.adb @@ -1740,6 +1740,70 @@ package body Sem_Ch4 is return; end if; + -- The expression must be of a discrete type which must be determinable + -- independently of the context in which the expression occurs, but + -- using the fact that the expression must be of a discrete type. + -- Moreover, the type this expression must not be a character literal + -- (which is always ambiguous). + + -- If error already reported by Resolve, nothing more to do + + if Exp_Btype = Any_Discrete or else Exp_Btype = Any_Type then + return; + + -- Special case message for character literal + + elsif Exp_Btype = Any_Character then + Error_Msg_N + ("character literal as case expression is ambiguous", Expr); + return; + end if; + + -- If the case expression is a formal object of mode in out, then + -- treat it as having a nonstatic subtype by forcing use of the base + -- type (which has to get passed to Check_Case_Choices below). Also + -- use base type when the case expression is parenthesized. + + if Paren_Count (Expr) > 0 + or else (Is_Entity_Name (Expr) + and then Ekind (Entity (Expr)) = E_Generic_In_Out_Parameter) + then + Exp_Type := Exp_Btype; + end if; + + -- The case expression alternatives cover the range of a static subtype + -- subject to aspect Static_Predicate. Do not check the choices when the + -- case expression has not been fully analyzed yet because this may lead + -- to bogus errors. + + if Is_OK_Static_Subtype (Exp_Type) + and then Has_Static_Predicate_Aspect (Exp_Type) + and then In_Spec_Expression + then + null; + + -- Call Analyze_Choices and Check_Choices to do the rest of the work + + else + Analyze_Choices (Alternatives (N), Exp_Type); + Check_Choices (N, Alternatives (N), Exp_Type, Others_Present); + + if Exp_Type = Universal_Integer and then not Others_Present then + Error_Msg_N + ("case on universal integer requires OTHERS choice", Expr); + return; + end if; + end if; + + -- RM 4.5.7(10/3): If the case_expression is the operand of a type + -- conversion, the type of the case_expression is the target type + -- of the conversion. + + if Nkind (Parent (N)) = N_Type_Conversion then + Set_Etype (N, Etype (Parent (N))); + return; + end if; + -- Loop through the interpretations of the first expression and check -- the other expressions if present. @@ -1763,25 +1827,6 @@ package body Sem_Ch4 is end loop; end if; - -- The expression must be of a discrete type which must be determinable - -- independently of the context in which the expression occurs, but - -- using the fact that the expression must be of a discrete type. - -- Moreover, the type this expression must not be a character literal - -- (which is always ambiguous). - - -- If error already reported by Resolve, nothing more to do - - if Exp_Btype = Any_Discrete or else Exp_Btype = Any_Type then - return; - - -- Special casee message for character literal - - elsif Exp_Btype = Any_Character then - Error_Msg_N - ("character literal as case expression is ambiguous", Expr); - return; - end if; - -- If no possible interpretation has been found, the type of the wrong -- alternative doesn't match any interpretation of the FIRST expression. @@ -1829,43 +1874,6 @@ package body Sem_Ch4 is Etype (Second_Expr)); end if; end if; - - return; - end if; - - -- If the case expression is a formal object of mode in out, then - -- treat it as having a nonstatic subtype by forcing use of the base - -- type (which has to get passed to Check_Case_Choices below). Also - -- use base type when the case expression is parenthesized. - - if Paren_Count (Expr) > 0 - or else (Is_Entity_Name (Expr) - and then Ekind (Entity (Expr)) = E_Generic_In_Out_Parameter) - then - Exp_Type := Exp_Btype; - end if; - - -- The case expression alternatives cover the range of a static subtype - -- subject to aspect Static_Predicate. Do not check the choices when the - -- case expression has not been fully analyzed yet because this may lead - -- to bogus errors. - - if Is_OK_Static_Subtype (Exp_Type) - and then Has_Static_Predicate_Aspect (Exp_Type) - and then In_Spec_Expression - then - null; - - -- Call Analyze_Choices and Check_Choices to do the rest of the work - - else - Analyze_Choices (Alternatives (N), Exp_Type); - Check_Choices (N, Alternatives (N), Exp_Type, Others_Present); - - if Exp_Type = Universal_Integer and then not Others_Present then - Error_Msg_N - ("case on universal integer requires OTHERS choice", Expr); - end if; end if; end Analyze_Case_Expression; @@ -2555,6 +2563,15 @@ package body Sem_Ch4 is Analyze_Expression (Else_Expr); end if; + -- RM 4.5.7(10/3): If the if_expression is the operand of a type + -- conversion, the type of the if_expression is the target type + -- of the conversion. + + if Nkind (Parent (N)) = N_Type_Conversion then + Set_Etype (N, Etype (Parent (N))); + return; + end if; + -- Loop through the interpretations of the THEN expression and check the -- ELSE expression if present. diff --git a/gcc/ada/sem_res.adb b/gcc/ada/sem_res.adb index e5b3612d186..c8652c959b7 100644 --- a/gcc/ada/sem_res.adb +++ b/gcc/ada/sem_res.adb @@ -171,6 +171,13 @@ package body Sem_Res is -- of the task, it must be replaced with a reference to the discriminant -- of the task being called. + procedure Resolve_Dependent_Expression + (N : Node_Id; + Expr : Node_Id; + Typ : Entity_Id); + -- Internal procedure to resolve the dependent expression Expr of the + -- conditional expression N with type Typ. + procedure Resolve_Op_Concat_Arg (N : Node_Id; Arg : Node_Id; @@ -291,12 +298,6 @@ package body Sem_Res is -- Called after N has been resolved and evaluated, but before range checks -- have been applied. This rewrites the conversion into a simpler form. - function Unique_Fixed_Point_Type (N : Node_Id) return Entity_Id; - -- A universal_fixed expression in an universal context is unambiguous if - -- there is only one applicable fixed point type. Determining whether there - -- is only one requires a search over all visible entities, and happens - -- only in very pathological cases (see 6115-006). - function Try_User_Defined_Literal (N : Node_Id; Typ : Entity_Id) return Boolean; @@ -306,6 +307,12 @@ package body Sem_Res is -- If such aspect exists, replace literal with a call to the -- corresponding function and return True, return false otherwise. + function Unique_Fixed_Point_Type (N : Node_Id) return Entity_Id; + -- A universal_fixed expression in an universal context is unambiguous if + -- there is only one applicable fixed point type. Determining whether there + -- is only one requires a search over all visible entities, and happens + -- only in very pathological cases (see 6115-006). + ------------------------- -- Ambiguous_Character -- ------------------------- @@ -2461,6 +2468,15 @@ package body Sem_Res is Found := True; Expr_Type := Etype (Expression (N)); + -- The resolution of a conditional expression that is the operand of a + -- type conversion is determined by the conversion (RM 4.5.7(10/3)). + + elsif Nkind (N) in N_Case_Expression | N_If_Expression + and then Nkind (Parent (N)) = N_Type_Conversion + then + Found := True; + Expr_Type := Etype (Parent (N)); + -- If not overloaded, then we know the type, and all that needs doing -- is to check that this type is compatible with the context. @@ -7390,7 +7406,8 @@ package body Sem_Res is return; end if; - Resolve (Alt_Expr, Typ); + Resolve_Dependent_Expression (N, Alt_Expr, Typ); + Check_Unset_Reference (Alt_Expr); Alt_Typ := Etype (Alt_Expr); @@ -7671,6 +7688,34 @@ package body Sem_Res is Check_Unset_Reference (Expr); end Resolve_Declare_Expression; + ----------------------------------- + -- Resolve_Dependent_Expression -- + ----------------------------------- + + procedure Resolve_Dependent_Expression + (N : Node_Id; + Expr : Node_Id; + Typ : Entity_Id) + is + begin + -- RM 4.5.7(8/3) says that the expected type of dependent expressions is + -- that of the conditional expression but RM 4.5.7(10/3) forces the type + -- of the conditional expression without changing the expected type (the + -- expected type of the operand of a type conversion is any type), so we + -- may have a gap between these two types that is bridged by the dynamic + -- semantics specified by RM 4.5.7(20/3) with the associated legality + -- rule RM 4.5.7(16/3) that will be automatically enforced. + + if Nkind (Parent (N)) = N_Type_Conversion + and then Nkind (Expr) /= N_Raise_Expression + then + Convert_To_And_Rewrite (Typ, Expr); + Analyze_And_Resolve (Expr); + else + Resolve (Expr, Typ); + end if; + end Resolve_Dependent_Expression; + ----------------------------------------- -- Resolve_Discrete_Subtype_Indication -- ----------------------------------------- @@ -9307,7 +9352,9 @@ package body Sem_Res is --------------------------- procedure Resolve_If_Expression (N : Node_Id; Typ : Entity_Id) is - procedure Apply_Check (Expr : Node_Id); + Condition : constant Node_Id := First (Expressions (N)); + + procedure Apply_Check (Expr : Node_Id; Result_Type : Entity_Id); -- When a dependent expression is of a subtype different from -- the context subtype, then insert a qualification to ensure -- the generation of a constraint check. This was previously @@ -9315,21 +9362,11 @@ package body Sem_Res is -- that the context in general allows sliding, while a qualified -- expression forces equality of bounds. - Result_Type : Entity_Id := Typ; - -- So in most cases the type of the If_Expression and of its - -- dependent expressions is that of the context. However, if - -- the expression is the index of an Indexed_Component, we must - -- ensure that a proper index check is applied, rather than a - -- range check on the index type (which might be discriminant - -- dependent). In this case we resolve with the base type of the - -- index type, and the index check is generated in the resolution - -- of the indexed_component above. - ----------------- -- Apply_Check -- ----------------- - procedure Apply_Check (Expr : Node_Id) is + procedure Apply_Check (Expr : Node_Id; Result_Type : Entity_Id) is Expr_Typ : constant Entity_Id := Etype (Expr); Loc : constant Source_Ptr := Sloc (Expr); @@ -9357,10 +9394,19 @@ package body Sem_Res is -- Local variables - Condition : constant Node_Id := First (Expressions (N)); Else_Expr : Node_Id; Then_Expr : Node_Id; + Result_Type : Entity_Id; + -- So in most cases the type of the if_expression and of its + -- dependent expressions is that of the context. However, if + -- the expression is the index of an Indexed_Component, we must + -- ensure that a proper index check is applied, rather than a + -- range check on the index type (which might be discriminant + -- dependent). In this case we resolve with the base type of the + -- index type, and the index check is generated in the resolution + -- of the indexed_component above. + -- Start of processing for Resolve_If_Expression begin @@ -9375,6 +9421,9 @@ package body Sem_Res is or else Nkind (Parent (Parent (N))) = N_Indexed_Component) then Result_Type := Base_Type (Typ); + + else + Result_Type := Typ; end if; Then_Expr := Next (Condition); @@ -9383,21 +9432,23 @@ package body Sem_Res is return; end if; - Else_Expr := Next (Then_Expr); - Resolve (Condition, Any_Boolean); - Resolve (Then_Expr, Result_Type); Check_Unset_Reference (Condition); + + Resolve_Dependent_Expression (N, Then_Expr, Result_Type); + Check_Unset_Reference (Then_Expr); + Apply_Check (Then_Expr, Result_Type); - Apply_Check (Then_Expr); + Else_Expr := Next (Then_Expr); -- If ELSE expression present, just resolve using the determined type if Present (Else_Expr) then - Resolve (Else_Expr, Result_Type); + Resolve_Dependent_Expression (N, Else_Expr, Result_Type); + Check_Unset_Reference (Else_Expr); - Apply_Check (Else_Expr); + Apply_Check (Else_Expr, Result_Type); -- Apply RM 4.5.7 (17/3): whether the expression is statically or -- dynamically tagged must be known statically. @@ -12158,6 +12209,12 @@ package body Sem_Res is then null; + -- Never give a warning if the operand is a conditional expression + -- because RM 4.5.7(10/3) forces its type to be the target type. + + elsif Nkind (Orig_N) in N_Case_Expression | N_If_Expression then + null; + -- Finally, if this type conversion occurs in a context requiring -- a prefix, and the expression is a qualified expression then the -- type conversion is not redundant, since a qualified expression -- cgit v1.2.1 From a645dc3c20c32ede1b5f50f1a35705ce87a3d61b Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Fri, 21 Oct 2022 20:12:15 +0200 Subject: ada: Propagate aspect Ghost when instantiating null formal procedures When instantiating generic package that includes a formal subprogram declaration with Ghost aspect and a subprogram_default of null, e.g.: generic with procedure Proc is null with Ghost; package P is ... the Ghost aspect should be propagated to the internally generated null subprogram, so this null subprogram can be used in contexts that require ghost entities. gcc/ada/ * sem_ch12.adb (Instantiate_Formal_Subprogram): Copy aspect Ghost from formal subprogram declaration to the internally generated procedure. --- gcc/ada/sem_ch12.adb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gcc/ada/sem_ch12.adb b/gcc/ada/sem_ch12.adb index ca0f4913e36..276656085be 100644 --- a/gcc/ada/sem_ch12.adb +++ b/gcc/ada/sem_ch12.adb @@ -11088,6 +11088,8 @@ package body Sem_Ch12 is Set_Convention (Defining_Unit_Name (New_Spec), Convention_Intrinsic); + Copy_Ghost_Aspect (Formal, To => Decl_Node); + -- Eliminate the calls to it when optimization is enabled Set_Is_Inlined (Defining_Unit_Name (New_Spec)); -- cgit v1.2.1 From 0ed20c72aa044c3fa4a20fad77218114c7310f52 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Sat, 22 Oct 2022 12:48:13 +0200 Subject: ada: Small consistency fix gcc/ada/ * fe.h (Get_Warn_On_Questionable_Layout): Add void parameter. --- gcc/ada/fe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gcc/ada/fe.h b/gcc/ada/fe.h index 8102c6d5ec4..12ad15b6d35 100644 --- a/gcc/ada/fe.h +++ b/gcc/ada/fe.h @@ -368,7 +368,7 @@ extern Boolean Stack_Check_Probes_On_Target; #define Get_Warn_On_Questionable_Layout warnsw__get_warn_on_questionable_layout -extern Boolean Get_Warn_On_Questionable_Layout (); +extern Boolean Get_Warn_On_Questionable_Layout (void); // The following corresponds to Ada code in Einfo.Utils. -- cgit v1.2.1 From 48e2e5b4c2f56b9e3497d57d0974c66604e087a6 Mon Sep 17 00:00:00 2001 From: Johannes Kliemann Date: Fri, 21 Oct 2022 13:21:04 +0000 Subject: ada: Set Support_Atomic_Primitives for VxWorks 7 runtimes gcc/ada/ * libgnat/system-vxworks7-aarch64-rtp-smp.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-aarch64.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-arm-rtp-smp.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-arm.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-ppc-kernel.ads: Set Support_Atomic_Primitives to False. * libgnat/system-vxworks7-ppc-rtp-smp.ads: Set Support_Atomic_Primitives to False. * libgnat/system-vxworks7-ppc64-kernel.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-ppc64-rtp-smp.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-x86-kernel.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-x86-rtp-smp.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-x86_64-kernel.ads: Set Support_Atomic_Primitives to True. * libgnat/system-vxworks7-x86_64-rtp-smp.ads: Set Support_Atomic_Primitives to True. --- gcc/ada/libgnat/system-vxworks7-aarch64-rtp-smp.ads | 2 +- gcc/ada/libgnat/system-vxworks7-aarch64.ads | 2 +- gcc/ada/libgnat/system-vxworks7-arm-rtp-smp.ads | 2 +- gcc/ada/libgnat/system-vxworks7-arm.ads | 2 +- gcc/ada/libgnat/system-vxworks7-ppc-kernel.ads | 1 + gcc/ada/libgnat/system-vxworks7-ppc-rtp-smp.ads | 1 + gcc/ada/libgnat/system-vxworks7-ppc64-kernel.ads | 1 + gcc/ada/libgnat/system-vxworks7-ppc64-rtp-smp.ads | 1 + gcc/ada/libgnat/system-vxworks7-x86-kernel.ads | 2 +- gcc/ada/libgnat/system-vxworks7-x86-rtp-smp.ads | 2 +- gcc/ada/libgnat/system-vxworks7-x86_64-kernel.ads | 2 +- gcc/ada/libgnat/system-vxworks7-x86_64-rtp-smp.ads | 2 +- 12 files changed, 12 insertions(+), 8 deletions(-) diff --git a/gcc/ada/libgnat/system-vxworks7-aarch64-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-aarch64-rtp-smp.ads index ae67cd0bab8..46b740eadf6 100644 --- a/gcc/ada/libgnat/system-vxworks7-aarch64-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-aarch64-rtp-smp.ads @@ -151,7 +151,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-aarch64.ads b/gcc/ada/libgnat/system-vxworks7-aarch64.ads index a943ecd9c4a..1aba15b212e 100644 --- a/gcc/ada/libgnat/system-vxworks7-aarch64.ads +++ b/gcc/ada/libgnat/system-vxworks7-aarch64.ads @@ -148,7 +148,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-arm-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-arm-rtp-smp.ads index 49e6e7adeeb..e81348e8f62 100644 --- a/gcc/ada/libgnat/system-vxworks7-arm-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-arm-rtp-smp.ads @@ -148,7 +148,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-arm.ads b/gcc/ada/libgnat/system-vxworks7-arm.ads index 6d3218f456f..4ced0f1e606 100644 --- a/gcc/ada/libgnat/system-vxworks7-arm.ads +++ b/gcc/ada/libgnat/system-vxworks7-arm.ads @@ -146,7 +146,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-ppc-kernel.ads b/gcc/ada/libgnat/system-vxworks7-ppc-kernel.ads index bddf9511d3b..ef290c06c6d 100644 --- a/gcc/ada/libgnat/system-vxworks7-ppc-kernel.ads +++ b/gcc/ada/libgnat/system-vxworks7-ppc-kernel.ads @@ -146,6 +146,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; + Support_Atomic_Primitives : constant Boolean := False; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-ppc-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-ppc-rtp-smp.ads index 3ead19365d2..5bab2e8faca 100644 --- a/gcc/ada/libgnat/system-vxworks7-ppc-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-ppc-rtp-smp.ads @@ -151,6 +151,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; + Support_Atomic_Primitives : constant Boolean := False; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-ppc64-kernel.ads b/gcc/ada/libgnat/system-vxworks7-ppc64-kernel.ads index a1a983b15ff..768fbee4bd5 100644 --- a/gcc/ada/libgnat/system-vxworks7-ppc64-kernel.ads +++ b/gcc/ada/libgnat/system-vxworks7-ppc64-kernel.ads @@ -148,6 +148,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-ppc64-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-ppc64-rtp-smp.ads index afdd820601b..75abe4c584d 100644 --- a/gcc/ada/libgnat/system-vxworks7-ppc64-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-ppc64-rtp-smp.ads @@ -151,6 +151,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-x86-kernel.ads b/gcc/ada/libgnat/system-vxworks7-x86-kernel.ads index e34c22aba0b..42ae9838b5d 100644 --- a/gcc/ada/libgnat/system-vxworks7-x86-kernel.ads +++ b/gcc/ada/libgnat/system-vxworks7-x86-kernel.ads @@ -146,7 +146,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-x86-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-x86-rtp-smp.ads index 68ca423cd12..47dd3aef8ff 100644 --- a/gcc/ada/libgnat/system-vxworks7-x86-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-x86-rtp-smp.ads @@ -149,7 +149,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-x86_64-kernel.ads b/gcc/ada/libgnat/system-vxworks7-x86_64-kernel.ads index 6504a027a28..7931241652e 100644 --- a/gcc/ada/libgnat/system-vxworks7-x86_64-kernel.ads +++ b/gcc/ada/libgnat/system-vxworks7-x86_64-kernel.ads @@ -146,7 +146,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; diff --git a/gcc/ada/libgnat/system-vxworks7-x86_64-rtp-smp.ads b/gcc/ada/libgnat/system-vxworks7-x86_64-rtp-smp.ads index ffcc78fc809..3c98b4c2ff3 100644 --- a/gcc/ada/libgnat/system-vxworks7-x86_64-rtp-smp.ads +++ b/gcc/ada/libgnat/system-vxworks7-x86_64-rtp-smp.ads @@ -149,7 +149,7 @@ private Stack_Check_Probes : constant Boolean := True; Stack_Check_Limits : constant Boolean := False; Support_Aggregates : constant Boolean := True; - Support_Atomic_Primitives : constant Boolean := False; + Support_Atomic_Primitives : constant Boolean := True; Support_Composite_Assign : constant Boolean := True; Support_Composite_Compare : constant Boolean := True; Support_Long_Shifts : constant Boolean := True; -- cgit v1.2.1 From 45656a992eb18bfefe2e6e20d3b425afe945af28 Mon Sep 17 00:00:00 2001 From: Ronan Desplanques Date: Mon, 24 Oct 2022 11:50:06 +0200 Subject: ada: Adjust classwide contract expression preanalysis Before this patch, a classwide contract expression was preanalyzed only when its primitive operation's type was frozen. It caused name resolution to be off in the cases where the freezing took place after the end of the declaration list the primitive operation was declared in. This patch makes it so that if the compiler gets to the end of the declaration list before the type is frozen, it preanalyzes the classwide contract expression, so that the names are resolved in the right context. gcc/ada/ * contracts.adb (Preanalyze_Class_Conditions): New procedure. (Preanalyze_Condition): Moved out from Merge_Class_Conditions in order to be spec-visible. * contracts.ads (Preanalyze_Class_Conditions): New procedure. * sem_prag.adb (Analyze_Pre_Post_Condition_In_Decl_Part): Call Preanalyze_Class_Conditions when necessary. --- gcc/ada/contracts.adb | 481 ++++++++++++++++++++++++++------------------------ gcc/ada/contracts.ads | 4 + gcc/ada/sem_prag.adb | 14 ++ 3 files changed, 267 insertions(+), 232 deletions(-) diff --git a/gcc/ada/contracts.adb b/gcc/ada/contracts.adb index 21f438f90f3..218fd66852f 100644 --- a/gcc/ada/contracts.adb +++ b/gcc/ada/contracts.adb @@ -107,6 +107,11 @@ package body Contracts is -- well as Contract_Cases, Subprogram_Variant, invariants and predicates. -- Body_Id denotes the entity of the subprogram body. + procedure Preanalyze_Condition + (Subp : Entity_Id; + Expr : Node_Id); + -- Preanalyze the class-wide condition Expr of Subp + procedure Set_Class_Condition (Kind : Condition_Kind; Subp : Entity_Id; @@ -4548,242 +4553,10 @@ package body Contracts is procedure Merge_Class_Conditions (Spec_Id : Entity_Id) is - procedure Preanalyze_Condition - (Subp : Entity_Id; - Expr : Node_Id); - -- Preanalyze the class-wide condition Expr of Subp - procedure Process_Inherited_Conditions (Kind : Condition_Kind); -- Collect all inherited class-wide conditions of Spec_Id and merge -- them into one big condition. - -------------------------- - -- Preanalyze_Condition -- - -------------------------- - - procedure Preanalyze_Condition - (Subp : Entity_Id; - Expr : Node_Id) - is - procedure Clear_Unset_References; - -- Clear unset references on formals of Subp since preanalysis - -- occurs in a place unrelated to the actual code. - - procedure Remove_Controlling_Arguments; - -- Traverse Expr and clear the Controlling_Argument of calls to - -- nonabstract functions. - - procedure Remove_Formals (Id : Entity_Id); - -- Remove formals from homonym chains and make them not visible - - procedure Restore_Original_Selected_Component; - -- Traverse Expr searching for dispatching calls to functions whose - -- original node was a selected component, and replace them with - -- their original node. - - ---------------------------- - -- Clear_Unset_References -- - ---------------------------- - - procedure Clear_Unset_References is - F : Entity_Id := First_Formal (Subp); - - begin - while Present (F) loop - Set_Unset_Reference (F, Empty); - Next_Formal (F); - end loop; - end Clear_Unset_References; - - ---------------------------------- - -- Remove_Controlling_Arguments -- - ---------------------------------- - - procedure Remove_Controlling_Arguments is - function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result; - -- Reset the Controlling_Argument of calls to nonabstract - -- function calls. - - --------------------- - -- Remove_Ctrl_Arg -- - --------------------- - - function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result is - begin - if Nkind (N) = N_Function_Call - and then Present (Controlling_Argument (N)) - and then not Is_Abstract_Subprogram (Entity (Name (N))) - then - Set_Controlling_Argument (N, Empty); - end if; - - return OK; - end Remove_Ctrl_Arg; - - procedure Remove_Ctrl_Args is new Traverse_Proc (Remove_Ctrl_Arg); - begin - Remove_Ctrl_Args (Expr); - end Remove_Controlling_Arguments; - - -------------------- - -- Remove_Formals -- - -------------------- - - procedure Remove_Formals (Id : Entity_Id) is - F : Entity_Id := First_Formal (Id); - - begin - while Present (F) loop - Set_Is_Immediately_Visible (F, False); - Remove_Homonym (F); - Next_Formal (F); - end loop; - end Remove_Formals; - - ----------------------------------------- - -- Restore_Original_Selected_Component -- - ----------------------------------------- - - procedure Restore_Original_Selected_Component is - Restored_Nodes_List : Elist_Id := No_Elist; - - procedure Fix_Parents (N : Node_Id); - -- Traverse the subtree of N fixing the Parent field of all the - -- nodes. - - function Restore_Node (N : Node_Id) return Traverse_Result; - -- Process dispatching calls to functions whose original node was - -- a selected component, and replace them with their original - -- node. Restored nodes are stored in the Restored_Nodes_List - -- to fix the parent fields of their subtrees in a separate - -- tree traversal. - - ----------------- - -- Fix_Parents -- - ----------------- - - procedure Fix_Parents (N : Node_Id) is - - function Fix_Parent - (Parent_Node : Node_Id; - Node : Node_Id) return Traverse_Result; - -- Process a single node - - ---------------- - -- Fix_Parent -- - ---------------- - - function Fix_Parent - (Parent_Node : Node_Id; - Node : Node_Id) return Traverse_Result - is - Par : constant Node_Id := Parent (Node); - - begin - if Par /= Parent_Node then - pragma Assert (not Is_List_Member (Node)); - Set_Parent (Node, Parent_Node); - end if; - - return OK; - end Fix_Parent; - - procedure Fix_Parents is - new Traverse_Proc_With_Parent (Fix_Parent); - - begin - Fix_Parents (N); - end Fix_Parents; - - ------------------ - -- Restore_Node -- - ------------------ - - function Restore_Node (N : Node_Id) return Traverse_Result is - begin - if Nkind (N) = N_Function_Call - and then Nkind (Original_Node (N)) = N_Selected_Component - and then Is_Dispatching_Operation (Entity (Name (N))) - then - Rewrite (N, Original_Node (N)); - Set_Original_Node (N, N); - - -- Save the restored node in the Restored_Nodes_List to fix - -- the parent fields of their subtrees in a separate tree - -- traversal. - - Append_New_Elmt (N, Restored_Nodes_List); - end if; - - return OK; - end Restore_Node; - - procedure Restore_Nodes is new Traverse_Proc (Restore_Node); - - -- Start of processing for Restore_Original_Selected_Component - - begin - Restore_Nodes (Expr); - - -- After restoring the original node we must fix the decoration - -- of the Parent attribute to ensure tree consistency; required - -- because when the class-wide condition is inherited, calls to - -- New_Copy_Tree will perform copies of this subtree, and formal - -- occurrences with wrong Parent field cannot be mapped to the - -- new formals. - - if Present (Restored_Nodes_List) then - declare - Elmt : Elmt_Id := First_Elmt (Restored_Nodes_List); - - begin - while Present (Elmt) loop - Fix_Parents (Node (Elmt)); - Next_Elmt (Elmt); - end loop; - end; - end if; - end Restore_Original_Selected_Component; - - -- Start of processing for Preanalyze_Condition - - begin - pragma Assert (Present (Expr)); - pragma Assert (Inside_Class_Condition_Preanalysis = False); - - Push_Scope (Subp); - Install_Formals (Subp); - Inside_Class_Condition_Preanalysis := True; - - Preanalyze_Spec_Expression (Expr, Standard_Boolean); - - Inside_Class_Condition_Preanalysis := False; - Remove_Formals (Subp); - Pop_Scope; - - -- If this preanalyzed condition has occurrences of dispatching calls - -- using the Object.Operation notation, during preanalysis such calls - -- are rewritten as dispatching function calls; if at later stages - -- this condition is inherited we must have restored the original - -- selected-component node to ensure that the preanalysis of the - -- inherited condition rewrites these dispatching calls in the - -- correct context to avoid reporting spurious errors. - - Restore_Original_Selected_Component; - - -- Traverse Expr and clear the Controlling_Argument of calls to - -- nonabstract functions. Required since the preanalyzed condition - -- is not yet installed on its definite context and will be cloned - -- and extended in derivations with additional conditions. - - Remove_Controlling_Arguments; - - -- Clear also attribute Unset_Reference; again because preanalysis - -- occurs in a place unrelated to the actual code. - - Clear_Unset_References; - end Preanalyze_Condition; - ---------------------------------- -- Process_Inherited_Conditions -- ---------------------------------- @@ -5116,6 +4889,250 @@ package body Contracts is end loop; end Merge_Class_Conditions; + --------------------------------- + -- Preanalyze_Class_Conditions -- + --------------------------------- + + procedure Preanalyze_Class_Conditions (Spec_Id : Entity_Id) is + Cond : Node_Id; + + begin + for Kind in Condition_Kind loop + Cond := Class_Condition (Kind, Spec_Id); + + if Present (Cond) then + Preanalyze_Condition (Spec_Id, Cond); + end if; + end loop; + end Preanalyze_Class_Conditions; + + -------------------------- + -- Preanalyze_Condition -- + -------------------------- + + procedure Preanalyze_Condition + (Subp : Entity_Id; + Expr : Node_Id) + is + procedure Clear_Unset_References; + -- Clear unset references on formals of Subp since preanalysis + -- occurs in a place unrelated to the actual code. + + procedure Remove_Controlling_Arguments; + -- Traverse Expr and clear the Controlling_Argument of calls to + -- nonabstract functions. + + procedure Remove_Formals (Id : Entity_Id); + -- Remove formals from homonym chains and make them not visible + + procedure Restore_Original_Selected_Component; + -- Traverse Expr searching for dispatching calls to functions whose + -- original node was a selected component, and replace them with + -- their original node. + + ---------------------------- + -- Clear_Unset_References -- + ---------------------------- + + procedure Clear_Unset_References is + F : Entity_Id := First_Formal (Subp); + + begin + while Present (F) loop + Set_Unset_Reference (F, Empty); + Next_Formal (F); + end loop; + end Clear_Unset_References; + + ---------------------------------- + -- Remove_Controlling_Arguments -- + ---------------------------------- + + procedure Remove_Controlling_Arguments is + function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result; + -- Reset the Controlling_Argument of calls to nonabstract + -- function calls. + + --------------------- + -- Remove_Ctrl_Arg -- + --------------------- + + function Remove_Ctrl_Arg (N : Node_Id) return Traverse_Result is + begin + if Nkind (N) = N_Function_Call + and then Present (Controlling_Argument (N)) + and then not Is_Abstract_Subprogram (Entity (Name (N))) + then + Set_Controlling_Argument (N, Empty); + end if; + + return OK; + end Remove_Ctrl_Arg; + + procedure Remove_Ctrl_Args is new Traverse_Proc (Remove_Ctrl_Arg); + begin + Remove_Ctrl_Args (Expr); + end Remove_Controlling_Arguments; + + -------------------- + -- Remove_Formals -- + -------------------- + + procedure Remove_Formals (Id : Entity_Id) is + F : Entity_Id := First_Formal (Id); + + begin + while Present (F) loop + Set_Is_Immediately_Visible (F, False); + Remove_Homonym (F); + Next_Formal (F); + end loop; + end Remove_Formals; + + ----------------------------------------- + -- Restore_Original_Selected_Component -- + ----------------------------------------- + + procedure Restore_Original_Selected_Component is + Restored_Nodes_List : Elist_Id := No_Elist; + + procedure Fix_Parents (N : Node_Id); + -- Traverse the subtree of N fixing the Parent field of all the + -- nodes. + + function Restore_Node (N : Node_Id) return Traverse_Result; + -- Process dispatching calls to functions whose original node was + -- a selected component, and replace them with their original + -- node. Restored nodes are stored in the Restored_Nodes_List + -- to fix the parent fields of their subtrees in a separate + -- tree traversal. + + ----------------- + -- Fix_Parents -- + ----------------- + + procedure Fix_Parents (N : Node_Id) is + + function Fix_Parent + (Parent_Node : Node_Id; + Node : Node_Id) return Traverse_Result; + -- Process a single node + + ---------------- + -- Fix_Parent -- + ---------------- + + function Fix_Parent + (Parent_Node : Node_Id; + Node : Node_Id) return Traverse_Result + is + Par : constant Node_Id := Parent (Node); + + begin + if Par /= Parent_Node then + pragma Assert (not Is_List_Member (Node)); + Set_Parent (Node, Parent_Node); + end if; + + return OK; + end Fix_Parent; + + procedure Fix_Parents is + new Traverse_Proc_With_Parent (Fix_Parent); + + begin + Fix_Parents (N); + end Fix_Parents; + + ------------------ + -- Restore_Node -- + ------------------ + + function Restore_Node (N : Node_Id) return Traverse_Result is + begin + if Nkind (N) = N_Function_Call + and then Nkind (Original_Node (N)) = N_Selected_Component + and then Is_Dispatching_Operation (Entity (Name (N))) + then + Rewrite (N, Original_Node (N)); + Set_Original_Node (N, N); + + -- Save the restored node in the Restored_Nodes_List to fix + -- the parent fields of their subtrees in a separate tree + -- traversal. + + Append_New_Elmt (N, Restored_Nodes_List); + end if; + + return OK; + end Restore_Node; + + procedure Restore_Nodes is new Traverse_Proc (Restore_Node); + + -- Start of processing for Restore_Original_Selected_Component + + begin + Restore_Nodes (Expr); + + -- After restoring the original node we must fix the decoration + -- of the Parent attribute to ensure tree consistency; required + -- because when the class-wide condition is inherited, calls to + -- New_Copy_Tree will perform copies of this subtree, and formal + -- occurrences with wrong Parent field cannot be mapped to the + -- new formals. + + if Present (Restored_Nodes_List) then + declare + Elmt : Elmt_Id := First_Elmt (Restored_Nodes_List); + + begin + while Present (Elmt) loop + Fix_Parents (Node (Elmt)); + Next_Elmt (Elmt); + end loop; + end; + end if; + end Restore_Original_Selected_Component; + + -- Start of processing for Preanalyze_Condition + + begin + pragma Assert (Present (Expr)); + pragma Assert (Inside_Class_Condition_Preanalysis = False); + + Push_Scope (Subp); + Install_Formals (Subp); + Inside_Class_Condition_Preanalysis := True; + + Preanalyze_Spec_Expression (Expr, Standard_Boolean); + + Inside_Class_Condition_Preanalysis := False; + Remove_Formals (Subp); + Pop_Scope; + + -- If this preanalyzed condition has occurrences of dispatching calls + -- using the Object.Operation notation, during preanalysis such calls + -- are rewritten as dispatching function calls; if at later stages + -- this condition is inherited we must have restored the original + -- selected-component node to ensure that the preanalysis of the + -- inherited condition rewrites these dispatching calls in the + -- correct context to avoid reporting spurious errors. + + Restore_Original_Selected_Component; + + -- Traverse Expr and clear the Controlling_Argument of calls to + -- nonabstract functions. Required since the preanalyzed condition + -- is not yet installed on its definite context and will be cloned + -- and extended in derivations with additional conditions. + + Remove_Controlling_Arguments; + + -- Clear also attribute Unset_Reference; again because preanalysis + -- occurs in a place unrelated to the actual code. + + Clear_Unset_References; + end Preanalyze_Condition; + ---------------------------------------- -- Save_Global_References_In_Contract -- ---------------------------------------- diff --git a/gcc/ada/contracts.ads b/gcc/ada/contracts.ads index bde32ffc5b4..ae6355ef410 100644 --- a/gcc/ada/contracts.ads +++ b/gcc/ada/contracts.ads @@ -276,6 +276,10 @@ package Contracts is -- which are invoked from the caller side; they are also used to build -- the dispatch-table wrapper (DTW), if required. + procedure Preanalyze_Class_Conditions (Spec_Id : Entity_Id); + -- Preanalyze class-wide pre-/postconditions of the given subprogram + -- specification. + procedure Process_Class_Conditions_At_Freeze_Point (Typ : Entity_Id); -- Merge, preanalyze, and check class-wide pre/postconditions of Typ -- primitives. diff --git a/gcc/ada/sem_prag.adb b/gcc/ada/sem_prag.adb index 2a3aca85a79..615c6d2110c 100644 --- a/gcc/ada/sem_prag.adb +++ b/gcc/ada/sem_prag.adb @@ -26201,6 +26201,20 @@ package body Sem_Prag is Check_Postcondition_Use_In_Inlined_Subprogram (N, Spec_Id); Set_Is_Analyzed_Pragma (N); + -- If the subprogram is frozen then its class-wide pre- and post- + -- conditions have been preanalyzed (see Merge_Class_Conditions); + -- otherwise they must be preanalyzed now to ensure the correct + -- visibility of their referenced entities. This scenario occurs + -- when the subprogram is defined in a nested package (since the + -- end of the package does not cause freezing). + + if Class_Present (N) + and then Is_Dispatching_Operation (Spec_Id) + and then not Is_Frozen (Spec_Id) + then + Preanalyze_Class_Conditions (Spec_Id); + end if; + Restore_Ghost_Region (Saved_GM, Saved_IGR); end Analyze_Pre_Post_Condition_In_Decl_Part; -- cgit v1.2.1 From 11f892571c7ab4964f16c0d432e6f5a9b4a091f1 Mon Sep 17 00:00:00 2001 From: Piotr Trojanek Date: Wed, 26 Oct 2022 09:58:47 +0200 Subject: ada: Clean up call to check if aspects are present Code cleanup; semantics is unaffected. gcc/ada/ * exp_ch6.adb, exp_put_image.adb, sem_aggr.adb, sem_attr.adb, sem_ch5.adb, sem_type.adb, sem_util.adb: Replace "Present (Find_Aspect (...))" with "Has_Aspect". --- gcc/ada/exp_ch6.adb | 4 ++-- gcc/ada/exp_put_image.adb | 2 +- gcc/ada/sem_aggr.adb | 2 +- gcc/ada/sem_attr.adb | 4 ++-- gcc/ada/sem_ch5.adb | 2 +- gcc/ada/sem_type.adb | 2 +- gcc/ada/sem_util.adb | 14 ++++++-------- 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/gcc/ada/exp_ch6.adb b/gcc/ada/exp_ch6.adb index fce7a7cebf5..1466e4dc36a 100644 --- a/gcc/ada/exp_ch6.adb +++ b/gcc/ada/exp_ch6.adb @@ -3305,8 +3305,8 @@ package body Exp_Ch6 is or else No (Aspect) -- Do not fold if multiple applicable predicate aspects - or else Present (Find_Aspect (Subt, Aspect_Static_Predicate)) - or else Present (Find_Aspect (Subt, Aspect_Predicate)) + or else Has_Aspect (Subt, Aspect_Static_Predicate) + or else Has_Aspect (Subt, Aspect_Predicate) or else Augments_Other_Dynamic_Predicate (Aspect) or else CodePeer_Mode then diff --git a/gcc/ada/exp_put_image.adb b/gcc/ada/exp_put_image.adb index f90f0206f27..eaedebe4001 100644 --- a/gcc/ada/exp_put_image.adb +++ b/gcc/ada/exp_put_image.adb @@ -1045,7 +1045,7 @@ package body Exp_Put_Image is declare U_Type : constant Entity_Id := Underlying_Type (Entity (Prefix (N))); begin - if Present (Find_Aspect (U_Type, Aspect_Put_Image)) then + if Has_Aspect (U_Type, Aspect_Put_Image) then return True; end if; diff --git a/gcc/ada/sem_aggr.adb b/gcc/ada/sem_aggr.adb index 4da05dd7317..3a093d26007 100644 --- a/gcc/ada/sem_aggr.adb +++ b/gcc/ada/sem_aggr.adb @@ -1052,7 +1052,7 @@ package body Sem_Aggr is elsif Is_Array_Type (Typ) and then Null_Record_Present (N) then Error_Msg_N ("null record forbidden in array aggregate", N); - elsif Present (Find_Aspect (Typ, Aspect_Aggregate)) + elsif Has_Aspect (Typ, Aspect_Aggregate) and then Ekind (Typ) /= E_Record_Type and then Ada_Version >= Ada_2022 then diff --git a/gcc/ada/sem_attr.adb b/gcc/ada/sem_attr.adb index 299ea04959c..de4e8aa681c 100644 --- a/gcc/ada/sem_attr.adb +++ b/gcc/ada/sem_attr.adb @@ -5996,8 +5996,8 @@ package body Sem_Attr is -- Verify that prefix can be iterated upon. if Is_Array_Type (Typ) - or else Present (Find_Aspect (Typ, Aspect_Default_Iterator)) - or else Present (Find_Aspect (Typ, Aspect_Iterable)) + or else Has_Aspect (Typ, Aspect_Default_Iterator) + or else Has_Aspect (Typ, Aspect_Iterable) then null; else diff --git a/gcc/ada/sem_ch5.adb b/gcc/ada/sem_ch5.adb index 5f0629d32b3..7bca6d39dd2 100644 --- a/gcc/ada/sem_ch5.adb +++ b/gcc/ada/sem_ch5.adb @@ -2191,7 +2191,7 @@ package body Sem_Ch5 is if Is_Array_Type (Typ) or else Is_Reversible_Iterator (Typ) or else - (Present (Find_Aspect (Typ, Aspect_Iterable)) + (Has_Aspect (Typ, Aspect_Iterable) and then Present (Get_Iterable_Type_Primitive (Typ, Name_Previous))) diff --git a/gcc/ada/sem_type.adb b/gcc/ada/sem_type.adb index 2fc82d14016..718c29754c1 100644 --- a/gcc/ada/sem_type.adb +++ b/gcc/ada/sem_type.adb @@ -1007,7 +1007,7 @@ package body Sem_Type is elsif Ada_Version >= Ada_2022 and then T2 = Any_Composite - and then Present (Find_Aspect (T1, Aspect_Aggregate)) + and then Has_Aspect (T1, Aspect_Aggregate) then return True; diff --git a/gcc/ada/sem_util.adb b/gcc/ada/sem_util.adb index 71548dcca17..e43581ec6e9 100644 --- a/gcc/ada/sem_util.adb +++ b/gcc/ada/sem_util.adb @@ -13309,7 +13309,7 @@ package body Sem_Util is begin return Nkind (Exp) = N_Aggregate - and then Present (Find_Aspect (Etype (Exp), Aspect_Aggregate)) + and then Has_Aspect (Etype (Exp), Aspect_Aggregate) and then not Is_Record_Aggregate; end Is_Container_Aggregate; @@ -21718,18 +21718,16 @@ package body Sem_Util is -- type has the appropriate user-defined literal aspect. return (Nkind (N) in N_Numeric_Or_String_Literal - and then Present (Find_Aspect (Typ, Literal_Aspect_Map (Nkind (N))))) + and then Has_Aspect (Typ, Literal_Aspect_Map (Nkind (N)))) or else (Is_Entity_Name (N) and then Present (Entity (N)) and then ((Ekind (Entity (N)) = E_Named_Integer - and then - Present (Find_Aspect (Typ, Aspect_Integer_Literal))) + and then Has_Aspect (Typ, Aspect_Integer_Literal)) or else (Ekind (Entity (N)) = E_Named_Real - and then - Present (Find_Aspect (Typ, Aspect_Real_Literal))))); + and then Has_Aspect (Typ, Aspect_Real_Literal)))); end Is_User_Defined_Literal; -------------------------------------- @@ -32563,7 +32561,7 @@ package body Sem_Util is (Typ : Entity_Id) return Boolean is begin - return Present (Find_Aspect (Typ, Aspect_Designated_Storage_Model)); + return Has_Aspect (Typ, Aspect_Designated_Storage_Model); end Has_Designated_Storage_Model_Aspect; ----------------------------------- @@ -32573,7 +32571,7 @@ package body Sem_Util is function Has_Storage_Model_Type_Aspect (Typ : Entity_Id) return Boolean is begin - return Present (Find_Aspect (Typ, Aspect_Storage_Model_Type)); + return Has_Aspect (Typ, Aspect_Storage_Model_Type); end Has_Storage_Model_Type_Aspect; -------------------------- -- cgit v1.2.1 From 10f193eb043e30741d9631999bc869d71d43264c Mon Sep 17 00:00:00 2001 From: Steve Baird Date: Tue, 25 Oct 2022 16:59:29 -0700 Subject: ada: Compile-time simplification of 'Image incorrectly ignores Put_Image In the case of Some_Enumeration_Type'Image (), the compiler will replace this expression in its internal program representation with a corresponding string literal. This is incorrect if the Put_Image aspect has been specified (directly or via inheritance) for the enumeration type. gcc/ada/ * sem_attr.adb (Eval_Attribute): Don't simplify 'Image call if Put_Image has been specified. --- gcc/ada/sem_attr.adb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gcc/ada/sem_attr.adb b/gcc/ada/sem_attr.adb index de4e8aa681c..5166b4be4e9 100644 --- a/gcc/ada/sem_attr.adb +++ b/gcc/ada/sem_attr.adb @@ -9203,13 +9203,15 @@ package body Sem_Attr is -- Image is a scalar attribute, but is never static, because it is -- not a static function (having a non-scalar argument (RM 4.9(22)) -- However, we can constant-fold the image of an enumeration literal - -- if names are available. + -- if names are available and default Image implementation has not + -- been overridden. when Attribute_Image => if Is_Entity_Name (E1) and then Ekind (Entity (E1)) = E_Enumeration_Literal and then not Discard_Names (First_Subtype (Etype (E1))) and then not Global_Discard_Names + and then not Has_Aspect (Etype (E1), Aspect_Put_Image) then declare Lit : constant Entity_Id := Entity (E1); -- cgit v1.2.1 From b2278f6b146595b3f80a072145a78877041cb8bc Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Tue, 18 Oct 2022 11:32:02 +0200 Subject: ada: Fix oversight in implementation of allocators for storage models When the allocator is of an unconstrained array type and has an initializing expression, the copy of the initializing expression must be done separately from that of the bounds. gcc/ada/ * gcc-interface/utils2.cc (build_allocator): For unconstrained array types with a storage model and an initializing expression, copy the initialization expression separately from the bounds. In all cases with a storage model, pass the locally computed size for the store. --- gcc/ada/gcc-interface/utils2.cc | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/gcc/ada/gcc-interface/utils2.cc b/gcc/ada/gcc-interface/utils2.cc index ef81f8dd56a..80d550c91e1 100644 --- a/gcc/ada/gcc-interface/utils2.cc +++ b/gcc/ada/gcc-interface/utils2.cc @@ -2439,8 +2439,8 @@ build_allocator (tree type, tree init, tree result_type, Entity_Id gnat_proc, tree storage_ptr_type = build_pointer_type (storage_type); tree lhs, rhs; - size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (TYPE_SIZE_UNIT (storage_type), - init); + size = TYPE_SIZE_UNIT (storage_type); + size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (size, init); /* If the size overflows, pass -1 so Storage_Error will be raised. */ if (TREE_CODE (size) == INTEGER_CST && !valid_constant_size_p (size)) @@ -2454,8 +2454,10 @@ build_allocator (tree type, tree init, tree result_type, Entity_Id gnat_proc, /* If there is an initializing expression, then make a constructor for the entire object including the bounds and copy it into the object. - If there is no initializing expression, just set the bounds. */ - if (init) + If there is no initializing expression, just set the bounds. Note + that, if we have a storage model, we need to copy the initializing + expression separately from the bounds. */ + if (init && !pool_is_storage_model) { vec *v; vec_alloc (v, 2); @@ -2472,11 +2474,28 @@ build_allocator (tree type, tree init, tree result_type, Entity_Id gnat_proc, { lhs = build_component_ref (storage_deref, TYPE_FIELDS (storage_type), false); - rhs = build_template (template_type, type, NULL_TREE); + rhs = build_template (template_type, type, init); } if (pool_is_storage_model) - storage_init = build_storage_model_store (gnat_pool, lhs, rhs); + { + storage_init = build_storage_model_store (gnat_pool, lhs, rhs); + if (init) + { + start_stmt_group (); + add_stmt (storage_init); + lhs + = build_component_ref (storage_deref, + DECL_CHAIN (TYPE_FIELDS (storage_type)), + false); + rhs = init; + size = TYPE_SIZE_UNIT (TREE_TYPE (lhs)); + size = SUBSTITUTE_PLACEHOLDER_IN_EXPR (size, init); + tree t = build_storage_model_store (gnat_pool, lhs, rhs, size); + add_stmt (t); + storage_init = end_stmt_group (); + } + } else storage_init = build_binary_op (INIT_EXPR, NULL_TREE, lhs, rhs); @@ -2520,7 +2539,7 @@ build_allocator (tree type, tree init, tree result_type, Entity_Id gnat_proc, TREE_THIS_NOTRAP (storage_deref) = 1; if (pool_is_storage_model) storage_init - = build_storage_model_store (gnat_pool, storage_deref, init); + = build_storage_model_store (gnat_pool, storage_deref, init, size); else storage_init = build_binary_op (INIT_EXPR, NULL_TREE, storage_deref, init); -- cgit v1.2.1 From e581490f0cfa80c58d2b648d71a44a597fbe3008 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 7 Nov 2022 13:58:49 -0800 Subject: gcc: fix PR rtl-optimization/107482 gcc/ PR rtl-optimization/107482 * ira-color.cc (assign_hard_reg): Only call update_costs_from_copies when retry_p is false. --- gcc/ira-color.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc index 4a1a325e8e3..ffe73b61c45 100644 --- a/gcc/ira-color.cc +++ b/gcc/ira-color.cc @@ -2209,8 +2209,8 @@ assign_hard_reg (ira_allocno_t a, bool retry_p) restore_costs_from_copies (a); ALLOCNO_HARD_REGNO (a) = best_hard_regno; ALLOCNO_ASSIGNED_P (a) = true; - if (best_hard_regno >= 0) - update_costs_from_copies (a, true, ! retry_p); + if (best_hard_regno >= 0 && !retry_p) + update_costs_from_copies (a, true, true); ira_assert (ALLOCNO_CLASS (a) == aclass); /* We don't need updated costs anymore. */ ira_free_allocno_updated_costs (a); -- cgit v1.2.1 From ee86bdd1d367bc174d7b50bd2ffa5622c4766322 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 8 Nov 2022 11:19:25 +0100 Subject: libstdc++: Uncomment denorm_min test As r13-3609-g6d9dbdf51f9afe8 has been committed, we can now enable even the denorm_min test. 2022-11-08 Jakub Jelinek * testsuite/20_util/to_chars/float128_c++23.cc (test): Uncomment denorm_min test. --- libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc index 4c01458e6d9..28824c9c6ff 100644 --- a/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc +++ b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc @@ -34,7 +34,7 @@ void test(std::chars_format fmt = std::chars_format{}) { std::float128_t tests[] = { -// std::numeric_limits::denorm_min(), + std::numeric_limits::denorm_min(), std::numeric_limits::min(), 0.0f128, -42.0f128, -- cgit v1.2.1 From fa271afb58423014e2feef9f15c1a87428e64ddc Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 8 Nov 2022 12:21:55 +0100 Subject: i386: Improve vector [GL]E{,U} comparison against vector constants [PR107546] For integer vector comparisons without XOP before AVX512{F,VL} we are constrained by only GT and EQ being supported in HW. For GTU we play tricks to implement it using GT or unsigned saturating subtraction, for LT/LTU we swap the operands and thus turn it into GT/GTU. For LE/LEU we handle it by using GT/GTU and negating the result and for GE/GEU by using GT/GTU on swapped operands and negating the result. If the second operand is a CONST_VECTOR, we can usually do better though, we can avoid the negation. For LE/LEU cst by doing LT/LTU cst+1 (and then cst+1 GT/GTU x) and for GE/GEU cst by doing GT/GTU cst-1, provided there is no wrap-around on those cst+1 or cst-1. GIMPLE canonicalizes x < cst to x <= cst-1 etc. (the rule is smaller absolute value on constant), but only for scalars or uniform vectors, so in some cases this undoes that canonicalization in order to avoid the extra negation, but it handles also non-uniform constants. E.g. with -mavx2 the testcase assembly difference is: - movl $47, %eax + movl $48, %eax vmovdqa %xmm0, %xmm1 vmovd %eax, %xmm0 vpbroadcastb %xmm0, %xmm0 - vpminsb %xmm0, %xmm1, %xmm0 - vpcmpeqb %xmm1, %xmm0, %xmm0 + vpcmpgtb %xmm1, %xmm0, %xmm0 and - vmovdqa %xmm0, %xmm1 - vmovdqa .LC1(%rip), %xmm0 - vpminsb %xmm1, %xmm0, %xmm1 - vpcmpeqb %xmm1, %xmm0, %xmm0 + vpcmpgtb .LC1(%rip), %xmm0, %xmm0 while with just SSE2: - pcmpgtb .LC0(%rip), %xmm0 - pxor %xmm1, %xmm1 - pcmpeqb %xmm1, %xmm0 + movdqa %xmm0, %xmm1 + movdqa .LC0(%rip), %xmm0 + pcmpgtb %xmm1, %xmm0 and - movdqa %xmm0, %xmm1 - movdqa .LC1(%rip), %xmm0 - pcmpgtb %xmm1, %xmm0 - pxor %xmm1, %xmm1 - pcmpeqb %xmm1, %xmm0 + pcmpgtb .LC1(%rip), %xmm0 2022-11-08 Jakub Jelinek PR target/107546 * config/i386/predicates.md (vector_or_const_vector_operand): New predicate. * config/i386/sse.md (vec_cmp, vec_cmpv2div2di, vec_cmpu, vec_cmpuv2div2di): Use nonimmediate_or_const_vector_operand predicate instead of nonimmediate_operand and vector_or_const_vector_operand instead of vector_operand. * config/i386/i386-expand.cc (ix86_expand_int_sse_cmp): For LE/LEU or GE/GEU with CONST_VECTOR cop1 try to transform those into LE/LEU or GT/GTU with larger or smaller by one cop1 if there is no wrap-around. Force CONST_VECTOR cop0 or cop1 into REG. Formatting fix. * gcc.target/i386/pr107546.c: New test. --- gcc/config/i386/i386-expand.cc | 86 ++++++++++++++++++++++++++++++-- gcc/config/i386/predicates.md | 7 +++ gcc/config/i386/sse.md | 12 ++--- gcc/testsuite/gcc.target/i386/pr107546.c | 19 +++++++ 4 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr107546.c diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc index 2e0d12c0108..9c92b07d5cd 100644 --- a/gcc/config/i386/i386-expand.cc +++ b/gcc/config/i386/i386-expand.cc @@ -4510,15 +4510,86 @@ ix86_expand_int_sse_cmp (rtx dest, enum rtx_code code, rtx cop0, rtx cop1, case GTU: break; - case NE: case LE: case LEU: + /* x <= cst can be handled as x < cst + 1 unless there is + wrap around in cst + 1. */ + if (GET_CODE (cop1) == CONST_VECTOR + && GET_MODE_INNER (mode) != TImode) + { + unsigned int n_elts = GET_MODE_NUNITS (mode), i; + machine_mode eltmode = GET_MODE_INNER (mode); + for (i = 0; i < n_elts; ++i) + { + rtx elt = CONST_VECTOR_ELT (cop1, i); + if (!CONST_INT_P (elt)) + break; + if (code == GE) + { + /* For LE punt if some element is signed maximum. */ + if ((INTVAL (elt) & (GET_MODE_MASK (eltmode) >> 1)) + == (GET_MODE_MASK (eltmode) >> 1)) + break; + } + /* For LEU punt if some element is unsigned maximum. */ + else if (elt == constm1_rtx) + break; + } + if (i == n_elts) + { + rtvec v = rtvec_alloc (n_elts); + for (i = 0; i < n_elts; ++i) + RTVEC_ELT (v, i) + = GEN_INT (INTVAL (CONST_VECTOR_ELT (cop1, i)) + 1); + cop1 = gen_rtx_CONST_VECTOR (mode, v); + std::swap (cop0, cop1); + code = code == LE ? GT : GTU; + break; + } + } + /* FALLTHRU */ + case NE: code = reverse_condition (code); *negate = true; break; case GE: case GEU: + /* x >= cst can be handled as x > cst - 1 unless there is + wrap around in cst - 1. */ + if (GET_CODE (cop1) == CONST_VECTOR + && GET_MODE_INNER (mode) != TImode) + { + unsigned int n_elts = GET_MODE_NUNITS (mode), i; + machine_mode eltmode = GET_MODE_INNER (mode); + for (i = 0; i < n_elts; ++i) + { + rtx elt = CONST_VECTOR_ELT (cop1, i); + if (!CONST_INT_P (elt)) + break; + if (code == GE) + { + /* For GE punt if some element is signed minimum. */ + if (INTVAL (elt) < 0 + && ((INTVAL (elt) & (GET_MODE_MASK (eltmode) >> 1)) + == 0)) + break; + } + /* For GEU punt if some element is zero. */ + else if (elt == const0_rtx) + break; + } + if (i == n_elts) + { + rtvec v = rtvec_alloc (n_elts); + for (i = 0; i < n_elts; ++i) + RTVEC_ELT (v, i) + = GEN_INT (INTVAL (CONST_VECTOR_ELT (cop1, i)) - 1); + cop1 = gen_rtx_CONST_VECTOR (mode, v); + code = code == GE ? GT : GTU; + break; + } + } code = reverse_condition (code); *negate = true; /* FALLTHRU */ @@ -4556,6 +4627,11 @@ ix86_expand_int_sse_cmp (rtx dest, enum rtx_code code, rtx cop0, rtx cop1, } } + if (GET_CODE (cop0) == CONST_VECTOR) + cop0 = force_reg (mode, cop0); + else if (GET_CODE (cop1) == CONST_VECTOR) + cop1 = force_reg (mode, cop1); + rtx optrue = op_true ? op_true : CONSTM1_RTX (data_mode); rtx opfalse = op_false ? op_false : CONST0_RTX (data_mode); if (*negate) @@ -4752,13 +4828,13 @@ ix86_expand_int_sse_cmp (rtx dest, enum rtx_code code, rtx cop0, rtx cop1, if (*negate) std::swap (op_true, op_false); + if (GET_CODE (cop1) == CONST_VECTOR) + cop1 = force_reg (mode, cop1); + /* Allow the comparison to be done in one mode, but the movcc to happen in another mode. */ if (data_mode == mode) - { - x = ix86_expand_sse_cmp (dest, code, cop0, cop1, - op_true, op_false); - } + x = ix86_expand_sse_cmp (dest, code, cop0, cop1, op_true, op_false); else { gcc_assert (GET_MODE_SIZE (data_mode) == GET_MODE_SIZE (mode)); diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 2a3f07224cc..f9955038386 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -1235,6 +1235,13 @@ (ior (match_operand 0 "register_operand") (match_operand 0 "vector_memory_operand"))) +; Return true when OP is register_operand, vector_memory_operand +; or const_vector. +(define_predicate "vector_or_const_vector_operand" + (ior (match_operand 0 "register_operand") + (match_operand 0 "vector_memory_operand") + (match_code "const_vector"))) + (define_predicate "bcst_mem_operand" (and (match_code "vec_duplicate") (and (match_test "TARGET_AVX512F") diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index fa93ae7bf21..9a4fc01856d 100644 --- a/gcc/config/i386/sse.md +++ b/gcc/config/i386/sse.md @@ -4311,7 +4311,7 @@ [(set (match_operand: 0 "register_operand") (match_operator: 1 "" [(match_operand:VI_256 2 "register_operand") - (match_operand:VI_256 3 "nonimmediate_operand")]))] + (match_operand:VI_256 3 "nonimmediate_or_const_vector_operand")]))] "TARGET_AVX2" { bool ok = ix86_expand_int_vec_cmp (operands); @@ -4323,7 +4323,7 @@ [(set (match_operand: 0 "register_operand") (match_operator: 1 "" [(match_operand:VI124_128 2 "register_operand") - (match_operand:VI124_128 3 "vector_operand")]))] + (match_operand:VI124_128 3 "vector_or_const_vector_operand")]))] "TARGET_SSE2" { bool ok = ix86_expand_int_vec_cmp (operands); @@ -4335,7 +4335,7 @@ [(set (match_operand:V2DI 0 "register_operand") (match_operator:V2DI 1 "" [(match_operand:V2DI 2 "register_operand") - (match_operand:V2DI 3 "vector_operand")]))] + (match_operand:V2DI 3 "vector_or_const_vector_operand")]))] "TARGET_SSE4_2" { bool ok = ix86_expand_int_vec_cmp (operands); @@ -4397,7 +4397,7 @@ [(set (match_operand: 0 "register_operand") (match_operator: 1 "" [(match_operand:VI_256 2 "register_operand") - (match_operand:VI_256 3 "nonimmediate_operand")]))] + (match_operand:VI_256 3 "nonimmediate_or_const_vector_operand")]))] "TARGET_AVX2" { bool ok = ix86_expand_int_vec_cmp (operands); @@ -4409,7 +4409,7 @@ [(set (match_operand: 0 "register_operand") (match_operator: 1 "" [(match_operand:VI124_128 2 "register_operand") - (match_operand:VI124_128 3 "vector_operand")]))] + (match_operand:VI124_128 3 "vector_or_const_vector_operand")]))] "TARGET_SSE2" { bool ok = ix86_expand_int_vec_cmp (operands); @@ -4421,7 +4421,7 @@ [(set (match_operand:V2DI 0 "register_operand") (match_operator:V2DI 1 "" [(match_operand:V2DI 2 "register_operand") - (match_operand:V2DI 3 "vector_operand")]))] + (match_operand:V2DI 3 "vector_or_const_vector_operand")]))] "TARGET_SSE4_2" { bool ok = ix86_expand_int_vec_cmp (operands); diff --git a/gcc/testsuite/gcc.target/i386/pr107546.c b/gcc/testsuite/gcc.target/i386/pr107546.c new file mode 100644 index 00000000000..e5cf56d7620 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr107546.c @@ -0,0 +1,19 @@ +/* PR target/107546 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse2 -mno-xop -mno-avx512f" } */ +/* { dg-final { scan-assembler-not "pcmpeqb\t" } } */ +/* { dg-final { scan-assembler-times "pcmpgtb\t" 2 } } */ + +typedef signed char V __attribute__((vector_size(16))); + +V +foo (V x) +{ + return x < 48; +} + +V +bar (V x) +{ + return x >= (V) { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 }; +} -- cgit v1.2.1