In perl.git, the branch mauke/overflow has been updated <https://perl5.git.perl.org/perl.git/commitdiff/82c76ddc179d6cdde6e203f99d72ad1342390c48?hp=bd7061cd2e3b79521294f526a937701c04437e48>
discards bd7061cd2e3b79521294f526a937701c04437e48 (commit) discards 1ca0866a62e4f9cbfc92fc7502335b593701f0d7 (commit) discards 90ba6e7724a7a6e4a83d986f278b2dc1c5928c67 (commit) - Log ----------------------------------------------------------------- commit 82c76ddc179d6cdde6e203f99d72ad1342390c48 Author: Lukas Mai <l....@web.de> Date: Sun Oct 22 14:40:44 2017 +0200 use __builtin_mul_overflow in pp_multiply commit 50ec6d9fef3d3a05d30efbb774471fc51618579b Author: Lukas Mai <l....@web.de> Date: Sun Oct 22 14:27:34 2017 +0200 use __builtin_sub_overflow in pp_subtract commit 767bc94bdb6a2fab55855316f9e2a08d0dd59160 Author: Lukas Mai <l....@web.de> Date: Sun Oct 22 13:43:03 2017 +0200 use __builtin_add_overflow in pp_add ----------------------------------------------------------------------- Summary of changes: .metaconf-exclusions.txt | 26 ++++ Configure | 74 +++++----- MANIFEST | 4 + Porting/bench.pl | 323 ++++++++++++++++++++++++++++++------------- configpm | 15 +- dist/Time-HiRes/t/utime.t | 4 +- dist/XSLoader/XSLoader_pm.PL | 12 +- ext/B/B.pm | 4 +- ext/B/t/b.t | 15 ++ ext/XS-APItest/APItest_BS | 4 +- ext/XS-APItest/t/bootstrap.t | 2 +- perl.h | 2 + pod/perlre.pod | 2 +- pod/perlvar.pod | 3 + sv.c | 1 + t/perf/benchmarks | 54 +++++--- t/perf/benchmarks.t | 24 +++- t/porting/bench.t | 65 ++++++++- t/porting/bench/badhash | 15 ++ t/porting/bench/badname | 14 ++ t/porting/bench/oddentry | 10 ++ 21 files changed, 492 insertions(+), 181 deletions(-) create mode 100644 .metaconf-exclusions.txt create mode 100644 t/porting/bench/badhash create mode 100644 t/porting/bench/badname create mode 100644 t/porting/bench/oddentry diff --git a/.metaconf-exclusions.txt b/.metaconf-exclusions.txt new file mode 100644 index 0000000000..ce9e3d5658 --- /dev/null +++ b/.metaconf-exclusions.txt @@ -0,0 +1,26 @@ +# This file is used when building the Configure script from the metaconfig +# units. Ordinarily, if any *.c or *.h or *.sh file mentions a symbol that can +# be provided by a metaconfig unit, the generated Configure will include the +# relevant probe. But in some cases, that's inappropriate: we have symbols that +# we no longer need to probe for, but must define in perl.h for backwards +# compatibilty with XS modules. Also, our C files contain tokens like "index" +# that are used as the names of Perl builtins, but would be treated by +# metaconfig as implicit requests for a particular unit that we don't need. +# +# Previously, we've used customised "stub" versions of metaconfig units to +# handle this situation, but that doesn't scale (and, in particular, requires +# us to maintain those customised units for as long as the relevant symbols are +# defined by the units in the upstream dist package). +# +# A better approach is to list those symbols here (and invoke metaconfig with +# the "-X .metaconf-exclusions.txt" option when generating Configure); that +# will prevent the relevant units from being used. +# +# See also metaconfig.h, which works in the other direction — it forces units +# to be included, even before specific code using the relevant symbols has been +# written. + +I_LIMITS +I_STDARG +index +rindex diff --git a/Configure b/Configure index 43872c1f3e..0db5887985 100755 --- a/Configure +++ b/Configure @@ -19057,52 +19057,52 @@ thread_start(void * arg) } int main() { - char * main_buffer; - char save_main_buffer[1000]; - pthread_t subthread; - pthread_attr_t attr; - - main_buffer = nl_langinfo_l(CODESET, newlocale(LC_ALL_MASK, "C", 0)); - - /* If too large for our generous allowance, just assume we don't have - * it. */ - if (strlen(main_buffer) >= sizeof(save_main_buffer)) { - exit(1); - } + char * main_buffer; + char save_main_buffer[1000]; + pthread_t subthread; + pthread_attr_t attr; - strcpy(save_main_buffer, main_buffer); + main_buffer = nl_langinfo_l(CODESET, newlocale(LC_ALL_MASK, "C", 0)); - if (pthread_attr_init(&attr) != 0) { - exit(1); - } + /* If too large for our generous allowance, just assume we don't have + * it. */ + if (strlen(main_buffer) >= sizeof(save_main_buffer)) { + exit(1); + } - if (pthread_create(&subthread, &attr, thread_start, NULL) != 0) { - exit(1); - } + strcpy(save_main_buffer, main_buffer); - if (pthread_join(subthread, NULL) != 0) { - exit(1); - } + if (pthread_attr_init(&attr) != 0) { + exit(1); + } - exit(! (strcmp(main_buffer, save_main_buffer) == 0)); + if (pthread_create(&subthread, &attr, thread_start, NULL) != 0) { + exit(1); + } + + if (pthread_join(subthread, NULL) != 0) { + exit(1); + } + + exit(! (strcmp(main_buffer, save_main_buffer) == 0)); } EOCP case "$usethreads" in define) - set try - if eval $compile; then - echo "Your system has nl_langinfo_l()..." >&4 - if $run ./try; then - echo "and it is thread-safe (just as I'd hoped)." >&4 - d_thread_safe_nl_langinfo_l="$define" - echo "$d_thread_safe_nl_langinfo_l" >&4 - else - echo "but it isn't thread-safe, so we won't use it." >&4 - fi - else - echo "your system does not have nl_langinfo_l()" >&4 - fi - ;; + set try + if eval $compile; then + echo "Your system has nl_langinfo_l()..." >&4 + if $run ./try; then + echo "and it is thread-safe (just as I'd hoped)." >&4 + d_thread_safe_nl_langinfo_l="$define" + echo "$d_thread_safe_nl_langinfo_l" >&4 + else + echo "but it isn't thread-safe, so we won't use it." >&4 + fi + else + echo "your system does not have nl_langinfo_l()" >&4 + fi + ;; *) echo "Since threads aren't selected, we won't bother looking for nl_langinfo_l()" >&4 esac if test X"$d_thread_safe_nl_langinfo_l" = X; then diff --git a/MANIFEST b/MANIFEST index ffbaa3de1e..b3207030a9 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,4 +1,5 @@ .dir-locals.el Emacs control file +.metaconf-exclusions.txt Symbols that should ignored when generating Configure .travis.yml continuous integration on github (where enabled) amigaos4/amigaio.c AmigaOS4 port amigaos4/amigaio.h AmigaOS4 port @@ -5805,9 +5806,12 @@ t/perl.supp Perl valgrind suppressions t/porting/args_assert.t Check that all PERL_ARGS_ASSERT* macros are used t/porting/authors.t Check that all authors have been acknowledged t/porting/bench.t Check Porting/bench.pl runs ok +t/porting/bench/badhash a test file for t/porting/bench.t +t/porting/bench/badname a test file for t/porting/bench.t t/porting/bench/badversion.json a test file for t/porting/bench.t t/porting/bench/callsub.json a test file for t/porting/bench.t t/porting/bench/callsub2.json a test file for t/porting/bench.t +t/porting/bench/oddentry a test file for t/porting/bench.t t/porting/bench/ret0 a test file for t/porting/bench.t t/porting/bench/synerr a test file for t/porting/bench.t t/porting/bench_selftest.t run Porting/bench.pl's selftest facility diff --git a/Porting/bench.pl b/Porting/bench.pl index 0bb3fd89ab..6087dca138 100755 --- a/Porting/bench.pl +++ b/Porting/bench.pl @@ -99,7 +99,7 @@ I<selftest>, which runs some basic sanity checks and produces TAP output. --debug -Enable verbose debugging output. +Enable debugging output. =item * @@ -109,6 +109,7 @@ Display basic usage information. =item * +-v --verbose Display progress information. @@ -188,6 +189,9 @@ but is actually only stripped down to: 5.20.0 5.22.0 5.24.0 +If the final results are plain integers, they are prefixed with "p" +to avoid looking like column numbers to switches like C<--norm=2>. + =item * @@ -303,7 +307,21 @@ If only one field is selected, the output is in more compact form. --norm=I<foo> Specify which perl column in the output to treat as the 100% norm. -It may be a column number (0..N-1) or a perl executable name or label. +It may be: + +=over + +* a column number (0..N-1), + +* a negative column number (-1..-N) which counts from the right (so -1 is +the right-most column), + +* or a perl executable name, + +* or a perl executable label. + +=back + It defaults to the leftmost column. =item * @@ -365,7 +383,7 @@ General options: selftest perform a selftest; produce TAP output --debug Enable verbose debugging output. --help Display this help. - --verbose Display progress information. + -v|--verbose Display progress information. Selection: @@ -478,7 +496,7 @@ my %OPTS = ( 'show' => \$OPTS{show}, 'sort=s' => \$OPTS{sort}, 'tests=s' => \$OPTS{tests}, - 'verbose' => \$OPTS{verbose}, + 'v|verbose' => \$OPTS{verbose}, 'write|w=s' => \$OPTS{write}, ) or die "Use the -h option for usage information.\n"; @@ -598,6 +616,32 @@ sub read_tests_file { die "Error: can't read '$file': $!\n"; } + # validate and process each test + + { + my %valid = map { $_ => 1 } qw(desc setup code pre post compile); + my @tests = @$ta; + if (!@tests || @tests % 2 != 0) { + die "Error: '$file' does not contain evenly paired test names and hashes\n"; + } + while (@tests) { + my $name = shift @tests; + my $hash = shift @tests; + + unless ($name =~ /^[a-zA-Z]\w*(::\w+)*$/) { + die "Error: '$file': invalid test name: '$name'\n"; + } + + for (sort keys %$hash) { + die "Error: '$file': invalid key '$_' for test '$name'\n" + unless exists $valid{$_}; + } + + # make description default to the code + $hash->{desc} = $hash->{code} unless exists $hash->{desc}; + } + } + my @orig_order; for (my $i=0; $i < @$ta; $i += 2) { push @orig_order, $ta->[$i]; @@ -614,18 +658,35 @@ sub read_tests_file { sub select_a_perl { my ($perl, $perls, $who) = @_; - $perls||=[]; - if ($perl =~ /^[0-9]$/) { + $perls ||= []; + my $n = @$perls; + + if ($perl =~ /^-([0-9]+)$/) { + my $p = $1; + die "Error: $who value $perl outside range -1..-$n\n" + if $p < 1 || $p > $n; + return $n - $p; + } + + if ($perl =~ /^[0-9]+$/) { die "Error: $who value $perl outside range 0.." . $#$perls . "\n" - unless $perl < @$perls; + unless $perl < $n; return $perl; } else { my @perl = grep $perls->[$_][0] eq $perl || $perls->[$_][1] eq $perl, 0..$#$perls; - die "Error: $who: unrecognised perl '$perl'\n" - unless @perl; + unless (@perl) { + my $valid = ''; + for (@$perls) { + $valid .= " $_->[1]"; + $valid .= " $_->[0]" if $_->[0] ne $_->[1]; + $valid .= "\n"; + } + die "Error: $who: unrecognised perl '$perl'\n" + . "Valid perl names are:\n$valid"; + } die "Error: $who: ambiguous perl '$perl'\n" if @perl > 1; return $perl[0]; @@ -747,6 +808,17 @@ sub process_executables_list { $post =~ s/^([0-9\.]*).*$/$1/; $labels[$_][0] .= $post for 0..$#labels; + # avoid degenerate empty string for single executable name + $labels[0][0] = '0' if @labels == 1 && !length $labels[0][0]; + + # if the auto-generated labels are plain integers, prefix + # them with 'p' (for perl) to distinguish them from column + # indices (otherwise e.g. --norm=2 is ambiguous) + + if ($labels[0][0] =~ /^\d*$/) { + $labels[$_][0] = "p$labels[$_][0]" for 0..$#labels; + } + # now de-duplicate labels my (%seen, %index); @@ -774,19 +846,43 @@ sub process_executables_list { -# Return a string containing perl test code wrapped in a loop -# that runs $ARGV[0] times +# Return a string containing a perl program which runs the benchmark code +# $ARGV[0] times. If $body is true, include the main body (setup) in +# the loop; otherwise create an empty loop with just pre and post. +# Note that an empty body is handled with '1;' so that a completely empty +# loop has a single nextstate rather than a stub op, so more closely +# matches the active loop; e.g.: +# {1;} => nextstate; unstack +# {$x=1;} => nextstate; const; gvsv; sassign; unstack +# Note also that each statement is prefixed with a label; this avoids +# adjacent nextstate ops being optimised away. +# +# A final 1; statement is added so that the code is always in void +# context. +# +# It the compile flag is set for a test, the body of the loop is wrapped in +# eval 'sub { .... }' to measure compile time rather than execution time sub make_perl_prog { - my ($test, $desc, $setup, $code) = @_; + my ($name, $test, $body) = @_; + my ($desc, $setup, $code, $pre, $post, $compile) = + @$test{qw(desc setup code pre post compile)}; + + $setup //= ''; + $pre = defined $pre ? "_PRE_: $pre; " : ""; + $post = defined $post ? "_POST_: $post; " : ""; + $code = $body ? $code : "1"; + $code = "_CODE_: $code; "; + my $full = "$pre$code$post _CXT_: 1; "; + $full = "eval q{sub { $full }};" if $compile; return <<EOF; # $desc -package $test; +package $name; BEGIN { srand(0) } $setup; for my \$__loop__ (1..\$ARGV[0]) { - $code; + $full } EOF } @@ -1071,14 +1167,9 @@ sub grind_run { for my $test (grep $tests->{$_}, @$order) { # Create two test progs: one with an empty loop and one with code. - # Note that the empty loop is actually '{1;}' rather than '{}'; - # this causes the loop to have a single nextstate rather than a - # stub op, so more closely matches the active loop; e.g.: - # {1;} => nextstate; unstack - # {$x=1;} => nextstate; const; gvsv; sassign; unstack my @prog = ( - make_perl_prog($test, @{$tests->{$test}}{qw(desc setup)}, '1'), - make_perl_prog($test, @{$tests->{$test}}{qw(desc setup code)}), + make_perl_prog($test, $tests->{$test}, 0), + make_perl_prog($test, $tests->{$test}, 1), ); for my $p (@$perls) { @@ -1459,6 +1550,27 @@ sub sorted_test_names { } +# format one cell data item + +sub grind_format_cell { + my ($val, $width) = @_; + my $s; + if (!defined $val) { + return sprintf "%*s", $width, '-'; + } + elsif (abs($val) >= 1_000_000) { + # avoid displaying very large numbers (which might be the + # result of e.g. 1 / 0.000001) + return sprintf "%*s", $width, 'Inf'; + } + elsif ($OPTS{raw}) { + return sprintf "%*.1f", $width, $val; + } + else { + return sprintf "%*.2f", $width, $val * 100; + } +} + # grind_print(): display the tabulated results of all the cachegrinds. # # Arguments are of the form: @@ -1466,6 +1578,7 @@ sub sorted_test_names { # $averages->{perl_label}{field_name} = M # $perls = [ [ perl-exe, perl-label ], ... ] # $tests->{test_name}{desc => ..., ...} +# $order = [ 'foo::bar1', ... ] # order to display tests sub grind_print { my ($results, $averages, $perls, $tests, $order) = @_; @@ -1475,103 +1588,103 @@ sub grind_print { my %perl_labels; $perl_labels{$_->[0]} = $_->[1] for @$perls; - my $field_label_width = 6; - # Calculate the width to display for each column. - my $min_width = $OPTS{raw} ? 8 : 6; - my @widths = map { length($_) < $min_width ? $min_width : length($_) } - @perl_labels; - # Print standard header. grind_blurb($perls); my @test_names = sorted_test_names($results, $order, $perls); + my @fields = qw(Ir Dr Dw COND IND + COND_m IND_m + Ir_m1 Dr_m1 Dw_m1 + Ir_mm Dr_mm Dw_mm + ); + + if ($OPTS{fields}) { + @fields = grep exists $OPTS{fields}{$_}, @fields; + } + # If only a single field is to be displayed, use a more compact # format with only a single line of output per test. - my $one_field = defined $OPTS{fields} && keys(%{$OPTS{fields}}) == 1; + my $one_field = @fields == 1; - if ($one_field) { - print "\nResults for field " . (keys(%{$OPTS{fields}}))[0] . ".\n"; + # The width of column 0: this is either field names, or for + # $one_field, test names - # The first column will now contain test names rather than - # field names; Calculate the max width. + my $width0 = 0; + for ($one_field ? @test_names : @fields) { + $width0 = length if length > $width0; + } - $field_label_width = 0; - for (@test_names) { - $field_label_width = length if length > $field_label_width; - } + # Calculate the widths of the data columns - # Print the perl executables header. + my @widths = map length, @perl_labels; - print "\n"; - for my $i (0,1) { - print " " x $field_label_width; - for (0..$#widths) { - printf " %*s", $widths[$_], - $i ? ('-' x$widths[$_]) : $perl_labels[$_]; + for my $test (@test_names) { + my $res = ($test eq 'AVERAGE') ? $averages : $results->{$test}; + for my $field (@fields) { + for my $i (0..$#widths) { + my $l = length grind_format_cell( + $res->{$perl_labels[$i]}{$field}, 1); + $widths[$i] = $l if $l > $widths[$i]; } - print "\n"; } } - # Dump the results for each test. + # Print the results for each test - for my $test_name (@test_names) { + for my $test (0..$#test_names) { + my $test_name = $test_names[$test]; my $doing_ave = ($test_name eq 'AVERAGE'); - my $res1 = $doing_ave ? $averages : $results->{$test_name}; + my $res = $doing_ave ? $averages : $results->{$test_name}; - unless ($one_field) { + # print per-test header + + if ($one_field) { + print "\nResults for field $fields[0]\n\n" if $test == 0; + } + else { print "\n$test_name"; print "\n$tests->{$test_name}{desc}" unless $doing_ave; print "\n\n"; + } + + # Print the perl executable names header. - # Print the perl executables header. + if (!$one_field || $test == 0) { for my $i (0,1) { - print " " x $field_label_width; + print " " x $width0; for (0..$#widths) { printf " %*s", $widths[$_], - $i ? ('-' x$widths[$_]) : $perl_labels[$_]; + $i ? ('-' x$widths[$_]) : $perl_labels[$_]; } print "\n"; } } - for my $field (qw(Ir Dr Dw COND IND - N - COND_m IND_m - N - Ir_m1 Dr_m1 Dw_m1 - N - Ir_mm Dr_mm Dw_mm - )) - { - next if $OPTS{fields} and ! exists $OPTS{fields}{$field}; + my $field_suffix = ''; - if ($field eq 'N') { - print "\n"; - next; - } + # print a line of data + for my $field (@fields) { if ($one_field) { - printf "%-*s", $field_label_width, $test_name; + printf "%-*s", $width0, $test_name; } else { - printf "%*s", $field_label_width, $field; + # If there are enough fields, print a blank line + # between groups of fields that have the same suffix + if (@fields > 4) { + my $s = ''; + $s = $1 if $field =~ /(_\w+)$/; + print "\n" if $s ne $field_suffix; + $field_suffix = $s; + } + printf "%*s", $width0, $field; } for my $i (0..$#widths) { - my $res2 = $res1->{$perl_labels[$i]}; - my $p = $res2->{$field}; - if (!defined $p) { - printf " %*s", $widths[$i], '-'; - } - elsif ($OPTS{raw}) { - printf " %*.1f", $widths[$i], $p; - } - else { - printf " %*.2f", $widths[$i], $p * 100; - } + print " ", grind_format_cell($res->{$perl_labels[$i]}{$field}, + $widths[$i]); } print "\n"; } @@ -1589,14 +1702,11 @@ sub grind_print { # $averages->{perl_label}{field_name} = M # $perls = [ [ perl-exe, perl-label ], ... ] # $tests->{test_name}{desc => ..., ...} +# $order = [ 'foo::bar1', ... ] # order to display tests sub grind_print_compact { my ($results, $averages, $which_perl, $perls, $tests, $order) = @_; - - # the width to display for each column. - my $width = $OPTS{raw} ? 7 : 6; - # Print standard header. grind_blurb($perls); @@ -1616,31 +1726,48 @@ sub grind_print_compact { @fields = grep exists $OPTS{fields}{$_}, @fields; } - printf " %*s", $width, $_ for @fields; + # calculate the the max width of the test names + + my $name_width = 0; + for (@test_names) { + $name_width = length if length > $name_width; + } + + # Calculate the widths of the data columns + + my @widths = map length, @fields; + + for my $test (@test_names) { + my $res = ($test eq 'AVERAGE') ? $averages : $results->{$test}; + $res = $res->{$perls->[$which_perl][1]}; + for my $i (0..$#fields) { + my $l = length grind_format_cell($res->{$fields[$i]}, 1); + $widths[$i] = $l if $l > $widths[$i]; + } + } + + # Print header + + printf " %*s", $widths[$_], $fields[$_] for 0..$#fields; print "\n"; - printf " %*s", $width, '------' for @fields; + printf " %*s", $_, ('-' x $_) for @widths; print "\n"; + # Print the results for each test + for my $test_name (@test_names) { my $doing_ave = ($test_name eq 'AVERAGE'); my $res = $doing_ave ? $averages : $results->{$test_name}; $res = $res->{$perls->[$which_perl][1]}; + my $desc = $doing_ave + ? $test_name + : sprintf "%-*s %s", $name_width, $test_name, + $tests->{$test_name}{desc}; - for my $field (@fields) { - my $p = $res->{$field}; - if (!defined $p) { - printf " %*s", $width, '-'; - } - elsif ($OPTS{raw}) { - printf " %*.1f", $width, $p; - } - else { - printf " %*.2f", $width, $p * 100; - } - + for my $i (0..$#fields) { + print " ", grind_format_cell($res->{$fields[$i]}, $widths[$i]); } - - print " $test_name\n"; + print " $desc\n"; } } diff --git a/configpm b/configpm index 09b9e67aab..ebbaaa9b44 100755 --- a/configpm +++ b/configpm @@ -212,6 +212,19 @@ my $in_v = 0; my %Data = (); my $quote; +# These variables were set in older versions of Perl, but are no longer needed +# by the core. However, some CPAN modules may rely on them; in particular, Tk +# (at least up to version 804.034) fails to build without them. We force them +# to be emitted to Config_heavy.pl for backcompat with such modules (and we may +# find that this set needs to be extended in future). See RT#132347. +my @v_forced = map "$_\n", split /\n+/, <<'EOT'; +i_limits='define' +i_stdlib='define' +i_string='define' +i_time='define' +prototype='define' +EOT + my %seen_quotes; { @@ -567,7 +580,7 @@ $heavy_txt .= join('', map { /^([^=]+)/ ? [ $1, $_ ] : [ $_, $_ ] # shouldnt happen - } @v_others + } @v_others, @v_forced ) . "!END!\n"; # Only need the dynamic byteorder code in Config.pm if 'byteorder' is one of diff --git a/dist/Time-HiRes/t/utime.t b/dist/Time-HiRes/t/utime.t index 7821837f8e..c404ec36db 100644 --- a/dist/Time-HiRes/t/utime.t +++ b/dist/Time-HiRes/t/utime.t @@ -8,7 +8,7 @@ sub has_subsecond_file_times { my $dirname = dirname($filename); require Cwd; $dirname = &Cwd::getcwd if $dirname eq '.'; - print(STDERR "\n# Testing for subsecond file timestamps (mtime) in $dirname\n"); + print("\n# Testing for subsecond file timestamps (mtime) in $dirname\n"); close $fh; my @mtimes; for (1..2) { @@ -22,7 +22,7 @@ sub has_subsecond_file_times { # print STDERR "mtimes = @mtimes, delta = $delta\n"; unlink $filename; my $ok = $delta > 0 && $delta < 1; - printf(STDERR "# Subsecond file timestamps in $dirname: %s\n", + printf("# Subsecond file timestamps in $dirname: %s\n", $ok ? "OK" : "NO"); return $ok; } diff --git a/dist/XSLoader/XSLoader_pm.PL b/dist/XSLoader/XSLoader_pm.PL index 8012e35e32..e09538843d 100644 --- a/dist/XSLoader/XSLoader_pm.PL +++ b/dist/XSLoader/XSLoader_pm.PL @@ -11,7 +11,7 @@ print OUT <<'EOT'; package XSLoader; -$VERSION = "0.27"; +$VERSION = "0.28"; #use strict; @@ -143,14 +143,8 @@ print OUT <<'EOT'; my $bs = $file; $bs =~ s/(\.\w+)?(;\d*)?$/\.bs/; # look for .bs 'beside' the library - if (-s $bs) { # only read file if it's not empty -# print STDERR "BS: $bs ($^O, $dlsrc)\n" if $dl_debug; - eval { local @INC = ('.'); do $bs; }; - warn "$bs: $@\n" if $@; - goto \&XSLoader::bootstrap_inherit; - } - - goto \&XSLoader::bootstrap_inherit if not -f $file; + # This calls DynaLoader::bootstrap, which will load the .bs file if present + goto \&XSLoader::bootstrap_inherit if not -f $file or -s $bs; my $bootname = "boot_$module"; $bootname =~ s/\W/_/g; diff --git a/ext/B/B.pm b/ext/B/B.pm index 3365a14f8c..12d8201619 100644 --- a/ext/B/B.pm +++ b/ext/B/B.pm @@ -15,7 +15,7 @@ require Exporter; # walkoptree comes from B.xs BEGIN { - $B::VERSION = '1.70'; + $B::VERSION = '1.71'; @B::EXPORT_OK = (); # Our BOOT code needs $VERSION set, and will append to @EXPORT_OK. @@ -261,6 +261,8 @@ sub walksymtable { no strict 'refs'; $prefix = '' unless defined $prefix; foreach my $sym ( sort keys %$symref ) { + my $dummy = $symref->{$sym}; # Copying the glob and incrementing + # the GPs refcnt clears cached methods $fullname = "*main::".$prefix.$sym; if ($sym =~ /::$/) { $sym = $prefix . $sym; diff --git a/ext/B/t/b.t b/ext/B/t/b.t index a5d724912b..587c8e665f 100644 --- a/ext/B/t/b.t +++ b/ext/B/t/b.t @@ -56,6 +56,21 @@ ok( join('', sort @syms) eq join('', sort keys %Subs), 'all symbols found' ); # Make sure we only hit them each once. ok( (!grep $_ != 1, values %Subs), '...and found once' ); + +# Make sure method caches are not present when walking the sym tab +@Testing::Method::Caches::Foo::ISA='Testing::Method::Caches::Bar'; +sub Testing::Method::Caches::Bar::foo{} +Testing::Method::Caches::Foo->foo; # caches the sub in the *foo glob + +my $have_cv; +sub B::GV::method_cache_test { ${shift->CV} and ++$have_cv } + +B::walksymtable(\%Testing::Method::Caches::, 'method_cache_test', + sub { 1 }, 'Testing::Method::Caches::'); +# $have_cv should only have been incremented for ::Bar::foo +is $have_cv, 1, 'walksymtable clears cached methods'; + + # Tests for MAGIC / MOREMAGIC ok( B::svref_2object(\$.)->MAGIC->TYPE eq "\0", '$. has \0 magic' ); { diff --git a/ext/XS-APItest/APItest_BS b/ext/XS-APItest/APItest_BS index 270dc9c682..d9ec22fb62 100644 --- a/ext/XS-APItest/APItest_BS +++ b/ext/XS-APItest/APItest_BS @@ -1,7 +1,7 @@ # -# test that non-empty .bs files get executed +# test that non-empty .bs files get executed (but only once) $bscode = <<'EOF'; -$::bs_file_got_executed = 1; +$::bs_file_got_executed++; EOF diff --git a/ext/XS-APItest/t/bootstrap.t b/ext/XS-APItest/t/bootstrap.t index 6992b10850..2c6c03466f 100644 --- a/ext/XS-APItest/t/bootstrap.t +++ b/ext/XS-APItest/t/bootstrap.t @@ -11,7 +11,7 @@ use strict; use Test::More; use XS::APItest; -is $::bs_file_got_executed, 1, "BS file was executed"; +is $::bs_file_got_executed, 1, "BS file was executed once"; done_testing(); diff --git a/perl.h b/perl.h index 853fd6f8d3..f433dc7f43 100644 --- a/perl.h +++ b/perl.h @@ -565,7 +565,9 @@ #define VOL volatile #define CAN_PROTOTYPE #define _(args) args +#define I_LIMITS #define I_STDARG +#define STANDARD_C #endif /* By compiling a perl with -DNO_TAINT_SUPPORT or -DSILENT_NO_TAINT_SUPPORT, diff --git a/pod/perlre.pod b/pod/perlre.pod index 9cab16e223..b11d862b40 100644 --- a/pod/perlre.pod +++ b/pod/perlre.pod @@ -1627,7 +1627,7 @@ constructs (like C<\g{NAME}>) and can be accessed by name after a successful match via C<%+> or C<%->. See L<perlvar> for more details on the C<%+> and C<%-> hashes. -If multiple distinct capture groups have the same name then the +If multiple distinct capture groups have the same name, then C<$+{NAME}> will refer to the leftmost defined group in the match. The forms C<(?'NAME'pattern)> and C<< (?<NAME>pattern) >> are equivalent. diff --git a/pod/perlvar.pod b/pod/perlvar.pod index 9ce9430b6b..257fdb6e1a 100644 --- a/pod/perlvar.pod +++ b/pod/perlvar.pod @@ -1121,6 +1121,9 @@ For example, C<$+{foo}> is equivalent to C<$1> after the following match: The keys of the C<%+> hash list only the names of buffers that have captured (and that are thus associated to defined values). +If multiple distinct capture groups have the same name, then +C<$+{NAME}> will refer to the leftmost defined group in the match. + The underlying behaviour of C<%+> is provided by the L<Tie::Hash::NamedCapture> module. diff --git a/sv.c b/sv.c index ac1fb4d823..06168501ee 100644 --- a/sv.c +++ b/sv.c @@ -14294,6 +14294,7 @@ S_sv_dup_common(pTHX_ const SV *const sstr, CLONE_PARAMS *const param) else LvTARG(dstr) = sv_dup_inc(LvTARG(dstr), param); if (isREGEXP(sstr)) goto duprex; + /* FALLTHROUGH */ case SVt_PVGV: /* non-GP case already handled above */ if(isGV_with_GP(sstr)) { diff --git a/t/perf/benchmarks b/t/perf/benchmarks index 61909b08a9..423230a914 100644 --- a/t/perf/benchmarks +++ b/t/perf/benchmarks @@ -39,15 +39,24 @@ # string:: string handling # # -# Each hash has three fields: +# Each hash has up to five fields: +# +# desc is a description of the test; if not present, it defaults +# to the same value as the 'code' field +# +# setup is an optional string containing setup code that is run once # -# desc is a description of the test -# setup is a string containing setup code # code is a string containing the code to run in a loop # -# So typically a benchmark tool might do something like +# pre is an optional string containing setup code which is executed +# just before 'code' for every iteration, but whose execution +# time is not included in the result +# +# post like pre, but executed just after 'code'. # -# eval "package $token; $setup; for (1..1000000) { $code }" +# So typically a benchmark tool might execute variations on something like +# +# eval "package $name; $setup; for (1..1000000) { $pre; $code; $post }" # # Currently the only tool that uses this file is Porting/bench.pl; # try C<perl Porting/bench.pl --help> for more info @@ -57,7 +66,9 @@ # Note: for the cachegrind variant, an entry like # 'foo::bar' => { # setup => 'SETUP', +# pre => 'PRE', # code => 'CODE', +# post => 'POST', # } # creates two temporary perl sources looking like: # @@ -65,12 +76,12 @@ # BEGIN { srand(0) } # SETUP; # for my $__loop__ (1..$ARGV[0]) { -# 1; +# PRE; 1; POST; # } # -# and as above, but with the '1;' in the loop body replaced with: +# and as above, but with the loop body replaced with: # -# CODE; +# PRE; CODE; POST; # # It then pipes each of the two sources into # @@ -79,11 +90,25 @@ # where N is set to 10 and then 20. # # It then uses the result of those four cachegrind runs to subtract out -# the perl startup and loop overheads. So only what's in SETUP and CODE -# can affect the benchmark, and if the loop happens to leave some state -# changed (such as storing a value in a hash), then the final benchmark -# timing is the result of running CODE with the hash entry populated -# rather than empty. +# the perl startup and loop overheads (including SETUP, PRE and POST), leaving +# (in theory only CODE); +# +# Note that misleading results may be obtained if each iteration is +# not identical. For example with +# +# code => '$x .= "foo"', +# +# the string $x gets longer on each iteration. Similarly, a hash might be +# empty on the first iteration, but have entries on subsequent iterations. +# +# To avoid this, use 'pre' or 'post', e.g. +# +# pre => '$x = ""', +# code => '$x .= "foo"', +# +# Finally, the optional 'compile' key causes the code body to be wrapped +# in eval qw{ sub { ... }}, so that compile time rather than execution +# time is measured. [ @@ -1052,12 +1077,10 @@ }, 'expr::arith::preinc' => { - desc => '++$x', setup => 'my $x = 1;', code => '++$x', }, 'expr::arith::predec' => { - desc => '--$x', setup => 'my $x = 1;', code => '--$x', }, @@ -1779,7 +1802,6 @@ 'regex::anyof_plus::anchored' => { - desc => '/^[acgt]+/', setup => '$_ = "a" x 100;', code => '/^[acgt]+/', }, diff --git a/t/perf/benchmarks.t b/t/perf/benchmarks.t index 873f8db0cf..57dbcf8793 100644 --- a/t/perf/benchmarks.t +++ b/t/perf/benchmarks.t @@ -31,22 +31,34 @@ while (@$benchmark_array) { $benchmarks{$key} = $hash; } -plan keys(%benchmarks) * 3; - +plan keys(%benchmarks) * 4; # check the hash of hashes is minimally consistent in format +my %valid_keys = map { $_=> 1 } qw(desc setup code pre post compile); +my @required_keys = qw(code); + for my $token (sort keys %benchmarks) { - like($token, qr/^[a-zA-Z](\w|::)+$/a, "legal token: $token"); - my $keys = join('-', sort keys %{$benchmarks{$token}}); - is($keys, 'code-desc-setup', "legal keys: $token"); + like($token, qr/^[a-zA-Z](\w|::)+$/a, "$token: legal token"); + + my @keys = sort keys %{$benchmarks{$token}}; + my @invalid = grep !exists $valid_keys{$_}, @keys; + ok(!@invalid, "$token: only valid keys present") + or diag("saw these invalid keys: (@invalid)"); + + my @missing = grep !exists $benchmarks{$token}{$_}, @required_keys; + ok(!@missing, "$token: all required keys present") + or diag("these keys are missing: (@missing)"); } # check that each bit of code compiles and runs for my $token (sort keys %benchmarks) { my $b = $benchmarks{$token}; - my $code = "package $token; $b->{setup}; for (1..1) { $b->{code} } 1;"; + my $setup = $b->{setup} // ''; + my $pre = $b->{pre} // ''; + my $post = $b->{post} // ''; + my $code = "package $token; $setup; for (1..1) { $pre; $b->{code}; $post; } 1;"; no warnings; no strict; ok(eval $code, "running $token") diff --git a/t/porting/bench.t b/t/porting/bench.t index a28a434d10..ee4c1c37ba 100644 --- a/t/porting/bench.t +++ b/t/porting/bench.t @@ -81,6 +81,15 @@ my %format_qrs; . ($l + 1) . ",}-)" }ge; + + # convert run of space chars into ' +' or ' *' + + $f =~ s/(\A|\n)(\\ )+/$1 */g; + $f =~ s/(\\ )+/ +/g; + + # convert '---' placeholders into a regex + $f =~ s/(\\-){2,}/-+/g; + $format_qrs{$name} = qr/\A$f\z/; } } @@ -159,14 +168,42 @@ for my $test ( "Error: can't load 't/porting/bench/ret0': code didn't return a true value\n", "croak: --benchfile which returns 0" ], + [ + "--benchfile=t/porting/bench/oddentry perl", + qr{\AError: 't/porting/bench/oddentry' does not contain evenly paired test names and hashes\n}, + "croak: --benchfile with odd number of entries" + ], + [ + "--benchfile=t/porting/bench/badname perl", + qr{\AError: 't/porting/bench/badname': invalid test name: '1='\n}, + "croak: --benchfile with invalid test name" + ], + [ + "--benchfile=t/porting/bench/badhash perl", + qr{\AError: 't/porting/bench/badhash': invalid key 'blah' for test 'foo::bar'\n}, + "croak: --benchfile with invalid test hash key" + ], [ "--norm=2 ./miniperl ./perl", "Error: --norm value 2 outside range 0..1\n", "croak: select-a-perl out of range" ], + [ + "--norm=-0 ./miniperl ./perl", + "Error: --norm value -0 outside range -1..-2\n", + "croak: select-a-perl out of range" + ], + [ + "--norm=-3 ./miniperl ./perl", + "Error: --norm value -3 outside range -1..-2\n", + "croak: select-a-perl out of range" + ], [ "--sort=Ir:myperl ./miniperl ./perl", - "Error: --sort: unrecognised perl 'myperl'\n", + "Error: --sort: unrecognised perl 'myperl'\n" + . "Valid perl names are:\n" + . " ./miniperl\n" + . " ./perl\n", "croak: select-a-perl unrecognised" ], [ @@ -231,7 +268,7 @@ for my $test ( ], [ "--grindargs=Boz --debug --tests=call::sub::empty ./perl=A ./perl=B", - qr{Error: while executing call::sub::empty/A empty/short loop:\nunexpected code or cachegrind output:\n}, + qr{Error: .*?(unexpected code or cachegrind output|gave return status)}s, "croak: cachegrind output format " ], [ @@ -415,6 +452,11 @@ like $out, $format_qrs{percent2}, "basic cachegrind percent format; 2 perls"; $out = qx($bench_cmd --read=$resultfile2 --norm=0 2>&1); like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm; 2 perls"; +# ditto with negative norm + +$out = qx($bench_cmd --read=$resultfile2 --norm=-2 2>&1); +like $out, $format_qrs{percent2}, "basic cachegrind percent format, norm -2; 2 perls"; + # read back the results in relative-percent form with sort $out = qx($bench_cmd --read=$resultfile2 --sort=Ir:0 2>&1); @@ -517,7 +559,7 @@ EOF $cmd =~ s/\n\s+/ /g; $out = qx($cmd); $out =~ s{^\./perl}{p0}m; -$out =~ s{\Q ./perl perl2 0 1} +$out =~ s{\Q ./perl perl2 p-0 p-1} { p0 p1 p2 p3}; like $out, $format_qrs{percent4}, "4 perls with autolabel and args and env"; @@ -528,12 +570,21 @@ done_testing(); # Templates for expected output formats. # # Lines starting with '#' are skipped. +# # Lines of the form 'FORMAT: foo' start and name a new template +# # All other lines are part of the template +# # Entries of the form NNNN.NN are converted into a regex of the form # ( \s* -? \d+\.\d\d | - ) # i.e. it expects number with a fixed number of digits after the point, # or a '-'. +# +# Any runs of space chars (but not tab) are converted into ' +', +# or ' *' if at the start of a line +# +# Entries of the form --- are converted into [-]+ +# # Lines of the form %%FOO%% are substituted with format 'FOO' @@ -732,7 +783,7 @@ Results for p1 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ - NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN call::sub::empty + NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN NNN.NN call::sub::empty function call with no args or body # =================================================================== FORMAT: compact_fields %%STD_HEADER%% @@ -746,7 +797,7 @@ Results for p1 Ir Dr ------ ------ - NNN.NN NNN.NN call::sub::empty + NNN.NN NNN.NN call::sub::empty function call with no args or body # =================================================================== FORMAT: 1field %%STD_HEADER%% @@ -756,7 +807,7 @@ p0 at 100.0%. Higher is better: for example, using half as many instructions gives 200%, while using twice as many gives 50%. -Results for field Ir. +Results for field Ir p0 p1 ------ ------ @@ -800,5 +851,5 @@ Results for p0 Ir Dr Dw COND IND COND_m IND_m Ir_m1 Dr_m1 Dw_m1 Ir_mm Dr_mm Dw_mm ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ - NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N call::sub::empty + NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N NNNNN.N call::sub::empty function call with no args or body # =================================================================== diff --git a/t/porting/bench/badhash b/t/porting/bench/badhash new file mode 100644 index 0000000000..38dd473392 --- /dev/null +++ b/t/porting/bench/badhash @@ -0,0 +1,15 @@ +#!perl +# for the use of t/porting/bench.pl. +# +# A file to load which has an invalid hash key + + +[ + 'foo::bar' => { + desc => 'my $x = "abc"', + setup => '', + code => 'my $x = "abc"', + blah => 1, + }, +]; + diff --git a/t/porting/bench/badname b/t/porting/bench/badname new file mode 100644 index 0000000000..f3de2d8ae4 --- /dev/null +++ b/t/porting/bench/badname @@ -0,0 +1,14 @@ +#!perl +# for the use of t/porting/bench.pl. +# +# A file to load which has an invalid test name + + +[ + '1=' => { + desc => 'my $x = "abc"', + setup => '', + code => 'my $x = "abc"', + }, +]; + diff --git a/t/porting/bench/oddentry b/t/porting/bench/oddentry new file mode 100644 index 0000000000..baca03ae17 --- /dev/null +++ b/t/porting/bench/oddentry @@ -0,0 +1,10 @@ +#!perl +# for the use of t/porting/bench.pl. +# +# A file to load which has an odd number of test/hash pairs + + +[ + 'foo::bar', +]; + -- Perl5 Master Repository