From 5bd4ab91660c8f5534c46979d4846a4f1a0972b0 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Mon, 23 Aug 2021 16:16:25 +0200 Subject: Fix previous ipa-modref patch gcc/ChangeLog: PR middle-end/101949 * ipa-modref.c (analyze_ssa_name_flags): Fix merging of EAF_NOCLOBBER --- gcc/ipa-modref.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c index cb0a314..6ab687a 100644 --- a/gcc/ipa-modref.c +++ b/gcc/ipa-modref.c @@ -1707,7 +1707,7 @@ analyze_ssa_name_flags (tree name, vec &lattice, int depth, is on since that would allow propagation of this from -fno-ipa-pta to -fipa-pta functions. */ if (gimple_call_fn (use_stmt) == name) - lattice[index].merge (~EAF_NOCLOBBER); + lattice[index].merge (~(EAF_NOCLOBBER | EAF_UNUSED)); /* Recursion would require bit of propagation; give up for now. */ if (callee && !ipa && recursive_call_p (current_function_decl, -- cgit v1.1 From 39baa886bcfa95c06d30a0978aa83f33fcd49745 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Mon, 23 Aug 2021 16:20:09 +0200 Subject: Fix template in g++.dg/tree-ssa/modref-1.C gcc/testsuite/ChangeLog: * g++.dg/tree-ssa/modref-1.C: Fix template. --- gcc/testsuite/g++.dg/tree-ssa/modref-1.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/testsuite/g++.dg/tree-ssa/modref-1.C b/gcc/testsuite/g++.dg/tree-ssa/modref-1.C index c742dfe..b9b9f6c 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/modref-1.C +++ b/gcc/testsuite/g++.dg/tree-ssa/modref-1.C @@ -29,4 +29,4 @@ int test2() return b; } // ipa-modref should analyze parameter B of test as noescape. -// { dg-final { scan-tree-dump "return 1234" } } +// { dg-final { scan-tree-dump "return 1234" "optimized" } } -- cgit v1.1 From 0230e69a3fabe6ad1d80cdf308ad1bf1934c4381 Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Mon, 23 Aug 2021 15:37:48 +0200 Subject: ipa/97565 - fix IPA PTA body availability check Looks like the existing check using has_gimple_body_p isn't enough at LTRANS time but I need to check in_other_partition as well. 2021-08-23 Richard Biener PR ipa/97565 * tree-ssa-structalias.c (ipa_pta_execute): Check in_other_partition in addition to has_gimple_body. * g++.dg/lto/pr97565_0.C: New testcase. * g++.dg/lto/pr97565_1.C: Likewise. --- gcc/testsuite/g++.dg/lto/pr97565_0.C | 7 +++++++ gcc/testsuite/g++.dg/lto/pr97565_1.C | 6 ++++++ gcc/tree-ssa-structalias.c | 22 ++++++++++++++-------- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/lto/pr97565_0.C create mode 100644 gcc/testsuite/g++.dg/lto/pr97565_1.C (limited to 'gcc') diff --git a/gcc/testsuite/g++.dg/lto/pr97565_0.C b/gcc/testsuite/g++.dg/lto/pr97565_0.C new file mode 100644 index 0000000..f4572e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr97565_0.C @@ -0,0 +1,7 @@ +// { dg-lto-do link } +// { dg-lto-options { "-O -flto -fipa-pta" } } + +extern "C" void abort(void) +{ + abort(); +} diff --git a/gcc/testsuite/g++.dg/lto/pr97565_1.C b/gcc/testsuite/g++.dg/lto/pr97565_1.C new file mode 100644 index 0000000..ff7b638 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr97565_1.C @@ -0,0 +1,6 @@ +extern "C" void abort(void); + +int main(int argc, char * argv[]) +{ + abort(); +} diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index fb0e429..c430855 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -8220,10 +8220,12 @@ ipa_pta_execute (void) FOR_EACH_DEFINED_FUNCTION (node) { varinfo_t vi; - /* Nodes without a body are not interesting. Especially do not - visit clones at this point for now - we get duplicate decls - there for inline clones at least. */ - if (!node->has_gimple_body_p () || node->inlined_to) + /* Nodes without a body in this partition are not interesting. + Especially do not visit clones at this point for now - we + get duplicate decls there for inline clones at least. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->inlined_to) continue; node->get_body (); @@ -8301,8 +8303,10 @@ ipa_pta_execute (void) struct function *func; basic_block bb; - /* Nodes without a body are not interesting. */ - if (!node->has_gimple_body_p () || node->clone_of) + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) continue; if (dump_file) @@ -8431,8 +8435,10 @@ ipa_pta_execute (void) unsigned i; basic_block bb; - /* Nodes without a body are not interesting. */ - if (!node->has_gimple_body_p () || node->clone_of) + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) continue; fn = DECL_STRUCT_FUNCTION (node->decl); -- cgit v1.1 From 89ff4f027b5c92e96a8527920b8ccc3b94aec672 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Mon, 23 Aug 2021 15:35:05 +0100 Subject: [Committed] Restore build on !TARGET_TRULY_NOOP_TRUNCATION targets My sincere apologies to everyone, but especially Andrew Pinski who warned me in advance that TRULY_NOOP_TRUNCATION results in different code paths/optimizations on some targets. This restores the build on nvptx-none (and presumably others) where mysteriously (truncate:QI (reg:QI)) fails to be simplified to (reg:QI), which is expected (everywhere) in my recently added self-tests. 2021-08-23 Roger Sayle gcc/ChangeLog * simplify-rtx.c (simplify_unary_operation_1): [TRUNCATE]: Handle case where the operand is already the desired mode. --- gcc/simplify-rtx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'gcc') diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index f3df614..8eea9fb 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -1268,6 +1268,9 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, return temp; } + /* Check for useless truncation. */ + if (GET_MODE (op) == mode) + return op; break; case FLOAT_TRUNCATE: -- cgit v1.1 From fedadb60b6fc6425387faf4d514b4e8b0e24180e Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Mon, 23 Aug 2021 10:33:35 -0400 Subject: Add tailcall/sibcall support to the H8 gcc/ * config/h8300/h8300-protos.h (h8300_expand_epilogue): Add new argument. * config/h8300/jumpcall.md (call, call_value): Restrict to !SIBLING_CALL_P cases. (subcall, sibcall_value): New patterns & expanders. * config/h8300/proepi.md (epilogue): Pass new argument to h8300_expand_epilogue. (sibcall_epilogue): New expander. * config/h8300/h8300.c (h8300_expand_epilogue): Handle sibcall epilogues too. (h8300_ok_for_sibcall_p): New function. (TARGET_FUNCTION_OK_FOR_SIBCALL): define. --- gcc/config/h8300/h8300-protos.h | 2 +- gcc/config/h8300/h8300.c | 29 ++++++++++++++-- gcc/config/h8300/jumpcall.md | 74 +++++++++++++++++++++++++++++++++++++++-- gcc/config/h8300/proepi.md | 10 +++++- 4 files changed, 108 insertions(+), 7 deletions(-) (limited to 'gcc') diff --git a/gcc/config/h8300/h8300-protos.h b/gcc/config/h8300/h8300-protos.h index 744337d..3d34401 100644 --- a/gcc/config/h8300/h8300-protos.h +++ b/gcc/config/h8300/h8300-protos.h @@ -94,7 +94,7 @@ extern int h8300_tiny_data_p (tree); extern int h8300_can_use_return_insn_p (void); extern void h8300_expand_prologue (void); -extern void h8300_expand_epilogue (void); +extern void h8300_expand_epilogue (bool); extern int h8300_current_function_interrupt_function_p (void); extern int h8300_current_function_monitor_function_p (void); extern int h8300_initial_elimination_offset (int, int); diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c index 8ccacec..5f7251a 100644 --- a/gcc/config/h8300/h8300.c +++ b/gcc/config/h8300/h8300.c @@ -874,7 +874,7 @@ h8300_can_use_return_insn_p (void) /* Generate RTL code for the function epilogue. */ void -h8300_expand_epilogue (void) +h8300_expand_epilogue (bool sibcall_p) { int regno; int saved_regs; @@ -919,6 +919,7 @@ h8300_expand_epilogue (void) /* See if this pop would be the last insn before the return. If so, use rte/l or rts/l instead of pop or ldm.l. */ if (TARGET_H8300SX + && !sibcall_p && !frame_pointer_needed && frame_size == 0 && (saved_regs & ((1 << (regno - n_regs + 1)) - 1)) == 0) @@ -931,12 +932,12 @@ h8300_expand_epilogue (void) /* Pop frame pointer if we had one. */ if (frame_pointer_needed) { - if (TARGET_H8300SX) + if (TARGET_H8300SX && !sibcall_p) returned_p = true; h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, true, returned_p); } - if (!returned_p) + if (!returned_p && !sibcall_p) emit_jump_insn (ret_rtx); } @@ -5533,6 +5534,25 @@ h8300_push_rounding (poly_int64 bytes) { return ((bytes + PARM_BOUNDARY / 8 - 1) & (-PARM_BOUNDARY / 8)); } + +static bool +h8300_ok_for_sibcall_p (tree fndecl, tree) +{ + /* If either the caller or target are special, then assume sibling + calls are not OK. */ + if (!fndecl + || h8300_os_task_function_p (fndecl) + || h8300_monitor_function_p (fndecl) + || h8300_interrupt_function_p (fndecl) + || h8300_saveall_function_p (fndecl) + || h8300_os_task_function_p (current_function_decl) + || h8300_monitor_function_p (current_function_decl) + || h8300_interrupt_function_p (current_function_decl) + || h8300_saveall_function_p (current_function_decl)) + return false; + + return 1; +} /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE @@ -5628,4 +5648,7 @@ h8300_push_rounding (poly_int64 bytes) #undef TARGET_FLAGS_REGNUM #define TARGET_FLAGS_REGNUM 12 +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL h8300_ok_for_sibcall_p + struct gcc_target targetm = TARGET_INITIALIZER; diff --git a/gcc/config/h8300/jumpcall.md b/gcc/config/h8300/jumpcall.md index 3e59fee..b596399 100644 --- a/gcc/config/h8300/jumpcall.md +++ b/gcc/config/h8300/jumpcall.md @@ -290,7 +290,7 @@ (define_insn "call_insn_" [(call (mem:QI (match_operand 0 "call_insn_operand" "Cr")) (match_operand:P 1 "general_operand" "g"))] - "" + "!SIBLING_CALL_P (insn)" { rtx xoperands[1]; xoperands[0] = gen_rtx_MEM (QImode, operands[0]); @@ -328,7 +328,7 @@ [(set (match_operand 0 "" "=r") (call (mem:QI (match_operand 1 "call_insn_operand" "Cr")) (match_operand:P 2 "general_operand" "g")))] - "" + "!SIBLING_CALL_P (insn)" { rtx xoperands[2]; gcc_assert (GET_MODE (operands[1]) == Pmode); @@ -347,3 +347,73 @@ (const_int 2) (const_int 4)))]) +(define_expand "sibcall" + [(call (match_operand:QI 0 "call_expander_operand" "") + (match_operand 1 "general_operand" ""))] + "" + { + if (!register_operand (XEXP (operands[0], 0), Pmode) + && GET_CODE (XEXP (operands[0], 0)) != SYMBOL_REF) + XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0)); + }) + +(define_insn "sibcall_insn_" + [(call (mem:QI (match_operand 0 "call_insn_operand" "Cr")) + (match_operand:P 1 "general_operand" "g"))] + "SIBLING_CALL_P (insn)" +{ + rtx xoperands[1]; + xoperands[0] = gen_rtx_MEM (QImode, operands[0]); + gcc_assert (GET_MODE (operands[0]) == Pmode); + if (GET_CODE (XEXP (xoperands[0], 0)) == SYMBOL_REF + && (SYMBOL_REF_FLAGS (XEXP (xoperands[0], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION)) + output_asm_insn ("jmp\\t@%0:8", xoperands); + else + output_asm_insn ("jmp\\t%0", xoperands); + return ""; +} + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (match_operand:QI 0 "small_call_insn_operand" "") + (const_int 2) + (const_int 4)))]) + +;; Call subroutine, returning value in operand 0 +;; (which must be a hard register). + +;; ??? Even though we use HImode here, this works on the H8/300H and H8S. + +(define_expand "sibcall_value" + [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "call_expander_operand" "") + (match_operand 2 "general_operand" "")))] + "" + { + if (!register_operand (XEXP (operands[1], 0), Pmode) + && GET_CODE (XEXP (operands[1], 0)) != SYMBOL_REF) + XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0)); + }) + +(define_insn "sibcall_value_insn_" + [(set (match_operand 0 "" "=r") + (call (mem:QI (match_operand 1 "call_insn_operand" "Cr")) + (match_operand:P 2 "general_operand" "g")))] + "SIBLING_CALL_P (insn)" +{ + rtx xoperands[2]; + gcc_assert (GET_MODE (operands[1]) == Pmode); + xoperands[0] = operands[0]; + xoperands[1] = gen_rtx_MEM (QImode, operands[1]); + if (GET_CODE (XEXP (xoperands[1], 0)) == SYMBOL_REF + && (SYMBOL_REF_FLAGS (XEXP (xoperands[1], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION)) + output_asm_insn ("jmp\\t@%1:8", xoperands); + else + output_asm_insn ("jmp\\t%1", xoperands); + return ""; +} + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (match_operand:QI 0 "small_call_insn_operand" "") + (const_int 2) + (const_int 4)))]) + diff --git a/gcc/config/h8300/proepi.md b/gcc/config/h8300/proepi.md index 44d5968..ab58d02 100644 --- a/gcc/config/h8300/proepi.md +++ b/gcc/config/h8300/proepi.md @@ -98,7 +98,7 @@ [(return)] "" { - h8300_expand_epilogue (); + h8300_expand_epilogue (false); DONE; }) @@ -121,3 +121,11 @@ gcc_unreachable (); } [(set_attr "length" "20")]) + +(define_expand "sibcall_epilogue" + [(const_int 0)] + "" + { + h8300_expand_epilogue (true); + DONE; + }) -- cgit v1.1 From bb75b22aba254e8ff144db27b1c8b4804bad73bb Mon Sep 17 00:00:00 2001 From: Thomas Schwinge Date: Mon, 2 Aug 2021 17:38:05 +0200 Subject: Allow matching Intel MIC in OpenMP 'declare variant' ..., and use that to improve XFAILing for Intel MIC offloading execution instead of compilation in 'libgomp.c-c++-common/target-45.c', 'libgomp.fortran/target10.f90'. gcc/ * config/i386/i386-options.c (ix86_omp_device_kind_arch_isa) [ACCEL_COMPILER]: Match "intel_mic". * config/i386/t-omp-device (omp-device-properties-i386) : Add "intel_mic". libgomp/ * testsuite/lib/libgomp.exp (check_effective_target_offload_target_intelmic): Remove 'proc'. (check_effective_target_offload_device_intel_mic): New 'proc'. * testsuite/libgomp.c-c++-common/on_device_arch.h (device_arch_intel_mic, on_device_arch_intel_mic): New. * testsuite/libgomp.c-c++-common/target-45.c: Use that for 'dg-xfail-run-if'. * testsuite/libgomp.fortran/target10.f90: Likewise. --- gcc/config/i386/i386-options.c | 4 ++++ gcc/config/i386/t-omp-device | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/config/i386/i386-options.c b/gcc/config/i386/i386-options.c index 6b78998..fee5a48 100644 --- a/gcc/config/i386/i386-options.c +++ b/gcc/config/i386/i386-options.c @@ -304,6 +304,10 @@ ix86_omp_device_kind_arch_isa (enum omp_device_kind_arch_isa trait, case omp_device_kind: return strcmp (name, "cpu") == 0; case omp_device_arch: +#ifdef ACCEL_COMPILER + if (strcmp (name, "intel_mic") == 0) + return 1; +#endif if (strcmp (name, "x86") == 0) return 1; if (TARGET_64BIT) diff --git a/gcc/config/i386/t-omp-device b/gcc/config/i386/t-omp-device index 037ae5e..29350a1 100644 --- a/gcc/config/i386/t-omp-device +++ b/gcc/config/i386/t-omp-device @@ -1,6 +1,6 @@ omp-device-properties-i386: $(srcdir)/config/i386/i386-options.c echo kind: cpu > $@ - echo arch: x86 x86_64 i386 i486 i586 i686 ia32 >> $@ + echo arch: intel_mic x86 x86_64 i386 i486 i586 i686 ia32 >> $@ echo isa: sse4 `sed -n '/^static struct ix86_target_opts isa2\?_opts\[\] =/,/^};/p' \ $(srcdir)/config/i386/i386-options.c | \ sed -n 's/",.*$$//;s/^ { "-m//p'` >> $@ -- cgit v1.1 From 6a64964212c8e5e10e474803d06a07514c1069b7 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Mon, 23 Aug 2021 17:56:51 +0200 Subject: Avoid redundant entries in modref access lists. In PR101296 Richard noticed that modref is giving up on analysis in milc by hitting --param=modref-max-accesses limit. While cleaning up original modref patch I removed code that tried to do smart things while merging accesses because it had bugs and wanted to reimplement it later which I later forgot. This patch adds logic that avoids adding access and its subaccess to the list which is just waste of memory and compile time. Incrementally I will add logic merging the ranges. gcc/ChangeLog: 2021-08-23 Jan Hubicka * ipa-modref-tree.h (modref_access_node::range_info_useful_p): Improve range compare. (modref_access_node::contains): New member function. (modref_access_node::search): Remove. (modref_access_node::insert): Be smarter about subaccesses. gcc/testsuite/ChangeLog: 2021-08-23 Jan Hubicka * gcc.dg/tree-ssa/modref-7.c: New test. --- gcc/ipa-modref-tree.h | 77 ++++++++++++++++++++++++-------- gcc/testsuite/gcc.dg/tree-ssa/modref-7.c | 13 ++++++ 2 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/modref-7.c (limited to 'gcc') diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h index d36c28c..2e26b75 100644 --- a/gcc/ipa-modref-tree.h +++ b/gcc/ipa-modref-tree.h @@ -66,7 +66,10 @@ struct GTY(()) modref_access_node /* Return true if range info is useful. */ bool range_info_useful_p () const { - return parm_index != -1 && parm_offset_known; + return parm_index != -1 && parm_offset_known + && (known_size_p (size) + || known_size_p (max_size) + || known_ge (offset, 0)); } /* Return true if both accesses are the same. */ bool operator == (modref_access_node &a) const @@ -88,6 +91,35 @@ struct GTY(()) modref_access_node return false; return true; } + /* Return true A is a subaccess. */ + bool contains (modref_access_node &a) const + { + if (parm_index != a.parm_index) + return false; + if (parm_index >= 0) + { + if (parm_offset_known + && (!a.parm_offset_known + || !known_eq (parm_offset, a.parm_offset))) + return false; + } + if (range_info_useful_p ()) + { + if (!a.range_info_useful_p ()) + return false; + /* Sizes of stores are used to check that object is big enough + to fit the store, so smaller or unknown sotre is more general + than large store. */ + if (known_size_p (size) + && !known_le (size, a.size)) + return false; + if (known_size_p (max_size)) + return known_subrange_p (a.offset, a.max_size, offset, max_size); + else + return known_le (offset, a.offset); + } + return true; + } }; /* Access node specifying no useful info. */ @@ -107,17 +139,6 @@ struct GTY((user)) modref_ref_node accesses (NULL) {} - /* Search REF; return NULL if failed. */ - modref_access_node *search (modref_access_node access) - { - size_t i; - modref_access_node *a; - FOR_EACH_VEC_SAFE_ELT (accesses, i, a) - if (*a == access) - return a; - return NULL; - } - /* Collapse the tree. */ void collapse () { @@ -136,16 +157,36 @@ struct GTY((user)) modref_ref_node return false; /* Otherwise, insert a node for the ref of the access under the base. */ - modref_access_node *access_node = search (a); - if (access_node) - return false; + size_t i; + modref_access_node *a2; + + if (!a.useful_p ()) + { + if (!every_access) + { + collapse (); + return true; + } + return false; + } + + FOR_EACH_VEC_SAFE_ELT (accesses, i, a2) + { + if (a2->contains (a)) + return false; + if (a.contains (*a2)) + { + *a2 = a; + return true; + } + gcc_checking_assert (!(a == *a2)); + } /* If this base->ref pair has too many accesses stored, we will clear all accesses and bail out. */ - if ((accesses && accesses->length () >= max_accesses) - || !a.useful_p ()) + if (accesses && accesses->length () >= max_accesses) { - if (dump_file && a.useful_p ()) + if (dump_file) fprintf (dump_file, "--param param=modref-max-accesses limit reached\n"); collapse (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c new file mode 100644 index 0000000..53ffa1c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c @@ -0,0 +1,13 @@ +/* { dg-options "-O2 --param modref-max-accesses=1 -fdump-tree-modref1" } */ +/* { dg-do compile } */ +struct a { + int array[10]; + int tail; +}; +int test(struct a *a, int p) +{ + a->array[p] = 0; + a->array[0] = 1; +} +/* All three accesses combine to one bigger access. */ +/* { dg-final { scan-tree-dump-not "param=modref-max-accesses" "modref1" } } */ -- cgit v1.1 From 4892b3087412e6afc261cc9977ef4b54c799660f Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 23 Aug 2021 14:01:01 -0400 Subject: analyzer: fix uninit false positive on overlapping bindings gcc/analyzer/ChangeLog: * store.cc (bit_range::intersects_p): New overload. (bit_range::operator-): New. (binding_cluster::maybe_get_compound_binding): Handle the partial overlap case. (selftest::test_bit_range_intersects_p): Add test coverage for new overload of bit_range::intersects_p. * store.h (bit_range::intersects_p): New overload. (bit_range::operator-): New. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/data-model-22.c: New test. * gcc.dg/analyzer/uninit-6.c: New test. * gcc.dg/analyzer/uninit-6b.c: New test. --- gcc/analyzer/store.cc | 77 +++++++++++++++++++- gcc/analyzer/store.h | 5 ++ gcc/testsuite/gcc.dg/analyzer/data-model-22.c | 101 ++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/uninit-6.c | 29 ++++++++ gcc/testsuite/gcc.dg/analyzer/uninit-6b.c | 29 ++++++++ 5 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-22.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/uninit-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/uninit-6b.c (limited to 'gcc') diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index eac1295..3760858 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -253,6 +253,35 @@ bit_range::contains_p (const bit_range &other, bit_range *out) const return false; } +/* If OTHER intersects this, return true and write + the relative range of OTHER within THIS to *OUT_THIS, + and the relative range of THIS within OTHER to *OUT_OTHER. + Otherwise return false. */ + +bool +bit_range::intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const +{ + if (get_start_bit_offset () < other.get_next_bit_offset () + && other.get_start_bit_offset () < get_next_bit_offset ()) + { + bit_offset_t overlap_start + = MAX (get_start_bit_offset (), + other.get_start_bit_offset ()); + bit_offset_t overlap_next + = MIN (get_next_bit_offset (), + other.get_next_bit_offset ()); + gcc_assert (overlap_next > overlap_start); + bit_range abs_overlap_bits (overlap_start, overlap_next - overlap_start); + *out_this = abs_overlap_bits - get_start_bit_offset (); + *out_other = abs_overlap_bits - other.get_start_bit_offset (); + return true; + } + else + return false; +} + int bit_range::cmp (const bit_range &br1, const bit_range &br2) { @@ -263,6 +292,14 @@ bit_range::cmp (const bit_range &br1, const bit_range &br2) return wi::cmpu (br1.m_size_in_bits, br2.m_size_in_bits); } +/* Offset this range by OFFSET. */ + +bit_range +bit_range::operator- (bit_offset_t offset) const +{ + return bit_range (m_start_bit_offset - offset, m_size_in_bits); +} + /* If MASK is a contiguous range of set bits, write them to *OUT and return true. Otherwise return false. */ @@ -1570,9 +1607,29 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, } else { - /* REG and the bound range partially overlap. - We don't handle this case yet. */ - return NULL; + /* REG and the bound range partially overlap. */ + bit_range reg_subrange (0, 0); + bit_range bound_subrange (0, 0); + reg_range.intersects_p (bound_range, + ®_subrange, &bound_subrange); + + /* Get the bits from the bound value for the bits at the + intersection (relative to the bound value). */ + const svalue *overlap_sval + = sval->extract_bit_range (NULL_TREE, + bound_subrange, + mgr->get_svalue_manager ()); + + /* Get key for overlap, relative to the REG. */ + const concrete_binding *overlap_concrete_key + = mgr->get_concrete_binding (reg_subrange); + result_map.put (overlap_concrete_key, overlap_sval); + + /* Clobber default_map, removing/trimming/spliting where + it overlaps with overlap_concrete_key. */ + default_map.remove_overlapping_bindings (mgr, + overlap_concrete_key, + NULL); } } else @@ -2905,6 +2962,20 @@ test_bit_range_intersects_p () ASSERT_FALSE (b3_to_5.intersects_p (b6_to_7)); ASSERT_FALSE (b6_to_7.intersects_p (b3_to_5)); + + bit_range r1 (0,0); + bit_range r2 (0,0); + ASSERT_TRUE (b1_to_6.intersects_p (b0_to_7, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 0); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 1); + ASSERT_EQ (r2.m_size_in_bits, 6); + + ASSERT_TRUE (b0_to_7.intersects_p (b1_to_6, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 1); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 0); + ASSERT_EQ (r2.m_size_in_bits, 6); } /* Implementation detail of ASSERT_BIT_RANGE_FROM_MASK_EQ. */ diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index b75691e..da82bd1 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -269,9 +269,14 @@ struct bit_range return (get_start_bit_offset () < other.get_next_bit_offset () && other.get_start_bit_offset () < get_next_bit_offset ()); } + bool intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const; static int cmp (const bit_range &br1, const bit_range &br2); + bit_range operator- (bit_offset_t offset) const; + static bool from_mask (unsigned HOST_WIDE_INT mask, bit_range *out); bool as_byte_range (byte_range *out) const; diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-22.c b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c new file mode 100644 index 0000000..8429b2f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c @@ -0,0 +1,101 @@ +#include +#include "analyzer-decls.h" + +extern void check_init_char (char v); +extern void check_init_int (int v); + +void test_1 (void) +{ + union + { + char c[16]; + int i[4]; + } v; + memset (&v, 0, sizeof (v)); + v.c[5] = 42; + check_init_int (v.c[0]); + check_init_int (v.c[4]); + check_init_int (v.c[6]); + check_init_int (v.i[1]); +} + +void test_2 (void) +{ + /* Intersection of byte ranges within "v". */ + union + { + struct { + int a; + char b; + char c; + } __attribute__((packed)) icc; + struct { + char a; + int b; + char c; + } __attribute__((packed)) cic; + struct { + char a; + char b; + int c; + } __attribute__((packed)) cci; + } v; + + v.icc.a = 1066; + v.icc.b = 42; + v.icc.c = 17; + + __analyzer_eval (v.icc.a == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.b == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cic.a = 42; + v.cic.b = 1066; + v.cic.c = 17; + + __analyzer_eval (v.cic.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.b == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cci.a = 42; + v.cci.b = 17; + v.cci.c = 1066; + + __analyzer_eval (v.cci.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.b == 17); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.c == 1066); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c new file mode 100644 index 0000000..75a99ad --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + struct ethtool_link_settings base; + u32 lanes; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c new file mode 100644 index 0000000..32ba30f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + u32 lanes; + struct ethtool_link_settings base; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} -- cgit v1.1 From e82e0f149b0aba660896ea9aa12c442c07a16d12 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 23 Aug 2021 14:07:39 -0400 Subject: analyzer: assume that POINTER_PLUS_EXPR of non-NULL is non-NULL [PR101962] gcc/analyzer/ChangeLog: PR analyzer/101962 * region-model.cc (region_model::eval_condition_without_cm): Refactor comparison against zero, adding a check for POINTER_PLUS_EXPR of non-NULL. gcc/testsuite/ChangeLog: PR analyzer/101962 * gcc.dg/analyzer/data-model-23.c: New test. * gcc.dg/analyzer/pr101962.c: New test. --- gcc/analyzer/region-model.cc | 73 +++++++++++++++++---------- gcc/testsuite/gcc.dg/analyzer/data-model-23.c | 26 ++++++++++ gcc/testsuite/gcc.dg/analyzer/pr101962.c | 51 +++++++++++++++++++ 3 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-23.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr101962.c (limited to 'gcc') diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 9870007..f54be14e 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2488,34 +2488,51 @@ region_model::eval_condition_without_cm (const svalue *lhs, if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) return constant_svalue::eval_condition (cst_lhs, op, cst_rhs); - /* Handle comparison of a region_svalue against zero. */ - - if (const region_svalue *ptr = lhs->dyn_cast_region_svalue ()) - if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) - if (zerop (cst_rhs->get_constant ())) - { - /* A region_svalue is a non-NULL pointer, except in certain - special cases (see the comment for region::non_null_p. */ - const region *pointee = ptr->get_pointee (); - if (pointee->non_null_p ()) - { - switch (op) - { - default: - gcc_unreachable (); - - case EQ_EXPR: - case GE_EXPR: - case LE_EXPR: - return tristate::TS_FALSE; - - case NE_EXPR: - case GT_EXPR: - case LT_EXPR: - return tristate::TS_TRUE; - } - } - } + /* Handle comparison against zero. */ + if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) + if (zerop (cst_rhs->get_constant ())) + { + if (const region_svalue *ptr = lhs->dyn_cast_region_svalue ()) + { + /* A region_svalue is a non-NULL pointer, except in certain + special cases (see the comment for region::non_null_p). */ + const region *pointee = ptr->get_pointee (); + if (pointee->non_null_p ()) + { + switch (op) + { + default: + gcc_unreachable (); + + case EQ_EXPR: + case GE_EXPR: + case LE_EXPR: + return tristate::TS_FALSE; + + case NE_EXPR: + case GT_EXPR: + case LT_EXPR: + return tristate::TS_TRUE; + } + } + } + else if (const binop_svalue *binop = lhs->dyn_cast_binop_svalue ()) + { + /* Treat offsets from a non-NULL pointer as being non-NULL. This + isn't strictly true, in that eventually ptr++ will wrap + around and be NULL, but it won't occur in practise and thus + can be used to suppress effectively false positives that we + shouldn't warn for. */ + if (binop->get_op () == POINTER_PLUS_EXPR) + { + tristate lhs_ts + = eval_condition_without_cm (binop->get_arg0 (), + op, rhs); + if (lhs_ts.is_known ()) + return lhs_ts; + } + } + } /* Handle rejection of equality for comparisons of the initial values of "external" values (such as params) with the address of locals. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-23.c b/gcc/testsuite/gcc.dg/analyzer/data-model-23.c new file mode 100644 index 0000000..c76dd4e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-23.c @@ -0,0 +1,26 @@ +#include "analyzer-decls.h" + +#define NULL ((void *)0) + +void * __attribute__((noinline)) +hide (void *ptr) +{ + return ptr; +} + +void test_1 (void) +{ + int a; + __analyzer_eval (hide (&a) == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (&a) + 1 != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (&a) + 1 == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (&a) - 1 != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (&a) - 1 == NULL); /* { dg-warning "FALSE" } */ +} + +void test_2 (void) +{ + __analyzer_eval (hide (NULL) == NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (NULL) - 1 == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (NULL) + 1 == NULL); /* { dg-warning "FALSE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101962.c b/gcc/testsuite/gcc.dg/analyzer/pr101962.c new file mode 100644 index 0000000..7b83d03 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101962.c @@ -0,0 +1,51 @@ +#include "analyzer-decls.h" + +#define NULL ((void *)0) + +/* Verify that the analyzer makes the simplifying assumption that we don't + hit NULL when incrementing pointers to non-NULL memory regions. */ + +static int * __attribute__((noinline)) +maybe_inc_int_ptr (int *ptr) +{ + if (!ptr) + return NULL; + return ++ptr; +} + +int +test_1 (void) +{ + int stack; + int *a = &stack; + a = maybe_inc_int_ptr (a); + a = maybe_inc_int_ptr (a); + __analyzer_eval (a == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (a != NULL); /* { dg-warning "TRUE" } */ + return *a; /* { dg-warning "use of uninitialized value '\\*a'" } */ + /* TODO: a complaint about out-of-bounds would be a better warning. */ +} + +static const char * __attribute__((noinline)) +maybe_inc_char_ptr (const char *ptr) +{ + if (!ptr) + return NULL; + return ++ptr; +} + +char +test_s (void) +{ + const char *msg = "hello world"; + const char *a = msg; + __analyzer_eval (*a == 'h'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'e'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'l'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'l'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'o'); /* { dg-warning "TRUE" } */ +} -- cgit v1.1 From 4b821c7efbe12cfbb129a88541108b39058da526 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 23 Aug 2021 14:09:44 -0400 Subject: analyzer: fix ICE when failing to reconstruct a fn ptr [PR101837] gcc/analyzer/ChangeLog: PR analyzer/101837 * analyzer.cc (maybe_reconstruct_from_def_stmt): Bail if fn is NULL, and assert that it's non-NULL before passing it to build_call_array_loc. gcc/testsuite/ChangeLog: PR analyzer/101837 * gcc.dg/analyzer/pr101837.c: New test. --- gcc/analyzer/analyzer.cc | 3 +++ gcc/testsuite/gcc.dg/analyzer/pr101837.c | 10 ++++++++++ 2 files changed, 13 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr101837.c (limited to 'gcc') diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index 5578877..f6e9c9d 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -145,6 +145,8 @@ maybe_reconstruct_from_def_stmt (tree ssa_name, tree return_type = gimple_call_return_type (call_stmt); tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt), visited); + if (fn == NULL_TREE) + return NULL_TREE; unsigned num_args = gimple_call_num_args (call_stmt); auto_vec args (num_args); for (unsigned i = 0; i < num_args; i++) @@ -155,6 +157,7 @@ maybe_reconstruct_from_def_stmt (tree ssa_name, return NULL_TREE; args.quick_push (arg); } + gcc_assert (fn); return build_call_array_loc (gimple_location (call_stmt), return_type, fn, num_args, args.address ()); diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101837.c b/gcc/testsuite/gcc.dg/analyzer/pr101837.c new file mode 100644 index 0000000..f99374d --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101837.c @@ -0,0 +1,10 @@ +/* { dg-additional-options "-O3 -fsanitize=undefined" } */ + +void memory_exhausted(); +void memcheck(void *ptr) { + if (ptr) /* { dg-warning "leak" } */ + memory_exhausted(); +} + +int emalloc(int size) { memcheck(__builtin_malloc(size)); } /* { dg-message "allocated here" } */ +int main() { int max_envvar_len = emalloc(max_envvar_len + 1); } /* { dg-message "use of uninitialized value 'max_envvar_len'" } */ -- cgit v1.1 From 3d654ca3f421ff9646470d312097602037176352 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 23 Aug 2021 14:11:58 -0400 Subject: analyzer: fix ICE with NULL change.m_expr [PR101875] gcc/analyzer/ChangeLog: PR analyzer/101875 * sm-file.cc (file_diagnostic::describe_state_change): Handle change.m_expr being NULL. gcc/testsuite/ChangeLog: PR analyzer/101875 * gcc.dg/analyzer/pr101875.c: New test. --- gcc/analyzer/sm-file.cc | 18 ++++++++++++++---- gcc/testsuite/gcc.dg/analyzer/pr101875.c | 16 ++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr101875.c (limited to 'gcc') diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index 6a17019..0c8cdf0 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -125,11 +125,21 @@ public: return label_text::borrow ("opened here"); if (change.m_old_state == m_sm.m_unchecked && change.m_new_state == m_sm.m_nonnull) - return change.formatted_print ("assuming %qE is non-NULL", - change.m_expr); + { + if (change.m_expr) + return change.formatted_print ("assuming %qE is non-NULL", + change.m_expr); + else + return change.formatted_print ("assuming FILE * is non-NULL"); + } if (change.m_new_state == m_sm.m_null) - return change.formatted_print ("assuming %qE is NULL", - change.m_expr); + { + if (change.m_expr) + return change.formatted_print ("assuming %qE is NULL", + change.m_expr); + else + return change.formatted_print ("assuming FILE * is NULL"); + } return label_text (); } diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101875.c b/gcc/testsuite/gcc.dg/analyzer/pr101875.c new file mode 100644 index 0000000..5988b8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101875.c @@ -0,0 +1,16 @@ +char * +fopen (const char *restrict, const char *restrict); + +void +err (void); + +void +k2 (void) +{ + char *setfiles[1]; + int i; + + setfiles[i] = fopen("", ""); /* { dg-warning "use of uninitialized value 'i'" } */ + if (!setfiles[i]) /* { dg-warning "use of uninitialized value 'i'" } */ + err (); +} /* { dg-warning "leak of FILE" } */ -- cgit v1.1 From 30c335ac44ecb4f17645925360177618763d7c48 Mon Sep 17 00:00:00 2001 From: Bill Schmidt Date: Thu, 19 Aug 2021 16:07:55 -0500 Subject: rs6000: Avoid buffer overruns 2021-08-19 Bill Schmidt gcc/ PR target/101830 * config/rs6000/rs6000-gen-builtins.c (consume_whitespace): Diagnose buffer overrun. (safe_inc_pos): Fix overrun detection. (match_identifier): Diagnose buffer overrun. (match_integer): Likewise. (match_to_right_bracket): Likewise. --- gcc/config/rs6000/rs6000-gen-builtins.c | 34 +++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'gcc') diff --git a/gcc/config/rs6000/rs6000-gen-builtins.c b/gcc/config/rs6000/rs6000-gen-builtins.c index e5d3b71..05b2d29 100644 --- a/gcc/config/rs6000/rs6000-gen-builtins.c +++ b/gcc/config/rs6000/rs6000-gen-builtins.c @@ -597,6 +597,13 @@ consume_whitespace (void) { while (pos < LINELEN && isspace(linebuf[pos]) && linebuf[pos] != '\n') pos++; + + if (pos >= LINELEN) + { + diag ("line length overrun at %d.\n", pos); + exit (1); + } + return; } @@ -623,7 +630,7 @@ advance_line (FILE *file) static inline void safe_inc_pos (void) { - if (pos++ >= LINELEN) + if (++pos >= LINELEN) { (*diag) ("line length overrun.\n"); exit (1); @@ -636,9 +643,16 @@ static char * match_identifier (void) { int lastpos = pos - 1; - while (isalnum (linebuf[lastpos + 1]) || linebuf[lastpos + 1] == '_') + while (lastpos < LINELEN - 1 + && (isalnum (linebuf[lastpos + 1]) || linebuf[lastpos + 1] == '_')) ++lastpos; + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return 0; @@ -660,9 +674,15 @@ match_integer (void) safe_inc_pos (); int lastpos = pos - 1; - while (isdigit (linebuf[lastpos + 1])) + while (lastpos < LINELEN - 1 && isdigit (linebuf[lastpos + 1])) ++lastpos; + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return NULL; @@ -680,7 +700,7 @@ static const char * match_to_right_bracket (void) { int lastpos = pos - 1; - while (linebuf[lastpos + 1] != ']') + while (lastpos < LINELEN - 1 && linebuf[lastpos + 1] != ']') { if (linebuf[lastpos + 1] == '\n') { @@ -690,6 +710,12 @@ match_to_right_bracket (void) ++lastpos; } + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return 0; -- cgit v1.1 From 34ad198138f7a64355c92090e1db260ee135495d Mon Sep 17 00:00:00 2001 From: Bill Schmidt Date: Tue, 27 Jul 2021 14:43:57 -0400 Subject: rs6000: Incorporate new builtins code into the build machinery 2021-07-27 Bill Schmidt gcc/ * config.gcc (powerpc*-*-*): Add rs6000-builtins.o to extra_objs. * config/rs6000/rs6000-gen-builtins.c (main): Close init_file last. * config/rs6000/t-rs6000 (rs6000-gen-builtins.o): New target. (rbtree.o): Likewise. (rs6000-gen-builtins): Likewise. (rs6000-builtins.c): Likewise. (rs6000-builtins.h): Likewise. (rs6000.o): Add dependency. (EXTRA_HEADERS): Add rs6000-vecdefines.h. (rs6000-vecdefines.h): New target. (rs6000-builtins.o): Likewise. (rs6000-call.o): Add rs6000-builtins.h as a dependency. (rs6000-c.o): Likewise. --- gcc/config.gcc | 1 + gcc/config/rs6000/rs6000-gen-builtins.c | 4 ++- gcc/config/rs6000/t-rs6000 | 46 +++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 6 deletions(-) (limited to 'gcc') diff --git a/gcc/config.gcc b/gcc/config.gcc index 08e6c67..a05c07e 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -476,6 +476,7 @@ powerpc*-*-*) cpu_type=rs6000 extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o" extra_objs="${extra_objs} rs6000-call.o rs6000-pcrel-opt.o" + extra_objs="${extra_objs} rs6000-builtins.o" extra_headers="ppc-asm.h altivec.h htmintrin.h htmxlintrin.h" extra_headers="${extra_headers} bmi2intrin.h bmiintrin.h" extra_headers="${extra_headers} xmmintrin.h mm_malloc.h emmintrin.h" diff --git a/gcc/config/rs6000/rs6000-gen-builtins.c b/gcc/config/rs6000/rs6000-gen-builtins.c index 05b2d29..8a7505b 100644 --- a/gcc/config/rs6000/rs6000-gen-builtins.c +++ b/gcc/config/rs6000/rs6000-gen-builtins.c @@ -3005,9 +3005,11 @@ main (int argc, const char **argv) exit (1); } + /* Always close init_file last. This avoids race conditions in the + build machinery. See comments in t-rs6000. */ fclose (header_file); - fclose (init_file); fclose (defines_file); + fclose (init_file); return 0; } diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000 index 44f7ffb..e0e8ab8 100644 --- a/gcc/config/rs6000/t-rs6000 +++ b/gcc/config/rs6000/t-rs6000 @@ -27,10 +27,6 @@ rs6000-pcrel-opt.o: $(srcdir)/config/rs6000/rs6000-pcrel-opt.c $(COMPILE) $< $(POSTCOMPILE) -rs6000-c.o: $(srcdir)/config/rs6000/rs6000-c.c - $(COMPILE) $< - $(POSTCOMPILE) - rs6000-string.o: $(srcdir)/config/rs6000/rs6000-string.c $(COMPILE) $< $(POSTCOMPILE) @@ -47,7 +43,47 @@ rs6000-logue.o: $(srcdir)/config/rs6000/rs6000-logue.c $(COMPILE) $< $(POSTCOMPILE) -rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call.c +rs6000-gen-builtins.o: $(srcdir)/config/rs6000/rs6000-gen-builtins.c + $(COMPILE) $< + $(POSTCOMPILE) + +rbtree.o: $(srcdir)/config/rs6000/rbtree.c + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-gen-builtins: rs6000-gen-builtins.o rbtree.o + $(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \ + $(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS) + +# TODO: Whenever GNU make 4.3 is the minimum required, we should use +# grouped targets on this: +# rs6000-builtins.c rs6000-builtins.h rs6000-vecdefines.h &: +# +# For now, the header files depend on rs6000-builtins.c, which avoids +# races because the .c file is closed last in rs6000-gen-builtins.c. +rs6000-builtins.c: rs6000-gen-builtins \ + $(srcdir)/config/rs6000/rs6000-builtin-new.def \ + $(srcdir)/config/rs6000/rs6000-overload.def + ./rs6000-gen-builtins $(srcdir)/config/rs6000/rs6000-builtin-new.def \ + $(srcdir)/config/rs6000/rs6000-overload.def rs6000-builtins.h \ + rs6000-builtins.c rs6000-vecdefines.h + +rs6000-builtins.h: rs6000-builtins.c + +rs6000.o: rs6000-builtins.h + +EXTRA_HEADERS += rs6000-vecdefines.h +rs6000-vecdefines.h: rs6000-builtins.c + +rs6000-builtins.o: rs6000-builtins.c + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call.c rs6000-builtins.h + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-c.o: $(srcdir)/config/rs6000/rs6000-c.c rs6000-builtins.h $(COMPILE) $< $(POSTCOMPILE) -- cgit v1.1 From 596f964f3272081a2320c1220e8aff06ee44fe91 Mon Sep 17 00:00:00 2001 From: Bill Schmidt Date: Tue, 27 Jul 2021 14:46:08 -0400 Subject: rs6000: Add gengtype handling to the build machinery 2021-06-07 Bill Schmidt gcc/ * config.gcc (target_gtfiles): Add ./rs6000-builtins.h. * config/rs6000/t-rs6000 (EXTRA_GTYPE_DEPS): Set. --- gcc/config.gcc | 1 + gcc/config/rs6000/t-rs6000 | 1 + 2 files changed, 2 insertions(+) (limited to 'gcc') diff --git a/gcc/config.gcc b/gcc/config.gcc index a05c07e..94199d7 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -492,6 +492,7 @@ powerpc*-*-*) extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt" target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c" target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel-opt.c" + target_gtfiles="$target_gtfiles ./rs6000-builtins.h" ;; pru-*-*) cpu_type=pru diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000 index e0e8ab8..92766d8 100644 --- a/gcc/config/rs6000/t-rs6000 +++ b/gcc/config/rs6000/t-rs6000 @@ -22,6 +22,7 @@ TM_H += $(srcdir)/config/rs6000/rs6000-builtin.def TM_H += $(srcdir)/config/rs6000/rs6000-cpus.def TM_H += $(srcdir)/config/rs6000/rs6000-modes.h PASSES_EXTRA += $(srcdir)/config/rs6000/rs6000-passes.def +EXTRA_GTYPE_DEPS += $(srcdir)/config/rs6000/rs6000-builtin-new.def rs6000-pcrel-opt.o: $(srcdir)/config/rs6000/rs6000-pcrel-opt.c $(COMPILE) $< -- cgit v1.1 From 192d4edd15cabf1f0e88e5a62142cd252542ea0c Mon Sep 17 00:00:00 2001 From: Bill Schmidt Date: Mon, 23 Aug 2021 17:26:43 -0500 Subject: rs6000: Fix AIX bootstrap (don't call asprintf) 2021-08-23 Bill Schmidt gcc/ * config/rs6000/rs6000-gen-builtins.c (parse_bif_entry): Don't call asprintf, which is not available on AIX. --- gcc/config/rs6000/rs6000-gen-builtins.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'gcc') diff --git a/gcc/config/rs6000/rs6000-gen-builtins.c b/gcc/config/rs6000/rs6000-gen-builtins.c index 8a7505b..000e5f9 100644 --- a/gcc/config/rs6000/rs6000-gen-builtins.c +++ b/gcc/config/rs6000/rs6000-gen-builtins.c @@ -1794,8 +1794,9 @@ parse_bif_entry (void) /* Append a number representing the order in which this function was encountered to its name, and save in another lookup structure. */ - char *buf; - asprintf (&buf, "%s:%05d", bifs[curr_bif].idname, curr_bif); + int orig_len = strlen (bifs[curr_bif].idname); + char *buf = (char *) malloc (orig_len + 7); + sprintf (buf, "%s:%05d", bifs[curr_bif].idname, curr_bif); if (!rbt_insert (&bifo_rbt, buf)) { -- cgit v1.1 From 8ca7fa84a3af355c3e2bbda2acc61934c16078b2 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 23 Aug 2021 19:27:21 -0400 Subject: analyzer: rewrite of switch handling When investigating false positives on the Linux kernel from -Wanalyzer-use-of-uninitialized-value, I noticed that the existing implementation of switch statements in the analyzer is broken. Specifically, the existing implementation assumes a 1:1 association between CFG out-edges from the basic block and case labels in the gimple switch statement. This happened to be the case in the examples I had tested, but there is no such association in general. In particular, in the motivating example: arch/x86/kernel/cpu/mtrr/if.c: mtrr_ioctl the switch statement has 3 blocks, each covering multiple ranges of ioctl command IDs for which different local variables are initialized, which the existing implementation gets badly wrong. [1] This patch reimplements switch handling in the analyzer to eliminate this false assumption - instead, for each out-edge we gather the set of case labels for that out-edge, and use that to determine the set of value ranges for the edge. Avoiding false positives for the above example requires that we accurately track value ranges for symbolic values, so the patch extends constraint_manager with a new bounded_ranges_constraint, adding just enough information to capture the ranges for switch statements whilst retaining combatility with the existing constraint-handling (ultimately I'd prefer to simply throw all of this into a SAT solver and let it track things). Doing so fixes the false positives seen on the Linux kernel and an existing xfail in the test suite. The patch also fixes a long-standing bug in constraint_manager::add_unknown_constraint when updating constraints due to combining equivalence classes, spotted when debugging the same logic for the new kind of constraints. [1] a reduced version of this code is captured in this patch, in gcc.dg/analyzer/torture/switch-3.c gcc/analyzer/ChangeLog: * analyzer.h (struct rejected_constraint): Convert to... (class rejected_constraint): ...this. (class bounded_ranges): New forward decl. (class bounded_ranges_manager): New forward decl. * constraint-manager.cc: Include "analyzer/analyzer-logging.h" and "tree-pretty-print.h". (can_plus_one_p): New. (plus_one): New. (can_minus_one_p): New. (minus_one): New. (bounded_range::bounded_range): New. (dump_cst): New. (bounded_range::dump_to_pp): New. (bounded_range::dump): New. (bounded_range::to_json): New. (bounded_range::set_json_attr): New. (bounded_range::contains_p): New. (bounded_range::intersects_p): New. (bounded_range::operator==): New. (bounded_range::cmp): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::bounded_ranges): New. (bounded_ranges::canonicalize): New. (bounded_ranges::validate): New. (bounded_ranges::operator==): New. (bounded_ranges::dump_to_pp): New. (bounded_ranges::dump): New. (bounded_ranges::to_json): New. (bounded_ranges::eval_condition): New. (bounded_ranges::contain_p): New. (bounded_ranges::cmp): New. (bounded_ranges_manager::~bounded_ranges_manager): New. (bounded_ranges_manager::get_or_create_empty): New. (bounded_ranges_manager::get_or_create_point): New. (bounded_ranges_manager::get_or_create_range): New. (bounded_ranges_manager::get_or_create_union): New. (bounded_ranges_manager::get_or_create_intersection): New. (bounded_ranges_manager::get_or_create_inverse): New. (bounded_ranges_manager::consolidate): New. (bounded_ranges_manager::get_or_create_ranges_for_switch): New. (bounded_ranges_manager::create_ranges_for_switch): New. (bounded_ranges_manager::make_case_label_ranges): New. (bounded_ranges_manager::log_stats): New. (bounded_ranges_constraint::print): New. (bounded_ranges_constraint::to_json): New. (bounded_ranges_constraint::operator==): New. (bounded_ranges_constraint::add_to_hash): New. (constraint_manager::constraint_manager): Update for new field m_bounded_ranges_constraints. (constraint_manager::operator=): Likewise. (constraint_manager::hash): Likewise. (constraint_manager::operator==): Likewise. (constraint_manager::print): Likewise. (constraint_manager::dump_to_pp): Likewise. (constraint_manager::to_json): Likewise. (constraint_manager::add_unknown_constraint): Update the lhs_ec_id if necessary in existing constraints when combining equivalence classes. Add similar code for handling m_bounded_ranges_constraints. (constraint_manager::add_constraint_internal): Add comment. (constraint_manager::add_bounded_ranges): New. (constraint_manager::eval_condition): Use new field m_bounded_ranges_constraints. (constraint_manager::purge): Update bounded_ranges_constraint instances. (constraint_manager::canonicalize): Update for new field. (merger_fact_visitor::on_ranges): New. (constraint_manager::for_each_fact): Use new field m_bounded_ranges_constraints. (constraint_manager::validate): Fix off-by-one error needed due to bug fixed above in add_unknown_constraint. Validate the EC IDs in m_bounded_ranges_constraints. (constraint_manager::get_range_manager): New. (selftest::assert_dump_bounded_range_eq): New. (ASSERT_DUMP_BOUNDED_RANGE_EQ): New. (selftest::test_bounded_range): New. (selftest::assert_dump_bounded_ranges_eq): New. (ASSERT_DUMP_BOUNDED_RANGES_EQ): New. (selftest::test_bounded_ranges): New. (selftest::run_constraint_manager_tests): Call the new selftests. * constraint-manager.h (struct bounded_range): New. (struct bounded_ranges): New. (template <> struct default_hash_traits): New. (class bounded_ranges_manager): New. (fact_visitor::on_ranges): New pure virtual function. (class bounded_ranges_constraint): New. (constraint_manager::add_bounded_ranges): New decl. (constraint_manager::get_range_manager): New decl. (constraint_manager::m_bounded_ranges_constraints): New field. * diagnostic-manager.cc (epath_finder::process_worklist_item): Transfer ownership of rc to add_feasibility_problem. * engine.cc (feasibility_problem::dump_to_pp): Use get_model. * feasible-graph.cc (infeasible_node::dump_dot): Update for conversion of m_rc to a pointer. (feasible_graph::add_feasibility_problem): Pass RC by pointer and take ownership. * feasible-graph.h (infeasible_node::infeasible_node): Pass RC by pointer and take ownership. (infeasible_node::~infeasible_node): New. (infeasible_node::m_rc): Convert to a pointer. (feasible_graph::add_feasibility_problem): Pass RC by pointer and take ownership. * region-model-manager.cc: Include "analyzer/constraint-manager.h". (region_model_manager::region_model_manager): Initializer new field m_range_mgr. (region_model_manager::~region_model_manager): Delete it. (region_model_manager::log_stats): Call log_stats on it. * region-model.cc (region_model::add_constraint): Use new subclass rejected_op_constraint. (region_model::apply_constraints_for_gswitch): Reimplement using bounded_ranges_manager. (rejected_constraint::dump_to_pp): Convert to... (rejected_op_constraint::dump_to_pp): ...this. (rejected_ranges_constraint::dump_to_pp): New. * region-model.h (struct purge_stats): Add field m_num_bounded_ranges_constraints. (region_model_manager::get_range_manager): New. (region_model_manager::m_range_mgr): New. (region_model::get_range_manager): New. (struct rejected_constraint): Split into... (class rejected_constraint):...this new abstract base class, and... (class rejected_op_constraint): ...this new concrete subclass. (class rejected_ranges_constraint): New. * supergraph.cc: Include "tree-cfg.h". (supergraph::supergraph): Drop idx param from add_cfg_edge. (supergraph::add_cfg_edge): Drop idx param. (switch_cfg_superedge::switch_cfg_superedge): Move here from header. Populate m_case_labels with all cases which go to DST. (switch_cfg_superedge::dump_label_to_pp): Reimplement to use m_case_labels. (switch_cfg_superedge::get_case_label): Delete. * supergraph.h (supergraphadd_cfg_edge): Drop "idx" param. (switch_cfg_superedge::switch_cfg_superedge): Drop idx param and move implementation to supergraph.cc. (switch_cfg_superedge::get_case_label): Delete. (switch_cfg_superedge::get_case_labels): New. (switch_cfg_superedge::m_idx): Delete. (switch_cfg_superedge::m_case_labels): New field. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/switch.c: Remove xfail. Add various tests. * gcc.dg/analyzer/torture/switch-2.c: New test. * gcc.dg/analyzer/torture/switch-3.c: New test. * gcc.dg/analyzer/torture/switch-4.c: New test. * gcc.dg/analyzer/torture/switch-5.c: New test. --- gcc/analyzer/analyzer.h | 4 +- gcc/analyzer/constraint-manager.cc | 1365 +++++++++++++++++++++- gcc/analyzer/constraint-manager.h | 191 +++ gcc/analyzer/diagnostic-manager.cc | 3 +- gcc/analyzer/engine.cc | 2 +- gcc/analyzer/feasible-graph.cc | 7 +- gcc/analyzer/feasible-graph.h | 7 +- gcc/analyzer/region-model-manager.cc | 7 +- gcc/analyzer/region-model.cc | 75 +- gcc/analyzer/region-model.h | 54 +- gcc/analyzer/supergraph.cc | 99 +- gcc/analyzer/supergraph.h | 15 +- gcc/testsuite/gcc.dg/analyzer/switch.c | 141 ++- gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c | 42 + gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c | 158 +++ gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c | 27 + gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c | 68 ++ 17 files changed, 2135 insertions(+), 130 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c (limited to 'gcc') diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 896b350..05d4751 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -75,10 +75,12 @@ class region_model; class region_model_context; class impl_region_model_context; class call_details; -struct rejected_constraint; +class rejected_constraint; class constraint_manager; class equiv_class; class reachable_regions; +class bounded_ranges; +class bounded_ranges_manager; class pending_diagnostic; class state_change_event; diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index f59929a..dc65c8d 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -42,12 +42,14 @@ along with GCC; see the file COPYING3. If not see #include "sbitmap.h" #include "bitmap.h" #include "tristate.h" +#include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" #include "analyzer/analyzer-selftests.h" +#include "tree-pretty-print.h" #if ENABLE_ANALYZER @@ -65,6 +67,50 @@ compare_constants (tree lhs_const, enum tree_code op, tree rhs_const) return tristate (tristate::TS_UNKNOWN); } +/* Return true iff CST is below the maximum value for its type. */ + +static bool +can_plus_one_p (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + return tree_int_cst_lt (cst, TYPE_MAX_VALUE (TREE_TYPE (cst))); +} + +/* Return (CST + 1). */ + +static tree +plus_one (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + gcc_assert (can_plus_one_p (cst)); + tree result = fold_build2 (PLUS_EXPR, TREE_TYPE (cst), + cst, integer_one_node); + gcc_assert (CONSTANT_CLASS_P (result)); + return result; +} + +/* Return true iff CST is above the minimum value for its type. */ + +static bool +can_minus_one_p (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + return tree_int_cst_lt (TYPE_MIN_VALUE (TREE_TYPE (cst)), cst); +} + +/* Return (CST - 1). */ + +static tree +minus_one (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + gcc_assert (can_minus_one_p (cst)); + tree result = fold_build2 (MINUS_EXPR, TREE_TYPE (cst), + cst, integer_one_node); + gcc_assert (CONSTANT_CLASS_P (result)); + return result; +} + /* struct bound. */ /* Ensure that this bound is closed by converting an open bound to a @@ -222,37 +268,709 @@ range::eval_condition (enum tree_code op, tree rhs_const) const return tristate (tristate::TS_TRUE); break; - default: - gcc_unreachable (); - break; - } - return tristate (tristate::TS_UNKNOWN); + default: + gcc_unreachable (); + break; + } + return tristate (tristate::TS_UNKNOWN); +} + +/* Return true if RHS_CONST is below the lower bound of this range. */ + +bool +range::below_lower_bound (tree rhs_const) const +{ + if (!m_lower_bound.m_constant) + return false; + + return compare_constants (rhs_const, + m_lower_bound.m_closed ? LT_EXPR : LE_EXPR, + m_lower_bound.m_constant).is_true (); +} + +/* Return true if RHS_CONST is above the upper bound of this range. */ + +bool +range::above_upper_bound (tree rhs_const) const +{ + if (!m_upper_bound.m_constant) + return false; + + return compare_constants (rhs_const, + m_upper_bound.m_closed ? GT_EXPR : GE_EXPR, + m_upper_bound.m_constant).is_true (); +} + +/* struct bounded_range. */ + +bounded_range::bounded_range (const_tree lower, const_tree upper) +: m_lower (const_cast (lower)), + m_upper (const_cast (upper)) +{ + if (lower && upper) + { + gcc_assert (TREE_CODE (m_lower) == INTEGER_CST); + gcc_assert (TREE_CODE (m_upper) == INTEGER_CST); + /* We should have lower <= upper. */ + gcc_assert (!tree_int_cst_lt (m_upper, m_lower)); + } + else + { + /* Purely for pending on-stack values, for + writing back to. */ + gcc_assert (m_lower == NULL_TREE); + gcc_assert (m_lower == NULL_TREE); + } +} + +static void +dump_cst (pretty_printer *pp, tree cst, bool show_types) +{ + gcc_assert (cst); + if (show_types) + { + pp_character (pp, '('); + dump_generic_node (pp, TREE_TYPE (cst), 0, (dump_flags_t)0, false); + pp_character (pp, ')'); + } + dump_generic_node (pp, cst, 0, (dump_flags_t)0, false); +} + +/* Dump this object to PP. */ + +void +bounded_range::dump_to_pp (pretty_printer *pp, bool show_types) const +{ + if (tree_int_cst_equal (m_lower, m_upper)) + dump_cst (pp, m_lower, show_types); + else + { + pp_character (pp, '['); + dump_cst (pp, m_lower, show_types); + pp_string (pp, ", "); + dump_cst (pp, m_upper, show_types); + pp_character (pp, ']'); + } +} + +/* Dump this object to stderr. */ + +void +bounded_range::dump (bool show_types) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + dump_to_pp (&pp, show_types); + pp_newline (&pp); + pp_flush (&pp); +} + +json::object * +bounded_range::to_json () const +{ + json::object *range_obj = new json::object (); + set_json_attr (range_obj, "lower", m_lower); + set_json_attr (range_obj, "upper", m_upper); + return range_obj; +} + +/* Subroutine of bounded_range::to_json. */ + +void +bounded_range::set_json_attr (json::object *obj, const char *name, tree value) +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%E", value); + obj->set (name, new json::string (pp_formatted_text (&pp))); +} + + +/* Return true iff CST is within this range. */ + +bool +bounded_range::contains_p (tree cst) const +{ + /* Reject if below lower bound. */ + if (tree_int_cst_lt (cst, m_lower)) + return false; + /* Reject if above lower bound. */ + if (tree_int_cst_lt (m_upper, cst)) + return false; + return true; +} + +/* If this range intersects OTHER, return true, writing + the intersection to *OUT if OUT is non-NULL. + Return false if they do not intersect. */ + +bool +bounded_range::intersects_p (const bounded_range &other, + bounded_range *out) const +{ + const tree max_lower + = (tree_int_cst_le (m_lower, other.m_lower) + ? other.m_lower : m_lower); + gcc_assert (TREE_CODE (max_lower) == INTEGER_CST); + const tree min_upper + = (tree_int_cst_le (m_upper, other.m_upper) + ? m_upper : other.m_upper); + gcc_assert (TREE_CODE (min_upper) == INTEGER_CST); + + if (tree_int_cst_le (max_lower, min_upper)) + { + if (out) + *out = bounded_range (max_lower, min_upper); + return true; + } + else + return false; +} + +bool +bounded_range::operator== (const bounded_range &other) const +{ + return (tree_int_cst_equal (m_lower, other.m_lower) + && tree_int_cst_equal (m_upper, other.m_upper)); +} + +int +bounded_range::cmp (const bounded_range &br1, const bounded_range &br2) +{ + if (int cmp_lower = tree_int_cst_compare (br1.m_lower, + br2.m_lower)) + return cmp_lower; + return tree_int_cst_compare (br1.m_upper, br2.m_upper); +} + +/* struct bounded_ranges. */ + +/* Construct a bounded_ranges instance from a single range. */ + +bounded_ranges::bounded_ranges (const bounded_range &range) +: m_ranges (1) +{ + m_ranges.quick_push (range); + canonicalize (); + validate (); +} + +/* Construct a bounded_ranges instance from multiple ranges. */ + +bounded_ranges::bounded_ranges (const vec &ranges) +: m_ranges (ranges.length ()) +{ + m_ranges.safe_splice (ranges); + canonicalize (); + validate (); +} + +/* Construct a bounded_ranges instance for values of LHS for which + (LHS OP RHS_CONST) is true (e.g. "(LHS > 3)". */ + +bounded_ranges::bounded_ranges (enum tree_code op, tree rhs_const) +: m_ranges () +{ + gcc_assert (TREE_CODE (rhs_const) == INTEGER_CST); + tree type = TREE_TYPE (rhs_const); + switch (op) + { + default: + gcc_unreachable (); + case EQ_EXPR: + m_ranges.safe_push (bounded_range (rhs_const, rhs_const)); + break; + + case GE_EXPR: + m_ranges.safe_push (bounded_range (rhs_const, TYPE_MAX_VALUE (type))); + break; + + case LE_EXPR: + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), rhs_const)); + break; + + case NE_EXPR: + if (tree_int_cst_lt (TYPE_MIN_VALUE (type), rhs_const)) + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), + minus_one (rhs_const))); + if (tree_int_cst_lt (rhs_const, TYPE_MAX_VALUE (type))) + m_ranges.safe_push (bounded_range (plus_one (rhs_const), + TYPE_MAX_VALUE (type))); + break; + case GT_EXPR: + if (tree_int_cst_lt (rhs_const, TYPE_MAX_VALUE (type))) + m_ranges.safe_push (bounded_range (plus_one (rhs_const), + TYPE_MAX_VALUE (type))); + break; + case LT_EXPR: + if (tree_int_cst_lt (TYPE_MIN_VALUE (type), rhs_const)) + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), + minus_one (rhs_const))); + break; + } + canonicalize (); + validate (); +} + +/* Subroutine of ctors for fixing up m_ranges. + Also, initialize m_hash. */ + +void +bounded_ranges::canonicalize () +{ + /* Sort the ranges. */ + m_ranges.qsort ([](const void *p1, const void *p2) -> int + { + const bounded_range &br1 = *(const bounded_range *)p1; + const bounded_range &br2 = *(const bounded_range *)p2; + return bounded_range::cmp (br1, br2); + }); + + /* Merge ranges that are touching or overlapping. */ + for (unsigned i = 1; i < m_ranges.length (); ) + { + bounded_range *prev = &m_ranges[i - 1]; + const bounded_range *next = &m_ranges[i]; + if (prev->intersects_p (*next, NULL) + || (can_plus_one_p (prev->m_upper) + && tree_int_cst_equal (plus_one (prev->m_upper), + next->m_lower))) + { + prev->m_upper = next->m_upper; + m_ranges.ordered_remove (i); + } + else + i++; + } + + /* Initialize m_hash. */ + inchash::hash hstate (0); + for (const auto &iter : m_ranges) + { + inchash::add_expr (iter.m_lower, hstate); + inchash::add_expr (iter.m_upper, hstate); + } + m_hash = hstate.end (); +} + +/* Assert that this object is valid. */ + +void +bounded_ranges::validate () const +{ + /* Skip this in a release build. */ +#if !CHECKING_P + return; +#endif + + for (unsigned i = 1; i < m_ranges.length (); i++) + { + const bounded_range &prev = m_ranges[i - 1]; + const bounded_range &next = m_ranges[i]; + + /* Give up if we somehow have incompatible different types. */ + if (!types_compatible_p (TREE_TYPE (prev.m_upper), + TREE_TYPE (next.m_lower))) + continue; + + /* Verify sorted. */ + gcc_assert (tree_int_cst_lt (prev.m_upper, next.m_lower)); + + gcc_assert (can_plus_one_p (prev.m_upper)); + /* otherwise there's no room for "next". */ + + /* Verify no ranges touch each other. */ + gcc_assert (tree_int_cst_lt (plus_one (prev.m_upper), next.m_lower)); + } +} + +/* bounded_ranges equality operator. */ + +bool +bounded_ranges::operator== (const bounded_ranges &other) const +{ + if (m_ranges.length () != other.m_ranges.length ()) + return false; + for (unsigned i = 0; i < m_ranges.length (); i++) + { + if (m_ranges[i] != other.m_ranges[i]) + return false; + } + return true; +} + +/* Dump this object to PP. */ + +void +bounded_ranges::dump_to_pp (pretty_printer *pp, bool show_types) const +{ + pp_character (pp, '{'); + for (unsigned i = 0; i < m_ranges.length (); ++i) + { + if (i > 0) + pp_string (pp, ", "); + m_ranges[i].dump_to_pp (pp, show_types); + } + pp_character (pp, '}'); +} + +/* Dump this object to stderr. */ + +DEBUG_FUNCTION void +bounded_ranges::dump (bool show_types) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + dump_to_pp (&pp, show_types); + pp_newline (&pp); + pp_flush (&pp); +} + +json::value * +bounded_ranges::to_json () const +{ + json::array *arr_obj = new json::array (); + + for (unsigned i = 0; i < m_ranges.length (); ++i) + arr_obj->append (m_ranges[i].to_json ()); + + return arr_obj; +} + +/* Determine whether (X OP RHS_CONST) is known to be true or false + for all X in the ranges expressed by this object. */ + +tristate +bounded_ranges::eval_condition (enum tree_code op, + tree rhs_const, + bounded_ranges_manager *mgr) const +{ + /* Convert (X OP RHS_CONST) to a bounded_ranges instance and find + the intersection of that with this object. */ + bounded_ranges other (op, rhs_const); + const bounded_ranges *intersection + = mgr->get_or_create_intersection (this, &other); + + if (intersection->m_ranges.length () > 0) + { + /* We can use pointer equality to check for equality, + due to instance consolidation. */ + if (intersection == this) + return tristate (tristate::TS_TRUE); + else + return tristate (tristate::TS_UNKNOWN); + } + else + /* No intersection. */ + return tristate (tristate::TS_FALSE); +} + +/* Return true if CST is within any of the ranges. */ + +bool +bounded_ranges::contain_p (tree cst) const +{ + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + for (const auto &iter : m_ranges) + { + /* TODO: should we optimize this based on sorting? */ + if (iter.contains_p (cst)) + return true; + } + return false; +} + +int +bounded_ranges::cmp (const bounded_ranges *a, const bounded_ranges *b) +{ + if (int cmp_length = ((int)a->m_ranges.length () + - (int)b->m_ranges.length ())) + return cmp_length; + for (unsigned i = 0; i < a->m_ranges.length (); i++) + { + if (int cmp_range = bounded_range::cmp (a->m_ranges[i], b->m_ranges[i])) + return cmp_range; + } + /* They are equal. They ought to have been consolidated, so we should + have two pointers to the same object. */ + gcc_assert (a == b); + return 0; +} + +/* class bounded_ranges_manager. */ + +/* bounded_ranges_manager's dtor. */ + +bounded_ranges_manager::~bounded_ranges_manager () +{ + /* Delete the managed objects. */ + for (const auto &iter : m_map) + delete iter.second; +} + +/* Get the bounded_ranges instance for the empty set, creating it if + necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_empty () +{ + auto_vec empty_vec; + + return consolidate (new bounded_ranges (empty_vec)); +} + +/* Get the bounded_ranges instance for {CST}, creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_point (const_tree cst) +{ + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + + return get_or_create_range (cst, cst); +} + +/* Get the bounded_ranges instance for {[LOWER_BOUND..UPPER_BOUND]}, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_range (const_tree lower_bound, + const_tree upper_bound) +{ + gcc_assert (TREE_CODE (lower_bound) == INTEGER_CST); + gcc_assert (TREE_CODE (upper_bound) == INTEGER_CST); + + return consolidate + (new bounded_ranges (bounded_range (lower_bound, upper_bound))); +} + +/* Get the bounded_ranges instance for the union of OTHERS, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager:: +get_or_create_union (const vec &others) +{ + auto_vec ranges; + for (const auto &r : others) + ranges.safe_splice (r->m_ranges); + return consolidate (new bounded_ranges (ranges)); +} + +/* Get the bounded_ranges instance for the intersection of A and B, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_intersection (const bounded_ranges *a, + const bounded_ranges *b) +{ + auto_vec ranges; + unsigned a_idx = 0; + unsigned b_idx = 0; + while (a_idx < a->m_ranges.length () + && b_idx < b->m_ranges.length ()) + { + const bounded_range &r_a = a->m_ranges[a_idx]; + const bounded_range &r_b = b->m_ranges[b_idx]; + + bounded_range intersection (NULL_TREE, NULL_TREE); + if (r_a.intersects_p (r_b, &intersection)) + { + ranges.safe_push (intersection); + } + if (tree_int_cst_lt (r_a.m_lower, r_b.m_lower)) + { + a_idx++; + } + else + { + if (tree_int_cst_lt (r_a.m_upper, r_b.m_upper)) + a_idx++; + else + b_idx++; + } + } + + return consolidate (new bounded_ranges (ranges)); +} + +/* Get the bounded_ranges instance for the inverse of OTHER relative + to TYPE, creating it if necessary. + This is for use when handling "default" in switch statements, where + OTHER represents all the other cases. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_inverse (const bounded_ranges *other, + tree type) +{ + tree min_val = TYPE_MIN_VALUE (type); + tree max_val = TYPE_MAX_VALUE (type); + if (other->m_ranges.length () == 0) + return get_or_create_range (min_val, max_val); + auto_vec ranges; + tree first_lb = other->m_ranges[0].m_lower; + if (tree_int_cst_lt (min_val, first_lb) + && can_minus_one_p (first_lb)) + ranges.safe_push (bounded_range (min_val, + minus_one (first_lb))); + for (unsigned i = 1; i < other->m_ranges.length (); i++) + { + tree prev_ub = other->m_ranges[i - 1].m_upper; + tree iter_lb = other->m_ranges[i].m_lower; + gcc_assert (tree_int_cst_lt (prev_ub, iter_lb)); + if (can_plus_one_p (prev_ub) && can_minus_one_p (iter_lb)) + ranges.safe_push (bounded_range (plus_one (prev_ub), + minus_one (iter_lb))); + } + tree last_ub + = other->m_ranges[other->m_ranges.length () - 1].m_upper; + if (tree_int_cst_lt (last_ub, max_val) + && can_plus_one_p (last_ub)) + ranges.safe_push (bounded_range (plus_one (last_ub), max_val)); + + return consolidate (new bounded_ranges (ranges)); +} + +/* If an object equal to INST is already present, delete INST and + return the existing object. + Otherwise add INST and return it. */ + +const bounded_ranges * +bounded_ranges_manager::consolidate (bounded_ranges *inst) +{ + if (bounded_ranges **slot = m_map.get (inst)) + { + delete inst; + return *slot; + } + m_map.put (inst, inst); + return inst; +} + +/* Get the bounded_ranges instance for EDGE of SWITCH_STMT, + creating it if necessary, and caching it by edge. */ + +const bounded_ranges * +bounded_ranges_manager:: +get_or_create_ranges_for_switch (const switch_cfg_superedge *edge, + const gswitch *switch_stmt) +{ + /* Look in per-edge cache. */ + if (const bounded_ranges ** slot = m_edge_cache.get (edge)) + return *slot; + + /* Not yet in cache. */ + const bounded_ranges *all_cases_ranges + = create_ranges_for_switch (*edge, switch_stmt); + m_edge_cache.put (edge, all_cases_ranges); + return all_cases_ranges; } -/* Return true if RHS_CONST is below the lower bound of this range. */ +/* Get the bounded_ranges instance for EDGE of SWITCH_STMT, + creating it if necessary, for edges for which the per-edge + cache has not yet been populated. */ -bool -range::below_lower_bound (tree rhs_const) const +const bounded_ranges * +bounded_ranges_manager:: +create_ranges_for_switch (const switch_cfg_superedge &edge, + const gswitch *switch_stmt) { - if (!m_lower_bound.m_constant) - return false; + /* Get the ranges for each case label. */ + auto_vec case_ranges_vec + (gimple_switch_num_labels (switch_stmt)); - return compare_constants (rhs_const, - m_lower_bound.m_closed ? LT_EXPR : LE_EXPR, - m_lower_bound.m_constant).is_true (); + for (tree case_label : edge.get_case_labels ()) + { + /* Get the ranges for this case label. */ + const bounded_ranges *case_ranges + = make_case_label_ranges (switch_stmt, case_label); + case_ranges_vec.quick_push (case_ranges); + } + + /* Combine all the ranges for each case label into a single collection + of ranges. */ + const bounded_ranges *all_cases_ranges + = get_or_create_union (case_ranges_vec); + return all_cases_ranges; } -/* Return true if RHS_CONST is above the upper bound of this range. */ +/* Get the bounded_ranges instance for CASE_LABEL within + SWITCH_STMT. */ -bool -range::above_upper_bound (tree rhs_const) const +const bounded_ranges * +bounded_ranges_manager:: +make_case_label_ranges (const gswitch *switch_stmt, + tree case_label) { - if (!m_upper_bound.m_constant) - return false; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + if (upper_bound) + /* Range. */ + return get_or_create_range (lower_bound, upper_bound); + else + /* Single-value. */ + return get_or_create_point (lower_bound); + } + else + { + /* The default case. + Add exclusions based on the other cases. */ + auto_vec other_case_ranges + (gimple_switch_num_labels (switch_stmt)); + for (unsigned other_idx = 1; + other_idx < gimple_switch_num_labels (switch_stmt); + other_idx++) + { + tree other_label = gimple_switch_label (switch_stmt, + other_idx); + const bounded_ranges *other_ranges + = make_case_label_ranges (switch_stmt, other_label); + other_case_ranges.quick_push (other_ranges); + } + const bounded_ranges *other_cases_ranges + = get_or_create_union (other_case_ranges); + tree type = TREE_TYPE (gimple_switch_index (switch_stmt)); + return get_or_create_inverse (other_cases_ranges, type); + } +} - return compare_constants (rhs_const, - m_upper_bound.m_closed ? GT_EXPR : GE_EXPR, - m_upper_bound.m_constant).is_true (); +/* Dump the number of objects of each class that were managed by this + manager to LOGGER. + If SHOW_OBJS is true, also dump the objects themselves. */ + +void +bounded_ranges_manager::log_stats (logger *logger, bool show_objs) const +{ + LOG_SCOPE (logger); + logger->log (" # %s: %li", "ranges", m_map.elements ()); + if (!show_objs) + return; + + auto_vec vec_objs (m_map.elements ()); + for (const auto &iter : m_map) + vec_objs.quick_push (iter.second); + vec_objs.qsort + ([](const void *p1, const void *p2) -> int + { + const bounded_ranges *br1 = *(const bounded_ranges * const *)p1; + const bounded_ranges *br2 = *(const bounded_ranges * const *)p2; + return bounded_ranges::cmp (br1, br2); + }); + + for (const auto &iter : vec_objs) + { + logger->start_log_line (); + pretty_printer *pp = logger->get_printer (); + pp_string (pp, " "); + iter->dump_to_pp (pp, true); + logger->end_log_line (); + } } /* class equiv_class. */ @@ -576,6 +1294,49 @@ constraint::implied_by (const constraint &other, return false; } +/* class bounded_ranges_constraint. */ + +void +bounded_ranges_constraint::print (pretty_printer *pp, + const constraint_manager &cm) const +{ + m_ec_id.print (pp); + pp_string (pp, ": "); + m_ec_id.get_obj (cm).print (pp); + pp_string (pp, ": "); + m_ranges->dump_to_pp (pp, true); +} + +json::object * +bounded_ranges_constraint::to_json () const +{ + json::object *con_obj = new json::object (); + + con_obj->set ("ec", new json::integer_number (m_ec_id.as_int ())); + con_obj->set ("ranges", m_ranges->to_json ()); + + return con_obj; +} + +bool +bounded_ranges_constraint:: +operator== (const bounded_ranges_constraint &other) const +{ + if (m_ec_id != other.m_ec_id) + return false; + + /* We can compare by pointer, since the bounded_ranges_manager + consolidates instances. */ + return m_ranges == other.m_ranges; +} + +void +bounded_ranges_constraint::add_to_hash (inchash::hash *hstate) const +{ + hstate->add_int (m_ec_id.m_idx); + hstate->merge_hash (m_ranges->get_hash ()); +} + /* class equiv_class_id. */ /* Get the underlying equiv_class for this ID from CM. */ @@ -612,6 +1373,7 @@ equiv_class_id::print (pretty_printer *pp) const constraint_manager::constraint_manager (const constraint_manager &other) : m_equiv_classes (other.m_equiv_classes.length ()), m_constraints (other.m_constraints.length ()), + m_bounded_ranges_constraints (other.m_bounded_ranges_constraints.length ()), m_mgr (other.m_mgr) { int i; @@ -621,6 +1383,8 @@ constraint_manager::constraint_manager (const constraint_manager &other) constraint *c; FOR_EACH_VEC_ELT (other.m_constraints, i, c) m_constraints.quick_push (*c); + for (const auto &iter : other.m_bounded_ranges_constraints) + m_bounded_ranges_constraints.quick_push (iter); } /* constraint_manager's assignment operator. */ @@ -630,6 +1394,7 @@ constraint_manager::operator= (const constraint_manager &other) { gcc_assert (m_equiv_classes.length () == 0); gcc_assert (m_constraints.length () == 0); + gcc_assert (m_bounded_ranges_constraints.length () == 0); int i; equiv_class *ec; @@ -640,6 +1405,8 @@ constraint_manager::operator= (const constraint_manager &other) m_constraints.reserve (other.m_constraints.length ()); FOR_EACH_VEC_ELT (other.m_constraints, i, c) m_constraints.quick_push (*c); + for (const auto &iter : other.m_bounded_ranges_constraints) + m_bounded_ranges_constraints.quick_push (iter); return *this; } @@ -658,6 +1425,8 @@ constraint_manager::hash () const hstate.merge_hash (ec->hash ()); FOR_EACH_VEC_ELT (m_constraints, i, c) hstate.merge_hash (c->hash ()); + for (const auto &iter : m_bounded_ranges_constraints) + iter.add_to_hash (&hstate); return hstate.end (); } @@ -670,6 +1439,9 @@ constraint_manager::operator== (const constraint_manager &other) const return false; if (m_constraints.length () != other.m_constraints.length ()) return false; + if (m_bounded_ranges_constraints.length () + != other.m_bounded_ranges_constraints.length ()) + return false; int i; equiv_class *ec; @@ -684,6 +1456,13 @@ constraint_manager::operator== (const constraint_manager &other) const if (!(*c == other.m_constraints[i])) return false; + for (unsigned i = 0; i < m_bounded_ranges_constraints.length (); i++) + { + if (m_bounded_ranges_constraints[i] + != other.m_bounded_ranges_constraints[i]) + return false; + } + return true; } @@ -711,6 +1490,18 @@ constraint_manager::print (pretty_printer *pp) const pp_string (pp, " && "); c->print (pp, *this); } + if (m_bounded_ranges_constraints.length ()) + { + pp_string (pp, " | "); + i = 0; + for (const auto &iter : m_bounded_ranges_constraints) + { + if (i > 0) + pp_string (pp, " && "); + iter.print (pp, *this); + i++; + } + } pp_printf (pp, "}"); } @@ -762,6 +1553,30 @@ constraint_manager::dump_to_pp (pretty_printer *pp, bool multiline) const } if (!multiline) pp_string (pp, "}"); + if (m_bounded_ranges_constraints.length ()) + { + if (multiline) + pp_string (pp, " "); + pp_string (pp, "ranges:"); + if (multiline) + pp_newline (pp); + else + pp_string (pp, "{"); + i = 0; + for (const auto &iter : m_bounded_ranges_constraints) + { + if (multiline) + pp_string (pp, " "); + else if (i > 0) + pp_string (pp, " && "); + iter.print (pp, *this); + if (multiline) + pp_newline (pp); + i++; + } + if (!multiline) + pp_string (pp, "}"); + } } /* Dump a multiline representation of this constraint_manager to FP. */ @@ -818,6 +1633,14 @@ constraint_manager::to_json () const cm_obj->set ("constraints", con_arr); } + /* m_bounded_ranges_constraints. */ + { + json::array *con_arr = new json::array (); + for (const auto &c : m_bounded_ranges_constraints) + con_arr->append (c.to_json ()); + cm_obj->set ("bounded_ranges_constraints", con_arr); + } + return cm_obj; } @@ -936,6 +1759,8 @@ constraint_manager::add_unknown_constraint (equiv_class_id lhs_ec_id, if (final_ec != old_ec) m_equiv_classes[rhs_ec_id.m_idx] = final_ec; delete old_ec; + if (lhs_ec_id == final_ec_id) + lhs_ec_id = rhs_ec_id; /* Update the constraints. */ constraint *c; @@ -955,6 +1780,14 @@ constraint_manager::add_unknown_constraint (equiv_class_id lhs_ec_id, if (c->m_rhs == final_ec_id) c->m_rhs = rhs_ec_id; } + bounded_ranges_constraint *brc; + FOR_EACH_VEC_ELT (m_bounded_ranges_constraints, i, brc) + { + if (brc->m_ec_id == rhs_ec_id) + brc->m_ec_id = lhs_ec_id; + if (brc->m_ec_id == final_ec_id) + brc->m_ec_id = rhs_ec_id; + } /* We may now have self-comparisons due to the merger; these constraints should be removed. */ @@ -1008,6 +1841,8 @@ constraint_manager::add_constraint_internal (equiv_class_id lhs_id, /* Add the constraint. */ m_constraints.safe_push (new_c); + /* We don't yet update m_bounded_ranges_constraints here yet. */ + if (!flag_analyzer_transitivity) return; @@ -1141,6 +1976,80 @@ constraint_manager::add_constraint_internal (equiv_class_id lhs_id, } } +/* Attempt to add the constraint that SVAL is within RANGES to this + constraint_manager. + + Return true if the constraint was successfully added (or is already + known to be true). + Return false if the constraint contradicts existing knowledge. */ + +bool +constraint_manager::add_bounded_ranges (const svalue *sval, + const bounded_ranges *ranges) +{ + sval = sval->unwrap_any_unmergeable (); + + /* Nothing can be known about unknown/poisoned values. */ + if (!sval->can_have_associated_state_p ()) + /* Not a contradiction. */ + return true; + + /* If SVAL is a constant, then we can look at RANGES directly. */ + if (tree cst = sval->maybe_get_constant ()) + { + /* If the ranges contain CST, then it's a successful no-op; + otherwise it's a contradiction. */ + return ranges->contain_p (cst); + } + + equiv_class_id ec_id = get_or_add_equiv_class (sval); + + /* If the EC has a constant, it's either true or false. */ + const equiv_class &ec = ec_id.get_obj (*this); + if (tree ec_cst = ec.get_any_constant ()) + { + if (ranges->contain_p (ec_cst)) + /* We already have SVAL == EC_CST, within RANGES, so + we can discard RANGES and succeed. */ + return true; + else + /* We already have SVAL == EC_CST, not within RANGES, so + we can reject RANGES as a contradiction. */ + return false; + } + + /* We have at most one per ec_id. */ + /* Iterate through each range in RANGES. */ + for (auto iter : m_bounded_ranges_constraints) + { + if (iter.m_ec_id == ec_id) + { + /* Update with intersection, or fail if empty. */ + bounded_ranges_manager *mgr = get_range_manager (); + const bounded_ranges *intersection + = mgr->get_or_create_intersection (iter.m_ranges, ranges); + if (intersection->empty_p ()) + { + /* No intersection; fail. */ + return false; + } + else + { + /* Update with intersection; succeed. */ + iter.m_ranges = intersection; + validate (); + return true; + } + } + } + m_bounded_ranges_constraints.safe_push + (bounded_ranges_constraint (ec_id, ranges)); + + validate (); + + return true; +} + /* Look for SVAL within the equivalence classes of this constraint_manager; if found, return true, writing the id to *OUT if OUT is non-NULL, otherwise return false. */ @@ -1279,6 +2188,8 @@ constraint_manager::eval_condition (equiv_class_id lhs_ec, } } + /* We don't use m_bounded_ranges_constraints here yet. */ + return tristate (tristate::TS_UNKNOWN); } @@ -1404,6 +2315,12 @@ constraint_manager::eval_condition (equiv_class_id lhs_ec, } } } + + bounded_ranges_manager *mgr = get_range_manager (); + for (const auto &iter : m_bounded_ranges_constraints) + if (iter.m_ec_id == lhs_ec) + return iter.m_ranges->eval_condition (op, rhs_const, mgr); + /* Look at existing bounds on LHS_EC. */ range lhs_bounds = get_ec_bounds (lhs_ec); return lhs_bounds.eval_condition (op, rhs_const); @@ -1552,6 +2469,29 @@ constraint_manager::purge (const PurgeCriteria &p, purge_stats *stats) con_idx++; } } + + /* Update bounded_ranges_constraint instances. */ + for (unsigned r_idx = 0; + r_idx < m_bounded_ranges_constraints.length (); ) + { + bounded_ranges_constraint *brc + = &m_bounded_ranges_constraints[r_idx]; + + /* Remove if it refers to the deleted EC. */ + if (brc->m_ec_id == ec_idx) + { + m_bounded_ranges_constraints.ordered_remove (r_idx); + if (stats) + stats->m_num_bounded_ranges_constraints++; + } + else + { + /* Renumber any EC ids that refer to ECs that have + had their idx changed. */ + brc->m_ec_id.update_for_removal (ec_idx); + r_idx++; + } + } } else ec_idx++; @@ -1610,6 +2550,17 @@ constraint_manager::purge (const PurgeCriteria &p, purge_stats *stats) c->m_lhs.update_for_removal (ec_idx); c->m_rhs.update_for_removal (ec_idx); } + + /* Likewise for m_bounded_ranges_constraints. */ + for (unsigned r_idx = 0; + r_idx < m_bounded_ranges_constraints.length (); + r_idx++) + { + bounded_ranges_constraint *brc + = &m_bounded_ranges_constraints[r_idx]; + brc->m_ec_id.update_for_removal (ec_idx); + } + continue; } } @@ -1751,6 +2702,9 @@ constraint_manager::canonicalize () used_ecs.add (m_equiv_classes[c->m_rhs.as_int ()]); } + for (const auto &iter : m_bounded_ranges_constraints) + used_ecs.add (m_equiv_classes[iter.m_ec_id.as_int ()]); + /* Purge unused ECs: those that aren't used by constraints and that effectively have only one svalue (either in m_constant or in m_vars). */ @@ -1791,6 +2745,9 @@ constraint_manager::canonicalize () ec_id_map.update (&c->m_rhs); } + for (auto &iter : m_bounded_ranges_constraints) + ec_id_map.update (&iter.m_ec_id); + /* Finally, sort the constraints. */ m_constraints.qsort (constraint_cmp); } @@ -1835,6 +2792,32 @@ public: } } + void on_ranges (const svalue *lhs_sval, + const bounded_ranges *ranges) FINAL OVERRIDE + { + for (const auto &iter : m_cm_b->m_bounded_ranges_constraints) + { + const equiv_class &ec_rhs = iter.m_ec_id.get_obj (*m_cm_b); + for (unsigned i = 0; i < ec_rhs.m_vars.length (); i++) + { + const svalue *rhs_sval = ec_rhs.m_vars[i]; + if (lhs_sval == rhs_sval) + { + /* Union of the two ranges. */ + auto_vec pair (2); + pair.quick_push (ranges); + pair.quick_push (iter.m_ranges); + bounded_ranges_manager *ranges_mgr + = m_cm_b->get_range_manager (); + const bounded_ranges *union_ + = ranges_mgr->get_or_create_union (pair); + bool sat = m_out->add_bounded_ranges (lhs_sval, union_); + gcc_assert (sat); + } + } + } + } + private: const constraint_manager *m_cm_b; constraint_manager *m_out; @@ -1908,6 +2891,16 @@ constraint_manager::for_each_fact (fact_visitor *visitor) const visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_vars[j]); } } + + for (const auto &iter : m_bounded_ranges_constraints) + { + const equiv_class &ec_lhs = iter.m_ec_id.get_obj (*this); + for (unsigned i = 0; i < ec_lhs.m_vars.length (); i++) + { + const svalue *lhs_sval = ec_lhs.m_vars[i]; + visitor->on_ranges (lhs_sval, iter.m_ranges); + } + } } /* Assert that this object is valid. */ @@ -1945,10 +2938,22 @@ constraint_manager::validate () const FOR_EACH_VEC_ELT (m_constraints, i, c) { gcc_assert (!c->m_lhs.null_p ()); - gcc_assert (c->m_lhs.as_int () <= (int)m_equiv_classes.length ()); + gcc_assert (c->m_lhs.as_int () < (int)m_equiv_classes.length ()); gcc_assert (!c->m_rhs.null_p ()); - gcc_assert (c->m_rhs.as_int () <= (int)m_equiv_classes.length ()); + gcc_assert (c->m_rhs.as_int () < (int)m_equiv_classes.length ()); } + + for (const auto &iter : m_bounded_ranges_constraints) + { + gcc_assert (!iter.m_ec_id.null_p ()); + gcc_assert (iter.m_ec_id.as_int () < (int)m_equiv_classes.length ()); + } +} + +bounded_ranges_manager * +constraint_manager::get_range_manager () const +{ + return m_mgr->get_range_manager (); } #if CHECKING_P @@ -2696,6 +3701,318 @@ test_many_constants () } } +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_range_eq (const location &loc, + const bounded_range &range, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + range.dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Assert that BR.dump (false) is EXPECTED. */ + +#define ASSERT_DUMP_BOUNDED_RANGE_EQ(BR, EXPECTED) \ + SELFTEST_BEGIN_STMT \ + assert_dump_bounded_range_eq ((SELFTEST_LOCATION), (BR), (EXPECTED)); \ + SELFTEST_END_STMT + +/* Verify that bounded_range works as expected. */ + +static void +test_bounded_range () +{ + tree u8_0 = build_int_cst (unsigned_char_type_node, 0); + tree u8_1 = build_int_cst (unsigned_char_type_node, 1); + tree u8_64 = build_int_cst (unsigned_char_type_node, 64); + tree u8_128 = build_int_cst (unsigned_char_type_node, 128); + tree u8_255 = build_int_cst (unsigned_char_type_node, 255); + + tree s8_0 = build_int_cst (signed_char_type_node, 0); + tree s8_1 = build_int_cst (signed_char_type_node, 1); + tree s8_2 = build_int_cst (signed_char_type_node, 2); + + bounded_range br_u8_0 (u8_0, u8_0); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_0, "0"); + ASSERT_TRUE (br_u8_0.contains_p (u8_0)); + ASSERT_FALSE (br_u8_0.contains_p (u8_1)); + ASSERT_TRUE (br_u8_0.contains_p (s8_0)); + ASSERT_FALSE (br_u8_0.contains_p (s8_1)); + + bounded_range br_u8_0_1 (u8_0, u8_1); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_0_1, "[0, 1]"); + + bounded_range tmp (NULL_TREE, NULL_TREE); + ASSERT_TRUE (br_u8_0.intersects_p (br_u8_0_1, &tmp)); + ASSERT_DUMP_BOUNDED_RANGE_EQ (tmp, "0"); + + bounded_range br_u8_64_128 (u8_64, u8_128); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_64_128, "[64, 128]"); + + ASSERT_FALSE (br_u8_0.intersects_p (br_u8_64_128, NULL)); + ASSERT_FALSE (br_u8_64_128.intersects_p (br_u8_0, NULL)); + + bounded_range br_u8_128_255 (u8_128, u8_255); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_128_255, "[128, 255]"); + ASSERT_TRUE (br_u8_128_255.intersects_p (br_u8_64_128, &tmp)); + ASSERT_DUMP_BOUNDED_RANGE_EQ (tmp, "128"); + + bounded_range br_s8_2 (s8_2, s8_2); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_s8_2, "2"); + bounded_range br_s8_2_u8_255 (s8_2, u8_255); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_s8_2_u8_255, "[2, 255]"); +} + +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_ranges_eq (const location &loc, + const bounded_ranges *ranges, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + ranges->dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_ranges_eq (const location &loc, + const bounded_ranges &ranges, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + ranges.dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Assert that BRS.dump (false) is EXPECTED. */ + +#define ASSERT_DUMP_BOUNDED_RANGES_EQ(BRS, EXPECTED) \ + SELFTEST_BEGIN_STMT \ + assert_dump_bounded_ranges_eq ((SELFTEST_LOCATION), (BRS), (EXPECTED)); \ + SELFTEST_END_STMT + +/* Verify that the bounded_ranges class works as expected. */ + +static void +test_bounded_ranges () +{ + bounded_ranges_manager mgr; + + tree ch0 = build_int_cst (unsigned_char_type_node, 0); + tree ch1 = build_int_cst (unsigned_char_type_node, 1); + tree ch2 = build_int_cst (unsigned_char_type_node, 2); + tree ch3 = build_int_cst (unsigned_char_type_node, 3); + tree ch128 = build_int_cst (unsigned_char_type_node, 128); + tree ch129 = build_int_cst (unsigned_char_type_node, 129); + tree ch254 = build_int_cst (unsigned_char_type_node, 254); + tree ch255 = build_int_cst (unsigned_char_type_node, 255); + + const bounded_ranges *empty = mgr.get_or_create_empty (); + ASSERT_DUMP_BOUNDED_RANGES_EQ (empty, "{}"); + + const bounded_ranges *point0 = mgr.get_or_create_point (ch0); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point0, "{0}"); + + const bounded_ranges *point1 = mgr.get_or_create_point (ch1); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point1, "{1}"); + + const bounded_ranges *point2 = mgr.get_or_create_point (ch2); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point2, "{2}"); + + const bounded_ranges *range0_128 = mgr.get_or_create_range (ch0, ch128); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range0_128, "{[0, 128]}"); + + const bounded_ranges *range0_255 = mgr.get_or_create_range (ch0, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range0_255, "{[0, 255]}"); + + ASSERT_FALSE (empty->contain_p (ch0)); + ASSERT_FALSE (empty->contain_p (ch1)); + ASSERT_FALSE (empty->contain_p (ch255)); + + ASSERT_TRUE (point0->contain_p (ch0)); + ASSERT_FALSE (point0->contain_p (ch1)); + ASSERT_FALSE (point0->contain_p (ch255)); + + ASSERT_FALSE (point1->contain_p (ch0)); + ASSERT_TRUE (point1->contain_p (ch1)); + ASSERT_FALSE (point0->contain_p (ch255)); + + ASSERT_TRUE (range0_128->contain_p (ch0)); + ASSERT_TRUE (range0_128->contain_p (ch1)); + ASSERT_TRUE (range0_128->contain_p (ch128)); + ASSERT_FALSE (range0_128->contain_p (ch129)); + ASSERT_FALSE (range0_128->contain_p (ch254)); + ASSERT_FALSE (range0_128->contain_p (ch255)); + + const bounded_ranges *inv0_128 + = mgr.get_or_create_inverse (range0_128, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv0_128, "{[129, 255]}"); + + const bounded_ranges *range128_129 = mgr.get_or_create_range (ch128, ch129); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range128_129, "{[128, 129]}"); + + const bounded_ranges *inv128_129 + = mgr.get_or_create_inverse (range128_129, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv128_129, "{[0, 127], [130, 255]}"); + + /* Intersection. */ + { + /* Intersection of disjoint ranges should be empty set. */ + const bounded_ranges *intersect0_1 + = mgr.get_or_create_intersection (point0, point1); + ASSERT_DUMP_BOUNDED_RANGES_EQ (intersect0_1, "{}"); + } + + /* Various tests of "union of ranges". */ + { + { + /* Touching points should be merged into a range. */ + auto_vec v; + v.safe_push (point0); + v.safe_push (point1); + const bounded_ranges *union_0_and_1 = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_0_and_1, "{[0, 1]}"); + } + + { + /* Overlapping and out-of-order. */ + auto_vec v; + v.safe_push (inv0_128); // {[129, 255]} + v.safe_push (range128_129); + const bounded_ranges *union_129_255_and_128_129 + = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_129_255_and_128_129, "{[128, 255]}"); + } + + { + /* Union of R and inverse(R) should be full range of type. */ + auto_vec v; + v.safe_push (range128_129); + v.safe_push (inv128_129); + const bounded_ranges *union_ = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_, "{[0, 255]}"); + } + + /* Union with an endpoint. */ + { + const bounded_ranges *range2_to_255 + = mgr.get_or_create_range (ch2, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range2_to_255, "{[2, 255]}"); + auto_vec v; + v.safe_push (point0); + v.safe_push (point2); + v.safe_push (range2_to_255); + const bounded_ranges *union_ = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_, "{0, [2, 255]}"); + } + + /* Construct from vector of bounded_range. */ + { + auto_vec v; + v.safe_push (bounded_range (ch2, ch2)); + v.safe_push (bounded_range (ch0, ch0)); + v.safe_push (bounded_range (ch2, ch255)); + bounded_ranges br (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (&br, "{0, [2, 255]}"); + } + } + + /* Various tests of "inverse". */ + { + { + const bounded_ranges *range_1_to_3 = mgr.get_or_create_range (ch1, ch3); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_1_to_3, "{[1, 3]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_1_to_3, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{0, [4, 255]}"); + } + { + const bounded_ranges *range_1_to_255 + = mgr.get_or_create_range (ch1, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_1_to_255, "{[1, 255]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_1_to_255, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{0}"); + } + { + const bounded_ranges *range_0_to_254 + = mgr.get_or_create_range (ch0, ch254); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_0_to_254, "{[0, 254]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_0_to_254, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{255}"); + } + } + + /* "case 'a'-'z': case 'A-Z':" vs "default:", for ASCII. */ + { + tree ch65 = build_int_cst (unsigned_char_type_node, 65); + tree ch90 = build_int_cst (unsigned_char_type_node, 90); + + tree ch97 = build_int_cst (unsigned_char_type_node, 97); + tree ch122 = build_int_cst (unsigned_char_type_node, 122); + + const bounded_ranges *A_to_Z = mgr.get_or_create_range (ch65, ch90); + ASSERT_DUMP_BOUNDED_RANGES_EQ (A_to_Z, "{[65, 90]}"); + const bounded_ranges *a_to_z = mgr.get_or_create_range (ch97, ch122); + ASSERT_DUMP_BOUNDED_RANGES_EQ (a_to_z, "{[97, 122]}"); + auto_vec v; + v.safe_push (A_to_Z); + v.safe_push (a_to_z); + const bounded_ranges *label_ranges = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (label_ranges, "{[65, 90], [97, 122]}"); + const bounded_ranges *default_ranges + = mgr.get_or_create_inverse (label_ranges, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (default_ranges, + "{[0, 64], [91, 96], [123, 255]}"); + } + + /* Verify ranges from ops. */ + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (EQ_EXPR, ch128), + "{128}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch128), + "{[0, 127], [129, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LT_EXPR, ch128), + "{[0, 127]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LE_EXPR, ch128), + "{[0, 128]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GE_EXPR, ch128), + "{[128, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GT_EXPR, ch128), + "{[129, 255]}"); + /* Ops at endpoints of type ranges. */ + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LE_EXPR, ch0), + "{0}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LT_EXPR, ch0), + "{}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch0), + "{[1, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GE_EXPR, ch255), + "{255}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GT_EXPR, ch255), + "{}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch255), + "{[0, 254]}"); + + /* Verify that instances are consolidated by mgr. */ + ASSERT_EQ (mgr.get_or_create_point (ch0), + mgr.get_or_create_point (ch0)); + ASSERT_NE (mgr.get_or_create_point (ch0), + mgr.get_or_create_point (ch1)); +} + /* Run the selftests in this file, temporarily overriding flag_analyzer_transitivity with TRANSITIVITY. */ @@ -2715,6 +4032,8 @@ run_constraint_manager_tests (bool transitivity) test_constraint_impl (); test_equality (); test_many_constants (); + test_bounded_range (); + test_bounded_ranges (); flag_analyzer_transitivity = saved_flag_analyzer_transitivity; } diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 2bb3215..0a430ea 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -64,6 +64,164 @@ struct range bound m_upper_bound; }; +/* A closed range of values with constant integer bounds + e.g. [3, 5] for the set {3, 4, 5}. */ + +struct bounded_range +{ + bounded_range (const_tree lower, const_tree upper); + + void dump_to_pp (pretty_printer *pp, bool show_types) const; + void dump (bool show_types) const; + + json::object *to_json () const; + + bool contains_p (tree cst) const; + + bool intersects_p (const bounded_range &other, + bounded_range *out) const; + + bool operator== (const bounded_range &other) const; + bool operator!= (const bounded_range &other) const + { + return !(*this == other); + } + + static int cmp (const bounded_range &a, const bounded_range &b); + + tree m_lower; + tree m_upper; + +private: + static void set_json_attr (json::object *obj, const char *name, tree value); +}; + +/* A collection of bounded_range instances, suitable + for representing the ranges on a case label within a switch + statement. */ + +struct bounded_ranges +{ +public: + typedef bounded_ranges key_t; + + bounded_ranges (const bounded_range &range); + bounded_ranges (const vec &ranges); + bounded_ranges (enum tree_code op, tree rhs_const); + + bool operator== (const bounded_ranges &other) const; + + hashval_t get_hash () const { return m_hash; } + + void dump_to_pp (pretty_printer *pp, bool show_types) const; + void dump (bool show_types) const; + + json::value *to_json () const; + + tristate eval_condition (enum tree_code op, + tree rhs_const, + bounded_ranges_manager *mgr) const; + + bool contain_p (tree cst) const; + bool empty_p () const { return m_ranges.length () == 0; } + + static int cmp (const bounded_ranges *a, const bounded_ranges *b); + +private: + void canonicalize (); + void validate () const; + + friend class bounded_ranges_manager; + + auto_vec m_ranges; + hashval_t m_hash; +}; + +} // namespace ana + +template <> struct default_hash_traits +: public member_function_hash_traits +{ + static const bool empty_zero_p = true; +}; + +namespace ana { + +/* An object to own and consolidate bounded_ranges instances. + This also caches the mapping from switch_cfg_superedge + bounded_ranges instances, so that get_or_create_ranges_for_switch is + memoized. */ + +class bounded_ranges_manager +{ +public: + ~bounded_ranges_manager (); + + const bounded_ranges * + get_or_create_ranges_for_switch (const switch_cfg_superedge *edge, + const gswitch *switch_stmt); + + const bounded_ranges *get_or_create_empty (); + const bounded_ranges *get_or_create_point (const_tree value); + const bounded_ranges *get_or_create_range (const_tree lower_bound, + const_tree upper_bound); + const bounded_ranges * + get_or_create_union (const vec &others); + const bounded_ranges * + get_or_create_intersection (const bounded_ranges *a, + const bounded_ranges *b); + const bounded_ranges * + get_or_create_inverse (const bounded_ranges *other, tree type); + + void log_stats (logger *logger, bool show_objs) const; + +private: + const bounded_ranges * + create_ranges_for_switch (const switch_cfg_superedge &edge, + const gswitch *switch_stmt); + + const bounded_ranges * + make_case_label_ranges (const gswitch *switch_stmt, + tree case_label); + + const bounded_ranges *consolidate (bounded_ranges *); + + struct hash_traits_t : public typed_noop_remove + { + typedef bounded_ranges *key_type; + typedef bounded_ranges *value_type; + + static inline bool + equal (const key_type &k1, const key_type &k2) + { + return *k1 == *k2; + } + static inline hashval_t + hash (const key_type &k) + { + return k->get_hash (); + } + static inline bool is_empty (key_type k) { return k == NULL; } + static inline void mark_empty (key_type &k) { k = NULL; } + static inline bool is_deleted (key_type k) + { + return k == reinterpret_cast (1); + } + + static const bool empty_zero_p = true; + }; + struct traits_t : public simple_hashmap_traits + { + }; + typedef hash_map map_t; + map_t m_map; + + typedef hash_map edge_cache_t; + edge_cache_t m_edge_cache; +}; + /* An equivalence class within a constraint manager: a set of svalues that are known to all be equal to each other, together with an optional tree constant that they are equal to. */ @@ -190,6 +348,33 @@ class fact_visitor virtual void on_fact (const svalue *lhs, enum tree_code, const svalue *rhs) = 0; + virtual void on_ranges (const svalue *lhs, + const bounded_ranges *ranges) = 0; +}; + +class bounded_ranges_constraint +{ +public: + bounded_ranges_constraint (equiv_class_id ec_id, + const bounded_ranges *ranges) + : m_ec_id (ec_id), m_ranges (ranges) + { + } + + void print (pretty_printer *pp, const constraint_manager &cm) const; + + json::object *to_json () const; + + bool operator== (const bounded_ranges_constraint &other) const; + bool operator!= (const bounded_ranges_constraint &other) const + { + return !(*this == other); + } + + void add_to_hash (inchash::hash *hstate) const; + + equiv_class_id m_ec_id; + const bounded_ranges *m_ranges; }; /* A collection of equivalence classes and constraints on them. @@ -248,6 +433,9 @@ public: enum tree_code op, equiv_class_id rhs_ec_id); + bool add_bounded_ranges (const svalue *sval, + const bounded_ranges *ranges); + bool get_equiv_class_by_svalue (const svalue *sval, equiv_class_id *out) const; equiv_class_id get_or_add_equiv_class (const svalue *sval); @@ -281,8 +469,11 @@ public: void validate () const; + bounded_ranges_manager *get_range_manager () const; + auto_delete_vec m_equiv_classes; auto_vec m_constraints; + auto_vec m_bounded_ranges_constraints; private: void add_constraint_internal (equiv_class_id lhs_id, diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 77dda4d..7ffe000 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -520,8 +520,7 @@ epath_finder::process_worklist_item (feasible_worklist *worklist, gcc_assert (rc); fg->add_feasibility_problem (fnode, succ_eedge, - *rc); - delete rc; + rc); /* Give up if there have been too many infeasible edges. */ if (fg->get_num_infeasible () diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index e66ca4e..4ee9279 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -3842,7 +3842,7 @@ feasibility_problem::dump_to_pp (pretty_printer *pp) const pp_string (pp, "; rejected constraint: "); m_rc->dump_to_pp (pp); pp_string (pp, "; rmodel: "); - m_rc->m_model.dump_to_pp (pp, true, false); + m_rc->get_model ().dump_to_pp (pp, true, false); } } diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc index 675bda9..3b85896 100644 --- a/gcc/analyzer/feasible-graph.cc +++ b/gcc/analyzer/feasible-graph.cc @@ -129,7 +129,7 @@ infeasible_node::dump_dot (graphviz_out *gv, pp_string (pp, "rejected constraint:"); pp_newline (pp); - m_rc.dump_to_pp (pp); + m_rc->dump_to_pp (pp); pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true); @@ -178,12 +178,13 @@ feasible_graph::add_node (const exploded_node *enode, } /* Add an infeasible_node to this graph and an infeasible_edge connecting - to it from SRC_FNODE, capturing a failure of RC along EEDGE. */ + to it from SRC_FNODE, capturing a failure of RC along EEDGE. + Takes ownership of RC. */ void feasible_graph::add_feasibility_problem (feasible_node *src_fnode, const exploded_edge *eedge, - const rejected_constraint &rc) + rejected_constraint *rc) { infeasible_node *dst_fnode = new infeasible_node (eedge->m_dest, m_nodes.length (), rc); diff --git a/gcc/analyzer/feasible-graph.h b/gcc/analyzer/feasible-graph.h index 5a580f4..07696fa 100644 --- a/gcc/analyzer/feasible-graph.h +++ b/gcc/analyzer/feasible-graph.h @@ -115,17 +115,18 @@ class infeasible_node : public base_feasible_node { public: infeasible_node (const exploded_node *inner_node, unsigned index, - const rejected_constraint &rc) + rejected_constraint *rc) : base_feasible_node (inner_node, index), m_rc (rc) { } + ~infeasible_node () { delete m_rc; } void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE; private: - rejected_constraint m_rc; + rejected_constraint *m_rc; }; /* Base class of edge within a feasible_graph. */ @@ -192,7 +193,7 @@ class feasible_graph : public digraph void add_feasibility_problem (feasible_node *src_fnode, const exploded_edge *eedge, - const rejected_constraint &rc); + rejected_constraint *rc); exploded_path *make_epath (feasible_node *fnode) const; diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index 9e4644f..1cdec1b 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" +#include "analyzer/constraint-manager.h" #if ENABLE_ANALYZER @@ -77,7 +78,8 @@ region_model_manager::region_model_manager () m_fndecls_map (), m_labels_map (), m_globals_region (alloc_region_id (), &m_root_region), m_globals_map (), - m_store_mgr (this) + m_store_mgr (this), + m_range_mgr (new bounded_ranges_manager ()) { } @@ -142,6 +144,8 @@ region_model_manager::~region_model_manager () for (string_map_t::iterator iter = m_string_map.begin (); iter != m_string_map.end (); ++iter) delete (*iter).second; + + delete m_range_mgr; } /* Return true if C exceeds the complexity limit for svalues. */ @@ -1574,6 +1578,7 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const logger->log (" # managed dynamic regions: %i", m_managed_dynamic_regions.length ()); m_store_mgr.log_stats (logger, show_objs); + m_range_mgr->log_stats (logger, show_objs); } /* Dump the number of objects of each class that were managed by this diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index f54be14e..787f2ed 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2773,7 +2773,7 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs, { bool sat = add_constraint (lhs, op, rhs, ctxt); if (!sat && out) - *out = new rejected_constraint (*this, lhs, op, rhs); + *out = new rejected_op_constraint (*this, lhs, op, rhs); return sat; } @@ -3329,56 +3329,15 @@ region_model::apply_constraints_for_gswitch (const switch_cfg_superedge &edge, region_model_context *ctxt, rejected_constraint **out) { + bounded_ranges_manager *ranges_mgr = get_range_manager (); + const bounded_ranges *all_cases_ranges + = ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt); tree index = gimple_switch_index (switch_stmt); - tree case_label = edge.get_case_label (); - gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); - tree lower_bound = CASE_LOW (case_label); - tree upper_bound = CASE_HIGH (case_label); - if (lower_bound) - { - if (upper_bound) - { - /* Range. */ - if (!add_constraint (index, GE_EXPR, lower_bound, ctxt, out)) - return false; - return add_constraint (index, LE_EXPR, upper_bound, ctxt, out); - } - else - /* Single-value. */ - return add_constraint (index, EQ_EXPR, lower_bound, ctxt, out); - } - else - { - /* The default case. - Add exclusions based on the other cases. */ - for (unsigned other_idx = 1; - other_idx < gimple_switch_num_labels (switch_stmt); - other_idx++) - { - tree other_label = gimple_switch_label (switch_stmt, - other_idx); - tree other_lower_bound = CASE_LOW (other_label); - tree other_upper_bound = CASE_HIGH (other_label); - gcc_assert (other_lower_bound); - if (other_upper_bound) - { - /* Exclude this range-valued case. - For now, we just exclude the boundary values. - TODO: exclude the values within the region. */ - if (!add_constraint (index, NE_EXPR, other_lower_bound, - ctxt, out)) - return false; - if (!add_constraint (index, NE_EXPR, other_upper_bound, - ctxt, out)) - return false; - } - else - /* Exclude this single-valued case. */ - if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt, out)) - return false; - } - return true; - } + const svalue *index_sval = get_rvalue (index, ctxt); + bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges); + if (!sat && out) + *out = new rejected_ranges_constraint (*this, index, all_cases_ranges); + return sat; } /* Apply any constraints due to an exception being thrown at LAST_STMT. @@ -3860,10 +3819,10 @@ debug (const region_model &rmodel) rmodel.dump (false); } -/* struct rejected_constraint. */ +/* class rejected_op_constraint : public rejected_constraint. */ void -rejected_constraint::dump_to_pp (pretty_printer *pp) const +rejected_op_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); const svalue *lhs_sval = m.get_rvalue (m_lhs, NULL); @@ -3873,6 +3832,18 @@ rejected_constraint::dump_to_pp (pretty_printer *pp) const rhs_sval->dump_to_pp (pp, true); } +/* class rejected_ranges_constraint : public rejected_constraint. */ + +void +rejected_ranges_constraint::dump_to_pp (pretty_printer *pp) const +{ + region_model m (m_model); + const svalue *sval = m.get_rvalue (m_expr, NULL); + sval->dump_to_pp (pp, true); + pp_string (pp, " in "); + m_ranges->dump_to_pp (pp, true); +} + /* class engine. */ /* Dump the managed objects by class to LOGGER, and the per-class totals. */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index a734f9f..f2c82b0 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -189,6 +189,7 @@ struct purge_stats m_num_regions (0), m_num_equiv_classes (0), m_num_constraints (0), + m_num_bounded_ranges_constraints (0), m_num_client_items (0) {} @@ -196,6 +197,7 @@ struct purge_stats int m_num_regions; int m_num_equiv_classes; int m_num_constraints; + int m_num_bounded_ranges_constraints; int m_num_client_items; }; @@ -320,6 +322,7 @@ public: unsigned alloc_region_id () { return m_next_region_id++; } store_manager *get_store_manager () { return &m_store_mgr; } + bounded_ranges_manager *get_range_manager () const { return m_range_mgr; } /* Dynamically-allocated region instances. The number of these within the analysis can grow arbitrarily. @@ -456,6 +459,8 @@ private: store_manager m_store_mgr; + bounded_ranges_manager *m_range_mgr; + /* "Dynamically-allocated" region instances. The number of these within the analysis can grow arbitrarily. They are still owned by the manager. */ @@ -698,6 +703,10 @@ class region_model void unset_dynamic_extents (const region *reg); region_model_manager *get_manager () const { return m_mgr; } + bounded_ranges_manager *get_range_manager () const + { + return m_mgr->get_range_manager (); + } void unbind_region_and_descendents (const region *reg, enum poison_kind pkind); @@ -945,21 +954,54 @@ struct model_merger /* A record that can (optionally) be written out when region_model::add_constraint fails. */ -struct rejected_constraint +class rejected_constraint { - rejected_constraint (const region_model &model, - tree lhs, enum tree_code op, tree rhs) - : m_model (model), m_lhs (lhs), m_op (op), m_rhs (rhs) - {} +public: + virtual ~rejected_constraint () {} + virtual void dump_to_pp (pretty_printer *pp) const = 0; - void dump_to_pp (pretty_printer *pp) const; + const region_model &get_model () const { return m_model; } + +protected: + rejected_constraint (const region_model &model) + : m_model (model) + {} region_model m_model; +}; + +class rejected_op_constraint : public rejected_constraint +{ +public: + rejected_op_constraint (const region_model &model, + tree lhs, enum tree_code op, tree rhs) + : rejected_constraint (model), + m_lhs (lhs), m_op (op), m_rhs (rhs) + {} + + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + tree m_lhs; enum tree_code m_op; tree m_rhs; }; +class rejected_ranges_constraint : public rejected_constraint +{ +public: + rejected_ranges_constraint (const region_model &model, + tree expr, const bounded_ranges *ranges) + : rejected_constraint (model), + m_expr (expr), m_ranges (ranges) + {} + + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + +private: + tree m_expr; + const bounded_ranges *m_ranges; +}; + /* A bundle of state. */ class engine diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index 66ef765..85acf44 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "cfg.h" #include "digraph.h" +#include "tree-cfg.h" #include "analyzer/supergraph.h" #include "analyzer/analyzer-logging.h" @@ -246,7 +247,7 @@ supergraph::supergraph (logger *logger) supernode *dest_supernode = *m_bb_to_initial_node.get (dest_cfg_block); cfg_superedge *cfg_sedge - = add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx); + = add_cfg_edge (src_supernode, dest_supernode, cfg_edge); m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge); } } @@ -505,17 +506,16 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call, adding it to this supergraph. If the edge is for a switch statement, create a switch_cfg_superedge - subclass using IDX (the index of E within the out-edges from SRC's - underlying basic block). */ + subclass. */ cfg_superedge * -supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx) +supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e) { /* Special-case switch edges. */ gimple *stmt = src->get_last_stmt (); cfg_superedge *new_edge; if (stmt && stmt->code == GIMPLE_SWITCH) - new_edge = new switch_cfg_superedge (src, dest, e, idx); + new_edge = new switch_cfg_superedge (src, dest, e); else new_edge = new cfg_superedge (src, dest, e); add_edge (new_edge); @@ -1072,6 +1072,23 @@ cfg_superedge::get_phi_arg (const gphi *phi) const return gimple_phi_arg_def (phi, index); } +switch_cfg_superedge::switch_cfg_superedge (supernode *src, + supernode *dst, + ::edge e) +: cfg_superedge (src, dst, e) +{ + /* Populate m_case_labels with all cases which go to DST. */ + const gswitch *gswitch = get_switch_stmt (); + for (unsigned i = 0; i < gimple_switch_num_labels (gswitch); i++) + { + tree case_ = gimple_switch_label (gswitch, i); + basic_block bb = label_to_block (src->get_function (), + CASE_LABEL (case_)); + if (bb == dst->m_bb) + m_case_labels.safe_push (case_); + } +} + /* Implementation of superedge::dump_label_to_pp for CFG superedges for "switch" statements. @@ -1081,31 +1098,63 @@ void switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp, bool user_facing ATTRIBUTE_UNUSED) const { - tree case_label = get_case_label (); - gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); - tree lower_bound = CASE_LOW (case_label); - tree upper_bound = CASE_HIGH (case_label); - if (lower_bound) + if (user_facing) { - pp_printf (pp, "case "); - dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); - if (upper_bound) + for (unsigned i = 0; i < m_case_labels.length (); ++i) { - pp_printf (pp, " ... "); - dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, false); + if (i > 0) + pp_string (pp, ", "); + tree case_label = m_case_labels[i]; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + pp_printf (pp, "case "); + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); + if (upper_bound) + { + pp_printf (pp, " ... "); + dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, + false); + } + pp_printf (pp, ":"); + } + else + pp_printf (pp, "default:"); } - pp_printf (pp, ":"); } else - pp_printf (pp, "default:"); -} - -/* Get the case label for this "switch" superedge. */ - -tree -switch_cfg_superedge::get_case_label () const -{ - return gimple_switch_label (get_switch_stmt (), m_idx); + { + pp_character (pp, '{'); + for (unsigned i = 0; i < m_case_labels.length (); ++i) + { + if (i > 0) + pp_string (pp, ", "); + tree case_label = m_case_labels[i]; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + if (upper_bound) + { + pp_character (pp, '['); + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, + false); + pp_string (pp, ", "); + dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, + false); + pp_character (pp, ']'); + } + else + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); + } + else + pp_printf (pp, "default"); + } + pp_character (pp, '}'); + } } /* Implementation of superedge::dump_label_to_pp for interprocedural diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index 335f513..09a12be 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -181,7 +181,7 @@ public: private: supernode *add_node (function *fun, basic_block bb, gcall *returning_call, gimple_seq phi_nodes); - cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx); + cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e); call_superedge *add_call_superedge (supernode *src, supernode *dest, cgraph_edge *cedge); return_superedge *add_return_superedge (supernode *src, supernode *dest, @@ -539,15 +539,12 @@ is_a_helper ::test (const superedge *sedge) namespace ana { /* A subclass for edges from switch statements, retaining enough - information to identify the pertinent case, and for adding labels + information to identify the pertinent cases, and for adding labels when rendering via graphviz. */ class switch_cfg_superedge : public cfg_superedge { public: - switch_cfg_superedge (supernode *src, supernode *dst, ::edge e, int idx) - : cfg_superedge (src, dst, e), - m_idx (idx) - {} + switch_cfg_superedge (supernode *src, supernode *dst, ::edge e); const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const FINAL OVERRIDE @@ -563,10 +560,10 @@ class switch_cfg_superedge : public cfg_superedge { return as_a (m_src->get_last_stmt ()); } - tree get_case_label () const; + const vec &get_case_labels () const { return m_case_labels; } - private: - const int m_idx; +private: + auto_vec m_case_labels; }; } // namespace ana diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c index 870b00f..0b9e7e3 100644 --- a/gcc/testsuite/gcc.dg/analyzer/switch.c +++ b/gcc/testsuite/gcc.dg/analyzer/switch.c @@ -8,23 +8,156 @@ void test (int i) { case 0: __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != -1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (i != 1); /* { dg-warning "TRUE" } */ break; case 3 ... 5: + __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (i >= 2); /* { dg-warning "TRUE" } */ __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i < 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i < 7); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i >= 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i >= 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i <= 3); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i <= 4); /* { dg-warning "UNKNOWN" } */ break; default: + __analyzer_eval (i == -1); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 0); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */ - __analyzer_eval (i == 4); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - /* TODO(xfail^^^): we're only checking against endpoints of case - ranges, not the insides. */ + __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 6); /* { dg-warning "UNKNOWN" } */ break; } } + +/* Verify that the analyzer follows the correct paths on a + switch statement guarded by an if, using noinline to defeat + optimizations. */ + +static void __attribute__((noinline)) +__analyzer_called_by_test_2 (int y) +{ + switch (y) + { + case 0: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + case 1: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 2: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + default: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + } +} + +void test_2 (int x) +{ + if (x == 1) + __analyzer_called_by_test_2 (x); +} + +void test_3 (int x, int y) +{ + if (y == 3) + switch (x) + { + case 0 ... 9: + case 20 ... 29: + if (x == y) + __analyzer_dump_path (); /* { dg-message "path" } */ + else + __analyzer_dump_path (); /* { dg-message "path" } */ + } +} + +struct s4 +{ + unsigned char level:3; + unsigned char key_id_mode:2; + unsigned char reserved:3; +}; + +void test_4 (struct s4 *p) +{ + switch (p->key_id_mode) + { + case 0: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 1: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 2: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 3: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + } + __analyzer_dump_path (); /* { dg-message "path" } */ +} + +int test_5 (unsigned v) +{ + switch (v) + { + case 0: + return 7; + break; + case 1: + return 23; + break; + default: + return v * 2; + } +} + +int test_6 (unsigned v) +{ + switch (v) + { + case 0: + return 3; + case -1: + return 22; + } + return -3; +} + +int g7 = -1; +int test_7 () +{ + switch (g7++) { + case 0: + return 32; + + case 100: + return 42; + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c new file mode 100644 index 0000000..3da2e30 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c @@ -0,0 +1,42 @@ +struct s +{ + int f0; + int f1; +}; + +int test (int cmd) +{ + int err = 0; + struct s foo; + struct s bar; + + switch (cmd) + { + case 0: + foo.f0 = 0; + break; + case 1: + foo.f0 = 1; + break; + case 30 ... 50: + case 70 ... 80: + __builtin_memset (&bar, 0, sizeof (bar)); + break; + } + + switch (cmd) + { + default: + return -1; + case 0 ... 1: + return foo.f0; + break; + case 42: + return bar.f1; + break; + case 65: + return bar.f1; + break; + } + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c new file mode 100644 index 0000000..57b8acd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c @@ -0,0 +1,158 @@ +typedef unsigned int __u32; +__extension__ typedef unsigned long long __u64; + +extern unsigned long +copy_from_user(void *to, const void *from, unsigned long n); + +extern unsigned long +copy_to_user(void *to, const void *from, unsigned long n); + +struct mtrr_sentry { + __u64 base; + __u32 size; + __u32 type; +}; + +struct mtrr_gentry { + __u64 base; + __u32 size; + __u32 regnum; + __u32 type; + __u32 _pad; +}; + +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +#define _IOC_TYPECHECK(t) (sizeof(t)) + +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) + +#define MTRR_IOCTL_BASE 'M' + +#define EFAULT 14 +#define EINVAL 22 +#define ENOTTY 25 + +#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) +#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) +#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) +#define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry) +#define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry) +#define MTRRIOC_ADD_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_sentry) +#define MTRRIOC_SET_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 6, struct mtrr_sentry) +#define MTRRIOC_DEL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 7, struct mtrr_sentry) +#define MTRRIOC_GET_PAGE_ENTRY _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry) +#define MTRRIOC_KILL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 9, struct mtrr_sentry) + +extern void check_init_u64 (__u64 v); +extern void check_init_u32 (__u32 v); + +/* Adapted/reduced from arch/x86/kernel/cpu/mtrr/if.c: mtrr_ioctl, + which is GPL-2.0 */ + +long mtrr_ioctl(unsigned int cmd, unsigned long __arg) { + int err = 0; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + void *arg = (void *)__arg; + + __builtin_memset(&gentry, 0, sizeof(gentry)); + + switch (cmd) { + case MTRRIOC_ADD_ENTRY: + case MTRRIOC_SET_ENTRY: + case MTRRIOC_DEL_ENTRY: + case MTRRIOC_KILL_ENTRY: + case MTRRIOC_ADD_PAGE_ENTRY: + case MTRRIOC_SET_PAGE_ENTRY: + case MTRRIOC_DEL_PAGE_ENTRY: + case MTRRIOC_KILL_PAGE_ENTRY: + if (copy_from_user(&sentry, arg, sizeof(sentry))) + return -EFAULT; + break; + case MTRRIOC_GET_ENTRY: + case MTRRIOC_GET_PAGE_ENTRY: + if (copy_from_user(&gentry, arg, sizeof(gentry))) + return -EFAULT; + break; + } + + switch (cmd) { + default: + return -ENOTTY; + case MTRRIOC_ADD_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_SET_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_DEL_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_KILL_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_GET_ENTRY: + check_init_u64 (gentry.base); + check_init_u32 (gentry.size); + check_init_u32 (gentry.regnum); + check_init_u32 (gentry.type); + check_init_u32 (gentry._pad); + break; + case MTRRIOC_ADD_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_SET_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_DEL_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_KILL_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_GET_PAGE_ENTRY: + check_init_u64 (gentry.base); + check_init_u32 (gentry.size); + check_init_u32 (gentry.regnum); + check_init_u32 (gentry.type); + check_init_u32 (gentry._pad); + break; + } + + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c new file mode 100644 index 0000000..f5cdb5c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c @@ -0,0 +1,27 @@ +struct snd_ac97 { + // snip + unsigned int id; + // snip +}; + +int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg) { + + switch (ac97->id) { + case 0x53544d02: + if (reg == 0x22 || reg == 0x7a) + return 1; + __attribute__((__fallthrough__)); + case 0x414b4d00: + return 0; + } + return 1; +} + +int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg) { + if (ac97->id == 0x414c4781) + { + if (!snd_ac97_valid_reg(ac97, reg)) + return -22; + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c new file mode 100644 index 0000000..10b2f29 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c @@ -0,0 +1,68 @@ +/* { dg-additional-options "-fno-analyzer-call-summaries" } */ + +typedef unsigned char u8; +typedef signed int s32; +typedef unsigned int u32; + +enum v4l2_mpeg_video_hevc_profile { + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2 +}; +enum v4l2_buf_type { + V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, + V4L2_BUF_TYPE_VIDEO_OUTPUT = 2 +}; +struct v4l2_fmtdesc { + u32 index; + u32 type; +}; +struct v4l2_ctrl; +s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); +struct create_channel_param { + u8 profile; +}; + +u8 +hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile) { + switch (profile) { + default: + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + return 1; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return 2; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return 3; + } +} + +int fill_create_channel_param(struct v4l2_ctrl *ctrl, + struct create_channel_param *param) { + enum v4l2_mpeg_video_hevc_profile profile; + profile = v4l2_ctrl_g_ctrl(ctrl); + param->profile = hevc_profile_to_mcu_profile(profile); + return 0; +} + +int allegro_enum_fmt_vid(struct v4l2_fmtdesc *f) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (f->index >= 1) + return -22; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (f->index >= 2) + return -22; + break; + default: + return -22; + } + return 0; +} + +int allegro_ioctl_streamon(struct v4l2_ctrl *ctrl, + struct create_channel_param *param) { + fill_create_channel_param(ctrl, param); + + return 0; +} -- cgit v1.1 From 38b19c5b0805f9acfcf52430cebca025fc3cdea6 Mon Sep 17 00:00:00 2001 From: GCC Administrator Date: Tue, 24 Aug 2021 00:17:00 +0000 Subject: Daily bump. --- gcc/ChangeLog | 197 ++++++++++++++++++++++++++++++++++++++++++++++++ gcc/DATESTAMP | 2 +- gcc/analyzer/ChangeLog | 181 ++++++++++++++++++++++++++++++++++++++++++++ gcc/c/ChangeLog | 5 ++ gcc/cp/ChangeLog | 5 ++ gcc/fortran/ChangeLog | 18 +++++ gcc/testsuite/ChangeLog | 126 +++++++++++++++++++++++++++++++ 7 files changed, 533 insertions(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b7a23d3..0183764 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,200 @@ +2021-08-23 Bill Schmidt + + * config/rs6000/rs6000-gen-builtins.c (parse_bif_entry): Don't call + asprintf, which is not available on AIX. + +2021-08-23 Bill Schmidt + + * config.gcc (target_gtfiles): Add ./rs6000-builtins.h. + * config/rs6000/t-rs6000 (EXTRA_GTYPE_DEPS): Set. + +2021-08-23 Bill Schmidt + + * config.gcc (powerpc*-*-*): Add rs6000-builtins.o to extra_objs. + * config/rs6000/rs6000-gen-builtins.c (main): Close init_file + last. + * config/rs6000/t-rs6000 (rs6000-gen-builtins.o): New target. + (rbtree.o): Likewise. + (rs6000-gen-builtins): Likewise. + (rs6000-builtins.c): Likewise. + (rs6000-builtins.h): Likewise. + (rs6000.o): Add dependency. + (EXTRA_HEADERS): Add rs6000-vecdefines.h. + (rs6000-vecdefines.h): New target. + (rs6000-builtins.o): Likewise. + (rs6000-call.o): Add rs6000-builtins.h as a dependency. + (rs6000-c.o): Likewise. + +2021-08-23 Bill Schmidt + + PR target/101830 + * config/rs6000/rs6000-gen-builtins.c (consume_whitespace): + Diagnose buffer overrun. + (safe_inc_pos): Fix overrun detection. + (match_identifier): Diagnose buffer overrun. + (match_integer): Likewise. + (match_to_right_bracket): Likewise. + +2021-08-23 Jan Hubicka + + * ipa-modref-tree.h (modref_access_node::range_info_useful_p): + Improve range compare. + (modref_access_node::contains): New member function. + (modref_access_node::search): Remove. + (modref_access_node::insert): Be smarter about subaccesses. + +2021-08-23 Thomas Schwinge + + * config/i386/i386-options.c (ix86_omp_device_kind_arch_isa) + [ACCEL_COMPILER]: Match "intel_mic". + * config/i386/t-omp-device (omp-device-properties-i386) : + Add "intel_mic". + +2021-08-23 Jeff Law + + * config/h8300/h8300-protos.h (h8300_expand_epilogue): Add new + argument. + * config/h8300/jumpcall.md (call, call_value): Restrict to + !SIBLING_CALL_P cases. + (subcall, sibcall_value): New patterns & expanders. + * config/h8300/proepi.md (epilogue): Pass new argument to + h8300_expand_epilogue. + (sibcall_epilogue): New expander. + * config/h8300/h8300.c (h8300_expand_epilogue): Handle sibcall + epilogues too. + (h8300_ok_for_sibcall_p): New function. + (TARGET_FUNCTION_OK_FOR_SIBCALL): define. + +2021-08-23 Roger Sayle + + * simplify-rtx.c (simplify_unary_operation_1): [TRUNCATE]: + Handle case where the operand is already the desired mode. + +2021-08-23 Richard Biener + + PR ipa/97565 + * tree-ssa-structalias.c (ipa_pta_execute): Check in_other_partition + in addition to has_gimple_body. + +2021-08-23 Jan Hubicka + + PR middle-end/101949 + * ipa-modref.c (analyze_ssa_name_flags): Fix merging of + EAF_NOCLOBBER + +2021-08-23 Martin Liska + + * doc/invoke.texi: Put the option out of -mxl-mode-app-model + table. + +2021-08-23 Richard Biener + + * tree-vect-loop.c (vect_compute_single_scalar_iteration_cost): + Properly scale the inner loop cost only once. + +2021-08-23 Roger Sayle + + * tree-ssa-ccp.c (bit_value_binop) [TRUNC_MOD_EXPR, TRUNC_DIV_EXPR]: + Provide bounds for unsigned (and signed with non-negative operands) + division and modulus. + +2021-08-23 Roger Sayle + + * simplify-rtx.c (simplify_truncation): Generalize simplification + of (truncate:A (subreg:B X)). + (simplify_unary_operation_1) [FLOAT_TRUNCATE, FLOAT_EXTEND, + SIGN_EXTEND, ZERO_EXTEND]: Handle cases where the operand + already has the desired machine mode. + (test_scalar_int_ops): Add tests that useless extensions and + truncations are optimized away. + (test_scalar_int_ext_ops): New self-test function to confirm + that truncations of extensions are correctly simplified. + (test_scalar_int_ext_ops2): New self-test function to check + truncations of truncations, extensions of extensions, and + truncations of extensions. + (test_scalar_ops): Call the above two functions with a + representative sampling of integer machine modes. + +2021-08-23 Roger Sayle + + * match.pd (shift transformations): Change the sign of an + LSHIFT_EXPR if it reduces the number of explicit conversions. + +2021-08-23 Jakub Jelinek + + PR tree-optimization/86723 + * gimple-ssa-store-merging.c (find_bswap_or_nop_finalize): Add + cast64_to_32 argument, set *cast64_to_32 to false, unless n is + non-memory permutation of 64-bit src which only has bytes of + 0 or [5..8] and n->range is 4. + (find_bswap_or_nop): Add cast64_to_32 and mask arguments, adjust + find_bswap_or_nop_finalize caller, support bswap with some bytes + zeroed, as long as at least two bytes are not zeroed. + (bswap_replace): Add mask argument and handle masking of bswap + result. + (maybe_optimize_vector_constructor): Adjust find_bswap_or_nop + caller, punt if cast64_to_32 or mask is not all ones. + (pass_optimize_bswap::execute): Adjust find_bswap_or_nop_finalize + caller, for now punt if cast64_to_32. + +2021-08-23 Richard Biener + + PR tree-optimization/79334 + * tree-ssa-sccvn.c (copy_reference_ops_from_ref): Record + a type also for COMPONENT_REFs. + (vn_reference_may_trap): Check ARRAY_REF with constant index + against the array domain. + +2021-08-23 liuhongt + + PR target/102016 + * config/i386/sse.md (*avx512f_pshufb_truncv8hiv8qi_1): Add + TARGET_AVX512BW to condition. + +2021-08-23 Jakub Jelinek + + PR debug/101905 + * dwarf2out.c (gen_variable_die): Add DW_AT_location for global + register variables already during early_dwarf if possible. + +2021-08-23 Christophe Lyon + + * config/arm/arm_mve.h: Fix __arm_vctp16q return type. + +2021-08-23 Christophe Lyon + + PR target/100856 + * config/arm/arm.opt: Fix typo. + * config/arm/t-rmprofile: Fix typo. + +2021-08-23 Jakub Jelinek + + * tree.h (OMP_CLAUSE_GRAINSIZE_STRICT): Define. + (OMP_CLAUSE_NUM_TASKS_STRICT): Define. + * tree-pretty-print.c (dump_omp_clause) : Print strict: modifier. + * omp-expand.c (expand_task_call): Use GOMP_TASK_FLAG_STRICT in iflags + if either grainsize or num_tasks clause has the strict modifier. + +2021-08-23 Martin Liska + + * dbgcnt.def (DEBUG_COUNTER): New counter. + * gimple.c (gimple_call_arg_flags): Use it in IPA PTA. + +2021-08-23 Jan Hubicka + + * ipa-modref.c (analyze_ssa_name_flags): Improve handling of return slot. + +2021-08-23 Xi Ruoyao + + PR target/101922 + * config/mips/mips-protos.h (mips_msa_output_shift_immediate): + Declare. + * config/mips/mips.c (mips_msa_output_shift_immediate): New + function. + * config/mips/mips-msa.md (vashl3, vashr3, + vlshr3): Call it. + 2021-08-22 Jan Hubicka Martin Liska diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index d2e7bda..83a5291 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20210823 +20210824 diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index da90011..211f34c 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,184 @@ +2021-08-23 David Malcolm + + * analyzer.h (struct rejected_constraint): Convert to... + (class rejected_constraint): ...this. + (class bounded_ranges): New forward decl. + (class bounded_ranges_manager): New forward decl. + * constraint-manager.cc: Include "analyzer/analyzer-logging.h" and + "tree-pretty-print.h". + (can_plus_one_p): New. + (plus_one): New. + (can_minus_one_p): New. + (minus_one): New. + (bounded_range::bounded_range): New. + (dump_cst): New. + (bounded_range::dump_to_pp): New. + (bounded_range::dump): New. + (bounded_range::to_json): New. + (bounded_range::set_json_attr): New. + (bounded_range::contains_p): New. + (bounded_range::intersects_p): New. + (bounded_range::operator==): New. + (bounded_range::cmp): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::canonicalize): New. + (bounded_ranges::validate): New. + (bounded_ranges::operator==): New. + (bounded_ranges::dump_to_pp): New. + (bounded_ranges::dump): New. + (bounded_ranges::to_json): New. + (bounded_ranges::eval_condition): New. + (bounded_ranges::contain_p): New. + (bounded_ranges::cmp): New. + (bounded_ranges_manager::~bounded_ranges_manager): New. + (bounded_ranges_manager::get_or_create_empty): New. + (bounded_ranges_manager::get_or_create_point): New. + (bounded_ranges_manager::get_or_create_range): New. + (bounded_ranges_manager::get_or_create_union): New. + (bounded_ranges_manager::get_or_create_intersection): New. + (bounded_ranges_manager::get_or_create_inverse): New. + (bounded_ranges_manager::consolidate): New. + (bounded_ranges_manager::get_or_create_ranges_for_switch): New. + (bounded_ranges_manager::create_ranges_for_switch): New. + (bounded_ranges_manager::make_case_label_ranges): New. + (bounded_ranges_manager::log_stats): New. + (bounded_ranges_constraint::print): New. + (bounded_ranges_constraint::to_json): New. + (bounded_ranges_constraint::operator==): New. + (bounded_ranges_constraint::add_to_hash): New. + (constraint_manager::constraint_manager): Update for new field + m_bounded_ranges_constraints. + (constraint_manager::operator=): Likewise. + (constraint_manager::hash): Likewise. + (constraint_manager::operator==): Likewise. + (constraint_manager::print): Likewise. + (constraint_manager::dump_to_pp): Likewise. + (constraint_manager::to_json): Likewise. + (constraint_manager::add_unknown_constraint): Update the lhs_ec_id + if necessary in existing constraints when combining equivalence + classes. Add similar code for handling + m_bounded_ranges_constraints. + (constraint_manager::add_constraint_internal): Add comment. + (constraint_manager::add_bounded_ranges): New. + (constraint_manager::eval_condition): Use new field + m_bounded_ranges_constraints. + (constraint_manager::purge): Update bounded_ranges_constraint + instances. + (constraint_manager::canonicalize): Update for new field. + (merger_fact_visitor::on_ranges): New. + (constraint_manager::for_each_fact): Use new field + m_bounded_ranges_constraints. + (constraint_manager::validate): Fix off-by-one error needed due + to bug fixed above in add_unknown_constraint. Validate the EC IDs + in m_bounded_ranges_constraints. + (constraint_manager::get_range_manager): New. + (selftest::assert_dump_bounded_range_eq): New. + (ASSERT_DUMP_BOUNDED_RANGE_EQ): New. + (selftest::test_bounded_range): New. + (selftest::assert_dump_bounded_ranges_eq): New. + (ASSERT_DUMP_BOUNDED_RANGES_EQ): New. + (selftest::test_bounded_ranges): New. + (selftest::run_constraint_manager_tests): Call the new selftests. + * constraint-manager.h (struct bounded_range): New. + (struct bounded_ranges): New. + (template <> struct default_hash_traits): New. + (class bounded_ranges_manager): New. + (fact_visitor::on_ranges): New pure virtual function. + (class bounded_ranges_constraint): New. + (constraint_manager::add_bounded_ranges): New decl. + (constraint_manager::get_range_manager): New decl. + (constraint_manager::m_bounded_ranges_constraints): New field. + * diagnostic-manager.cc (epath_finder::process_worklist_item): + Transfer ownership of rc to add_feasibility_problem. + * engine.cc (feasibility_problem::dump_to_pp): Use get_model. + * feasible-graph.cc (infeasible_node::dump_dot): Update for + conversion of m_rc to a pointer. + (feasible_graph::add_feasibility_problem): Pass RC by pointer and + take ownership. + * feasible-graph.h (infeasible_node::infeasible_node): Pass RC by + pointer and take ownership. + (infeasible_node::~infeasible_node): New. + (infeasible_node::m_rc): Convert to a pointer. + (feasible_graph::add_feasibility_problem): Pass RC by pointer and + take ownership. + * region-model-manager.cc: Include + "analyzer/constraint-manager.h". + (region_model_manager::region_model_manager): Initializer new + field m_range_mgr. + (region_model_manager::~region_model_manager): Delete it. + (region_model_manager::log_stats): Call log_stats on it. + * region-model.cc (region_model::add_constraint): Use new subclass + rejected_op_constraint. + (region_model::apply_constraints_for_gswitch): Reimplement using + bounded_ranges_manager. + (rejected_constraint::dump_to_pp): Convert to... + (rejected_op_constraint::dump_to_pp): ...this. + (rejected_ranges_constraint::dump_to_pp): New. + * region-model.h (struct purge_stats): Add field + m_num_bounded_ranges_constraints. + (region_model_manager::get_range_manager): New. + (region_model_manager::m_range_mgr): New. + (region_model::get_range_manager): New. + (struct rejected_constraint): Split into... + (class rejected_constraint):...this new abstract base class, + and... + (class rejected_op_constraint): ...this new concrete subclass. + (class rejected_ranges_constraint): New. + * supergraph.cc: Include "tree-cfg.h". + (supergraph::supergraph): Drop idx param from add_cfg_edge. + (supergraph::add_cfg_edge): Drop idx param. + (switch_cfg_superedge::switch_cfg_superedge): Move here from + header. Populate m_case_labels with all cases which go to DST. + (switch_cfg_superedge::dump_label_to_pp): Reimplement to use + m_case_labels. + (switch_cfg_superedge::get_case_label): Delete. + * supergraph.h (supergraphadd_cfg_edge): Drop "idx" param. + (switch_cfg_superedge::switch_cfg_superedge): Drop idx param and + move implementation to supergraph.cc. + (switch_cfg_superedge::get_case_label): Delete. + (switch_cfg_superedge::get_case_labels): New. + (switch_cfg_superedge::m_idx): Delete. + (switch_cfg_superedge::m_case_labels): New field. + +2021-08-23 David Malcolm + + PR analyzer/101875 + * sm-file.cc (file_diagnostic::describe_state_change): Handle + change.m_expr being NULL. + +2021-08-23 David Malcolm + + PR analyzer/101837 + * analyzer.cc (maybe_reconstruct_from_def_stmt): Bail if fn is + NULL, and assert that it's non-NULL before passing it to + build_call_array_loc. + +2021-08-23 David Malcolm + + PR analyzer/101962 + * region-model.cc (region_model::eval_condition_without_cm): + Refactor comparison against zero, adding a check for + POINTER_PLUS_EXPR of non-NULL. + +2021-08-23 David Malcolm + + * store.cc (bit_range::intersects_p): New overload. + (bit_range::operator-): New. + (binding_cluster::maybe_get_compound_binding): Handle the partial + overlap case. + (selftest::test_bit_range_intersects_p): Add test coverage for + new overload of bit_range::intersects_p. + * store.h (bit_range::intersects_p): New overload. + (bit_range::operator-): New. + +2021-08-23 Ankur Saini + + PR analyzer/102020 + * diagnostic-manager.cc + (diagnostic_manager::prune_for_sm_diagnostic): Fix typo. + 2021-08-21 Ankur Saini PR analyzer/101980 diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 6ca524b..27ccc32 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,8 @@ +2021-08-23 Jakub Jelinek + + * c-parser.c (c_parser_omp_clause_num_tasks, + c_parser_omp_clause_grainsize): Parse the optional strict: modifier. + 2021-08-22 Martin Uecker PR c/98397 diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d496202..ddea2a2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2021-08-23 Jakub Jelinek + + * parser.c (cp_parser_omp_clause_num_tasks, + cp_parser_omp_clause_grainsize): Parse the optional strict: modifier. + 2021-08-20 Jakub Jelinek * parser.c (cp_parser_handle_statement_omp_attributes): Determine if diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 7da56e7..307886d 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,21 @@ +2021-08-23 Tobias Burnus + + * openmp.c (gfc_match_dupl_check, gfc_match_dupl_memorder, + gfc_match_dupl_atomic): New. + (gfc_match_omp_clauses): Use them; remove duplicate + 'release'/'relaxed' clause matching; improve error dignostic + for 'default'. + +2021-08-23 Tobias Burnus + + * dump-parse-tree.c (show_omp_clauses): Handle 'strict' modifier + on grainsize/num_tasks + * gfortran.h (gfc_omp_clauses): Add grainsize_strict + and num_tasks_strict. + * trans-openmp.c (gfc_trans_omp_clauses, gfc_split_omp_clauses): + Handle 'strict' modifier on grainsize/num_tasks. + * openmp.c (gfc_match_omp_clauses): Likewise. + 2021-08-20 Tobias Burnus * error.c diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index fa50fe5..5c49bd3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,129 @@ +2021-08-23 David Malcolm + + * gcc.dg/analyzer/switch.c: Remove xfail. Add various tests. + * gcc.dg/analyzer/torture/switch-2.c: New test. + * gcc.dg/analyzer/torture/switch-3.c: New test. + * gcc.dg/analyzer/torture/switch-4.c: New test. + * gcc.dg/analyzer/torture/switch-5.c: New test. + +2021-08-23 David Malcolm + + PR analyzer/101875 + * gcc.dg/analyzer/pr101875.c: New test. + +2021-08-23 David Malcolm + + PR analyzer/101837 + * gcc.dg/analyzer/pr101837.c: New test. + +2021-08-23 David Malcolm + + PR analyzer/101962 + * gcc.dg/analyzer/data-model-23.c: New test. + * gcc.dg/analyzer/pr101962.c: New test. + +2021-08-23 David Malcolm + + * gcc.dg/analyzer/data-model-22.c: New test. + * gcc.dg/analyzer/uninit-6.c: New test. + * gcc.dg/analyzer/uninit-6b.c: New test. + +2021-08-23 Jan Hubicka + + * gcc.dg/tree-ssa/modref-7.c: New test. + +2021-08-23 Richard Biener + + PR ipa/97565 + * g++.dg/lto/pr97565_0.C: New testcase. + * g++.dg/lto/pr97565_1.C: Likewise. + +2021-08-23 Jan Hubicka + + * g++.dg/tree-ssa/modref-1.C: Fix template. + +2021-08-23 Tobias Burnus + + * gfortran.dg/goacc/asyncwait-1.f95: Update dg-error. + * gfortran.dg/goacc/default-2.f: Update dg-error. + * gfortran.dg/goacc/enter-exit-data.f95: Update dg-error. + * gfortran.dg/goacc/if.f95: Update dg-error. + * gfortran.dg/goacc/parallel-kernels-clauses.f95: Update dg-error. + * gfortran.dg/goacc/routine-6.f90: Update dg-error. + * gfortran.dg/goacc/sie.f95: Update dg-error. + * gfortran.dg/goacc/update-if_present-2.f90: Update dg-error. + * gfortran.dg/gomp/cancel-2.f90: Update dg-error. + * gfortran.dg/gomp/declare-simd-1.f90: Update dg-error. + * gfortran.dg/gomp/error-3.f90: Update dg-error. + * gfortran.dg/gomp/loop-2.f90: Update dg-error. + * gfortran.dg/gomp/masked-2.f90: Update dg-error. + +2021-08-23 Ankur Saini + + PR analyzer/102020 + * gcc.dg/analyzer/malloc-callbacks.c : Fix faulty test. + +2021-08-23 Roger Sayle + + * gcc.dg/fold-convlshift-1.c: New test case. + * gcc.dg/fold-convlshift-2.c: New test case. + +2021-08-23 Jakub Jelinek + + PR tree-optimization/86723 + * gcc.dg/pr86723.c: New test. + * gcc.target/i386/pr86723.c: New test. + * gcc.dg/optimize-bswapdi-1.c: Use -fdump-tree-optimized instead of + -fdump-tree-bswap and scan for number of __builtin_bswap64 calls. + * gcc.dg/optimize-bswapdi-2.c: Likewise. + * gcc.dg/optimize-bswapsi-1.c: Use -fdump-tree-optimized instead of + -fdump-tree-bswap and scan for number of __builtin_bswap32 calls. + * gcc.dg/optimize-bswapsi-5.c: Likewise. + * gcc.dg/optimize-bswapsi-3.c: Likewise. Expect one __builtin_bswap32 + call instead of zero. + +2021-08-23 Richard Biener + + PR tree-optimization/79334 + * gcc.dg/torture/pr79334-0.c: New testcase. + * gcc.dg/torture/pr79334-1.c: Likewise. + +2021-08-23 liuhongt + + PR target/102016 + * gcc.target/i386/pr102016.c: New test. + +2021-08-23 Jakub Jelinek + + PR debug/101905 + * gcc.dg/guality/pr101905.c: New test. + +2021-08-23 Christophe Lyon + + PR target/100856 + * gcc.target/arm/acle/pr100856.c: Use arm_v8m_main_cde_multilib + and arm_v8m_main_cde. + * lib/target-supports.exp: Add + check_effective_target_FUNC_multilib for ARM CDE. + +2021-08-23 Jan Hubicka + + * g++.dg/tree-ssa/modref-1.C: New test. + +2021-08-23 Xi Ruoyao + + PR target/101922 + * gcc.target/mips/pr101922.c: New test. + +2021-08-23 Jonathan Yong <10walls@gmail.com> + + * gcc.c-torture/execute/gcc_tmpnam.h: Fix tmpnam case on Windows + where it can return a filename with "\" to indicate current + directory. + * gcc.c-torture/execute/fprintf-2.c: Use wrapper. + * gcc.c-torture/execute/printf-2.c: Use wrapper. + * gcc.c-torture/execute/user-printf.c: Use wrapper. + 2021-08-22 Martin Uecker PR c/98397 -- cgit v1.1 From 819b7c3a339e3bdaf85cd55954c5536bd98aae09 Mon Sep 17 00:00:00 2001 From: liuhongt Date: Wed, 4 Aug 2021 16:39:31 +0800 Subject: Disable slp in loop vectorizer when cost model is very-cheap. Performance impact for the commit with option: -march=x86-64 -O2 -ftree-vectorize -fvect-cost-model=very-cheap SPEC2017 fprate 503.bwaves_r BuildSame 507.cactuBSSN_r -0.04 508.namd_r 0.14 510.parest_r -0.54 511.povray_r 0.10 519.lbm_r BuildSame 521.wrf_r 0.64 526.blender_r -0.32 527.cam4_r 0.17 538.imagick_r 0.09 544.nab_r BuildSame 549.fotonik3d_r BuildSame 554.roms_r BuildSame 997.specrand_fr -0.09 Geometric mean: 0.02 SPEC2017 intrate 500.perlbench_r 0.26 502.gcc_r 0.21 505.mcf_r -0.09 520.omnetpp_r BuildSame 523.xalancbmk_r BuildSame 525.x264_r -0.41 531.deepsjeng_r BuildSame 541.leela_r 0.13 548.exchange2_r BuildSame 557.xz_r BuildSame 999.specrand_ir BuildSame Geometric mean: 0.02 EEMBC: no regression, only improvement or build the same, the below is improved benchmarks. mp2decoddata1 7.59 mp2decoddata2 31.80 mp2decoddata3 12.15 mp2decoddata4 11.16 mp2decoddata5 11.19 mp2decoddata1 7.06 mp2decoddata2 24.12 mp2decoddata3 10.83 mp2decoddata4 10.04 mp2decoddata5 10.07 gcc/ChangeLog: PR tree-optimization/100089 * tree-vectorizer.c (try_vectorize_loop_1): Disable slp in loop vectorizer when cost model is very-cheap. --- gcc/tree-vectorizer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c index b9709a6..813f468 100644 --- a/gcc/tree-vectorizer.c +++ b/gcc/tree-vectorizer.c @@ -1033,7 +1033,10 @@ try_vectorize_loop_1 (hash_table *&simduid_to_vf_htab, only non-if-converted parts took part in BB vectorization. */ if (flag_tree_slp_vectorize != 0 && loop_vectorized_call - && ! loop->inner) + && ! loop->inner + /* This would purely be a workaround and should be removed + once PR100089 is fixed. */ + && flag_vect_cost_model != VECT_COST_MODEL_VERY_CHEAP) { basic_block bb = loop->header; bool require_loop_vectorize = false; -- cgit v1.1 From 78fa5112b4c2dcd94b78ee79baddebbf14d6ad98 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Tue, 24 Aug 2021 02:59:02 +0100 Subject: [Committed] PR middle-end/102029: Stricter typing in LSHIFT_EXPR sign folding. My sincere apologies to everyone (again). As diagnosed by Jakub Jelinek, my recent patch to fold the signedness of LSHIFT_EXPR needs to be careful not to attempt transforming a left shift in an integer type into an invalid left shift of a pointer type. 2021-08-24 Roger Sayle Jakub Jelinek gcc/ChangeLog PR middle-end/102029 * match.pd (shift transformations): Add an additional check for !POINTER_TYPE_P in the recently added left shift transformation. gcc/testsuite/ChangeLog PR middle-end/102029 * gcc.dg/fold-convlshift-3.c: New test case. --- gcc/match.pd | 4 +++- gcc/testsuite/gcc.dg/fold-convlshift-3.c | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/fold-convlshift-3.c (limited to 'gcc') diff --git a/gcc/match.pd b/gcc/match.pd index 978a1b0..e5bbb12 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -3389,7 +3389,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) the form that minimizes the number of conversions. */ (simplify (convert (lshift:s@0 (convert:s@1 @2) INTEGER_CST@3)) - (if (tree_nop_conversion_p (type, TREE_TYPE (@0)) + (if (INTEGRAL_TYPE_P (type) + && !POINTER_TYPE_P (type) + && tree_nop_conversion_p (type, TREE_TYPE (@0)) && INTEGRAL_TYPE_P (TREE_TYPE (@2)) && TYPE_PRECISION (TREE_TYPE (@2)) <= TYPE_PRECISION (type)) (lshift (convert @2) @3))) diff --git a/gcc/testsuite/gcc.dg/fold-convlshift-3.c b/gcc/testsuite/gcc.dg/fold-convlshift-3.c new file mode 100644 index 0000000..8d01191 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-convlshift-3.c @@ -0,0 +1,8 @@ +/* PR middle-end/102029 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +int * +foo (const __PTRDIFF_TYPE__ l) +{ + return (int *) (l << 2); +} -- cgit v1.1 From f8977166135de09fe36a3b57cc11daa67587604e Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Tue, 24 Aug 2021 03:04:48 +0100 Subject: Tweak -Os costs for scalar-to-vector pass. Back in June I briefly mentioned in one of my gcc-patches posts that a change that should have always reduced code size, would mysteriously occasionally result in slightly larger code (according to CSiBE): https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573233.html Investigating further, the cause turns out to be that x86_64's scalar-to-vector (stv) pass is relying on poor estimates of the size costs/benefits. This patch tweaks the backend's compute_convert_gain method to provide slightly more accurate values when compiling with -Os. Compilation without -Os is (should be) unaffected. And for completeness, I'll mention that the stv pass is a net win for code size so it's much better to improve its heuristics than simply gate the pass on !optimize_for_size. The net effect of this change is to save 1399 bytes on the CSiBE code size benchmark when compiling with -Os. 2021-08-24 Roger Sayle Richard Biener gcc/ChangeLog * config/i386/i386-features.c (compute_convert_gain): Provide more accurate values for CONST_INT, when optimizing for size. * config/i386/i386.c (COSTS_N_BYTES): Move definition from here... * config/i386/i386.h (COSTS_N_BYTES): to here. --- gcc/config/i386/i386-features.c | 38 +++++++++++++++++++++++++++++++++----- gcc/config/i386/i386.c | 2 -- gcc/config/i386/i386.h | 5 +++++ 3 files changed, 38 insertions(+), 7 deletions(-) (limited to 'gcc') diff --git a/gcc/config/i386/i386-features.c b/gcc/config/i386/i386-features.c index d9c6652..5a99ea7 100644 --- a/gcc/config/i386/i386-features.c +++ b/gcc/config/i386/i386-features.c @@ -610,12 +610,40 @@ general_scalar_chain::compute_convert_gain () case CONST_INT: if (REG_P (dst)) - /* DImode can be immediate for TARGET_64BIT and SImode always. */ - igain += m * COSTS_N_INSNS (1); + { + if (optimize_insn_for_size_p ()) + { + /* xor (2 bytes) vs. xorps (3 bytes). */ + if (src == const0_rtx) + igain -= COSTS_N_BYTES (1); + /* movdi_internal vs. movv2di_internal. */ + /* => mov (5 bytes) vs. movaps (7 bytes). */ + else if (x86_64_immediate_operand (src, SImode)) + igain -= COSTS_N_BYTES (2); + else + /* ??? Larger immediate constants are placed in the + constant pool, where the size benefit/impact of + STV conversion is affected by whether and how + often each constant pool entry is shared/reused. + The value below is empirically derived from the + CSiBE benchmark (and the optimal value may drift + over time). */ + igain += COSTS_N_BYTES (0); + } + else + { + /* DImode can be immediate for TARGET_64BIT + and SImode always. */ + igain += m * COSTS_N_INSNS (1); + igain -= vector_const_cost (src); + } + } else if (MEM_P (dst)) - igain += (m * ix86_cost->int_store[2] - - ix86_cost->sse_store[sse_cost_idx]); - igain -= vector_const_cost (src); + { + igain += (m * ix86_cost->int_store[2] + - ix86_cost->sse_store[sse_cost_idx]); + igain -= vector_const_cost (src); + } break; default: diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 46844fa..5bff131 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -19982,8 +19982,6 @@ ix86_division_cost (const struct processor_costs *cost, return cost->divide[MODE_INDEX (mode)]; } -#define COSTS_N_BYTES(N) ((N) * 2) - /* Return cost of shift in MODE. If CONSTANT_OP1 is true, the op1 value is known and set in OP1_VAL. AND_IN_OP1 specify in op1 is result of and and SHIFT_AND_TRUNCATE diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 8aba86d..11ac8d0 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -88,6 +88,11 @@ struct stringop_algs } size [MAX_STRINGOP_ALGS]; }; +/* Analog of COSTS_N_INSNS when optimizing for size. */ +#ifndef COSTS_N_BYTES +#define COSTS_N_BYTES(N) ((N) * 2) +#endif + /* Define the specific costs for a given cpu. NB: hard_register is used by TARGET_REGISTER_MOVE_COST and TARGET_MEMORY_MOVE_COST to compute hard register move costs by register allocator. Relative costs of -- cgit v1.1