On Thu, Jan 4, 2018 at 10:52 PM, David Malcolm <dmalc...@redhat.com> wrote: > PR lto/83121 reports an ICE deep inside the linemap code when -Wodr > reports on a type mismatch. > > The root cause is that the warning can access the DECL_SOURCE_LOCATION > of a streamed-in decl before the lto_location_cache has been applied. > > lto_location_cache::input_location stores RESERVED_LOCATION_COUNT (==2) > as a poison value until the cache is applied: > 250 /* Keep value RESERVED_LOCATION_COUNT in *loc as linemap lookups > will > 251 ICE on it. */ > > The fix is relatively simple: apply the cache before reading the > DECL_SOURCE_LOCATION. > > (I wonder if we should instead have a INVALID_LOCATION value to handle > this case more explicitly? e.g. 0xffffffff? or reserve 2 in libcpp for > that purpose, and have the non-reserved locations start at 3? Either > would be more invasive, though) > > Triggering the ICE was fiddly: it seems to be affected by many things, > including the order of files, and (I think) by filenames. My theory is > that it's affected by the ordering of the tree nodes in the LTO stream: > for the ICE to occur, the types in question need to be compared before > some other operation flushes the lto_location_cache. This ordering > is affected by the hash-based ordering in DFS in lto-streamer-out.c, which > might explain why r255066 seemed to trigger the bug; the only relevant > change to LTO there seemed to be: > * lto-streamer-out.c (hash_tree): Hash TYPE_EMPTY_P and DECL_PADDING_P. > If so, then the bug was presumably already present, but hidden. > > The patch also adds regression test coverage for the ICE, which is more > involved - as far as I can tell, we don't have an existing way to verify > diagnostics emitted during link-time optimization. > > Hence the patch adds some machinery to lib/lto.exp to support two new > directives: dg-lto-warning and dg-lto-message, corresponding to > dg-warning and dg-message respectively, where the diagnostics are > expected to be emitted at link-time. > > The test case includes examples of LTO warnings and notes in both the > primary and secondary source files > > Doing so required reusing the logic from DejaGnu for handling diagnostics. > Unfortunately the pertinent code is a 50 line loop within a ~200 line Tcl > function in dg.exp (dg-test), so I had to copy it from DejaGnu, making > various changes as necessary (see lto_handle_diagnostics_for_file in the > patch; for example the LTO version supports multiple source files, > identifying which source file emitted a diagnostic). > > For non-LTO diagnostics we currently ignore surplus "note" diagnostics. > This patch updates lto_prune_warns to follow this behavior (since > otherwise we'd need numerous dg-lto-message directives for the motivating > test case). > > The patch adds these PASS results to g++.sum: > > PASS: g++.dg/lto/pr83121 cp_lto_pr83121_0.o assemble, -O0 -flto > PASS: g++.dg/lto/pr83121 cp_lto_pr83121_1.o assemble, -O0 -flto > PASS: g++.dg/lto/pr83121 (test for LTO warnings, pr83121_0.C line 6) > PASS: g++.dg/lto/pr83121 (test for LTO warnings, pr83121_0.C line 8) > PASS: g++.dg/lto/pr83121 (test for LTO warnings, pr83121_1.C line 2) > PASS: g++.dg/lto/pr83121 (test for LTO warnings, pr83121_1.C line 3) > PASS: g++.dg/lto/pr83121 cp_lto_pr83121_0.o-cp_lto_pr83121_1.o link, -O0 -flto > > The output for dg-lto-message above refers to "warnings", rather than > "messages" but that's the same as for the non-LTO case, where dg-message > also refers to "warnings". > > Successfully bootstrapped®rtested on x86_64-pc-linux-gnu. > > OK for trunk?
Hmm, but we do this in warn_odr already? How's that not enough? At least it seems the place you add this isn't ideal (not at the "root cause"). Richard. > gcc/ChangeLog: > PR lto/83121 > * ipa-devirt.c (add_type_duplicate): When comparing memory layout, > call the lto_location_cache before reading the > DECL_SOURCE_LOCATION of the types. > > gcc/testsuite/ChangeLog: > PR lto/83121 > * g++.dg/lto/pr83121_0.C: New test case. > * g++.dg/lto/pr83121_1.C: New test case. > * lib/lto.exp (lto_handle_diagnostics_for_file): New procedure, > adapted from DejaGnu's dg-test. > (lto_handle_diagnostics): New procedure. > (lto_prune_warns): Ignore informational notes. > (lto-link-and-maybe-run): Add "messages_by_file" param. > Call lto_handle_diagnostics. Avoid issuing "unresolved" for > "execute" when "link" fails if "execute" was not specified. > (lto-can-handle-directive): New procedure. > (lto-get-options-main): Call lto-can-handle-directive. Add a > dg-messages local, using it to set the caller's > dg-messages-by-file for the given source file. > (lto-get-options): Likewise. > (lto-execute): Add dg-messages-by-file local, and pass it to > lto-link-and-maybe-run. > --- > gcc/ipa-devirt.c | 7 +- > gcc/testsuite/g++.dg/lto/pr83121_0.C | 12 +++ > gcc/testsuite/g++.dg/lto/pr83121_1.C | 10 ++ > gcc/testsuite/lib/lto.exp | 199 > ++++++++++++++++++++++++++++++++++- > 4 files changed, 222 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/lto/pr83121_0.C > create mode 100644 gcc/testsuite/g++.dg/lto/pr83121_1.C > > diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c > index 540f038..f3d2e4a 100644 > --- a/gcc/ipa-devirt.c > +++ b/gcc/ipa-devirt.c > @@ -1844,7 +1844,12 @@ add_type_duplicate (odr_type val, tree type) > } > } > > - /* Next compare memory layout. */ > + /* Next compare memory layout. > + The DECL_SOURCE_LOCATIONs in this invocation came from LTO streaming. > + We must apply the location cache to ensure that they are valid > + before we can pass them to odr_types_equivalent_p (PR lto/83121). */ > + if (lto_location_cache::current_cache) > + lto_location_cache::current_cache->apply_location_cache (); > if (!odr_types_equivalent_p (val->type, type, > !flag_ltrans && !val->odr_violated && !warned, > &warned, &visited, > diff --git a/gcc/testsuite/g++.dg/lto/pr83121_0.C > b/gcc/testsuite/g++.dg/lto/pr83121_0.C > new file mode 100644 > index 0000000..ef894c7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lto/pr83121_0.C > @@ -0,0 +1,12 @@ > +// { dg-lto-do link } > +// { dg-lto-options {{-O0 -flto}} } > +/* We need -O0 to avoid the "Environment" locals in the test functions > + from being optimized away. */ > + > +struct Environment { // { dg-lto-warning "8: type 'struct Environment' > violates the C\\+\\+ One Definition Rule" } > + struct AsyncHooks { > + int providers_[2]; // { dg-lto-message "a field of same name but > different type is defined in another translation unit" } > + }; > + AsyncHooks async_hooks_; > +}; > +void fn2() { Environment a; } > diff --git a/gcc/testsuite/g++.dg/lto/pr83121_1.C > b/gcc/testsuite/g++.dg/lto/pr83121_1.C > new file mode 100644 > index 0000000..2aef1b5 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/lto/pr83121_1.C > @@ -0,0 +1,10 @@ > +struct Environment { > + struct AsyncHooks { // { dg-lto-warning "10: type 'struct AsyncHooks' > violates the C\\+\\+ One Definition Rule" } > + int providers_[1]; // { dg-lto-message "the first difference of > corresponding definitions is field 'providers_'" } > + }; > + AsyncHooks async_hooks_; > +}; > +void fn1() { Environment a; } > +int main () > +{ > +} > diff --git a/gcc/testsuite/lib/lto.exp b/gcc/testsuite/lib/lto.exp > index 477f683..e69d8d4 100644 > --- a/gcc/testsuite/lib/lto.exp > +++ b/gcc/testsuite/lib/lto.exp > @@ -16,6 +16,122 @@ > > # Contributed by Diego Novillo <dnovi...@google.com> > > +# A subroutine of lto_handle_diagnostics: check TEXT for the expected > +# diagnostics for one specific source file, issuing PASS/FAIL results. > +# Return TEXT, stripped of any diagnostics that were handled. > +# > +# NAME is the testcase name to use when reporting PASS/FAIL results. > +# FILENAME is the name (with full path) of the file we're interested in. > +# MESSAGES_FOR_FILE is a list of expected messages, akin to DejaGnu's > +# "dg-messages" variable. > +# TEXT is the textual output from the LTO link. > + > +proc lto_handle_diagnostics_for_file { name filename messages_for_file text > } { > + global dg-linenum-format > + > + set filename_without_path [file tail $filename] > + > + # This loop is adapted from the related part of DejaGnu's dg-test, > + # with changes as detailed below to cope with the LTO case. > + > + foreach i ${messages_for_file} { > + verbose "Scanning for message: $i" 4 > + > + # Remove all error messages for the line [lindex $i 0] > + # in the source file. If we find any, success! > + set line [lindex $i 0] > + set pattern [lindex $i 2] > + set comment [lindex $i 3] > + verbose "line: $line" 4 > + verbose "pattern: $pattern" 4 > + verbose "comment: $comment" 4 > + #send_user "Before:\n$text\n" > + > + # Unlike dg-test, we use $filename_without_path in this pattern. > + # This is to ensure that we have the correct file/line combination. > + # This imposes the restriction that the filename can't contain > + # any regexp control characters. We have to strip the path, since > + # e.g. the '+' in "g++.dg" wouldn't be valid. > + set pat > "(^|\n)(\[^\n\]+$filename_without_path$line\[^\n\]*($pattern)\[^\n\]*\n?)+" > + if {[regsub -all $pat $text "\n" text]} { > + set text [string trimleft $text] > + set ok pass > + set uhoh fail > + } else { > + set ok fail > + set uhoh pass > + } > + #send_user "After:\n$text\n" > + > + # $line will either be a formatted line number or a number all by > + # itself. Delete the formatting. > + scan $line ${dg-linenum-format} line > + > + # Unlike dg-test, add the filename to the PASS/FAIL message (rather > + # than just the line number) so that the user can identify the > + # pertinent directive. > + set describe_where "$filename_without_path line $line" > + > + # Issue the PASS/FAIL, adding "LTO" to the messages (e.g. "LTO > errors") > + # to distinguish them from the non-LTO case (in case we ever need to > + # support both). > + switch [lindex $i 1] { > + "ERROR" { > + $ok "$name $comment (test for LTO errors, $describe_where)" > + } > + "XERROR" { > + x$ok "$name $comment (test for LTO errors, $describe_where)" > + } > + "WARNING" { > + $ok "$name $comment (test for LTO warnings, $describe_where)" > + } > + "XWARNING" { > + x$ok "$name $comment (test for LTO warnings, $describe_where)" > + } > + "BOGUS" { > + $uhoh "$name $comment (test for LTO bogus messages, > $describe_where)" > + } > + "XBOGUS" { > + x$uhoh "$name $comment (test for LTO bogus messages, > $describe_where)" > + } > + "BUILD" { > + $uhoh "$name $comment (test for LTO build failure, > $describe_where)" > + } > + "XBUILD" { > + x$uhoh "$name $comment (test for LTO build failure, > $describe_where)" > + } > + "EXEC" { } > + "XEXEC" { } > + } > + } > + return $text > +} > + > +# Support for checking for link-time diagnostics: check for > +# the expected diagnostics within TEXT, issuing PASS/FAIL results. > +# Return TEXT, stripped of any diagnostics that were handled. > +# > +# MESSAGES_BY_FILE is a dict; the keys are source files (with paths) > +# the values are lists of expected messages, akin to DejaGnu's "dg-messages" > +# variable. > +# TEXT is the textual output from the LTO link. > + > +proc lto_handle_diagnostics { messages_by_file text } { > + global testcase > + > + verbose "lto_handle_diagnostics: entry: $text" 2 > + verbose " messages_by_file $messages_by_file" 3 > + > + dict for {src dg-messages} $messages_by_file { > + set text [lto_handle_diagnostics_for_file $testcase $src \ > + ${dg-messages} $text] > + } > + > + verbose "lto_handle_diagnostics: exit: $text" 2 > + > + return $text > +} > + > # Prune messages that aren't useful. > > proc lto_prune_warns { text } { > @@ -39,6 +155,9 @@ proc lto_prune_warns { text } { > regsub -all "(^|\n)\[ \t\]*\[\(\]file \[^\n\]* value=\[^\n\]*; file > \[^\n\]* value=\[^\n\]*\[)\];" $text "" text > regsub -all "(^|\n)\[ \t\]*\[^\n\]* definition taken" $text "" text > > + # Ignore informational notes. > + regsub -all "(^|\n)\[^\n\]*: note: \[^\n\]*" $text "" text > + > verbose "lto_prune_warns: exit: $text" 2 > > return $text > @@ -175,12 +294,17 @@ proc lto-obj { source dest optall optfile optstr > xfaildata } { > # OPTALL is a list of compiler and linker options to use for all tests > # OPTFILE is a list of compiler and linker options to use for this test > # OPTSTR is the list of options to list in messages > -proc lto-link-and-maybe-run { testname objlist dest optall optfile optstr } { > +# MESSAGES_BY_FILE is a dict of (src, dg-messages) > +proc lto-link-and-maybe-run { testname objlist dest optall optfile optstr \ > + messages_by_file } { > global testcase > global tool > global compile_type > global board_info > > + verbose "lto-link-and-maybe-run" 2 > + verbose " messages_by_file $messages_by_file" 3 > + > # Check that all of the objects were built successfully. > foreach obj [split $objlist] { > if ![file_on_host exists $obj] then { > @@ -217,12 +341,17 @@ proc lto-link-and-maybe-run { testname objlist dest > optall optfile optstr } { > set board_info($target_board,ldscript) $saved_ldscript > } > > + # Check for diagnostics specified by directives > + set comp_output [lto_handle_diagnostics $messages_by_file $comp_output] > + > # Prune unimportant visibility warnings before checking output. > set comp_output [lto_prune_warns $comp_output] > > if ![${tool}_check_compile "$testcase $testname link" $optstr \ > $dest $comp_output] then { > - unresolved "$testcase $testname execute $optstr" > + if { ![string compare "execute" $compile_type] } { > + unresolved "$testcase $testname execute $optstr" > + } > return > } > > @@ -243,6 +372,51 @@ proc lto-link-and-maybe-run { testname objlist dest > optall optfile optstr } { > $status "$testcase $testname execute $optstr" > } > > +# Potentially handle the given dg- directive (a list) > +# Return true is the directive was handled, false otherwise. > + > +proc lto-can-handle-directive { op } { > + set cmd [lindex $op 0] > + > + # dg-warning and dg-message append to dg-messages. > + upvar dg-messages dg-messages > + > + # A list of directives to recognize, and a list of directives > + # to remap them to. > + # For example, "dg-lto-warning" is implemented by calling "dg-warning". > + set directives { dg-lto-warning dg-lto-message } > + set remapped_directives { dg-warning dg-message } > + > + set idx [lsearch -exact $directives $cmd] > + if { $idx != -1 } { > + verbose "remapping from: $op" 4 > + > + set remapped_cmd [lindex $remapped_directives $idx] > + set op [lreplace $op 0 0 $remapped_cmd] > + > + verbose "remapped to: $op" 4 > + > + set status [catch "$op" errmsg] > + if { $status != 0 } { > + if { 0 && [info exists errorInfo] } { > + # This also prints a backtrace which will just confuse > + # testcase writers, so it's disabled. > + perror "$name: $errorInfo\n" > + } else { > + perror "$name: $errmsg for \"$op\"\n" > + } > + # ??? The call to unresolved here is necessary to clear `errcnt'. > + # What we really need is a proc like perror that doesn't set > errcnt. > + # It should also set exit_status to 1. > + unresolved "$name: $errmsg for \"$op\"" > + } > + > + return true > + } > + > + return false > +} > + > # lto-get-options-main -- get target requirements for a test and > # options for the primary source file and the test as a whole > # > @@ -266,6 +440,10 @@ proc lto-get-options-main { src } { > upvar dg-final-code dg-final-code > set dg-final-code "" > > + # dg-warning and dg-message append to dg-messages. > + upvar dg-messages-by-file dg-messages-by-file > + set dg-messages "" > + > set tmp [dg-get-options $src] > verbose "getting options for $src: $tmp" > foreach op $tmp { > @@ -342,12 +520,15 @@ proc lto-get-options-main { src } { > } else { > append dg-final-code "[lindex $op 2]\n" > } > - } else { > + } elseif { ![lto-can-handle-directive $op] } { > # Ignore unrecognized dg- commands, but warn about them. > warning "lto.exp does not support $cmd" > } > } > > + verbose "dg-messages: ${dg-messages}" 3 > + dict append dg-messages-by-file $src ${dg-messages} > + > # Return flags to use for compiling the primary source file and for > # linking. > verbose "dg-extra-tool-flags for main is ${dg-extra-tool-flags}" > @@ -373,6 +554,10 @@ proc lto-get-options { src } { > # dg-xfail-if needs access to dg-do-what. > upvar dg-do-what dg-do-what > > + # dg-warning appends to dg-messages. > + upvar dg-messages-by-file dg-messages-by-file > + set dg-messages "" > + > set tmp [dg-get-options $src] > foreach op $tmp { > set cmd [lindex $op 0] > @@ -386,12 +571,15 @@ proc lto-get-options { src } { > } > } elseif { [string match "dg-require-*" $cmd] } { > warning "lto.exp does not support $cmd in secondary source files" > - } else { > + } elseif { ![lto-can-handle-directive $op] } { > # Ignore unrecognized dg- commands, but warn about them. > warning "lto.exp does not support $cmd in secondary source files" > } > } > > + verbose "dg-messages: ${dg-messages}" 3 > + dict append dg-messages-by-file $src ${dg-messages} > + > return ${dg-extra-tool-flags} > } > > @@ -421,6 +609,7 @@ proc lto-execute { src1 sid } { > verbose "lto-execute: $src1" 1 > set compile_type "run" > set dg-do-what [list ${dg-do-what-default} "" P] > + set dg-messages-by-file [dict create] > set extra_flags(0) [lto-get-options-main $src1] > set compile_xfail(0) "" > > @@ -544,7 +733,7 @@ proc lto-execute { src1 sid } { > lto-link-and-maybe-run \ > "[lindex $obj_list 0]-[lindex $obj_list end]" \ > $obj_list $execname $filtered ${dg-extra-ld-options} \ > - $filtered > + $filtered ${dg-messages-by-file} > } > > > -- > 1.8.5.3 >