addbits('aelem',
7 => qw(OPpLVAL_INTRO LVINTRO),
- '5..6' => {
+ 6 => qw(OPpLVAL_DEFER LVDEFER),
+ '4..5' => {
mask_def => 'OPpDEREF',
enum => [ qw(
1 OPpDEREF_AV DREFAV
3 OPpDEREF_SV DREFSV
)],
},
- 4 => qw(OPpLVAL_DEFER LVDEFER),
);
-Here for the op C<aelem>, bits 4 and 7 (bits are numbered 0..7) are
+Here for the op C<aelem>, bits 6 and 7 (bits are numbered 0..7) are
defined as single-bit flags. The first string following the bit number is
the define name that gets emitted in F<opcode.h>, and the second string is
the label, which will be displayed by F<Concise.pm> and Perl_do_op_dump()
# find which ops use 0,1,2,3 or 4 bits of op_private for arg count info
- $args0{$_} = 1 for qw(entersub); # UNOPs that usurp bit 0
+ $args0{$_} = 1 for qw(entersub avhvswitch
+ rv2hv); # UNOPs that usurp bit 0
$args1{$_} = 1 for (
qw(reverse), # ck_fun(), but most bits stolen
+ qw(mapstart grepstart), # set in ck_fun, but
+ # cleared in ck_grep,
+ # unless there is an error
grep !$maxarg{$_} && !$args0{$_},
ops_with_flag('1'), # UNOP
+ ops_with_flag('+'), # UNOP_AUX
ops_with_flag('%'), # BASEOP/UNOP
ops_with_flag('|'), # LOGOP
ops_with_flag('-'), # FILESTATOP
ops_with_flag('}'), # LOOPEXOP
+ ops_with_flag('.'), # METHOP
);
$args2{$_} = 1 for (
qw(vec),
grep !$maxarg{$_} && !$args0{$_} && !$args1{$_},
ops_with_flag('2'), # BINOP
- # this is a binop, but special-cased as a
- # baseop in regen/opcodes
- 'sassign',
);
$args3{$_} = 1 for grep !$maxarg{$_} && !$args0{$_}
ops_with_check('ck_lfun'),
ops_with_check('ck_open'),
ops_with_check('ck_select'),
+ ops_with_check('ck_stringify'),
ops_with_check('ck_tell'),
ops_with_check('ck_trunc'),
;
for (qw(nextstate dbstate)) {
addbits($_,
5 => qw(OPpHUSH_VMSISH HUSH),
- # should match HINT_M_VMSISH_STATUS, HINT_M_VMSISH_TIME
- 6 => qw(OPpHINT_M_VMSISH_STATUS VMSISH_STATUS),
- 7 => qw(OPpHINT_M_VMSISH_TIME VMSISH_TIME),
-
);
}
# my $x
addbits($_, 7 => qw(OPpLVAL_INTRO LVINTRO))
- for qw(gvsv rv2sv rv2hv rv2gv rv2av aelem helem aslice
+ for qw(gvsv rv2sv rv2hv rv2gv rv2av aelem helem aslice split
hslice delete padsv padav padhv enteriter entersub padrange
- pushmark cond_expr),
+ pushmark cond_expr refassign lvref lvrefslice lvavref multideref
+ multiconcat),
'list', # this gets set in my_attrs() for some reason
;
# Safe to set if the ppcode uses:
# tryAMAGICbin, tryAMAGICun, SETn, SETi, SETu, PUSHn, PUSHTARG, SETTARG,
# SETs(TARG), XPUSHn, XPUSHu,
+# but make sure set-magic is invoked separately for SETs(TARG) (or change
+# it to SETTARG).
#
# Unsafe to set if the ppcode uses dTARG or [X]RETPUSH[YES|NO|UNDEF]
#
-# lt and friends do SETs (including ncmp, but not scmp)
+# Only the code paths that handle scalar rvalue context matter. If dTARG
+# or RETPUSHNO occurs only in list or lvalue paths, T is safe.
+#
+# lt and friends do SETs (including ncmp, but not scmp or i_ncmp)
#
# Additional mode of failure: the opcode can modify TARG before it "used"
# all the arguments (or may call an external function which does the same).
# If the target coincides with one of the arguments ==> kaboom.
#
# pp.c pos substr each not OK (RETPUSHUNDEF)
-# substr vec also not OK due to LV to target (are they???)
# ref not OK (RETPUSHNO)
-# trans not OK (dTARG; TARG = sv_newmortal();)
+# trans not OK (target is used for lhs, not retval)
# ucfirst etc not OK: TMP arg processed inplace
# quotemeta not OK (unsafe when TARG == arg)
-# each repeat not OK too due to list context
-# pack split - unknown whether they are safe
+# pack - unknown whether it is safe
# sprintf: is calling do_sprintf(TARG,...) which can act on TARG
# before other args are processed.
#
# grepwhile not OK (not always setting)
# join not OK (unsafe when TARG == arg)
#
-# Suspicious wrt "additional mode of failure": concat (dealt with
-# in ck_sassign()), join (same).
+# concat - pp_concat special-cases TARG==arg to avoid
+# "additional mode of failure"
#
# pp_ctl.c
# mapwhile flip caller not OK (not always setting)
addbits($_, 4 => qw(OPpTARGET_MY TARGMY))
for ops_with_flag('T'),
- # This flag is also used to indicate matches against implicit $_,
- # where $_ is lexical; e.g. my $_; ....; /foo/
- qw(match subst trans transr);
;
-# Pattern coming in on the stack
-addbits($_, 6 => qw(OPpRUNTIME RTIME))
- for qw(match subst substcont qr pushre);
-
-
-
# autovivify: Want ref to something
for (qw(rv2gv rv2sv padsv aelem helem entersub)) {
- addbits($_, '5..6' => {
+ addbits($_, '4..5' => {
mask_def => 'OPpDEREF',
enum => [ qw(
1 OPpDEREF_AV DREFAV
# Defer creation of array/hash elem
-addbits($_, 4 => qw(OPpLVAL_DEFER LVDEFER)) for qw(aelem helem);
+addbits($_, 6 => qw(OPpLVAL_DEFER LVDEFER)) for qw(aelem helem multideref);
# XXX Concise seemed to think that OPpOUR_INTRO is used in rv2gv too,
# but I can't see it - DAPM
-addbits($_, 4 => qw(OPpOUR_INTRO OURINTR)) # Variable was in an our()
- for qw(gvsv rv2sv rv2av rv2hv enteriter);
+addbits($_, 6 => qw(OPpOUR_INTRO OURINTR)) # Variable was in an our()
+ for qw(gvsv rv2sv rv2av rv2hv enteriter split);
# We might be an lvalue to return
+# 'values' doesn't actually use this bit, but we reserve it here as
+# pp_values may call Perl_do_kv() which is shared among several ops which
+# do.
+
addbits($_, 3 => qw(OPpMAYBE_LVSUB LVSUB))
for qw(aassign rv2av rv2gv rv2hv padav padhv aelem helem aslice hslice
- av2arylen keys rkeys kvaslice kvhslice substr pos vec);
+ av2arylen keys akeys avhvswitch kvaslice kvhslice substr pos vec
+ multideref values);
-for (qw(rv2hv padhv)) {
+for (qw(rv2hv padhv ref)) {
addbits($_, # e.g. %hash in (%hash || $foo) ...
- 5 => qw(OPpTRUEBOOL BOOL), # ... in void cxt
- 6 => qw(OPpMAYBE_TRUEBOOL BOOL?), # ... cx not known till run time
+ 4 => qw(OPpMAYBE_TRUEBOOL BOOL?), # but cx not known till run time
+ 5 => qw(OPpTRUEBOOL BOOL),
+ );
+}
+for (qw(grepwhile index length padav pos rindex rv2av subst)) {
+ addbits($_,
+ 5 => qw(OPpTRUEBOOL BOOL), # if (@a) {...}
);
}
-
-addbits($_, 1 => qw(OPpHINT_STRICT_REFS STRICT)) for qw(rv2sv rv2av rv2hv rv2gv);
+addbits($_, 1 => qw(OPpHINT_STRICT_REFS STRICT))
+ for qw(rv2sv rv2av rv2hv rv2gv multideref);
-addbits($_, 4 => qw(OPpPAD_STATE STATE)) for qw(padav padhv padsv pushmark);
+# note that for refassign, this bit can mean either OPpPAD_STATE or
+# OPpOUR_INTRO depending on the type of the LH child, .e.g.
+# \our $foo = ...
+# \state $foo = ...
+addbits($_, 6 => qw(OPpPAD_STATE STATE)) for qw(padav padhv padsv lvavref
+ lvref refassign pushmark);
+
+# NB: both sassign and aassign use the 'OPpASSIGN' naming convention
+# for their private flags
+
+# there *may* be common scalar items on both sides of a list assign:
+# run-time checking will be needed.
+addbits('aassign', 6 => qw(OPpASSIGN_COMMON_SCALAR COM_SCALAR));
+#
+# as above, but it's possible to check for non-commonality with just
+# a SvREFCNT(lhs) == 1 test for each lhs element
+addbits('aassign', 5 => qw(OPpASSIGN_COMMON_RC1 COM_RC1));
+# run-time checking is required for an aggregate on the LHS
+addbits('aassign', 4 => qw(OPpASSIGN_COMMON_AGG COM_AGG));
-addbits('aassign', 6 => qw(OPpASSIGN_COMMON COMMON));
+addbits('aassign', 2 => qw(OPpASSIGN_TRUEBOOL BOOL)); # if (@a = (...)) {...}
+# NB: both sassign and aassign use the 'OPpASSIGN' naming convention
+# for their private flags
addbits('sassign',
6 => qw(OPpASSIGN_BACKWARDS BKWARD), # Left & right switched
for (qw(trans transr)) {
addbits($_,
- 0 => qw(OPpTRANS_FROM_UTF <UTF),
- 1 => qw(OPpTRANS_TO_UTF >UTF),
+ 0 => qw(OPpTRANS_FROM_UTF <UTF), # search chars are utf8
+ 1 => qw(OPpTRANS_TO_UTF >UTF), # replacement chars are utf8
2 => qw(OPpTRANS_IDENTICAL IDENT), # right side is same as left
- 3 => qw(OPpTRANS_SQUASH SQUASH),
+ 3 => qw(OPpTRANS_SQUASH SQUASH), # /s
# 4 is used for OPpTARGET_MY
- 5 => qw(OPpTRANS_COMPLEMENT COMPL),
- 6 => qw(OPpTRANS_GROWS GROWS),
- 7 => qw(OPpTRANS_DELETE DEL),
+ 5 => qw(OPpTRANS_COMPLEMENT COMPL), # /c
+ 6 => qw(OPpTRANS_GROWS GROWS), # replacement chars longer than
+ # src chars
+ 7 => qw(OPpTRANS_DELETE DEL), # /d
);
}
# 1 HINT_STRICT_REFS check HINT_STRICT_REFS check
# 2 OPpENTERSUB_HASTARG checki OPpENTERSUB_HASTARG
# 3 OPpENTERSUB_AMPER check OPpENTERSUB_AMPER parser
-# 4 OPpENTERSUB_DB check OPpENTERSUB_DB
-# 5 OPpDEREF_AV context
-# 6 OPpDEREF_HV context OPpMAY_RETURN_CONSTANT parser/context
+# 4 OPpDEREF_AV context
+# 5 OPpDEREF_HV context OPpMAY_RETURN_CONSTANT parser/context
+# 6 OPpENTERSUB_DB check OPpENTERSUB_DB
# 7 OPpLVAL_INTRO context OPpENTERSUB_NOPAREN parser
# NB: OPpHINT_STRICT_REFS must equal HINT_STRICT_REFS
1 => qw(OPpHINT_STRICT_REFS STRICT), # 'use strict' in scope
2 => qw(OPpENTERSUB_HASTARG TARG ), # Called from OP tree
3 => qw(OPpENTERSUB_AMPER AMPER), # Used & form to call
- 4 => qw(OPpENTERSUB_DB DBG ), # Debug subroutine
- # 5..6 => OPpDEREF, already defined above
+ # 4..5 => OPpDEREF, already defined above
+ 6 => qw(OPpENTERSUB_DB DBG ), # Debug subroutine
# 7 => OPpLVAL_INTRO, already defined above
);
1 => qw(OPpHINT_STRICT_REFS STRICT), # 'use strict' in scope
2 => qw(OPpENTERSUB_HASTARG TARG ), # If const sub, return the const
3 => qw(OPpENTERSUB_AMPER AMPER ), # Used & form to call
- 4 => qw(OPpENTERSUB_DB DBG ), # Debug subroutine
- 6 => qw(OPpMAY_RETURN_CONSTANT CONST ),
+ 5 => qw(OPpMAY_RETURN_CONSTANT CONST ),
+ 6 => qw(OPpENTERSUB_DB DBG ), # Debug subroutine
7 => qw(OPpENTERSUB_NOPAREN NO() ), # bare sub call (without parens)
);
addbits('padrange',
# bits 0..6 hold target range
'0..6' => {
- label => '-',
+ label => 'range',
mask_def => 'OPpPADRANGE_COUNTMASK',
bitcount_def => 'OPpPADRANGE_COUNTSHIFT',
}
for (qw(aelemfast aelemfast_lex)) {
addbits($_,
'0..7' => {
- label => '-',
+ label => 'key',
}
);
}
2 => qw(OPpDONT_INIT_GV NOINIT), # Call gv_fetchpv with GV_NOINIT
# (Therefore will return whatever is currently in
# the symbol table, not guaranteed to be a PVGV)
- 4 => qw(OPpALLOW_FAKE FAKE), # OK to return fake glob
+ 6 => qw(OPpALLOW_FAKE FAKE), # OK to return fake glob
);
+# NB OPpITER_REVERSED must always be bit 1: see pp_iter()
addbits('enteriter',
- 2 => qw(OPpITER_REVERSED REVERSED),# for (reverse ...)
- 3 => qw(OPpITER_DEF DEF), # 'for $_' or 'for my $_'
+ 1 => qw(OPpITER_REVERSED REVERSED),# for (reverse ...)
+ 3 => qw(OPpITER_DEF DEF), # 'for $_'
);
-addbits('iter', 2 => qw(OPpITER_REVERSED REVERSED));
+addbits('iter', 1 => qw(OPpITER_REVERSED REVERSED));
-# Operating on a list of keys
-addbits('delete', 6 => qw(OPpSLICE SLICE));
-# also 7 => OPpLVAL_INTRO, already defined above
+addbits('delete',
+ 5 => qw(OPpKVSLICE KVSLICE), # Operating on a list of key/value pairs
+ 6 => qw(OPpSLICE SLICE ), # Operating on a list of keys
+ #7 => OPpLVAL_INTRO, already defined above
+);
2 => qw(OPpSORT_REVERSE REV ), # Reversed sort
3 => qw(OPpSORT_INPLACE INPLACE), # sort in-place; eg @a = sort @a
4 => qw(OPpSORT_DESCEND DESC ), # Descending sort
- 5 => qw(OPpSORT_QSORT QSORT ), # Use quicksort (not mergesort)
6 => qw(OPpSORT_STABLE STABLE ), # Use a stable algorithm
+ 7 => qw(OPpSORT_UNSTABLE UNSTABLE),# Use an unstable algorithm
);
-addbits($_, 1 => qw(OPpGREP_LEX GREPLEX)) # iterate over lexical $_
- for qw(mapwhile mapstart grepwhile grepstart);
-
-
-
addbits('entereval',
1 => qw(OPpEVAL_HAS_HH HAS_HH ), # Does it have a copy of %^H ?
2 => qw(OPpEVAL_UNICODE UNI ),
-addbits('split', 7 => qw(OPpSPLIT_IMPLIM IMPLIM)); # implicit limit
+addbits('split',
+ # @a = split() has been replaced with split() where split itself
+ # does the array assign
+ 4 => qw(OPpSPLIT_ASSIGN ASSIGN),
+ 3 => qw(OPpSPLIT_LEX LEX), # the OPpSPLIT_ASSIGN is a lexical array
+ 2 => qw(OPpSPLIT_IMPLIM IMPLIM), # implicit limit
+);
+
+
+addbits($_,
+ 2 => qw(OPpLVREF_ELEM ELEM ),
+ 3 => qw(OPpLVREF_ITER ITER ),
+'4..5'=> {
+ mask_def => 'OPpLVREF_TYPE',
+ enum => [ qw(
+ 0 OPpLVREF_SV SV
+ 1 OPpLVREF_AV AV
+ 2 OPpLVREF_HV HV
+ 3 OPpLVREF_CV CV
+ )],
+ },
+ #6 => qw(OPpPAD_STATE STATE),
+ #7 => qw(OPpLVAL_INTRO LVINTRO),
+) for 'refassign', 'lvref';
+
+
+
+addbits('multideref',
+ 4 => qw(OPpMULTIDEREF_EXISTS EXISTS), # deref is actually exists
+ 5 => qw(OPpMULTIDEREF_DELETE DELETE), # deref is actually delete
+);
+
+
+
+addbits('avhvswitch',
+ '0..1' => {
+ mask_def => 'OPpAVHVSWITCH_MASK',
+ label => 'offset',
+ }
+);
+
+
+addbits('argelem',
+ '1..2' => {
+ mask_def => 'OPpARGELEM_MASK',
+ enum => [ qw(
+ 0 OPpARGELEM_SV SV
+ 1 OPpARGELEM_AV AV
+ 2 OPpARGELEM_HV HV
+ )],
+ },
+);
+
+
+# rv2hv and padhv in void/scalar context implementing 'keys %h'
+# directly, without a following OP_KEYS
+
+addbits('padhv',
+ 0 => qw(OPpPADHV_ISKEYS KEYS),
+);
+addbits('rv2hv',
+ 0 => qw(OPpRV2HV_ISKEYS KEYS),
+);
+
+# In conjunction with OPpTRUEBOOL, indicates that the test should be
+# inverted. This allows both (index() == -1) and (index() != -1)
+# to optimise away the const and eq/ne
+
+for (qw(index rindex)) {
+ addbits($_, 6 => qw(OPpINDEX_BOOLNEG NEG));
+}
+
+
+addbits('concat',
+ # OPf_STACKED normally indicates .=; but it also gets set to optimise
+ # $a . $b . $c into ($a . $b) .= $c
+ # so that the first concat's PADTMP (which holds the result of $a.$b)
+ # can be reused. Set a flag in this case to help deparse and warn
+ # distinguish the cases.
+ 6 => qw(OPpCONCAT_NESTED NESTED),
+);
+
+
+addbits('multiconcat',
+ # 7 OPpLVAL_INTRO
+ 6 => qw(OPpMULTICONCAT_APPEND APPEND), # $x .= ....
+ 5 => qw(OPpMULTICONCAT_FAKE FAKE), # sprintf() optimised to MC.
+ # 4 OPpTARGET_MY
+ 3 => qw(OPpMULTICONCAT_STRINGIFY STRINGIFY), # "$a$b..."
+);
+
+
1;