diff --git a/opcode.h b/opcode.h index 8f040e1813c9..d824e17db15c 100644 --- a/opcode.h +++ b/opcode.h @@ -1869,8 +1869,26 @@ INIT({ Perl_ck_null, /* methstart */ Perl_ck_null, /* initfield */ Perl_ck_classname, /* classname */ + +/* The final entries are function pointers not attached to an opcode. + * These are used to compare with function pointers in the earlier part of + * the array, since in some platforms (notably z/OS), it is undefined + * behavior to compare function pointers for equality, even though calling + * them will invoke the same function. This minimizes the risk of them + * being different by initializing them to the same values in the same + * array. */ + Perl_ck_null, + Perl_ck_exists, + Perl_ck_delete, }); +/* Indexes into PL_check for the comparison function pointers */ +#ifdef PERL_IN_PEEP_C + #define PERL_CK_NULL 426 + #define PERL_CK_EXISTS 427 + #define PERL_CK_DELETE 428 +#endif + EXTCONST U32 PL_opargs[] INIT({ 0x00000000, /* null */ 0x00000000, /* stub */ diff --git a/peep.c b/peep.c index 5980ea1c2fca..a73deff1cf76 100644 --- a/peep.c +++ b/peep.c @@ -2203,16 +2203,20 @@ S_maybe_multideref(pTHX_ OP *start, OP *orig_o, UV orig_action, U8 hints) * OP_EXISTS or OP_DELETE */ /* if a custom array/hash access checker is in scope, - * abandon optimisation attempt */ + * abandon optimisation attempt. Check two different ways because + * of z/OS (see comments in opcode.h) */ if ( (o->op_type == OP_AELEM || o->op_type == OP_HELEM) - && PL_check[o->op_type] != Perl_ck_null) + && UNLIKELY(PL_check[o->op_type] != Perl_ck_null) + && UNLIKELY(PL_check[o->op_type] != PL_check[PERL_CK_NULL])) return; /* similarly for customised exists and delete */ if ( (o->op_type == OP_EXISTS) - && PL_check[o->op_type] != Perl_ck_exists) + && UNLIKELY(PL_check[OP_EXISTS] != Perl_ck_exists) + && UNLIKELY(PL_check[OP_EXISTS] != PL_check[PERL_CK_EXISTS])) return; if ( (o->op_type == OP_DELETE) - && PL_check[o->op_type] != Perl_ck_delete) + && UNLIKELY(PL_check[OP_DELETE] != Perl_ck_delete) + && UNLIKELY(PL_check[OP_DELETE] != PL_check[PERL_CK_DELETE])) return; if ( o->op_type != OP_AELEM diff --git a/regen/opcode.pl b/regen/opcode.pl index d59ed7c7d73f..e0301a3cc062 100755 --- a/regen/opcode.pl +++ b/regen/opcode.pl @@ -1114,13 +1114,39 @@ sub generate_opcode_h_pl_check { INIT({ END + for (@ops) { print "\t", tab(3, "Perl_$check{$_},"), "\t/* $_ */\n"; } + print <<~'END'; + + /* The final entries are function pointers not attached to an opcode. + * These are used to compare with function pointers in the earlier part of + * the array, since in some platforms (notably z/OS), it is undefined + * behavior to compare function pointers for equality, even though calling + * them will invoke the same function. This minimizes the risk of them + * being different by initializing them to the same values in the same + * array. */ + END + my @perl_internal_extras = qw(ck_null ck_exists ck_delete); + for (@perl_internal_extras) { + print "\t", tab(3, "Perl_$_,\n"); + } + print <<~'END'; }); + + /* Indexes into PL_check for the comparison function pointers */ + #ifdef PERL_IN_PEEP_C END + + for (my $i = 0; $i < @perl_internal_extras; $i++) { + my $index = @ops + $i; + my $define = uc $perl_internal_extras[$i]; + print " #define PERL_$define $index\n"; + } + print "#endif\n"; } sub generate_opcode_h_pl_opargs {