Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Documentation for inlining of built-ins?

Tags:

perl

I ran into a situation where I can't inhibit warnings in an intuitive way because perl is in-lining a call to a built-in function. e.g.

use strict;
use warnings;

{
    no warnings 'substr';      # no effect
    foo(substr('123', 4, 6));  # out of range but shouldn't emit a warning
}

sub foo {
    my $s = shift;  # warning reported here
    # do something
}

Running this code results in

substr outside of string at c:\temp\foo.pl line 10.

In order to inhibit the warning I have to move the no warnings 'substr' inside the function.

sub foo {
    no warnings 'substr'; # works here, but there's no call to substr
    my $s = shift;        # no warnings here
    # do something
}

I can see that the call to substr is being inlined by passing the code through perl -MO=Terse

LISTOP (0x27dcaa8) leave [1]
    OP (0x27a402c) enter
    COP (0x27dcac8) nextstate
    BINOP (0x27dcb00) leaveloop
        LOOP (0x27dcb20) enterloop
        LISTOP (0x27dcb68) lineseq
            COP (0x27dcb88) nextstate
            UNOP (0x27dcbc0) entersub [5]          # entry point for foo
                UNOP (0x27dcbf4) null [148]
                    OP (0x27dcbdc) pushmark
                    LISTOP (0x27dcc48) substr [4]  # substr gets called here
                        OP (0x27dcc30) null [3]
                        SVOP (0x27dcc84) const [6] PV (0x2319944) "123"
                        SVOP (0x27dcc68) const [7] IV (0x2319904) 4
                        SVOP (0x27dcc14) const [8] IV (0x231944c) 6
                    UNOP (0x27dcca0) null [17]
                        PADOP (0x27dccf4) gv  GV (0x2318e5c) *foo

Is this optimizer behavior documented anywhere? perlsub only mentions inlining of constant functions. Given that the warning is being reported on the wrong line and that no warnings isn't working in the lexical scope where the call is being made I'm inclined to report this as a bug, although I can't think of how it could reasonably be fixed while preserving the optimization.

Note: This behavior was observed under Perl 5.16.1.

like image 481
Michael Carman Avatar asked Jan 05 '15 17:01

Michael Carman


2 Answers

This is a documented behaviour (in perldiag):

substr outside of string

(W substr),(F) You tried to reference a substr() that pointed outside of a string. That is, the absolute value of the offset was larger than the length of the string. See "substr" in perlfunc. This warning is fatal if substr is used in an lvalue context (as the left hand side of an assignment or as a subroutine argument for example).

Emphasis mine.

Changing the call to

foo(my $o = substr('123', 4, 6));

makes the warnings disappear.

Moving the no warnings into the sub doesn't change the behaviour for me. What Perl version do you have? (5.14.4 here).

The code I used for testing:

#!/usr/bin/perl
use strict;
use warnings;

$| = 1;

print 1, foo(my $s1 = substr('abc', 4, 6));
print 2, bar(my $s2 = substr('def', 4, 6));

{
    no warnings 'substr';
    print 3, foo(my $s3 = substr('ghi', 4, 6));
    print 4, bar(my $s4 = substr('jkl', 4, 6));
    print 5, bar(substr('mno', 4, 6)); # Stops here, reports line 12.
    print 6, foo(substr('pqr', 4, 6));
}
print "ok\n";

sub foo {
    my $s = shift;
}

sub bar {
    no warnings 'substr';
    my $s = shift;
}

Update:

I'm getting the same behaviour in 5.10.1, but in 5.20.1, the behaviour is as you described.

like image 70
choroba Avatar answered Oct 22 '22 07:10

choroba


As you saw from B::Terse, the substr is not inlined.

$ perl -MO=Concise,-exec -e'f(substr($_, 3, 4))'
1  <0> enter
2  <;> nextstate(main 1 -e:1) v:{
3  <0> pushmark s
4  <#> gvsv[*_] s
5  <$> const[IV 3] s
6  <$> const[IV 4] s
7  <@> substr[t4] sKM/3        <-- The substr operator is evaluated first.
8  <#> gv[*f] s/EARLYCV
9  <1> entersub[t5] vKS/TARG   <-- The sub call second.
a  <@> leave[1 ref] vKP/REFC
-e syntax OK

When substr is called as an lvalue context, substr returns a magical scalar that contains the operands passed to substr.

$ perl -MDevel::Peek -e'$_ = "abcdef"; Dump(${\ substr($_, 3, 4) })'
SV = PVLV(0x2865d60) at 0x283fbd8
  REFCNT = 2
  FLAGS = (GMG,SMG)          <--- Gets and sets are magical.
  IV = 0                          GMG: A function that mods the scalar
  NV = 0                               is called before fetches.
  PV = 0                          SMG: A function is called after the
  MAGIC = 0x2856810                    scalar is modified.
    MG_VIRTUAL = &PL_vtbl_substr
    MG_TYPE = PERL_MAGIC_substr(x)
  TYPE = x
  TARGOFF = 3                      <--- substr's second arg
  TARGLEN = 4                      <--- substr's third arg
  TARG = 0x287bfd0                 <--- substr's first arg
  FLAGS = 0              
  SV = PV(0x28407f0) at 0x287bfd0  <--- A dump of substr's first arg
    REFCNT = 2
    FLAGS = (POK,IsCOW,pPOK)
    PV = 0x2865d20 "abcdef"\0
    CUR = 6
    LEN = 10
    COW_REFCNT = 1

Subroutine arguments are evaluated in lvalue context because subroutine arguments are always passed by reference in Perl[1].

$ perl -E'sub f { $_[0] = "def"; }  $x = "abc"; f($x); say $x;'
def

The substring operation happens when the magical scalar is accessed.

$ perl -E'$x = "abc"; $r = \substr($x, 0, 1); $x = "def"; say $$r;'
d

This is done to allow substr(...) = "abc";


  1. This is probably documented using language similar to the following: "The elements of @_ are aliased to the subroutine arguments."
like image 45
ikegami Avatar answered Oct 22 '22 08:10

ikegami