mirror of
https://github.com/bdnugget/php-src.git
synced 2026-11-08 09:47:30 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15d2dda909 | ||
|
|
6726111972 |
No files matched your search
@@ -1,60 +0,0 @@
|
||||
version: "{branch}.build.{build}"
|
||||
|
||||
image: Visual Studio 2019
|
||||
|
||||
branches:
|
||||
except:
|
||||
- PHP-5.6
|
||||
- PHP-7.0
|
||||
|
||||
clone_depth: 64
|
||||
|
||||
skip_commits:
|
||||
files:
|
||||
- docs/*
|
||||
- NEWS
|
||||
- UPGRADING
|
||||
- UPGRADING.INTERNALS
|
||||
|
||||
cache:
|
||||
- c:\build-cache
|
||||
|
||||
environment:
|
||||
PHP_BUILD_CACHE_BASE_DIR: c:\build-cache
|
||||
PHP_BUILD_OBJ_DIR: c:\obj
|
||||
PHP_BUILD_CACHE_SDK_DIR: c:\build-cache\sdk
|
||||
PHP_BUILD_SDK_BRANCH: php-sdk-2.2.0
|
||||
PHP_BUILD_CRT: vs16
|
||||
# ext and env setup for tests
|
||||
#MYSQL_TEST_PASSWD: Password12!
|
||||
#MYSQL_TEST_USER: root
|
||||
#PDO_MYSQL_TEST_DSN: "pgsql:host=127.0.0.1 port=5432 dbname=test user=root password=Password12!"
|
||||
#PDO_MYSQL_TEST_USER: root
|
||||
#PDO_MYSQL_TEST_PASS: Password12!
|
||||
#PGSQL_TEST_CONNSTR: "host=127.0.0.1 dbname=test port=5432 user=postgres password=Password12!"
|
||||
#PDO_PGSQL_TEST_DSN: "pgsql:host=127.0.0.1 port=5432 dbname=test user=postgres password=Password12!"
|
||||
#build permutations
|
||||
matrix:
|
||||
- THREAD_SAFE: 0
|
||||
OPCACHE: 0
|
||||
PARALLEL: -j2
|
||||
- THREAD_SAFE: 1
|
||||
OPCACHE: 1
|
||||
PARALLEL: -j2
|
||||
INTRINSICS: AVX
|
||||
|
||||
services:
|
||||
# the setup scripts have to be touched, once some other db version is used
|
||||
- mysql
|
||||
- postgresql101
|
||||
- mssql2017
|
||||
|
||||
platform:
|
||||
- x64
|
||||
# - x86
|
||||
|
||||
build_script:
|
||||
- appveyor\build.bat
|
||||
|
||||
test_script:
|
||||
- appveyor\test.bat
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
ARCH: amd64
|
||||
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0
|
||||
|
||||
task:
|
||||
name: FREEBSD_DEBUG_NTS
|
||||
install_script:
|
||||
#- sed -i -e 's/quarterly/latest/g' /etc/pkg/FreeBSD.conf
|
||||
#- pkg upgrade -y
|
||||
- kldload accf_http
|
||||
- pkg install -y autoconf bison gmake re2c icu libiconv png freetype2 enchant2 bzip2 krb5 t1lib gmp tidyp libsodium libzip libxml2 libxslt openssl oniguruma pkgconf webp
|
||||
script:
|
||||
- ./buildconf -f
|
||||
- ./configure --prefix=/usr/local --enable-debug --enable-option-checking=fatal --enable-fpm --with-pdo-sqlite --without-pear --with-bz2 --with-jpeg --with-webp --with-freetype --enable-gd --enable-exif --with-zip --with-zlib --enable-soap --enable-xmlreader --with-xsl --with-libxml --enable-shmop --enable-pcntl --enable-mbstring --with-curl --enable-sockets --with-openssl --with-iconv=/usr/local --enable-bcmath --enable-calendar --enable-ftp --with-kerberos --with-ffi --enable-zend-test --enable-intl --with-mhash --with-sodium --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d
|
||||
- gmake -j2
|
||||
- mkdir /etc/php.d
|
||||
- gmake install
|
||||
- echo opcache.enable_cli=1 > /etc/php.d/opcache.ini
|
||||
- echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini
|
||||
# Specify opcache.preload_user as we're running as root.
|
||||
- echo opcache.preload_user=root >> /etc/php.d/opcache.ini
|
||||
tests_script:
|
||||
- export SKIP_IO_CAPTURE_TESTS=1
|
||||
- export CI_NO_IPV6=1
|
||||
- sapi/cli/php run-tests.php -P -q -j2 -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP --offline --show-diff --show-slow 1000 --set-timeout 120 -d zend_extension=opcache.so
|
||||
@@ -1,34 +0,0 @@
|
||||
# https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
tab_width = 4
|
||||
|
||||
[{*.{awk,bat,c,cpp,d,h,l,re,skl,w32,y},Makefile*}]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
|
||||
[*.{dtd,html,inc,php,phpt,rng,wsdl,xml,xsd,xsl}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[*.{ac,m4,sh,yml}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
|
||||
[*.patch]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -1,645 +0,0 @@
|
||||
define set_ts
|
||||
set $tsrm_ls = $arg0
|
||||
end
|
||||
|
||||
document set_ts
|
||||
set the ts resource, it is impossible for gdb to
|
||||
call ts_resource_ex while no process is running,
|
||||
but we could get the resource from the argument
|
||||
of frame info.
|
||||
end
|
||||
|
||||
define ____executor_globals
|
||||
if basic_functions_module.zts
|
||||
set $tsrm_ls = _tsrm_ls_cache
|
||||
set $eg = ((zend_executor_globals*) (*((void ***) $tsrm_ls))[executor_globals_id-1])
|
||||
set $cg = ((zend_compiler_globals*) (*((void ***) $tsrm_ls))[compiler_globals_id-1])
|
||||
set $eg_ptr = $eg
|
||||
else
|
||||
set $eg = executor_globals
|
||||
set $cg = compiler_globals
|
||||
set $eg_ptr = (zend_executor_globals*) &executor_globals
|
||||
end
|
||||
end
|
||||
|
||||
document ____executor_globals
|
||||
portable way of accessing executor_globals, set $eg
|
||||
this also sets compiler_globals to $cg
|
||||
ZTS detection is automatically based on ext/standard module struct
|
||||
end
|
||||
|
||||
define print_cvs
|
||||
if $argc == 0
|
||||
____executor_globals
|
||||
set $cv_ex_ptr = $eg.current_execute_data
|
||||
else
|
||||
set $cv_ex_ptr = (zend_execute_data *)$arg0
|
||||
end
|
||||
set $cv_count = $cv_ex_ptr.func.op_array.last_var
|
||||
set $cv = $cv_ex_ptr.func.op_array.vars
|
||||
set $cv_idx = 0
|
||||
set $callFrameSize = (sizeof(zend_execute_data) + sizeof(zval) - 1) / sizeof(zval)
|
||||
|
||||
printf "Compiled variables count: %d\n\n", $cv_count
|
||||
while $cv_idx < $cv_count
|
||||
printf "[%d] '%s'\n", $cv_idx, $cv[$cv_idx].val
|
||||
set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx
|
||||
printzv $zvalue
|
||||
set $cv_idx = $cv_idx + 1
|
||||
end
|
||||
end
|
||||
|
||||
document print_cvs
|
||||
Prints the compiled variables and their values.
|
||||
If a zend_execute_data pointer is set this will print the compiled
|
||||
variables of that scope. If no parameter is used it will use
|
||||
current_execute_data for scope.
|
||||
|
||||
usage: print_cvs [zend_execute_data *]
|
||||
end
|
||||
|
||||
define dump_bt
|
||||
set $ex = $arg0
|
||||
while $ex
|
||||
printf "[%p] ", $ex
|
||||
set $func = $ex->func
|
||||
if $func
|
||||
if $ex->This->value.obj
|
||||
if $func->common.scope
|
||||
printf "%s->", $func->common.scope->name->val
|
||||
else
|
||||
printf "%s->", $ex->This->value.obj->ce.name->val
|
||||
end
|
||||
else
|
||||
if $func->common.scope
|
||||
printf "%s::", $func->common.scope->name->val
|
||||
end
|
||||
end
|
||||
|
||||
if $func->common.function_name
|
||||
printf "%s(", $func->common.function_name->val
|
||||
else
|
||||
printf "(main"
|
||||
end
|
||||
|
||||
set $callFrameSize = (sizeof(zend_execute_data) + sizeof(zval) - 1) / sizeof(zval)
|
||||
|
||||
set $count = $ex->This.u2.num_args
|
||||
set $arg = 0
|
||||
while $arg < $count
|
||||
if $arg > 0
|
||||
printf ", "
|
||||
end
|
||||
|
||||
set $zvalue = (zval *) $ex + $callFrameSize + $arg
|
||||
set $type = $zvalue->u1.v.type
|
||||
if $type == 1
|
||||
printf "NULL"
|
||||
end
|
||||
if $type == 2
|
||||
printf "false"
|
||||
end
|
||||
if $type == 3
|
||||
printf "true"
|
||||
end
|
||||
if $type == 4
|
||||
printf "%ld", $zvalue->value.lval
|
||||
end
|
||||
if $type == 5
|
||||
printf "%f", $zvalue->value.dval
|
||||
end
|
||||
if $type == 6
|
||||
____print_str $zvalue->value.str->val $zvalue->value.str->len
|
||||
end
|
||||
if $type == 7
|
||||
printf "array(%d)[%p]", $zvalue->value.arr->nNumOfElements, $zvalue
|
||||
end
|
||||
if $type == 8
|
||||
printf "object[%p]", $zvalue
|
||||
end
|
||||
if $type == 9
|
||||
printf "resource(#%d)", $zvalue->value.lval
|
||||
end
|
||||
if $type == 10
|
||||
printf "reference"
|
||||
end
|
||||
if $type > 10
|
||||
printf "unknown type %d", $type
|
||||
end
|
||||
set $arg = $arg + 1
|
||||
end
|
||||
|
||||
printf ") "
|
||||
else
|
||||
printf "??? "
|
||||
end
|
||||
if $func != 0
|
||||
if $func->type == 2
|
||||
printf "%s:%d ", $func->op_array.filename->val, $ex->opline->lineno
|
||||
else
|
||||
printf "[internal function]"
|
||||
end
|
||||
end
|
||||
set $ex = $ex->prev_execute_data
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
|
||||
document dump_bt
|
||||
dumps the current execution stack. usage: dump_bt executor_globals.current_execute_data
|
||||
end
|
||||
|
||||
define printzv
|
||||
set $ind = 1
|
||||
____printzv $arg0 0
|
||||
end
|
||||
|
||||
document printzv
|
||||
prints zval contents
|
||||
end
|
||||
|
||||
define ____printzv_contents
|
||||
set $zvalue = $arg0
|
||||
set $type = $zvalue->u1.v.type
|
||||
|
||||
# 15 == IS_INDIRECT
|
||||
if $type > 5 && $type < 12
|
||||
printf "(refcount=%d) ", $zvalue->value.counted->gc.refcount
|
||||
end
|
||||
|
||||
if $type == 0
|
||||
printf "UNDEF"
|
||||
end
|
||||
if $type == 1
|
||||
printf "NULL"
|
||||
end
|
||||
if $type == 2
|
||||
printf "bool: false"
|
||||
end
|
||||
if $type == 3
|
||||
printf "bool: true"
|
||||
end
|
||||
if $type == 4
|
||||
printf "long: %ld", $zvalue->value.lval
|
||||
end
|
||||
if $type == 5
|
||||
printf "double: %f", $zvalue->value.dval
|
||||
end
|
||||
if $type == 6
|
||||
printf "string: %s", $zvalue->value.str->val
|
||||
end
|
||||
if $type == 7
|
||||
printf "array: "
|
||||
if ! $arg1
|
||||
set $ind = $ind + 1
|
||||
____print_ht $zvalue->value.arr 1
|
||||
set $ind = $ind - 1
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
end
|
||||
set $type = 0
|
||||
end
|
||||
if $type == 8
|
||||
printf "object"
|
||||
____executor_globals
|
||||
set $handle = $zvalue->value.obj.handle
|
||||
set $handlers = $zvalue->value.obj.handlers
|
||||
set $zobj = $zvalue->value.obj
|
||||
set $cname = $zobj->ce->name->val
|
||||
printf "(%s) #%d", $cname, $handle
|
||||
if ! $arg1
|
||||
if $handlers->get_properties == &zend_std_get_properties
|
||||
if $zobj->properties
|
||||
printf "\nProperties "
|
||||
set $ht = $zobj->properties
|
||||
set $ind = $ind + 1
|
||||
____print_ht $ht 1
|
||||
set $ind = $ind - 1
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
else
|
||||
printf " {\n"
|
||||
set $ht = &$zobj->ce->properties_info
|
||||
set $k = 0
|
||||
set $num = $ht->nNumUsed
|
||||
while $k < $num
|
||||
set $p = (Bucket*)($ht->arData + $k)
|
||||
set $name = $p->key
|
||||
set $prop = (zend_property_info*)$p->val.value.ptr
|
||||
set $val = (zval*)((char*)$zobj + $prop->offset)
|
||||
printf "%s => ", $name->val
|
||||
printzv $val
|
||||
set $k = $k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
set $type = 0
|
||||
end
|
||||
if $type == 9
|
||||
printf "resource: #%d", $zvalue->value.res->handle
|
||||
end
|
||||
if $type == 10
|
||||
printf "reference: "
|
||||
____printzv &$zvalue->value.ref->val $arg1
|
||||
end
|
||||
if $type == 11
|
||||
printf "CONSTANT_AST"
|
||||
end
|
||||
if $type == 12
|
||||
printf "indirect: "
|
||||
____printzv $zvalue->value.zv $arg1
|
||||
end
|
||||
if $type == 13
|
||||
printf "pointer: %p", $zvalue->value.ptr
|
||||
end
|
||||
if $type == 15
|
||||
printf "_ERROR"
|
||||
end
|
||||
if $type == 16
|
||||
printf "_BOOL"
|
||||
end
|
||||
if $type == 17
|
||||
printf "_NUMBER"
|
||||
end
|
||||
if $type > 17
|
||||
printf "unknown type %d", $type
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
define ____printzv
|
||||
____executor_globals
|
||||
set $zvalue = $arg0
|
||||
|
||||
printf "[%p] ", $zvalue
|
||||
|
||||
set $zcontents = (zval*) $zvalue
|
||||
if $arg1
|
||||
____printzv_contents $zcontents $arg1
|
||||
else
|
||||
____printzv_contents $zcontents 0
|
||||
end
|
||||
end
|
||||
|
||||
define print_global_vars
|
||||
____executor_globals
|
||||
set $symtable = ((HashTable *)&($eg_ptr->symbol_table))
|
||||
print_ht $symtable
|
||||
end
|
||||
|
||||
document print_global_vars
|
||||
Prints the global variables
|
||||
end
|
||||
|
||||
define print_const_table
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ht $arg0 4
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_const_table
|
||||
Dumps elements of Constants HashTable
|
||||
Example: print_const_table executor_globals.zend_constants
|
||||
end
|
||||
|
||||
define ____print_ht
|
||||
set $ht = (HashTable*)$arg0
|
||||
set $n = $ind
|
||||
while $n > 0
|
||||
printf " "
|
||||
set $n = $n - 1
|
||||
end
|
||||
|
||||
if $ht->u.v.flags & 4
|
||||
printf "Packed"
|
||||
else
|
||||
printf "Hash"
|
||||
end
|
||||
printf "(%d)[%p]: {\n", $ht->nNumOfElements, $ht
|
||||
|
||||
set $num = $ht->nNumUsed
|
||||
set $i = 0
|
||||
set $ind = $ind + 1
|
||||
while $i < $num
|
||||
set $p = (Bucket*)($ht->arData + $i)
|
||||
set $n = $ind
|
||||
if $p->val.u1.v.type > 0
|
||||
while $n > 0
|
||||
printf " "
|
||||
set $n = $n - 1
|
||||
end
|
||||
printf "[%d] ", $i
|
||||
if $p->key
|
||||
____print_str $p->key->val $p->key->len
|
||||
printf " => "
|
||||
else
|
||||
printf "%d => ", $p->h
|
||||
end
|
||||
if $arg1 == 0
|
||||
printf "%p\n", (zval *)&$p->val
|
||||
end
|
||||
if $arg1 == 1
|
||||
set $zval = (zval *)&$p->val
|
||||
____printzv $zval 1
|
||||
end
|
||||
if $arg1 == 2
|
||||
printf "%s\n", (char*)$p->val.value.ptr
|
||||
end
|
||||
if $arg1 == 3
|
||||
set $func = (zend_function*)$p->val.value.ptr
|
||||
printf "\"%s\"\n", $func->common.function_name->val
|
||||
end
|
||||
if $arg1 == 4
|
||||
set $const = (zend_constant *)$p->val.value.ptr
|
||||
____printzv $const 1
|
||||
end
|
||||
end
|
||||
set $i = $i + 1
|
||||
end
|
||||
set $ind = $ind - 1
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
define print_ht
|
||||
set $ind = 0
|
||||
____print_ht $arg0 1
|
||||
end
|
||||
|
||||
document print_ht
|
||||
dumps elements of HashTable made of zval
|
||||
end
|
||||
|
||||
define print_htptr
|
||||
set $ind = 0
|
||||
____print_ht $arg0 0
|
||||
end
|
||||
|
||||
document print_htptr
|
||||
dumps elements of HashTable made of pointers
|
||||
end
|
||||
|
||||
define print_htstr
|
||||
set $ind = 0
|
||||
____print_ht $arg0 2
|
||||
end
|
||||
|
||||
document print_htstr
|
||||
dumps elements of HashTable made of strings
|
||||
end
|
||||
|
||||
define print_ft
|
||||
set $ind = 0
|
||||
____print_ht $arg0 3
|
||||
end
|
||||
|
||||
document print_ft
|
||||
dumps a function table (HashTable)
|
||||
end
|
||||
|
||||
define ____print_inh_class
|
||||
set $ce = $arg0
|
||||
if $ce->ce_flags & 0x10 || $ce->ce_flags & 0x20
|
||||
printf "abstract "
|
||||
else
|
||||
if $ce->ce_flags & 0x40
|
||||
printf "final "
|
||||
end
|
||||
end
|
||||
printf "class %s", $ce->name->val
|
||||
if $ce->parent != 0
|
||||
printf " extends %s", $ce->parent->name->val
|
||||
end
|
||||
if $ce->num_interfaces != 0
|
||||
printf " implements"
|
||||
set $tmp = 0
|
||||
while $tmp < $ce->num_interfaces
|
||||
printf " %s", $ce->interfaces[$tmp]->name->val
|
||||
set $tmp = $tmp + 1
|
||||
if $tmp < $ce->num_interfaces
|
||||
printf ","
|
||||
end
|
||||
end
|
||||
end
|
||||
set $ce = $ce->parent
|
||||
end
|
||||
|
||||
define ____print_inh_iface
|
||||
set $ce = $arg0
|
||||
printf "interface %s", $ce->name->val
|
||||
if $ce->num_interfaces != 0
|
||||
set $ce = $ce->interfaces[0]
|
||||
printf " extends %s", $ce->name->val
|
||||
else
|
||||
set $ce = 0
|
||||
end
|
||||
end
|
||||
|
||||
define print_inh
|
||||
set $ce = $arg0
|
||||
set $depth = 0
|
||||
while $ce != 0
|
||||
set $tmp = $depth
|
||||
while $tmp != 0
|
||||
printf " "
|
||||
set $tmp = $tmp - 1
|
||||
end
|
||||
set $depth = $depth + 1
|
||||
if $ce->ce_flags & 0x80
|
||||
____print_inh_iface $ce
|
||||
else
|
||||
____print_inh_class $ce
|
||||
end
|
||||
printf " {\n"
|
||||
end
|
||||
while $depth != 0
|
||||
set $tmp = $depth
|
||||
while $tmp != 1
|
||||
printf " "
|
||||
set $tmp = $tmp - 1
|
||||
end
|
||||
printf "}\n"
|
||||
set $depth = $depth - 1
|
||||
end
|
||||
end
|
||||
|
||||
define print_pi
|
||||
set $pi = (zend_property_info *)$arg0
|
||||
set $initial_offset = ((uint32_t)(zend_uintptr_t)(&((zend_object*)0)->properties_table[(0)]))
|
||||
set $ptr_to_val = (zval*)((char*)$pi->ce->default_properties_table + $pi->offset - $initial_offset)
|
||||
printf "[%p] {\n", $pi
|
||||
printf " offset = %p\n", $pi->offset
|
||||
printf " ce = [%p] %s\n", $pi->ce, $pi->ce->name->val
|
||||
printf " flags = 0x%x (", $pi->flags
|
||||
if $pi->flags & 0x100
|
||||
printf "ZEND_ACC_PUBLIC"
|
||||
else
|
||||
if $pi->flags & 0x200
|
||||
printf "ZEND_ACC_PROTECTED"
|
||||
else
|
||||
if $pi->flags & 0x400
|
||||
printf "ZEND_ACC_PRIVATE"
|
||||
else
|
||||
if $pi->flags & 0x800
|
||||
printf "ZEND_ACC_EARLY_BINDING"
|
||||
else
|
||||
if $pi->flags & 0x20000
|
||||
printf "ZEND_ACC_SHADOW"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
printf ")\n"
|
||||
printf " name = "
|
||||
print_zstr $pi->name
|
||||
printf " default value: "
|
||||
printzv $ptr_to_val
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_pi
|
||||
Takes a pointer to an object's property and prints the property information
|
||||
usage: print_pi <ptr>
|
||||
end
|
||||
|
||||
define ____print_str
|
||||
set $tmp = 0
|
||||
set $str = $arg0
|
||||
if $argc > 2
|
||||
set $maxlen = $arg2
|
||||
else
|
||||
set $maxlen = 256
|
||||
end
|
||||
|
||||
printf "\""
|
||||
while $tmp < $arg1 && $tmp < $maxlen
|
||||
if $str[$tmp] > 31 && $str[$tmp] < 127
|
||||
printf "%c", $str[$tmp]
|
||||
else
|
||||
printf "\\%o", $str[$tmp]
|
||||
end
|
||||
set $tmp = $tmp + 1
|
||||
end
|
||||
if $tmp != $arg1
|
||||
printf "..."
|
||||
end
|
||||
printf "\""
|
||||
end
|
||||
|
||||
define printzn
|
||||
____executor_globals
|
||||
set $ind = 0
|
||||
set $znode = $arg0
|
||||
if $znode->op_type == 1
|
||||
set $optype = "IS_CONST"
|
||||
end
|
||||
if $znode->op_type == 2
|
||||
set $optype = "IS_TMP_VAR"
|
||||
end
|
||||
if $znode->op_type == 4
|
||||
set $optype = "IS_VAR"
|
||||
end
|
||||
if $znode->op_type == 8
|
||||
set $optype = "IS_UNUSED"
|
||||
end
|
||||
|
||||
printf "[%p] %s", $znode, $optype
|
||||
|
||||
if $znode->op_type == 1
|
||||
printf ": "
|
||||
____printzv &$znode->u.constant 0
|
||||
end
|
||||
if $znode->op_type == 2
|
||||
printf ": "
|
||||
set $tvar = (union _temp_variable *)((char *)$eg.current_execute_data->Ts + $znode->u.var)
|
||||
____printzv ((union _temp_variable *)$tvar)->tmp_var 0
|
||||
end
|
||||
if $znode->op_type == 4
|
||||
printf ": "
|
||||
set $tvar = (union _temp_variable *)((char *)$eg.current_execute_data->Ts + $znode->u.var)
|
||||
____printzv *$tvar->var.ptr_ptr 0
|
||||
end
|
||||
if $znode->op_type == 8
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
|
||||
document printzn
|
||||
print type and content of znode.
|
||||
usage: printzn &opline->op1
|
||||
end
|
||||
|
||||
define printzops
|
||||
printf "op1 => "
|
||||
printzn &execute_data->opline.op1
|
||||
printf "op2 => "
|
||||
printzn &execute_data->opline.op2
|
||||
printf "result => "
|
||||
printzn &execute_data->opline.result
|
||||
end
|
||||
|
||||
document printzops
|
||||
dump operands of the current opline
|
||||
end
|
||||
|
||||
define print_zstr
|
||||
set $zstr = (zend_string *)$arg0
|
||||
if $argc == 2
|
||||
set $maxlen = $arg1
|
||||
else
|
||||
set $maxlen = $zstr->len
|
||||
end
|
||||
printf "string(%d) ", $zstr->len
|
||||
____print_str $zstr->val $zstr->len $maxlen
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
document print_zstr
|
||||
print the length and contents of a zend string
|
||||
usage: print_zstr <ptr> [max length]
|
||||
end
|
||||
|
||||
define zbacktrace
|
||||
____executor_globals
|
||||
dump_bt $eg.current_execute_data
|
||||
end
|
||||
|
||||
document zbacktrace
|
||||
prints backtrace.
|
||||
This command is almost a short cut for
|
||||
> (gdb) ____executor_globals
|
||||
> (gdb) dump_bt $eg.current_execute_data
|
||||
end
|
||||
|
||||
define lookup_root
|
||||
set $found = 0
|
||||
if gc_globals->roots
|
||||
set $current = gc_globals->roots->next
|
||||
printf "looking ref %p in roots\n", $arg0
|
||||
while $current != &gc_globals->roots
|
||||
if $current->ref == $arg0
|
||||
set $found = $current
|
||||
break
|
||||
end
|
||||
set $current = $current->next
|
||||
end
|
||||
if $found != 0
|
||||
printf "found root %p\n", $found
|
||||
else
|
||||
printf "not found\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
document lookup_root
|
||||
lookup a refcounted in root
|
||||
usage: lookup_root [ptr].
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
# Replace $Id$ strings with 40-character hexadecimal blob object name.
|
||||
/ext/ext_skel.php ident
|
||||
/ext/phar/phar/pharcommand.inc ident
|
||||
/ext/dba/libinifile/inifile.c ident
|
||||
/ext/dba/libflatfile/flatfile.c ident
|
||||
/ext/dba/libcdb/cdb_make.c ident
|
||||
/ext/dba/libcdb/cdb.c ident
|
||||
/run-tests.php ident
|
||||
|
||||
# A custom merge driver to avoid automatic merges. These are manually merged.
|
||||
# See https://wiki.php.net/vcs/gitfaq
|
||||
/NEWS merge=NEWS
|
||||
/UPGRADING merge=NEWS
|
||||
/UPGRADING.INTERNALS merge=NEWS
|
||||
|
||||
# Enable commit diffs for binary PHP test files. Some PHP test files include
|
||||
# special characters, such as ASCII control characters. Git recognizes these as
|
||||
# binary and wouldn't generate diffs.
|
||||
*.phpt diff
|
||||
-288
@@ -1,288 +0,0 @@
|
||||
# These files are generated during building or development and are intentionally
|
||||
# untracked to ignore by Git. For other development environment specific files,
|
||||
# such as editor configuration, a good practice is to exclude them using the
|
||||
# .git/info/exclude in the cloned repository or a global .gitignore file.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Common files ignored across the repository
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Backup copies created by various editors or development tools
|
||||
*~
|
||||
|
||||
# Patches downloaded to patch files or to apply open source contributions
|
||||
*.patch
|
||||
|
||||
# Swap files created by editors and tools to indicate a locked file
|
||||
*.swp
|
||||
|
||||
# Various temporary generated files
|
||||
*.tmp
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by the PHP build system
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Archive library containing .o files, generated during build process
|
||||
*.a
|
||||
|
||||
# Libtool library files generated during build process
|
||||
*.la
|
||||
|
||||
# Directories created by Libtool for storing generated library files
|
||||
.libs/
|
||||
|
||||
# Library object files generated during build process
|
||||
*.lo
|
||||
|
||||
# Temporary library object files generated by compiler and libtool due to failure
|
||||
*.loT
|
||||
|
||||
# Standard object files generated during build process
|
||||
*.o
|
||||
|
||||
# Dependency files generated during build process
|
||||
*.dep
|
||||
|
||||
# Cache directories created by Autoconf tools
|
||||
autom4te.cache/
|
||||
|
||||
# Cache file(s) generated by Autoconf's configure when run as `configure -C`
|
||||
config.cache
|
||||
|
||||
# A log file(s) generated by Autoconf during *nix build system
|
||||
config.log
|
||||
|
||||
# Helper(s) generated by *nix build system to save the last configure command
|
||||
config.nice
|
||||
|
||||
# Generated by Autoconf's configure script for instantiating templates
|
||||
config.status
|
||||
|
||||
# Main *nix build system configuration script(s) generated by Autoconf
|
||||
configure
|
||||
|
||||
# Generated by `./configure` when test programs create temporary files
|
||||
confdefs.h
|
||||
conftest*
|
||||
|
||||
# Generated by configure scripts on all systems
|
||||
/main/internal_functions.c
|
||||
/main/internal_functions_cli.c
|
||||
|
||||
# The Libtool script(s) generated by the *nix build system
|
||||
libtool
|
||||
|
||||
# Makefile(s) generated from Makefile fragment templates by `./configure`
|
||||
Makefile
|
||||
Makefile.fragments
|
||||
Makefile.objects
|
||||
|
||||
# Directories for shared object files and headers generated by `./configure`
|
||||
include/
|
||||
libs/
|
||||
modules/
|
||||
|
||||
# Used by build/gen_stub.php
|
||||
build/PHP-Parser-*
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Configuration headers generated by the PHP build system
|
||||
# ------------------------------------------------------------------------------
|
||||
config.h
|
||||
config.h.in
|
||||
/ext/date/lib/timelib_config.h
|
||||
/main/build-defs.h
|
||||
/main/php_config.h.in
|
||||
/main/php_config.h
|
||||
/Zend/zend_config.h
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Manual (man 1 and 8) pages generated from templates for *nix alike systems
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/phar/phar.1
|
||||
/ext/phar/phar.phar.1
|
||||
/sapi/cgi/php-cgi.1
|
||||
/sapi/cli/php.1
|
||||
/sapi/fpm/php-fpm.1
|
||||
/sapi/fpm/php-fpm.8
|
||||
/sapi/phpdbg/phpdbg.1
|
||||
/scripts/man1/*.1
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# SAPIs specific ignores
|
||||
# ------------------------------------------------------------------------------
|
||||
/sapi/apache2handler/libphp.module
|
||||
/sapi/fpm/fpm/php-cgi
|
||||
/sapi/fpm/init.d.php-fpm
|
||||
/sapi/fpm/php-fpm.conf
|
||||
/sapi/fpm/php-fpm.service
|
||||
/sapi/fpm/status.html
|
||||
/sapi/fpm/www.conf
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Executable binaries and scripts generated during the build process
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/phar/phar.phar
|
||||
/ext/phar/phar.php
|
||||
/pear/install-pear-nozlib.phar
|
||||
/sapi/cgi/php-cgi
|
||||
/sapi/fpm/php-fpm
|
||||
/sapi/phpdbg/phpdbg
|
||||
/scripts/php-config
|
||||
/scripts/phpize
|
||||
php
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Lexer files generated by re2c
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_scanner.c
|
||||
/ext/json/php_json_scanner_defs.h
|
||||
/ext/pdo/pdo_sql_parser.c
|
||||
/ext/phar/phar_path_check.c
|
||||
/ext/standard/url_scanner_ex.c
|
||||
/ext/standard/var_unserializer.c
|
||||
/sapi/phpdbg/phpdbg_lexer.c
|
||||
/Zend/zend_ini_scanner.c
|
||||
/Zend/zend_ini_scanner_defs.h
|
||||
/Zend/zend_language_scanner.c
|
||||
/Zend/zend_language_scanner_defs.h
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PHP parser files generated by bison during the build process
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_parser.tab.h
|
||||
/ext/json/json_parser.tab.c
|
||||
/sapi/phpdbg/phpdbg_parser.c
|
||||
/sapi/phpdbg/phpdbg_parser.h
|
||||
/sapi/phpdbg/phpdbg_parser.output
|
||||
/Zend/zend_ini_parser.c
|
||||
/Zend/zend_ini_parser.h
|
||||
/Zend/zend_ini_parser.output
|
||||
/Zend/zend_language_parser.c
|
||||
/Zend/zend_language_parser.h
|
||||
/Zend/zend_language_parser.output
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Extensions files
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Miscellaneous extensions files
|
||||
/ext/opcache/jit/zend_jit_x86.c
|
||||
/ext/opcache/minilua
|
||||
|
||||
# Generated by `cd ext/name && phpize && ./configure`
|
||||
/ext/*/build/
|
||||
/ext/*/configure.ac
|
||||
/ext/*/run-tests.php
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by Windows build system
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Generated by `/buildconf.bat`
|
||||
/configure.bat
|
||||
/configure.js
|
||||
|
||||
# Generated by `/configure.(bat|js)` (architecture dependent)
|
||||
/config.nice.bat
|
||||
|
||||
# NTS debug build x86
|
||||
/Debug/
|
||||
|
||||
# ZTS debug build x86
|
||||
/Debug_TS/
|
||||
|
||||
# NTS build x86
|
||||
/Release/
|
||||
|
||||
# ZTS build x86
|
||||
/Release_TS/
|
||||
|
||||
# Generated by x64 compiler, includes Debug, Debug_TS, Release, Release_TS
|
||||
/x64/
|
||||
|
||||
# Miscellaneous files generated by Windows build system
|
||||
/main/config.w32.h
|
||||
/win32/build/deplister.exe
|
||||
/win32/build/deplister.obj
|
||||
/win32/*.aps
|
||||
/win32/*.positions
|
||||
/win32/*.suo
|
||||
/win32/ext
|
||||
/win32/syslog.reg
|
||||
/win32/wsyslog.h
|
||||
|
||||
# Standard object files generated by Visual Studio
|
||||
*.obj
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tests
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Generated by `./run-tests.php` upon failure
|
||||
**/tests/**/*.diff
|
||||
**/tests/**/*.out*
|
||||
**/tests/**/*.php
|
||||
**/tests/**/*.exp
|
||||
**/tests/**/*.log
|
||||
**/tests/**/*.sh
|
||||
|
||||
# Generated by some test cases
|
||||
**/tests/**/*.db
|
||||
|
||||
# Microsoft Access database created for passing to tests
|
||||
/ext/pdo_odbc/tests/*.mdb
|
||||
|
||||
# Files generated by `./run-tests.php -m` when checking for memory leaks
|
||||
**/tests/**/*.mem
|
||||
|
||||
# Test results generated by `./run-tests.php`
|
||||
php_test_results_*.txt
|
||||
|
||||
# Temporary test information generated by `./run-tests.php`
|
||||
/run-test-info.php
|
||||
|
||||
# Temporary POST data placeholder files generated by `./run-tests.php`
|
||||
phpt.*
|
||||
|
||||
# Temporary PHP INI configuration file(s) for tests, generated by `make test`
|
||||
tmp-php.ini
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by GCC's gcov and LCOV via build/Makefile.gcov and gcov.php.net
|
||||
# ------------------------------------------------------------------------------
|
||||
*.gcda
|
||||
*.gcno
|
||||
/gcovr.xml
|
||||
/gcovr_html/
|
||||
/lcov_html/
|
||||
/php_lcov.info
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Archives generated during the PHP release process
|
||||
# ------------------------------------------------------------------------------
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
/*.tar.xz
|
||||
/*.tar.gz.asc
|
||||
/*.tar.bz2.asc
|
||||
/*.tar.xz.asc
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by `./configure --enable-dtrace` for using DTrace framework
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/oci8/oci8_dtrace_gen.h
|
||||
/ext/oci8/oci8_dtrace_gen.h.bak
|
||||
/Zend/zend_dtrace_gen.h
|
||||
/Zend/zend_dtrace_gen.h.bak
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Special cases to invert previous ignore patterns
|
||||
# ------------------------------------------------------------------------------
|
||||
!/ext/bcmath/libbcmath/src/config.h
|
||||
!/ext/fileinfo/libmagic/config.h
|
||||
!/ext/fileinfo/libmagic.patch
|
||||
!/ext/fileinfo/magicdata.patch
|
||||
!/ext/pcre/pcre2lib/config.h
|
||||
!/win32/build/Makefile
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
git:
|
||||
quiet: true
|
||||
|
||||
dist: bionic
|
||||
language: c
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- locales
|
||||
- language-pack-de
|
||||
- re2c
|
||||
- ccache
|
||||
- mysql-server
|
||||
- libaspell-dev
|
||||
- libbz2-dev
|
||||
- libcurl4-gnutls-dev
|
||||
- libenchant-dev
|
||||
- libfreetype6-dev
|
||||
- libgmp-dev
|
||||
- libicu-dev
|
||||
- libjpeg-dev
|
||||
- libkrb5-dev
|
||||
- libonig-dev
|
||||
- libpng-dev
|
||||
- libpq-dev
|
||||
- libpspell-dev
|
||||
- libsasl2-dev
|
||||
- libsqlite3-dev
|
||||
- libsodium-dev
|
||||
- libtidy-dev
|
||||
- libwebp-dev
|
||||
- libxml2-dev
|
||||
- libxpm-dev
|
||||
- libxslt1-dev
|
||||
- libzip-dev
|
||||
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_failure: change
|
||||
irc:
|
||||
template:
|
||||
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} - Change view : %{compare_url} - Build details : %{build_url}"
|
||||
channels:
|
||||
- "irc.efnet.org#php.pecl"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
ccache: true
|
||||
|
||||
env:
|
||||
global:
|
||||
- MYSQL_TEST_HOST=127.0.0.1
|
||||
- MYSQL_TEST_USER=travis
|
||||
- PDO_MYSQL_TEST_DSN="mysql:host=127.0.0.1;dbname=test"
|
||||
- PDO_MYSQL_TEST_USER=travis
|
||||
- PDO_MYSQL_TEST_PASS=
|
||||
- PDO_MYSQL_TEST_HOST=127.0.0.1
|
||||
- PDO_PGSQL_TEST_DSN="pgsql:host=localhost port=5432 dbname=test user=postgres password="
|
||||
- REPORT_EXIT_STATUS=1
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- env: ENABLE_ZTS=1 ENABLE_DEBUG=1 SKIP_IO_CAPTURE_TESTS=1 ARM64=1
|
||||
arch: arm64-graviton2
|
||||
dist: bionic
|
||||
virt: lxd
|
||||
group: edge
|
||||
if: type = cron
|
||||
- env: ENABLE_ZTS=1 ENABLE_DEBUG=1 SKIP_IO_CAPTURE_TESTS=1 S390X=1
|
||||
arch: s390x
|
||||
if: type = cron
|
||||
|
||||
before_script:
|
||||
- ccache --version
|
||||
- ccache --zero-stats
|
||||
- export USE_CCACHE=1
|
||||
# Enable IPv6
|
||||
- sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'
|
||||
# Compile PHP
|
||||
- travis_wait ./travis/compile.sh
|
||||
# Setup Extensions
|
||||
- ./travis/setup-mysql.sh
|
||||
- ./travis/setup-pgsql.sh
|
||||
|
||||
# Run PHPs run-tests.php
|
||||
script:
|
||||
# ARM64 CI reports nproc=32, which is excessive.
|
||||
- if [ -z "$ARM64" ]; then export JOBS=$(nproc); else export JOBS=16; fi
|
||||
- ./sapi/cli/php run-tests.php -P -d extension=`pwd`/modules/zend_test.so $(if [ $ENABLE_DEBUG == 0 ]; then echo "-d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit_buffer_size=16M -d zend_extension=`pwd`/modules/opcache.so"; fi) -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --offline --show-diff --show-slow 1000 --set-timeout 120 -j$JOBS
|
||||
- sapi/cli/php -d extension_dir=`pwd`/modules -r 'dl("zend_test");'
|
||||
|
||||
after_success:
|
||||
- ccache --show-stats
|
||||
@@ -1,320 +0,0 @@
|
||||
# PHP coding standards
|
||||
|
||||
This file lists several standards that any programmer adding or changing code in
|
||||
PHP should follow. Since this file was added at a very late stage of the
|
||||
development of PHP v3.0, the code base does not fully follow it, but new
|
||||
features are going in that general direction. Many sections have been rewritten to
|
||||
use these rules.
|
||||
|
||||
## Code implementation
|
||||
|
||||
1. Document your code in source files and the manual. (tm)
|
||||
|
||||
2. Functions that are given pointers to resources should not free them.
|
||||
|
||||
For instance, `function int mail(char *to, char *from)` should NOT free to
|
||||
and/or from.
|
||||
|
||||
Exceptions:
|
||||
|
||||
* The function's designated behavior is freeing that resource. E.g.
|
||||
`efree()`
|
||||
|
||||
* The function is given a boolean argument, that controls whether or not the
|
||||
function may free its arguments (if true - the function must free its
|
||||
arguments, if false - it must not)
|
||||
|
||||
* Low-level parser routines, that are tightly integrated with the token
|
||||
cache and the bison code for minimum memory copying overhead.
|
||||
|
||||
3. Functions that are tightly integrated with other functions within the same
|
||||
module, and rely on each other non-trivial behavior, should be documented as
|
||||
such and declared `static`. They should be avoided if possible.
|
||||
|
||||
4. Use definitions and macros whenever possible, so that constants have
|
||||
meaningful names and can be easily manipulated. The only exceptions to this
|
||||
rule are 0 and 1, when used as `false` and `true` (respectively). Any other
|
||||
use of a numeric constant to specify different behavior or actions should be
|
||||
done through a `#define`.
|
||||
|
||||
5. When writing functions that deal with strings, be sure to remember that PHP
|
||||
holds the length property of each string, and that it shouldn't be
|
||||
calculated with `strlen()`. Write your functions in such a way so that
|
||||
they'll take advantage of the length property, both for efficiency and in
|
||||
order for them to be binary-safe. Functions that change strings and obtain
|
||||
their new lengths while doing so, should return that new length, so it
|
||||
doesn't have to be recalculated with `strlen()` (e.g. `php_addslashes()`).
|
||||
|
||||
6. NEVER USE `strncat()`. If you're absolutely sure you know what you're doing,
|
||||
check its man page again, and only then, consider using it, and even then,
|
||||
try avoiding it.
|
||||
|
||||
7. Use `PHP_*` macros in the PHP source, and `ZEND_*` macros in the Zend part of
|
||||
the source. Although the `PHP_*` macros are mostly aliased to the `ZEND_*`
|
||||
macros it gives a better understanding on what kind of macro you're calling.
|
||||
|
||||
8. When commenting out code using a `#if` statement, do NOT use `0` only.
|
||||
Instead use `"<git username here>_0"`. For example, `#if FOO_0`, where `FOO`
|
||||
is your git user `foo`. This allows easier tracking of why code was
|
||||
commented out, especially in bundled libraries.
|
||||
|
||||
9. Do not define functions that are not available. For instance, if a library is
|
||||
missing a function, do not define the PHP version of the function, and do
|
||||
not raise a run-time error about the function not existing. End users should
|
||||
use `function_exists()` to test for the existence of a function.
|
||||
|
||||
10. Prefer `emalloc()`, `efree()`, `estrdup()`, etc. to their standard C library
|
||||
counterparts. These functions implement an internal "safety-net" mechanism
|
||||
that ensures the deallocation of any unfreed memory at the end of a request.
|
||||
They also provide useful allocation and overflow information while running
|
||||
in debug mode.
|
||||
|
||||
In almost all cases, memory returned to the engine must be allocated using
|
||||
`emalloc()`.
|
||||
|
||||
The use of `malloc()` should be limited to cases where a third-party library
|
||||
may need to control or free the memory, or when the memory in question needs
|
||||
to survive between multiple requests.
|
||||
|
||||
## User functions/methods naming conventions
|
||||
|
||||
1. Function names for user-level functions should be enclosed with in the
|
||||
`PHP_FUNCTION()` macro. They should be in lowercase, with words underscore
|
||||
delimited, with care taken to minimize the letter count. Abbreviations
|
||||
should not be used when they greatly decrease the readability of the
|
||||
function name itself:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
str_word_count
|
||||
array_key_exists
|
||||
```
|
||||
|
||||
Ok:
|
||||
|
||||
```php
|
||||
date_interval_create_from_date_string
|
||||
// Could be 'date_intvl_create_from_date_str'?
|
||||
get_html_translation_table()
|
||||
// Could be 'html_get_trans_table'?
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
hw_GetObjectByQueryCollObj
|
||||
pg_setclientencoding
|
||||
jf_n_s_i
|
||||
```
|
||||
|
||||
2. If they are part of a "parent set" of functions, that parent should be
|
||||
included in the user function name, and should be clearly related to the
|
||||
parent program or function family. This should be in the form of `parent_*`:
|
||||
|
||||
A family of `foo` functions, for example:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
foo_select_bar
|
||||
foo_insert_baz
|
||||
foo_delete_baz
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
fooselect_bar
|
||||
fooinsertbaz
|
||||
delete_foo_baz
|
||||
```
|
||||
|
||||
3. Function names used by user functions should be prefixed with `_php_`, and
|
||||
followed by a word or an underscore-delimited list of words, in lowercase
|
||||
letters, that describes the function. If applicable, they should be declared
|
||||
`static`.
|
||||
|
||||
4. Variable names must be meaningful. One letter variable names must be avoided,
|
||||
except for places where the variable has no real meaning or a trivial
|
||||
meaning (e.g. `for (i=0; i<100; i++) ...`).
|
||||
|
||||
5. Variable names should be in lowercase. Use underscores to separate between
|
||||
words.
|
||||
|
||||
6. Method names follow the *studlyCaps* (also referred to as *bumpy case* or
|
||||
*camel caps*) naming convention, with care taken to minimize the letter
|
||||
count. The initial letter of the name is lowercase, and each letter that
|
||||
starts a new `word` is capitalized:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
connect()
|
||||
getData()
|
||||
buildSomeWidget()
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
get_Data()
|
||||
buildsomewidget()
|
||||
getI()
|
||||
```
|
||||
|
||||
7. Class names should be descriptive nouns in *PascalCase* and as short as
|
||||
possible. Each word in the class name should start with a capital letter,
|
||||
without underscore delimiters. The class name should be prefixed with the
|
||||
name of the "parent set" (e.g. the name of the extension) if no namespaces
|
||||
are used. Abbreviations and acronyms as well as initialisms should be
|
||||
avoided wherever possible, unless they are much more widely used than the
|
||||
long form (e.g. HTTP or URL). Abbreviations start with a capital letter
|
||||
followed by lowercase letters, whereas acronyms and initialisms are written
|
||||
according to their standard notation. Usage of acronyms and initialisms is
|
||||
not allowed if they are not widely adopted and recognized as such.
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
Curl
|
||||
CurlResponse
|
||||
HTTPStatusCode
|
||||
URL
|
||||
BTreeMap // B-tree Map
|
||||
Id // Identifier
|
||||
ID // Identity Document
|
||||
Char // Character
|
||||
Intl // Internationalization
|
||||
Radar // Radio Detecting and Ranging
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
curl
|
||||
curl_response
|
||||
HttpStatusCode
|
||||
Url
|
||||
BtreeMap
|
||||
ID // Identifier
|
||||
CHAR
|
||||
INTL
|
||||
RADAR // Radio Detecting and Ranging
|
||||
```
|
||||
|
||||
## Internal function naming conventions
|
||||
|
||||
1. Functions that are part of the external API should be named
|
||||
`php_modulename_function()` to avoid symbol collision. They should be in
|
||||
lowercase, with words underscore delimited. Exposed API must be defined in
|
||||
`php_modulename.h`.
|
||||
|
||||
```c
|
||||
PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS);
|
||||
```
|
||||
|
||||
Unexposed module function should be static and should not be defined in
|
||||
`php_modulename.h`.
|
||||
|
||||
```c
|
||||
static int php_session_destroy()
|
||||
```
|
||||
|
||||
2. Main module source file must be named `modulename.c`.
|
||||
|
||||
3. Header file that is used by other sources must be named `php_modulename.h`.
|
||||
|
||||
## Syntax and indentation
|
||||
|
||||
1. Never use C++ style comments (i.e. `//` comment). Always use C-style comments
|
||||
instead. PHP is written in C, and is aimed at compiling under any ANSI-C
|
||||
compliant compiler. Even though many compilers accept C++-style comments in
|
||||
C code, you have to ensure that your code would compile with other compilers
|
||||
as well. The only exception to this rule is code that is Win32-specific,
|
||||
because the Win32 port is MS-Visual C++ specific, and this compiler is known
|
||||
to accept C++-style comments in C code.
|
||||
|
||||
2. Use K&R-style. Of course, we can't and don't want to force anybody to use a
|
||||
style he or she is not used to, but, at the very least, when you write code
|
||||
that goes into the core of PHP or one of its standard modules, please
|
||||
maintain the K&R style. This applies to just about everything, starting with
|
||||
indentation and comment styles and up to function declaration syntax. Also
|
||||
see [Indentstyle](http://www.catb.org/~esr/jargon/html/I/indent-style.html).
|
||||
|
||||
3. Be generous with whitespace and braces. Keep one empty line between the
|
||||
variable declaration section and the statements in a block, as well as
|
||||
between logical statement groups in a block. Maintain at least one empty
|
||||
line between two functions, preferably two. Always prefer:
|
||||
|
||||
```c
|
||||
if (foo) {
|
||||
bar;
|
||||
}
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```c
|
||||
if(foo)bar;
|
||||
```
|
||||
|
||||
4. When indenting, use the tab character. A tab is expected to represent four
|
||||
spaces. It is important to maintain consistency in indenture so that
|
||||
definitions, comments, and control structures line up correctly.
|
||||
|
||||
5. Preprocessor statements (`#if` and such) MUST start at column one. To indent
|
||||
preprocessor directives you should put the `#` at the beginning of a line,
|
||||
followed by any number of whitespace.
|
||||
|
||||
## Testing
|
||||
|
||||
1. Extensions should be well tested using `*.phpt` tests. Read about that at
|
||||
[qa.php.net](https://qa.php.net/write-test.php) documentation.
|
||||
|
||||
## Folding hooks
|
||||
|
||||
Use `{{{` symbols for the folding mode in Emacs and vim (`set fdm=marker`).
|
||||
Folding is very useful when dealing with large files because you can scroll
|
||||
through the file quickly and just unfold the function you wish to work on.
|
||||
The `}}}` at the end of each function marks the end of the fold, and should
|
||||
be on a separate line.
|
||||
|
||||
```c
|
||||
/* {{{ Returns the absolute value of the number */
|
||||
PHP_FUNCTION(abs)
|
||||
{
|
||||
...
|
||||
}
|
||||
/* }}} */
|
||||
```
|
||||
|
||||
## New and experimental functions
|
||||
|
||||
To reduce the problems normally associated with the first public implementation
|
||||
of a new set of functions, it has been suggested that the first implementation
|
||||
include a file labeled `EXPERIMENTAL` in the function directory, and that the
|
||||
functions follow the standard prefixing conventions during their initial
|
||||
implementation.
|
||||
|
||||
The file labelled `EXPERIMENTAL` should include the following information:
|
||||
|
||||
* Any authoring information (known bugs, future directions of the module).
|
||||
* Ongoing status notes which may not be appropriate for Git comments.
|
||||
|
||||
In general new features should go to PECL or experimental branches until there
|
||||
are specific reasons for directly adding it to the core distribution.
|
||||
|
||||
## Aliases & legacy documentation
|
||||
|
||||
You may also have some deprecated aliases with close to duplicate names, for
|
||||
example, `somedb_select_result` and `somedb_selectresult`. For documentation
|
||||
purposes, these will only be documented by the most current name, with the
|
||||
aliases listed in the documentation for the parent function. For ease of
|
||||
reference, user-functions with completely different names, that alias to the
|
||||
same function (such as `highlight_file` and `show_source`), will be separately
|
||||
documented.
|
||||
|
||||
Backwards compatible functions and names should be maintained as long as the
|
||||
code can be reasonably be kept as part of the codebase. See the `README` in the
|
||||
PHP documentation repository for more information on documentation.
|
||||
-425
@@ -1,425 +0,0 @@
|
||||
# Contributing to PHP
|
||||
|
||||
Anybody who programs in PHP can be a contributing member of the community that
|
||||
develops and deploys it; the task of deploying PHP, documentation and associated
|
||||
websites is a never ending one. With every release, or release candidate comes a
|
||||
wave of work, which takes a lot of organization and co-ordination.
|
||||
|
||||
You don't need any special access to download, build, debug and begin submitting
|
||||
PHP or PECL code, tests or documentation. Once you've followed this guide and
|
||||
had several contributions accepted, commit privileges are often quickly granted.
|
||||
|
||||
## Index
|
||||
|
||||
* [Pull requests](#pull-requests)
|
||||
* [Filing bugs](#filing-bugs)
|
||||
* [Feature requests](#feature-requests)
|
||||
* [Writing tests](#writing-tests)
|
||||
* [Writing documentation](#writing-documentation)
|
||||
* [Getting help](#getting-help)
|
||||
* [PHP source code directory structure](#php-source-code-directory-structure)
|
||||
* [PHP internals](#php-internals)
|
||||
* [PECL extensions](#pecl-extensions)
|
||||
* [Checklist for submitting contribution](#checklist-for-submitting-contribution)
|
||||
* [What happens after submitting contribution?](#what-happens-after-submitting-contribution)
|
||||
* [What happens when your contribution is applied?](#what-happens-when-your-contribution-is-applied)
|
||||
* [Git commit rules](#git-commit-rules)
|
||||
* [Copyright and license headers](#copyright-and-license-headers)
|
||||
|
||||
## Pull requests
|
||||
|
||||
PHP welcomes pull requests to [add tests](#writing-tests), fix bugs and to
|
||||
implement RFCs. Please be sure to include tests as appropriate!
|
||||
|
||||
If you are fixing a bug, then please submit your PR against the lowest actively
|
||||
supported branch of PHP that the bug affects (only green branches on
|
||||
[the supported version page](https://www.php.net/supported-versions.php) are
|
||||
supported). For example, at the time of writing, the lowest supported version is
|
||||
PHP 7.3, which corresponds to the `PHP-7.3` branch in Git. Please also make sure
|
||||
you add a link to the PR in the bug on [the bug tracker](https://bugs.php.net/).
|
||||
|
||||
Pull requests implementing RFCs should be submitted against `master`.
|
||||
|
||||
Pull requests should *never* be submitted against `PHP-x.y.z` branches, as these
|
||||
are only used for release management.
|
||||
|
||||
If your pull request exhibits conflicts with the base branch, please resolve
|
||||
them by using `git rebase` instead of `git merge`.
|
||||
|
||||
Fork the official PHP repository and send a pull request. A notification will be
|
||||
sent to the pull request mailing list. Sending a note to PHP Internals list
|
||||
(internals@lists.php.net) may help getting more feedback and quicker turnaround.
|
||||
You can also add pull requests to [bug reports](https://bugs.php.net/).
|
||||
|
||||
Read [Git access page](https://www.php.net/git.php) for help on using Git to get
|
||||
and build PHP source code. We recommend to look at our
|
||||
[workflow](https://wiki.php.net/vcs/gitworkflow) and our
|
||||
[FAQ](https://wiki.php.net/vcs/gitfaq).
|
||||
|
||||
## Filing bugs
|
||||
|
||||
Bugs can be filed on the [PHP bug tracker](https://bugs.php.net/). If this is
|
||||
the first time you've filed a bug, we suggest reading the
|
||||
[guide to reporting a bug](https://bugs.php.net/how-to-report.php).
|
||||
|
||||
Where possible, please include a self-contained reproduction case!
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are generally submitted in the form of
|
||||
[Requests for Comments (RFC)](https://wiki.php.net/rfc/howto), ideally
|
||||
accompanied by [pull requests](#pull-requests). You can find the extremely large
|
||||
list of RFCs that have been previously considered on the
|
||||
[PHP Wiki](https://wiki.php.net/rfc).
|
||||
|
||||
To create a RFC, discuss it with the extension maintainer, and discuss it on the
|
||||
development mailing list internals@lists.php.net. RFC Wiki accounts can be
|
||||
requested on https://wiki.php.net/start?do=register. PHP extension maintainers
|
||||
can be found in the [EXTENSIONS](/EXTENSIONS) file in the PHP source code
|
||||
repository. Mailing list subscription is explained on the
|
||||
[mailing lists page](https://www.php.net/mailing-lists.php).
|
||||
|
||||
You may also want to read
|
||||
[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process)
|
||||
for additional notes on the best way to approach submitting an RFC.
|
||||
|
||||
## Writing tests
|
||||
|
||||
We love getting new tests! PHP is a huge project and improving code coverage is
|
||||
a huge win for every PHP user.
|
||||
|
||||
[Our QA site includes a page detailing how to write test cases.](https://qa.php.net/write-test.php)
|
||||
|
||||
Submitting test scripts helps us to understand what functionality has changed.
|
||||
It is important for the stability and maintainability of PHP that tests are
|
||||
comprehensive.
|
||||
|
||||
Failure conditions of `zend_parse_parameters`, `ZEND_PARSE_PARAMETERS()` and
|
||||
similar functions should not be tested. These parameter parsing APIs are already
|
||||
extensively tested, and additional tests only complicate future modifications.
|
||||
|
||||
For newly created tests, a `--CREDITS--` section should no longer be included,
|
||||
as test authorship is already accurately tracked by Git. If multiple authors
|
||||
should be credited, the `Co-authored-by` tag in the commit message may be used.
|
||||
|
||||
## Writing documentation
|
||||
|
||||
There are two ways to contribute to the PHP manual. You can edit the manual and
|
||||
send patches anonymously via [the online editor](https://edit.php.net/), or you
|
||||
can check the XML source out from Subversion and edit that and build it
|
||||
[per the instructions on the documentation site](http://doc.php.net/tutorial/).
|
||||
Patches created that way should be sent to the
|
||||
[documentation mailing list](mailto:phpdoc@lists.php.net).
|
||||
|
||||
## Getting help
|
||||
|
||||
If you are having trouble contributing to PHP, or just want to talk to a human
|
||||
about what you're working on, you can contact us via the
|
||||
[internals mailing list](mailto:internals@lists.php.net), or the
|
||||
[documentation mailing list](mailto:phpdoc@lists.php.net) for documentation
|
||||
issues.
|
||||
|
||||
Although not a formal channel, you can also find a number of core developers on
|
||||
the #php.pecl channel on [EFnet](http://www.efnet.org/). Similarly, many
|
||||
documentation writers can be found on #php.doc. Windows development IRC channel
|
||||
is available at #winphp-dev on FreeNode.
|
||||
|
||||
## PHP source code directory structure
|
||||
|
||||
PHP source code also includes several files generated during development and
|
||||
several parts where maintenance is happening upstream in their respective
|
||||
locations.
|
||||
|
||||
```bash
|
||||
<php-src>/
|
||||
├─ .git/ # Git configuration and source directory
|
||||
├─ TSRM/ # Thread Safe Resource Manager
|
||||
└─ Zend/ # Zend Engine
|
||||
├─ asm/ # Bundled from src/asm in https://github.com/boostorg/context
|
||||
├─ zend_vm_execute.h # Generated by `Zend/zend_vm_gen.php`
|
||||
├─ zend_vm_opcodes.c # Generated by `Zend/zend_vm_gen.php`
|
||||
├─ zend_vm_opcodes.h # Generated by `Zend/zend_vm_gen.php`
|
||||
└─ ...
|
||||
├─ appveyor/ # Appveyor CI service files
|
||||
└─ build/ # *nix build system files
|
||||
├─ ax_*.m4 # https://github.com/autoconf-archive/autoconf-archive
|
||||
├─ config.guess # https://git.savannah.gnu.org/cgit/config.git
|
||||
├─ config.sub # https://git.savannah.gnu.org/cgit/config.git
|
||||
├─ libtool.m4 # https://git.savannah.gnu.org/cgit/libtool.git
|
||||
├─ ltmain.sh # https://git.savannah.gnu.org/cgit/libtool.git
|
||||
├─ pkg.m4 # https://gitlab.freedesktop.org/pkg-config/pkg-config
|
||||
├─ shtool # https://www.gnu.org/software/shtool/
|
||||
└─ ...
|
||||
├─ docs/ # PHP internals and repository documentation
|
||||
└─ ext/ # PHP core extensions
|
||||
└─ bcmath/
|
||||
├─ libbcmath/ # Forked and maintained in php-src
|
||||
└─ ...
|
||||
└─ curl/
|
||||
├─ sync-constants.php # The curl symbols checker
|
||||
└─ ...
|
||||
└─ date/
|
||||
└─ lib/ # Bundled datetime library https://github.com/derickr/timelib
|
||||
├─ parse_date.c # Generated by re2c 0.15.3
|
||||
├─ parse_iso_intervals.c # Generated by re2c 0.15.3
|
||||
└─ ...
|
||||
└─ ...
|
||||
└─ ffi/
|
||||
├─ ffi_parser.c # Generated by https://github.com/dstogov/llk
|
||||
└─ ...
|
||||
└─ fileinfo/
|
||||
├─ libmagic/ # Modified libmagic https://github.com/file/file
|
||||
├─ data_file.c # Generated by `ext/fileinfo/create_data_file.php`
|
||||
├─ libmagic.patch # Modifications patch from upstream libmagic
|
||||
├─ magicdata.patch # Modifications patch from upstream libmagic
|
||||
└─ ...
|
||||
└─ gd/
|
||||
├─ libgd/ # Bundled and modified GD library https://github.com/libgd/libgd
|
||||
└─ ...
|
||||
└─ mbstring/
|
||||
├─ libmbfl/ # Forked and maintained in php-src
|
||||
├─ unicode_data.h # Generated by `ext/mbstring/ucgendat/ucgendat.php`
|
||||
└─ ...
|
||||
└─ pcre/
|
||||
├─ pcre2lib/ # https://www.pcre.org/
|
||||
└─ ...
|
||||
└─ pdo_mysql/
|
||||
├─ php_pdo_mysql_sqlstate.h # Generated by `ext/pdo_mysql/get_error_codes.php`
|
||||
└─ ...
|
||||
└─ skeleton/ # Skeleton for developing new extensions with `ext/ext_skel.php`
|
||||
└─ ...
|
||||
└─ standard/
|
||||
└─ html_tables/
|
||||
├─ mappings/ # https://www.unicode.org/Public/MAPPINGS/
|
||||
└─ ...
|
||||
├─ credits_ext.h # Generated by `scripts/dev/credits`
|
||||
├─ credits_sapi.h # Generated by `scripts/dev/credits`
|
||||
├─ html_tables.h # Generated by `ext/standard/html_tables/html_table_gen.php`
|
||||
└─ ...
|
||||
└─ tokenizer/
|
||||
├─ tokenizer_data.c # Generated by `ext/tokenizer/tokenizer_data_gen.sh`
|
||||
└─ ...
|
||||
└─ zend_test # For testing internal APIs. Not needed for regular builds.
|
||||
└─ ...
|
||||
└─ zip/ # Bundled https://github.com/pierrejoye/php_zip
|
||||
└─ ...
|
||||
└─ ...
|
||||
└─ main/ # Binding that ties extensions, SAPIs, and engine together
|
||||
├─ streams/ # Streams layer subsystem
|
||||
├─ php_version.h # Generated by release managers using `configure`
|
||||
└─ ...
|
||||
├─ pear/ # PEAR installation
|
||||
└─ sapi/ # PHP SAPI modules
|
||||
└─ cli/
|
||||
├─ mime_type_map.h # Generated by `sapi/cli/generate_mime_type_map.php`
|
||||
└─ ...
|
||||
└─ ...
|
||||
├─ scripts/ # php-config, phpize and internal development scripts
|
||||
├─ tests/ # Core features tests
|
||||
├─ travis/ # Travis CI service files
|
||||
└─ win32/ # Windows build system files
|
||||
├─ cp_enc_map.c # Generated by `win32/cp_enc_map_gen.exe`
|
||||
└─ ...
|
||||
└─ ...
|
||||
```
|
||||
|
||||
## PHP internals
|
||||
|
||||
For information on PHP internal C functions see
|
||||
[References about Maintaining and Extending PHP](https://wiki.php.net/internals/references).
|
||||
Various external resources can be found on the web. A standard printed reference
|
||||
is the book "Extending and Embedding PHP" by Sara Golemon.
|
||||
|
||||
## PECL extensions
|
||||
|
||||
If you are fixing broken functionality in a [PECL](https://pecl.php.net)
|
||||
extension then create a bug or identify an existing bug at
|
||||
[bugs.php.net](https://bugs.php.net). A bug can be used to track the change
|
||||
progress and prevent your changes getting lost in the PHP mail archives. Some
|
||||
PECL extensions have their own bug tracker locations and different contributing
|
||||
procedures.
|
||||
|
||||
If your change is large then create a
|
||||
[Request for Comments (RFC)](https://wiki.php.net/rfc), discuss it with the
|
||||
extension maintainer, and discuss it on the development mailing list
|
||||
pecl-dev@lists.php.net depending on the extension. PECL mailing list
|
||||
subscription is explained on the
|
||||
[PECL support page](https://pecl.php.net/support.php).
|
||||
|
||||
Update any open bugs and add a link to the source of your change. Send the patch
|
||||
or pointer to the bug to pecl-dev@lists.php.net. Also CC the extension
|
||||
maintainer. Explain what has been changed by your patch. Test scripts should be
|
||||
included.
|
||||
|
||||
## Checklist for submitting contribution
|
||||
|
||||
- Read [Coding standards](/CODING_STANDARDS.md) before you start working.
|
||||
- Update git source just before running your final `diff` and before testing.
|
||||
- Add in-line comments and/or have external documentation ready. Use only
|
||||
`/* */` style comments, not `//`.
|
||||
- Create test scripts for use with `make test`.
|
||||
- Run `make test` to check your change doesn't break other features.
|
||||
- Rebuild PHP with `--enable-debug` which will show some kinds of memory errors
|
||||
and check the PHP and web server error logs after running your PHP tests.
|
||||
- Rebuild PHP with `--enable-zts` to check your change compiles and operates
|
||||
correctly in a thread safe PHP.
|
||||
- Review the change once more just before submitting it.
|
||||
|
||||
## What happens after submitting contribution?
|
||||
|
||||
If your change is easy to review and obviously has no side-effects, it might be
|
||||
committed relatively quickly.
|
||||
|
||||
Because PHP is a volunteer-driven effort more complex changes will require
|
||||
patience on your side. If you do not receive feedback in a few days, consider
|
||||
bumping. Before doing this think about these questions:
|
||||
|
||||
- Did I send the patch to the right mailing list?
|
||||
- Did I review the mailing list archives to see if these kind of changes had
|
||||
been discussed before?
|
||||
- Did I explain my change clearly?
|
||||
- Is my change too hard to review? Because of what factors?
|
||||
|
||||
## What happens when your contribution is applied?
|
||||
|
||||
Your name will likely be included in the Git commit log. If your change affects
|
||||
end users, a brief description and your name might be added to the [NEWS](/NEWS)
|
||||
file.
|
||||
|
||||
## Git commit rules
|
||||
|
||||
This section refers to contributors that have Git push access and make commit
|
||||
changes themselves. We'll assume you're basically familiar with Git, but feel
|
||||
free to post your questions on the mailing list. Please have a look at the more
|
||||
detailed [information on Git](https://git-scm.com/).
|
||||
|
||||
PHP is developed through the efforts of a large number of people. Collaboration
|
||||
is a Good Thing(tm), and Git lets us do this. Thus, following some basic rules
|
||||
with regards to Git usage will:
|
||||
|
||||
* Make everybody happier, especially those responsible for maintaining PHP
|
||||
itself.
|
||||
* Keep the changes consistently well documented and easily trackable.
|
||||
* Prevent some of those 'Oops' moments.
|
||||
* Increase the general level of good will on planet Earth.
|
||||
|
||||
Having said that, here are the organizational rules:
|
||||
|
||||
1. Respect other people working on the project.
|
||||
|
||||
2. Discuss any significant changes on the list before committing and get
|
||||
confirmation from the release manager for the given branch.
|
||||
|
||||
3. Look at [EXTENSIONS](/EXTENSIONS) file to see who is the primary maintainer
|
||||
of the code you want to contribute to.
|
||||
|
||||
4. If you "strongly disagree" about something another person did, don't start
|
||||
fighting publicly - take it up in private email.
|
||||
|
||||
5. If you don't know how to do something, ask first!
|
||||
|
||||
6. Test your changes before committing them. We mean it. Really. To do so use
|
||||
`make test`.
|
||||
|
||||
7. For development use the `--enable-debug` switch to avoid memory leaks and the
|
||||
`--enable-zts` switch to ensure your code handles TSRM correctly and doesn't
|
||||
break for those who need that.
|
||||
|
||||
Currently we have the following branches in use:
|
||||
|
||||
| Branch | |
|
||||
| --------- | --------- |
|
||||
| master | Active development branch for PHP 8.1, which is open for backwards incompatible changes and major internal API changes. |
|
||||
| PHP-8.0 | Is used to release the PHP 8.0.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.4 | Is used to release the PHP 7.4.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.3 | Is used to release the PHP 7.3.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.2 | Is used to release the PHP 7.2.x series. This is an old stable version and is open for security fixes only. |
|
||||
| PHP-7.1 | This branch is closed. |
|
||||
| PHP-7.0 | This branch is closed. |
|
||||
| PHP-5.6 | This branch is closed. |
|
||||
| PHP-5.5 | This branch is closed. |
|
||||
| PHP-5.4 | This branch is closed. |
|
||||
| PHP-5.3 | This branch is closed. |
|
||||
| PHP-5.2 | This branch is closed. |
|
||||
| PHP-5.1 | This branch is closed. |
|
||||
| PHP-4.4 | This branch is closed. |
|
||||
| PHP-X.Y.Z | These branches are used for the release managers for tagging the releases, hence they are closed to the general public. |
|
||||
|
||||
The next few rules are more of a technical nature:
|
||||
|
||||
1. All non-security bugfix changes should first go to the lowest bugfix branch
|
||||
(i.e. 7.3) and then get merged up to all other branches. All security fixes
|
||||
should go to the lowest security fixes branch (i.e 7.2). If a change is not
|
||||
needed for later branches (i.e. fixes for features which were dropped from
|
||||
later branches) an empty merge should be done.
|
||||
|
||||
2. All news updates intended for public viewing, such as new features, bug
|
||||
fixes, improvements, etc., should go into the NEWS file of *any stable
|
||||
release* version with the given change. In other words, news about a bug fix
|
||||
which went into PHP-5.4, PHP-5.5 and master should be noted in both
|
||||
PHP-5.4/NEWS and PHP-5.5/NEWS but not master, which is not a public released
|
||||
version yet.
|
||||
|
||||
3. Do not commit multiple files and dump all messages in one commit. If you
|
||||
modified several unrelated files, commit each group separately and provide a
|
||||
nice commit message for each one. See example below.
|
||||
|
||||
4. Do write your commit message in such a way that it makes sense even without
|
||||
the corresponding diff. One should be able to look at it, and immediately
|
||||
know what was modified. Definitely include the function name in the message
|
||||
as shown below.
|
||||
|
||||
5. In your commit messages, keep each line shorter than 80 characters. And try
|
||||
to align your lines vertically, if they wrap. It looks bad otherwise.
|
||||
|
||||
6. If you modified a function that is callable from PHP, prepend PHP to the
|
||||
function name as shown below.
|
||||
|
||||
The format of the commit messages is pretty simple.
|
||||
|
||||
<max 79 characters short description>\n
|
||||
\n
|
||||
<long description, 79 chars per line>
|
||||
\n
|
||||
|
||||
An Example from the git project (commit 2b34e486bc):
|
||||
|
||||
pack-objects: Fix compilation with NO_PTHREDS
|
||||
|
||||
It looks like commit 99fb6e04 (pack-objects: convert to use parse_options(),
|
||||
2012-02-01) moved the #ifdef NO_PTHREDS around but hasn't noticed that the
|
||||
'arg' variable no longer is available.
|
||||
|
||||
If you fix some bugs, you should note the bug ID numbers in your commit message.
|
||||
Bug ID should be prefixed by `#`.
|
||||
|
||||
Example:
|
||||
|
||||
Fixed bug #14016 (pgsql notice handler double free crash bug.)
|
||||
|
||||
When you change the NEWS file for a bug fix, then please keep the bugs sorted in
|
||||
decreasing order under the fixed version.
|
||||
|
||||
## Copyright and license headers
|
||||
|
||||
New source code files should include the following header block:
|
||||
|
||||
```c
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
```
|
||||
|
||||
Thank you for contributing to PHP!
|
||||
-516
@@ -1,516 +0,0 @@
|
||||
List of PHP maintainers
|
||||
=======================
|
||||
|
||||
Maintenance legend
|
||||
------------------
|
||||
Maintained: Someone actually looks after it.
|
||||
Odd Fixes: It has a maintainer but they don't have time to do
|
||||
much other than throw the odd patch in. See below.
|
||||
Orphan: No current maintainer [but maybe you could take the
|
||||
role as you write your new code].
|
||||
Obsolete: Old code. Something tagged obsolete generally means
|
||||
it has been replaced by a better system and you
|
||||
should be using that.
|
||||
Unknown: Not known at this time.
|
||||
|
||||
Status legend
|
||||
-------------
|
||||
Working: Working under both Windows and Unix.
|
||||
Windows: Working only under Windows.
|
||||
Unix: Working only under Unix.
|
||||
Experimental: Under development or initial release.
|
||||
Not Working: Not working.
|
||||
Unknown: Status unknown.
|
||||
|
||||
|
||||
== Server APIs ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: apache2handler
|
||||
PRIMARY MAINTAINER: Aaron Bannert <aaron@php.net>, Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: cgi
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: cli
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net>, Edin Kadribasic <edink@php.net>
|
||||
MAINTENANCE: Xinchen Hui <laruence@php.net> (2013 - 2020)
|
||||
STATUS: Working
|
||||
SINCE: 4.3.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: embed
|
||||
PRIMARY MAINTAINER: Edin Kadribasic <edink@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: fpm
|
||||
PRIMARY MAINTAINER: Jakub Zelenka <bukka@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: litespeed
|
||||
PRIMARY MAINTAINER: George Wang <gwang@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: phpdbg
|
||||
PRIMARY MAINTAINER: Joe Watkins <krakjoe@php.net>, Bob Weinand <bwoebi@php.net>
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: 5.6
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
== Database extensions ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: dba
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2002 - 2007)
|
||||
Christopher Jones <sixd@php.net> (2008 - 2013)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2011 - 2011)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: DBM abstraction for db2, db3, db4, dbm, ndbm, gdbm, ini
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mysqli
|
||||
PRIMARY MAINTAINER: Georg Richter <georg@php.net> (2003 - 2006)
|
||||
Andrey Hristov <andrey@php.net> (2003 - 2016)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2014)
|
||||
Ulf Wendel <uw@php.net> (2007 - 2013)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mysqlnd
|
||||
PRIMARY MAINTAINER: Andrey Hristov <andrey@php.net> (2007 - 2017)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2018)
|
||||
Ulf Wendel <uw@php.net> (2009 - 2011)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: oci8
|
||||
PRIMARY MAINTAINER: Christopher Jones <sixd@php.net> (2007 - 2017)
|
||||
Antony Dovgal <tony2001@php.net> (2003 - 2009)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: odbc
|
||||
PRIMARY MAINTAINER: Daniel R. Kalowsky <kalowsky@php.net> (2000 - 2004)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2011)
|
||||
Wez Furlong <wez@php.net> (2004 - 2006)
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_dblib
|
||||
PRIMARY MAINTAINER: Adam Baratz <adambaratz@php.net> (2016 - 2017)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_firebird
|
||||
PRIMARY MAINTAINER: Lars Westermann <lwe@php.net> (2007 - 2007)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_mysql
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2010)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2014)
|
||||
Andrey Hristov <andrey@php.net> (2005 - 2015)
|
||||
Ulf Wendel <uw@php.net> (2008 - 2012)
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_odbc
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_oci
|
||||
PRIMARY MAINTAINER: Christopher Jones <sixd@php.net> (2007 - 2017)
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_pgsql
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2011)
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_sqlite
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2005 - 2011)
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pgsql
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2002 - 2007)
|
||||
Yasuo Ohgaki <yohgaki@php.net> (2001 - 2015)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: Use PostgreSQL 7.0.x or later. PostgreSQL 6.5.3 or less have fatal bug.
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sqlite3
|
||||
PRIMARY MAINTAINER: Scott MacVicar <scottmac@php.net> (2008 - 2011)
|
||||
Christoph M. Becker <cmb@php.net> (2016 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
COMMENT: Integrates SQLite 3 embeddable SQL database engine.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
== XML extensions ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: dom
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net> (2003 - 2011)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2012)
|
||||
Marcus Börger <helly@php.net> (2003 - 2006)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: simplexml
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2008)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: soap
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2004 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xml
|
||||
PRIMARY MAINTAINER: Thies C. Arntzen <thies@thieso.net> (1999 - 2002)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2013)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: libxml
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2003 - 2009)
|
||||
Christian Stocker <chregu@php.net> (2004 - 2011)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xmlreader
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2004 - 2010)
|
||||
Christian Stocker <chregu@php.net> (2004 - 2004)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xmlwriter
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2004 - 2010)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2005-2009)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xsl
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net> (2003 - 2011)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2010)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
== Other extensions ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: bcmath
|
||||
PRIMARY MAINTAINER: Andi Gutmans <andi@php.net> (2000 - 2004)
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: bz2
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2004 - 2004)
|
||||
MAINTENANCE: Odd Fixes
|
||||
STATUS: Working
|
||||
SINCE: 4.0.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: calendar
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2000 - 2004)
|
||||
MAINTENANCE: Odd Fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: com_dotnet
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net> (2003 - 2005)
|
||||
Christoph M. Becker <cmb@php.net> (2018 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Windows
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ctype
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2000 - 2004)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: curl
|
||||
PRIMARY MAINTAINER: Sterling Hughes <sterling@php.net> (2000 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2011)
|
||||
Pierrick Charron <pierrick@php.net> (2010 - 2016)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: date
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2005 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: enchant
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: exif
|
||||
PRIMARY MAINTAINER: Kalle Sommer Nielsen <kalle@php.net> (2010 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ffi
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2018 - 2019)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Experimental
|
||||
SINCE: 7.4.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: fileinfo
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2004 - 2008)
|
||||
Anatol Belski <ab@php.net> (2012 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: filter
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2006 - 2006)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2006 - 2011)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ftp
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gd
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net> (2002 - 2016)
|
||||
Christoph M. Becker <cmb@php.net> (2015 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gettext
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gmp
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net> (2000 - 2019)
|
||||
Antony Dovgal <tony2001@php.net> (2005 - 2010)
|
||||
Nikita Popov <nikic@php.net> (2013 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.4
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: hash
|
||||
PRIMARY MAINTAINER: Sara Golemon <pollita@php.net> (2005 - 2017)
|
||||
Mike Wallner <mike@php.net> (2005 - 2013)
|
||||
Anatol Belski <ab@php.net> (2014 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.1.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: iconv
|
||||
PRIMARY MAINTAINER: Moriyoshi Koizumi <moriyoshi@php.net> (2002 - 2010)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: intl
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net> (2008 - 2019)
|
||||
Anatol Belski <ab@php.net> (2017 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: imap
|
||||
PRIMARY MAINTAINER: Chuck Hagenbuch <chuck@horde.org> (1999 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2010)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2008 - 2010)
|
||||
Bishop Bettini <bishop@php.net> (2018 - 2019)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: json
|
||||
PRIMARY MAINTAINER: Jakub Zelenka <bukka@php.net> (2014 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ldap
|
||||
PRIMARY MAINTAINER: Stig Venaas <venaas@php.net> (2000 - 2002)
|
||||
Douglas Goldstein <cardoe@php.net> (2007 - 2007)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2008 - 2010)
|
||||
Côme Bernigaud <mcmic@php.net> (2015 - 2017)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mbstring
|
||||
PRIMARY MAINTAINER: Rui Hirokawa <hirokawa@php.net> (2001 - 2013)
|
||||
Nikita Popov <nikic@php.net> (2017 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: opcache
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2013 - 2018)
|
||||
Xinchen Hui <laruence@php.net> (2013 - 2020)
|
||||
Nikita Popov <nikic@php.net> (2016 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: openssl
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net> (2001 - 2006)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Jakub Zelenka <bukka@php.net> (2015 - 2019)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.4
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pcntl
|
||||
PRIMARY MAINTAINER: Arnaud Le Blanc <lbarnaud@php.net> (2008 - 2010)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pcre
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net> (1999 - 2006)
|
||||
Nuno Lopes <nlopess@php.net> (2006 - 2009)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: phar
|
||||
PRIMARY MAINTAINER: Greg Beaver <cellog@php.net> (2008 - 2009)
|
||||
Marcus Börger <helly@php.net> (2008 - 2008)
|
||||
Steph Fox <sfox@php.net> (2008 - 2008)
|
||||
Bishop Bettini <bishop@php.net> (2018 - 2019)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: posix
|
||||
PRIMARY MAINTAINER: Kristian Köhntopp <kris@koehntopp.de> (2000 - 2000)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pspell
|
||||
PRIMARY MAINTAINER: Vlad Krupin <phpdevel@echospace.com> (2000 - 2004)
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: readline
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: reflection
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2009)
|
||||
Johannes Schlüter <johannes@php.net> (2006 - 2014)
|
||||
Nikita Popov <nikic@php.net> (2019 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: session
|
||||
PRIMARY MAINTAINER: Sascha Schumann <sascha@schumann.cx> (1999 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2012)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: shmop
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2002 - 2008)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: snmp
|
||||
PRIMARY MAINTAINER: Boris Lytochkin <lytboris@php.net> (2011 - 2013)
|
||||
Rasmus Lerdorf <rasmus@php.net> (1999 - 2002)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2010 - 2011)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sockets
|
||||
PRIMARY MAINTAINER: Chris Vandomelen <chrisv@b0rked.dhs.org> (2000 - 2000)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sodium
|
||||
PRIMARY MAINTAINER: Frank Denis <jedisct1@php.net> (2017 - 2017)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 7.2.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: spl
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2009)
|
||||
Etienne Kneuss <colder@php.net> (2008 - 2014)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvmsg
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvsem
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvshm
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: tidy
|
||||
PRIMARY MAINTAINER: John Coggeshall <john@php.net> (2003 - 2006)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2003 - 2009)
|
||||
Nuno Lopes <nlopess@php.net> (2006 - 2012)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: tokenizer
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net> (2002 - 2002)
|
||||
Nikita Popov <nikic@php.net> (2013 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: zip
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Remi Collet <remi@php.net> (2013-2020)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: zlib
|
||||
PRIMARY MAINTAINER: Stefan Roehrich <sr@linux.de> (1999 - 2003)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -1,68 +0,0 @@
|
||||
--------------------------------------------------------------------
|
||||
The PHP License, version 3.01
|
||||
Copyright (c) 1999 - 2021 The PHP Group. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. The name "PHP" must not be used to endorse or promote products
|
||||
derived from this software without prior written permission. For
|
||||
written permission, please contact group@php.net.
|
||||
|
||||
4. Products derived from this software may not be called "PHP", nor
|
||||
may "PHP" appear in their name, without prior written permission
|
||||
from group@php.net. You may indicate that your software works in
|
||||
conjunction with PHP by saying "Foo for PHP" instead of calling
|
||||
it "PHP Foo" or "phpfoo"
|
||||
|
||||
5. The PHP Group may publish revised and/or new versions of the
|
||||
license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
Once covered code has been published under a particular version
|
||||
of the license, you may always continue to use it under the terms
|
||||
of that version. You may also choose to use such covered code
|
||||
under the terms of any subsequent version of the license
|
||||
published by the PHP Group. No one other than the PHP Group has
|
||||
the right to modify the terms applicable to covered code created
|
||||
under this License.
|
||||
|
||||
6. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes PHP software, freely available from
|
||||
<http://www.php.net/software/>".
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
|
||||
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals on behalf of the PHP Group.
|
||||
|
||||
The PHP Group can be contacted via Email at group@php.net.
|
||||
|
||||
For more information on the PHP Group and the PHP project,
|
||||
please see <http://www.php.net>.
|
||||
|
||||
PHP includes the Zend Engine, freely available at
|
||||
<http://www.zend.com>.
|
||||
@@ -1,139 +0,0 @@
|
||||
PHP NEWS
|
||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
?? ??? ????, PHP 8.1.0alpha1
|
||||
|
||||
- Core:
|
||||
. Fixed inclusion order for phpize builds on Windows. (cmb)
|
||||
. Added missing hashtable insertion APIs for arr/obj/ref. (Sara)
|
||||
. Fixed bug #75474 (function scope static variables are not bound to a unique
|
||||
function). (Nikita)
|
||||
. Fixed bug #53826 (__callStatic fired in base class through a parent call if
|
||||
the method is private). (Nikita)
|
||||
. Implemented FR #77372 (Relative file path is removed from uploaded file).
|
||||
(Björn Tantau)
|
||||
. Fixed bug #81076 (incorrect debug info on Closures with implicit binds).
|
||||
(krakjoe)
|
||||
|
||||
- Standard:
|
||||
. Fixed bug #77627 (method_exists on Closure::__invoke inconsistency).
|
||||
(krakjoe)
|
||||
|
||||
- Date:
|
||||
. Fixed bug #52480 (Incorrect difference using DateInterval) (Derick)
|
||||
. Fixed bug #62326 (date_diff() function returns false result) (Derick)
|
||||
. Fixed bug #64992 (dst not handled past 2038) (Derick)
|
||||
. Fixed bug #65003 (Wrong date diff) (Derick)
|
||||
. Fixed bug #66545 (DateTime. diff returns negative values) (Derick)
|
||||
. Fixed bug #68503 (date_diff on two dates with timezone set localised
|
||||
returns wrong results) (Derick)
|
||||
. Fixed bug #69806 (Incorrect date from timestamp) (Derick)
|
||||
. Fixed bug #71700 (Extra day on diff between begin and end of march 2016)
|
||||
(Derick)
|
||||
. Fixed bug #71826 (DateTime::diff confuse on timezone 'Asia/Tokyo') (Derick)
|
||||
. Fixed bug #73460 (Datetime add not realising it already applied DST
|
||||
change) (Derick)
|
||||
. Fixed bug #74173 (DateTimeImmutable::getTimestamp() triggers DST switch in
|
||||
incorrect time) (Derick)
|
||||
. Fixed bug #74274 (Handling DST transitions correctly) (Derick)
|
||||
. Fixed bug #74524 (Date diff is bad calculated, in same time zone) (Derick)
|
||||
. Fixed bug #75167 (DateTime::add does only care about backward DST
|
||||
transition, not forward) (Derick)
|
||||
. Fixed bug #76032 (DateTime->diff having issues with leap days for
|
||||
timezones ahead of UTC) (Derick)
|
||||
. Fixed bug #76374 (Date difference varies according day time) (Derick)
|
||||
. Fixed bug #77571 (DateTime's diff DateInterval incorrect in timezones from
|
||||
UTC+01:00 to UTC+12:00 (Derick)
|
||||
. Fixed bug #78452 (diff makes wrong in hour for Asia/Tehran) (Derick)
|
||||
. Fixed bug #79452 (DateTime::diff() generates months differently between
|
||||
time zones) (Derick)
|
||||
. Fixed bug #79698 (timelib mishandles future timestamps (triggered by 'zic
|
||||
-b slim')) (Derick)
|
||||
. Fixed bug #79716 (Invalid date time created (with day "00")) (Derick)
|
||||
. Fixed bug #80610 (DateTime calculate wrong with DateInterval) (Derick)
|
||||
. Fixed bug #80664 (DateTime objects behave incorrectly around DST
|
||||
transition) (Derick)
|
||||
. Fixed bug #80913 (DateTime(Immutable)::sub around DST yield incorrect
|
||||
time) (Derick)
|
||||
|
||||
- FPM:
|
||||
. Added openmetrics status format. (Cees-Jan Kiewiet)
|
||||
. Enable process renaming on macOS. (devnexen)
|
||||
. Added pm.max_spawn_rate option to configure max spawn child processes rate.
|
||||
(Paulius Sapragonas)
|
||||
. Fixed bug #65800 (Events port mechanism). (psumbera)
|
||||
|
||||
- FTP:
|
||||
. Convert resource<ftp> to object \FTP\Connection. (Sara)
|
||||
|
||||
- GD:
|
||||
. Convert resource<gd font> to object \GdFont. (Sara)
|
||||
|
||||
- hash:
|
||||
. Implemented FR #68109 (Add MurmurHash V3). (Anatol, Michael)
|
||||
. Implemented FR #73385 (Add xxHash support). (Anatol)
|
||||
|
||||
- LDAP:
|
||||
. Convert resource<ldap link> to object \LDAP\Connection. (Máté)
|
||||
. Convert resource<ldap result> to object \LDAP\Result. (Máté)
|
||||
. Convert resource<ldap result entry> to object \LDAP\ResultEntry. (Máté)
|
||||
|
||||
- MySQLi:
|
||||
. Fixed bug #70372 (Emulate mysqli_fetch_all() for libmysqlclient). (Nikita)
|
||||
. Fixed bug #80330 (Replace language in APIs and source code/docs).
|
||||
(Darek Ślusarczyk)
|
||||
. Fixed bug #80329 (Add option to specify LOAD DATA LOCAL white list folder
|
||||
(including libmysql)). (Darek Ślusarczyk)
|
||||
|
||||
- MySQLnd:
|
||||
. Fixed bug #80761 (PDO uses too much memory). (Nikita)
|
||||
|
||||
- OPcache:
|
||||
. Added inheritance cache. (Dmitry)
|
||||
|
||||
- OpenSSL:
|
||||
. Bump minimal OpenSSL version to 1.0.2. (Jakub Zelenka)
|
||||
|
||||
- PCRE:
|
||||
. Bundled PCRE2 is 10.37.
|
||||
|
||||
- PDO:
|
||||
. Fixed bug #40913 (PDO_MYSQL: PDO::PARAM_LOB does not bind to a stream for
|
||||
fetching a BLOB). (Nikita)
|
||||
|
||||
. PDO MySQL:
|
||||
. Fixed bug #80908 (PDO::lastInsertId() return wrong). (matt)
|
||||
. Fixed bug #81037 (PDO discards error message text from prepared
|
||||
statement). (Kamil Tekiela)
|
||||
|
||||
. PDO ODBC:
|
||||
. Implement PDO_ATTR_SERVER_VERSION and PDO_ATTR_SERVER_INFO for
|
||||
PDO::getAttribute(). (Calvin Buckley)
|
||||
|
||||
. PDO SQLite:
|
||||
. Fixed bug #38334 (Proper data-type support for PDO_SQLITE). (Nikita)
|
||||
|
||||
- PgSQL:
|
||||
. Convert resource<pgsql link> to object \PgSql\Connection. (Máté)
|
||||
. Convert resource<pgsql result> to object \PgSql\Result. (Máté)
|
||||
. Convert resource<pgsql large object> to object \PgSql\Lob. (Máté)
|
||||
|
||||
- PSpell:
|
||||
. Convert resource<pspell> to object \PSpell\Dictionary. (Sara)
|
||||
. Convert resource<pspell config> to object \PSpell\Config. (Sara)
|
||||
|
||||
- readline:
|
||||
. Fixed bug #72998 (invalid read in readline completion). (krakjoe)
|
||||
|
||||
- Reflection:
|
||||
. Implement ReflectionFunctionAbstract::getClosureUsedVariables (krakjoe)
|
||||
|
||||
- Sodium:
|
||||
. Added the XChaCha20 stream cipher functions. (P.I.E. Security Team)
|
||||
. Added the Ristretto255 functions, which are available in libsodium 1.0.18.
|
||||
(P.I.E. Security Team)
|
||||
|
||||
- SPL:
|
||||
. Fixed bug #80724 (FilesystemIterator::FOLLOW_SYMLINKS remove KEY_AS_FILE
|
||||
from bitmask). (Cameron Porter)
|
||||
|
||||
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
|
||||
@@ -1,642 +0,0 @@
|
||||
1. libmagic (ext/fileinfo) see ext/fileinfo/libmagic/LICENSE
|
||||
2. libmbfl (ext/mbstring) see ext/mbstring/libmbfl/LICENSE
|
||||
3. pcre2lib (ext/pcre)
|
||||
4. ext/standard crypt
|
||||
5. ext/standard crypt's blowfish implementation
|
||||
6. ext/standard/rand
|
||||
7. ext/standard/scanf
|
||||
8. ext/standard/strnatcmp.c
|
||||
9. ext/standard/uuencode
|
||||
10. main/snprintf.c
|
||||
11. main/strlcat
|
||||
12. main/strlcpy
|
||||
13. libgd (ext/gd)
|
||||
14. ext/phar portions of tar implementations
|
||||
15. ext/phar/zip.c portion extracted from libzip
|
||||
16. libbcmath (ext/bcmath) see ext/bcmath/libbcmath/LICENSE
|
||||
17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP
|
||||
|
||||
|
||||
3. pcre2lib (ext/pcre)
|
||||
|
||||
PCRE2 LICENCE
|
||||
-------------
|
||||
|
||||
PCRE2 is a library of functions to support regular expressions whose syntax
|
||||
and semantics are as close as possible to those of the Perl 5 language.
|
||||
|
||||
Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD"
|
||||
licence, as specified below, with one exemption for certain binary
|
||||
redistributions. The documentation for PCRE2, supplied in the "doc" directory,
|
||||
is distributed under the same terms as the software itself. The data in the
|
||||
testdata directory is not copyrighted and is in the public domain.
|
||||
|
||||
The basic library functions are written in C and are freestanding. Also
|
||||
included in the distribution is a just-in-time compiler that can be used to
|
||||
optimize pattern matching. This is an optional feature that can be omitted when
|
||||
the library is built.
|
||||
|
||||
|
||||
THE BASIC LIBRARY FUNCTIONS
|
||||
---------------------------
|
||||
|
||||
Written by: Philip Hazel
|
||||
Email local part: ph10
|
||||
Email domain: cam.ac.uk
|
||||
|
||||
University of Cambridge Computing Service,
|
||||
Cambridge, England.
|
||||
|
||||
Copyright (c) 1997-2019 University of Cambridge
|
||||
All rights reserved.
|
||||
|
||||
|
||||
PCRE2 JUST-IN-TIME COMPILATION SUPPORT
|
||||
--------------------------------------
|
||||
|
||||
Written by: Zoltan Herczeg
|
||||
Email local part: hzmester
|
||||
Email domain: freemail.hu
|
||||
|
||||
Copyright(c) 2010-2019 Zoltan Herczeg
|
||||
All rights reserved.
|
||||
|
||||
|
||||
STACK-LESS JUST-IN-TIME COMPILER
|
||||
--------------------------------
|
||||
|
||||
Written by: Zoltan Herczeg
|
||||
Email local part: hzmester
|
||||
Email domain: freemail.hu
|
||||
|
||||
Copyright(c) 2009-2019 Zoltan Herczeg
|
||||
All rights reserved.
|
||||
|
||||
|
||||
THE "BSD" LICENCE
|
||||
-----------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notices,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notices, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Cambridge nor the names of any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES
|
||||
------------------------------------------
|
||||
|
||||
The second condition in the BSD licence (covering binary redistributions) does
|
||||
not apply all the way down a chain of software. If binary package A includes
|
||||
PCRE2, it must respect the condition, but if package B is software that
|
||||
includes package A, the condition is not imposed on package B unless it uses
|
||||
PCRE2 independently.
|
||||
|
||||
End
|
||||
|
||||
|
||||
4. ext/standard crypt
|
||||
|
||||
FreeSec: libcrypt for NetBSD
|
||||
|
||||
Copyright (c) 1994 David Burren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the author nor the names of other contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
|
||||
5. ext/standard crypt's blowfish implementation
|
||||
|
||||
The crypt_blowfish homepage is:
|
||||
|
||||
http://www.openwall.com/crypt/
|
||||
|
||||
This code comes from John the Ripper password cracker, with reentrant
|
||||
and crypt(3) interfaces added, but optimizations specific to password
|
||||
cracking removed.
|
||||
|
||||
Written by Solar Designer <solar at openwall.com> in 1998-2011.
|
||||
No copyright is claimed, and the software is hereby placed in the public
|
||||
domain. In case this attempt to disclaim copyright and place the software
|
||||
in the public domain is deemed null and void, then the software is
|
||||
Copyright (c) 1998-2011 Solar Designer and it is hereby released to the
|
||||
general public under the following terms:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted.
|
||||
|
||||
There's ABSOLUTELY NO WARRANTY, express or implied.
|
||||
|
||||
It is my intent that you should be able to use this on your system,
|
||||
as part of a software package, or anywhere else to improve security,
|
||||
ensure compatibility, or for any other purpose. I would appreciate
|
||||
it if you give credit where it is due and keep your modifications in
|
||||
the public domain as well, but I don't require that in order to let
|
||||
you place this code and any modifications you make under a license
|
||||
of your choice.
|
||||
|
||||
This implementation is mostly compatible with OpenBSD's bcrypt.c (prefix
|
||||
"$2a$") by Niels Provos <provos at citi.umich.edu>, and uses some of his
|
||||
ideas. The password hashing algorithm was designed by David Mazieres
|
||||
<dm at lcs.mit.edu>. For more information on the level of compatibility,
|
||||
please refer to the comments in BF_set_key() and to the crypt(3) man page
|
||||
included in the crypt_blowfish tarball.
|
||||
|
||||
There's a paper on the algorithm that explains its design decisions:
|
||||
|
||||
http://www.usenix.org/events/usenix99/provos.html
|
||||
|
||||
Some of the tricks in BF_ROUND might be inspired by Eric Young's
|
||||
Blowfish library (I can't be sure if I would think of something if I
|
||||
hadn't seen his code).
|
||||
|
||||
|
||||
6. ext/standard/rand
|
||||
|
||||
The following php_mt_...() functions are based on a C++ class MTRand by
|
||||
Richard J. Wagner. For more information see the web page at
|
||||
http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
|
||||
|
||||
Mersenne Twister random number generator -- a C++ class MTRand
|
||||
Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
|
||||
Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
|
||||
|
||||
The Mersenne Twister is an algorithm for generating random numbers. It
|
||||
was designed with consideration of the flaws in various other generators.
|
||||
The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
|
||||
are far greater. The generator is also fast; it avoids multiplication and
|
||||
division, and it benefits from caches and pipelines. For more information
|
||||
see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
|
||||
|
||||
Reference
|
||||
M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
|
||||
Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
|
||||
Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
Copyright (C) 2000 - 2003, Richard J. Wagner
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
7. ext/standard/scanf
|
||||
|
||||
scanf.c --
|
||||
|
||||
This file contains the base code which implements sscanf and by extension
|
||||
fscanf. Original code is from TCL8.3.0 and bears the following copyright:
|
||||
|
||||
This software is copyrighted by the Regents of the University of
|
||||
California, Sun Microsystems, Inc., Scriptics Corporation,
|
||||
and other parties. The following terms apply to all files associated
|
||||
with the software unless explicitly disclaimed in individual files.
|
||||
|
||||
The authors hereby grant permission to use, copy, modify, distribute,
|
||||
and license this software and its documentation for any purpose, provided
|
||||
that existing copyright notices are retained in all copies and that this
|
||||
notice is included verbatim in any distributions. No written agreement,
|
||||
license, or royalty fee is required for any of the authorized uses.
|
||||
Modifications to this software may be copyrighted by their authors
|
||||
and need not follow the licensing terms described here, provided that
|
||||
the new terms are clearly indicated on the first page of each file where
|
||||
they apply.
|
||||
|
||||
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
||||
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
||||
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
||||
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
||||
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
||||
|
||||
GOVERNMENT USE: If you are acquiring this software on behalf of the
|
||||
U.S. government, the Government shall have only "Restricted Rights"
|
||||
in the software and related documentation as defined in the Federal
|
||||
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
|
||||
are acquiring the software on behalf of the Department of Defense, the
|
||||
software shall be classified as "Commercial Computer Software" and the
|
||||
Government shall have only "Restricted Rights" as defined in Clause
|
||||
252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
|
||||
authors grant the U.S. Government and others acting in its behalf
|
||||
permission to use and distribute the software in accordance with the
|
||||
terms specified in this license.
|
||||
|
||||
|
||||
8. ext/standard/strnatcmp.c
|
||||
|
||||
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
|
||||
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
9. ext/standard/uuencode
|
||||
|
||||
Portions of this code are based on Berkeley's uuencode/uudecode
|
||||
implementation.
|
||||
|
||||
Copyright (c) 1983, 1993
|
||||
The Regents of the University of California. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by the University of
|
||||
California, Berkeley and its contributors.
|
||||
4. Neither the name of the University nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
|
||||
10. main/snprintf.c
|
||||
|
||||
Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
Sponsored in part by the Defense Advanced Research Projects
|
||||
Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
|
||||
main/spprintf
|
||||
Copyright (c) 1995-1998 The Apache Group. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
|
||||
4. The names "Apache Server" and "Apache Group" must not be used to
|
||||
endorse or promote products derived from this software without
|
||||
prior written permission.
|
||||
|
||||
5. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
|
||||
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
|
||||
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
====================================================================
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals on behalf of the Apache Group and was originally based
|
||||
on public domain software written at the National Center for
|
||||
Supercomputing Applications, University of Illinois, Urbana-Champaign.
|
||||
For more information on the Apache Group and the Apache HTTP server
|
||||
project, please see <http://www.apache.org/>.
|
||||
|
||||
This code is based on, and used with the permission of, the
|
||||
SIO stdio-replacement strx_* functions by Panos Tsirigotis
|
||||
<panos@alumni.cs.colorado.edu> for xinetd.
|
||||
|
||||
|
||||
11. main/strlcat
|
||||
12. main/strlcpy
|
||||
|
||||
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
13. libgd (ext/gd)
|
||||
|
||||
* Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
|
||||
2002, 2003, 2004 by Cold Spring Harbor Laboratory. Funded under
|
||||
Grant P41-RR02188 by the National Institutes of Health.
|
||||
|
||||
* Portions copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
|
||||
2004 by Boutell.Com, Inc.
|
||||
|
||||
* Portions relating to GD2 format copyright 1999, 2000, 2001, 2002,
|
||||
2003, 2004 Philip Warner.
|
||||
|
||||
* Portions relating to PNG copyright 1999, 2000, 2001, 2002, 2003,
|
||||
2004 Greg Roelofs.
|
||||
|
||||
* Portions relating to gdttf.c copyright 1999, 2000, 2001, 2002,
|
||||
2003, 2004 John Ellson (ellson@graphviz.org).
|
||||
|
||||
* Portions relating to gdft.c copyright 2001, 2002, 2003, 2004 John
|
||||
Ellson (ellson@graphviz.org).
|
||||
|
||||
* Portions copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
Pierre-Alain Joye (pierre@libgd.org).
|
||||
|
||||
* Portions relating to JPEG and to color quantization copyright
|
||||
2000, 2001, 2002, 2003, 2004, Doug Becker and copyright (C) 1994,
|
||||
1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Thomas
|
||||
G. Lane. This software is based in part on the work of the
|
||||
Independent JPEG Group. See the file README-JPEG.TXT for more
|
||||
information.
|
||||
|
||||
* Portions relating to GIF compression copyright 1989 by Jef
|
||||
Poskanzer and David Rowley, with modifications for thread safety
|
||||
by Thomas Boutell.
|
||||
|
||||
* Portions relating to GIF decompression copyright 1990, 1991, 1993
|
||||
by David Koblas, with modifications for thread safety by Thomas
|
||||
Boutell.
|
||||
|
||||
* Portions relating to WBMP copyright 2000, 2001, 2002, 2003, 2004
|
||||
Maurice Szmurlo and Johan Van den Brande.
|
||||
|
||||
* Portions relating to GIF animations copyright 2004 Jaakko Hyvätti
|
||||
(jaakko.hyvatti@iki.fi)
|
||||
|
||||
Permission has been granted to copy, distribute and modify gd in
|
||||
any context without fee, including a commercial application,
|
||||
provided that this notice is present in user-accessible supporting
|
||||
documentation.
|
||||
|
||||
This does not affect your ownership of the derived work itself,
|
||||
and the intent is to assure proper credit for the authors of gd,
|
||||
not to interfere with your productive use of gd. If you have
|
||||
questions, ask. "Derived works" includes all programs that utilize
|
||||
the library. Credit must be given in user-accessible
|
||||
documentation.
|
||||
|
||||
This software is provided "AS IS." The copyright holders disclaim
|
||||
all warranties, either express or implied, including but not
|
||||
limited to implied warranties of merchantability and fitness for a
|
||||
particular purpose, with respect to this code and accompanying
|
||||
documentation.
|
||||
|
||||
Although their code does not appear in the current release, the
|
||||
authors wish to thank David Koblas, David Rowley, and Hutchison
|
||||
Avenue Software Corporation for their prior contributions.
|
||||
|
||||
END OF COPYRIGHT STATEMENT
|
||||
|
||||
|
||||
14. ext/phar portions of tar implementations
|
||||
|
||||
portions of tar implementations in ext/phar - phar_tar_octal() are based on an
|
||||
implementation by Tim Kientzle from libarchive, licensed with this license:
|
||||
|
||||
Copyright (c) 2003-2007 Tim Kientzle
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
15. ext/phar/zip.c portion extracted from libzip
|
||||
|
||||
zip_dirent.c -- read directory entry (local or central), clean dirent
|
||||
Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
|
||||
|
||||
This function is part of libzip, a library to manipulate ZIP archives.
|
||||
The authors can be contacted at <nih@giga.or.at>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
3. The names of the authors may not be used to endorse or promote
|
||||
products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
16. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP
|
||||
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
@@ -1,144 +0,0 @@
|
||||
<div align="center">
|
||||
<a href="https://php.net">
|
||||
<img
|
||||
alt="PHP"
|
||||
src="https://www.php.net/images/logos/new-php-logo.svg"
|
||||
width="150">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
# The PHP Interpreter
|
||||
|
||||
PHP is a popular general-purpose scripting language that is especially suited to
|
||||
web development. Fast, flexible and pragmatic, PHP powers everything from your
|
||||
blog to the most popular websites in the world. PHP is distributed under the
|
||||
[PHP License v3.01](LICENSE).
|
||||
|
||||
[](https://travis-ci.org/php/php-src)
|
||||
[](https://ci.appveyor.com/project/php/php-src)
|
||||
[](https://dev.azure.com/phpazuredevops/php/_build/latest?definitionId=1&branchName=master)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:php)
|
||||
|
||||
## Documentation
|
||||
|
||||
The PHP manual is available at [php.net/docs](https://php.net/docs).
|
||||
|
||||
## Installation
|
||||
|
||||
### Prebuilt packages and binaries
|
||||
|
||||
Prebuilt packages and binaries can be used to get up and running fast with PHP.
|
||||
|
||||
For Windows, the PHP binaries can be obtained from
|
||||
[windows.php.net](https://windows.php.net). After extracting the archive the
|
||||
`*.exe` files are ready to use.
|
||||
|
||||
For other systems, see the [installation chapter](https://php.net/install).
|
||||
|
||||
### Building PHP source code
|
||||
|
||||
*For Windows, see [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2).*
|
||||
|
||||
For a minimal PHP build from Git, you will need autoconf, bison, and re2c. For
|
||||
a default build, you will additionally need libxml2 and libsqlite3.
|
||||
|
||||
On Ubuntu, you can install these using:
|
||||
|
||||
sudo apt install -y pkg-config build-essential autoconf bison re2c \
|
||||
libxml2-dev libsqlite3-dev
|
||||
|
||||
On Fedora, you can install these using:
|
||||
|
||||
sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel
|
||||
|
||||
Generate configure:
|
||||
|
||||
./buildconf
|
||||
|
||||
Configure your build. `--enable-debug` is recommended for development, see
|
||||
`./configure --help` for a full list of options.
|
||||
|
||||
# For development
|
||||
./configure --enable-debug
|
||||
# For production
|
||||
./configure
|
||||
|
||||
Build PHP. To speed up the build, specify the maximum number of jobs using `-j`:
|
||||
|
||||
make -j4
|
||||
|
||||
The number of jobs should usually match the number of available cores, which
|
||||
can be determined using `nproc`.
|
||||
|
||||
## Testing PHP source code
|
||||
|
||||
PHP ships with an extensive test suite, the command `make test` is used after
|
||||
successful compilation of the sources to run this test suite.
|
||||
|
||||
It is possible to run tests using multiple cores by setting `-jN` in
|
||||
`TEST_PHP_ARGS`:
|
||||
|
||||
make TEST_PHP_ARGS=-j4 test
|
||||
|
||||
Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum
|
||||
number of jobs should not exceed the number of cores available.
|
||||
|
||||
The [qa.php.net](https://qa.php.net) site provides more detailed info about
|
||||
testing and quality assurance.
|
||||
|
||||
## Installing PHP built from source
|
||||
|
||||
After a successful build (and test), PHP may be installed with:
|
||||
|
||||
make install
|
||||
|
||||
Depending on your permissions and prefix, `make install` may need super user
|
||||
permissions.
|
||||
|
||||
## PHP extensions
|
||||
|
||||
Extensions provide additional functionality on top of PHP. PHP consists of many
|
||||
essential bundled extensions. Additional extensions can be found in the PHP
|
||||
Extension Community Library - [PECL](https://pecl.php.net).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are most welcome by forking the
|
||||
[GitHub repository](https://github.com/php/php-src) and sending a pull request.
|
||||
|
||||
Discussions are done on GitHub, but depending on the topic can also be relayed
|
||||
to the official PHP developer mailing list internals@lists.php.net.
|
||||
|
||||
New features require an RFC and must be accepted by the developers. See
|
||||
[Request for comments - RFC](https://wiki.php.net/rfc) and
|
||||
[Voting on PHP features](https://wiki.php.net/rfc/voting) for more information
|
||||
on the process.
|
||||
|
||||
Bug fixes **do not** require an RFC but require a bug tracker ticket. Open a
|
||||
ticket at [bugs.php.net](https://bugs.php.net) and reference the bug id using
|
||||
`#NNNNNN`.
|
||||
|
||||
Fix #55371: get_magic_quotes_gpc() throws deprecation warning
|
||||
|
||||
After removing magic quotes, the get_magic_quotes_gpc function caused a
|
||||
deprecated warning. get_magic_quotes_gpc can be used to detect the
|
||||
magic_quotes behavior and therefore should not raise a warning at any time.
|
||||
The patch removes this warning.
|
||||
|
||||
See [Git workflow](https://wiki.php.net/vcs/gitworkflow) for more details on the
|
||||
pull request workflow.
|
||||
|
||||
### Guidelines for contributors
|
||||
|
||||
See further documents in the repository for more information on how to
|
||||
contribute:
|
||||
|
||||
- [Contributing to PHP](/CONTRIBUTING.md)
|
||||
- [PHP coding standards](/CODING_STANDARDS.md)
|
||||
- [Mailinglist rules](/docs/mailinglist-rules.md)
|
||||
- [PHP release process](/docs/release-process.md)
|
||||
|
||||
## Credits
|
||||
|
||||
For the list of people who've put work into PHP, please see the
|
||||
[PHP credits page](https://php.net/credits.php).
|
||||
@@ -1,26 +0,0 @@
|
||||
Copyright (c) 1999-2006, Andi Gutmans, Sascha Schumann, Zeev Suraski.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
- Neither name of the copyright holders nor the names of their contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
|
||||
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-783
@@ -1,783 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Thread Safe Resource Manager |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski |
|
||||
| This source file is subject to the TSRM license, that is bundled |
|
||||
| with this package in the file LICENSE |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Zeev Suraski <zeev@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "TSRM.h"
|
||||
|
||||
#ifdef ZTS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if ZEND_DEBUG
|
||||
# include <assert.h>
|
||||
# define TSRM_ASSERT(c) assert(c)
|
||||
#else
|
||||
# define TSRM_ASSERT(c)
|
||||
#endif
|
||||
|
||||
typedef struct _tsrm_tls_entry tsrm_tls_entry;
|
||||
|
||||
/* TSRMLS_CACHE_DEFINE; is already done in Zend, this is being always compiled statically. */
|
||||
TSRMLS_CACHE_EXTERN();
|
||||
|
||||
struct _tsrm_tls_entry {
|
||||
void **storage;
|
||||
int count;
|
||||
THREAD_T thread_id;
|
||||
tsrm_tls_entry *next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
ts_allocate_ctor ctor;
|
||||
ts_allocate_dtor dtor;
|
||||
size_t fast_offset;
|
||||
int done;
|
||||
} tsrm_resource_type;
|
||||
|
||||
|
||||
/* The memory manager table */
|
||||
static tsrm_tls_entry **tsrm_tls_table=NULL;
|
||||
static int tsrm_tls_table_size;
|
||||
static ts_rsrc_id id_count;
|
||||
|
||||
/* The resource sizes table */
|
||||
static tsrm_resource_type *resource_types_table=NULL;
|
||||
static int resource_types_table_size;
|
||||
|
||||
/* Reserved space for fast globals access */
|
||||
static size_t tsrm_reserved_pos = 0;
|
||||
static size_t tsrm_reserved_size = 0;
|
||||
|
||||
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */
|
||||
static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */
|
||||
|
||||
/* New thread handlers */
|
||||
static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler = NULL;
|
||||
static tsrm_thread_end_func_t tsrm_new_thread_end_handler = NULL;
|
||||
static tsrm_shutdown_func_t tsrm_shutdown_handler = NULL;
|
||||
|
||||
/* Debug support */
|
||||
int tsrm_error(int level, const char *format, ...);
|
||||
|
||||
/* Read a resource from a thread's resource storage */
|
||||
static int tsrm_error_level;
|
||||
static FILE *tsrm_error_file;
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
#define TSRM_ERROR(args) tsrm_error args
|
||||
#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \
|
||||
{ \
|
||||
int unshuffled_offset = TSRM_UNSHUFFLE_RSRC_ID(offset); \
|
||||
\
|
||||
if (offset==0) { \
|
||||
return &array; \
|
||||
} else if ((unshuffled_offset)>=0 && (unshuffled_offset)<(range)) { \
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - 0x%0.8X", \
|
||||
unshuffled_offset, (long) thread_resources->thread_id, array[unshuffled_offset])); \
|
||||
return array[unshuffled_offset]; \
|
||||
} else { \
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", \
|
||||
unshuffled_offset, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1))); \
|
||||
return NULL; \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define TSRM_ERROR(args)
|
||||
#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \
|
||||
if (offset==0) { \
|
||||
return &array; \
|
||||
} else { \
|
||||
return array[TSRM_UNSHUFFLE_RSRC_ID(offset)]; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
static DWORD tls_key;
|
||||
# define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() TlsGetValue(tls_key)
|
||||
#else
|
||||
static pthread_key_t tls_key;
|
||||
# define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() pthread_getspecific(tls_key)
|
||||
#endif
|
||||
|
||||
TSRM_TLS uint8_t in_main_thread = 0;
|
||||
TSRM_TLS uint8_t is_thread_shutdown = 0;
|
||||
|
||||
/* Startup TSRM (call once for the entire process) */
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, const char *debug_filename)
|
||||
{/*{{{*/
|
||||
#ifdef TSRM_WIN32
|
||||
tls_key = TlsAlloc();
|
||||
#else
|
||||
pthread_key_create(&tls_key, 0);
|
||||
#endif
|
||||
|
||||
/* ensure singleton */
|
||||
in_main_thread = 1;
|
||||
is_thread_shutdown = 0;
|
||||
|
||||
tsrm_error_file = stderr;
|
||||
tsrm_error_set(debug_level, debug_filename);
|
||||
tsrm_tls_table_size = expected_threads;
|
||||
|
||||
tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
|
||||
if (!tsrm_tls_table) {
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
|
||||
is_thread_shutdown = 1;
|
||||
return 0;
|
||||
}
|
||||
id_count=0;
|
||||
|
||||
resource_types_table_size = expected_resources;
|
||||
resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
|
||||
if (!resource_types_table) {
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
|
||||
is_thread_shutdown = 1;
|
||||
free(tsrm_tls_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tsmm_mutex = tsrm_mutex_alloc();
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
|
||||
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = 0;
|
||||
|
||||
tsrm_env_mutex = tsrm_mutex_alloc();
|
||||
|
||||
return 1;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* Shutdown TSRM (call once for the entire process) */
|
||||
TSRM_API void tsrm_shutdown(void)
|
||||
{/*{{{*/
|
||||
int i;
|
||||
|
||||
if (is_thread_shutdown) {
|
||||
/* shutdown must only occur once */
|
||||
return;
|
||||
}
|
||||
|
||||
is_thread_shutdown = 1;
|
||||
|
||||
if (!in_main_thread) {
|
||||
/* only the main thread may shutdown tsrm */
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
|
||||
|
||||
while (p) {
|
||||
int j;
|
||||
|
||||
next_p = p->next;
|
||||
for (j=0; j<p->count; j++) {
|
||||
if (p->storage[j]) {
|
||||
if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
|
||||
resource_types_table[j].dtor(p->storage[j]);
|
||||
}
|
||||
if (!resource_types_table[j].fast_offset) {
|
||||
free(p->storage[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(p->storage);
|
||||
free(p);
|
||||
p = next_p;
|
||||
}
|
||||
}
|
||||
free(tsrm_tls_table);
|
||||
free(resource_types_table);
|
||||
tsrm_mutex_free(tsmm_mutex);
|
||||
tsrm_mutex_free(tsrm_env_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
|
||||
if (tsrm_error_file!=stderr) {
|
||||
fclose(tsrm_error_file);
|
||||
}
|
||||
#ifdef TSRM_WIN32
|
||||
TlsFree(tls_key);
|
||||
#else
|
||||
pthread_setspecific(tls_key, 0);
|
||||
pthread_key_delete(tls_key);
|
||||
#endif
|
||||
if (tsrm_shutdown_handler) {
|
||||
tsrm_shutdown_handler();
|
||||
}
|
||||
tsrm_new_thread_begin_handler = NULL;
|
||||
tsrm_new_thread_end_handler = NULL;
|
||||
tsrm_shutdown_handler = NULL;
|
||||
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = 0;
|
||||
}/*}}}*/
|
||||
|
||||
/* {{{ */
|
||||
/* environ lock api */
|
||||
TSRM_API void tsrm_env_lock(void) {
|
||||
tsrm_mutex_lock(tsrm_env_mutex);
|
||||
}
|
||||
|
||||
TSRM_API void tsrm_env_unlock(void) {
|
||||
tsrm_mutex_unlock(tsrm_env_mutex);
|
||||
} /* }}} */
|
||||
|
||||
/* enlarge the arrays for the already active threads */
|
||||
static void tsrm_update_active_threads(void)
|
||||
{/*{{{*/
|
||||
int i;
|
||||
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i];
|
||||
|
||||
while (p) {
|
||||
if (p->count < id_count) {
|
||||
int j;
|
||||
|
||||
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
|
||||
for (j=p->count; j<id_count; j++) {
|
||||
if (resource_types_table[j].fast_offset) {
|
||||
p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
|
||||
} else {
|
||||
p->storage[j] = (void *) malloc(resource_types_table[j].size);
|
||||
}
|
||||
if (resource_types_table[j].ctor) {
|
||||
resource_types_table[j].ctor(p->storage[j]);
|
||||
}
|
||||
}
|
||||
p->count = id_count;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* allocates a new thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
/* obtain a resource id */
|
||||
*rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
|
||||
|
||||
/* store the new resource type in the resource sizes table */
|
||||
if (resource_types_table_size < id_count) {
|
||||
tsrm_resource_type *_tmp;
|
||||
_tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
|
||||
if (!_tmp) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
|
||||
*rsrc_id = 0;
|
||||
return 0;
|
||||
}
|
||||
resource_types_table = _tmp;
|
||||
resource_types_table_size = id_count;
|
||||
}
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
|
||||
|
||||
tsrm_update_active_threads();
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
|
||||
return *rsrc_id;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* Reserve space for fast thread-safe-resources */
|
||||
TSRM_API void tsrm_reserve(size_t size)
|
||||
{/*{{{*/
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = TSRM_ALIGNED_SIZE(size);
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* allocates a new fast thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
/* obtain a resource id */
|
||||
*rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
|
||||
|
||||
size = TSRM_ALIGNED_SIZE(size);
|
||||
if (tsrm_reserved_size - tsrm_reserved_pos < size) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource"));
|
||||
*rsrc_id = 0;
|
||||
*offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
|
||||
tsrm_reserved_pos += size;
|
||||
|
||||
/* store the new resource type in the resource sizes table */
|
||||
if (resource_types_table_size < id_count) {
|
||||
tsrm_resource_type *_tmp;
|
||||
_tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
|
||||
if (!_tmp) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
|
||||
*rsrc_id = 0;
|
||||
return 0;
|
||||
}
|
||||
resource_types_table = _tmp;
|
||||
resource_types_table_size = id_count;
|
||||
}
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
|
||||
|
||||
tsrm_update_active_threads();
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
|
||||
return *rsrc_id;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
|
||||
{/*{{{*/
|
||||
int i;
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
|
||||
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
|
||||
(*thread_resources_ptr)->storage = NULL;
|
||||
if (id_count > 0) {
|
||||
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
|
||||
}
|
||||
(*thread_resources_ptr)->count = id_count;
|
||||
(*thread_resources_ptr)->thread_id = thread_id;
|
||||
(*thread_resources_ptr)->next = NULL;
|
||||
|
||||
/* Set thread local storage to this new thread resources structure */
|
||||
tsrm_tls_set(*thread_resources_ptr);
|
||||
TSRMLS_CACHE = *thread_resources_ptr;
|
||||
|
||||
if (tsrm_new_thread_begin_handler) {
|
||||
tsrm_new_thread_begin_handler(thread_id);
|
||||
}
|
||||
for (i=0; i<id_count; i++) {
|
||||
if (resource_types_table[i].done) {
|
||||
(*thread_resources_ptr)->storage[i] = NULL;
|
||||
} else {
|
||||
if (resource_types_table[i].fast_offset) {
|
||||
(*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
|
||||
} else {
|
||||
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
|
||||
}
|
||||
if (resource_types_table[i].ctor) {
|
||||
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tsrm_new_thread_end_handler) {
|
||||
tsrm_new_thread_end_handler(thread_id);
|
||||
}
|
||||
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* fetches the requested resource for the current thread */
|
||||
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
|
||||
{/*{{{*/
|
||||
THREAD_T thread_id;
|
||||
int hash_value;
|
||||
tsrm_tls_entry *thread_resources;
|
||||
|
||||
if (!th_id) {
|
||||
/* Fast path for looking up the resources for the current
|
||||
* thread. Its used by just about every call to
|
||||
* ts_resource_ex(). This avoids the need for a mutex lock
|
||||
* and our hashtable lookup.
|
||||
*/
|
||||
thread_resources = tsrm_tls_get();
|
||||
|
||||
if (thread_resources) {
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id));
|
||||
/* Read a specific resource from the thread's resources.
|
||||
* This is called outside of a mutex, so have to be aware about external
|
||||
* changes to the structure as we read it.
|
||||
*/
|
||||
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
|
||||
}
|
||||
thread_id = tsrm_thread_id();
|
||||
} else {
|
||||
thread_id = *th_id;
|
||||
}
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id));
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
|
||||
thread_resources = tsrm_tls_table[hash_value];
|
||||
|
||||
if (!thread_resources) {
|
||||
allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
|
||||
return ts_resource_ex(id, &thread_id);
|
||||
} else {
|
||||
do {
|
||||
if (thread_resources->thread_id == thread_id) {
|
||||
break;
|
||||
}
|
||||
if (thread_resources->next) {
|
||||
thread_resources = thread_resources->next;
|
||||
} else {
|
||||
allocate_new_resource(&thread_resources->next, thread_id);
|
||||
return ts_resource_ex(id, &thread_id);
|
||||
/*
|
||||
* thread_resources = thread_resources->next;
|
||||
* break;
|
||||
*/
|
||||
}
|
||||
} while (thread_resources);
|
||||
}
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
/* Read a specific resource from the thread's resources.
|
||||
* This is called outside of a mutex, so have to be aware about external
|
||||
* changes to the structure as we read it.
|
||||
*/
|
||||
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* frees all resources allocated for the current thread */
|
||||
void ts_free_thread(void)
|
||||
{/*{{{*/
|
||||
tsrm_tls_entry *thread_resources;
|
||||
int i;
|
||||
THREAD_T thread_id = tsrm_thread_id();
|
||||
int hash_value;
|
||||
tsrm_tls_entry *last=NULL;
|
||||
|
||||
TSRM_ASSERT(!in_main_thread);
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
|
||||
thread_resources = tsrm_tls_table[hash_value];
|
||||
|
||||
while (thread_resources) {
|
||||
if (thread_resources->thread_id == thread_id) {
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (resource_types_table[i].dtor) {
|
||||
resource_types_table[i].dtor(thread_resources->storage[i]);
|
||||
}
|
||||
}
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (!resource_types_table[i].fast_offset) {
|
||||
free(thread_resources->storage[i]);
|
||||
}
|
||||
}
|
||||
free(thread_resources->storage);
|
||||
if (last) {
|
||||
last->next = thread_resources->next;
|
||||
} else {
|
||||
tsrm_tls_table[hash_value] = thread_resources->next;
|
||||
}
|
||||
tsrm_tls_set(0);
|
||||
free(thread_resources);
|
||||
break;
|
||||
}
|
||||
if (thread_resources->next) {
|
||||
last = thread_resources;
|
||||
}
|
||||
thread_resources = thread_resources->next;
|
||||
}
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
}/*}}}*/
|
||||
|
||||
/* deallocates all occurrences of a given id */
|
||||
void ts_free_id(ts_rsrc_id id)
|
||||
{/*{{{*/
|
||||
int i;
|
||||
int j = TSRM_UNSHUFFLE_RSRC_ID(id);
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id));
|
||||
|
||||
if (tsrm_tls_table) {
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i];
|
||||
|
||||
while (p) {
|
||||
if (p->count > j && p->storage[j]) {
|
||||
if (resource_types_table && resource_types_table[j].dtor) {
|
||||
resource_types_table[j].dtor(p->storage[j]);
|
||||
}
|
||||
if (!resource_types_table[j].fast_offset) {
|
||||
free(p->storage[j]);
|
||||
}
|
||||
p->storage[j] = NULL;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
resource_types_table[j].done = 1;
|
||||
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id));
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/*
|
||||
* Utility Functions
|
||||
*/
|
||||
|
||||
/* Obtain the current thread id */
|
||||
TSRM_API THREAD_T tsrm_thread_id(void)
|
||||
{/*{{{*/
|
||||
#ifdef TSRM_WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* Allocate a mutex */
|
||||
TSRM_API MUTEX_T tsrm_mutex_alloc(void)
|
||||
{/*{{{*/
|
||||
MUTEX_T mutexp;
|
||||
#ifdef TSRM_WIN32
|
||||
mutexp = malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSection(mutexp);
|
||||
#else
|
||||
mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(mutexp,NULL);
|
||||
#endif
|
||||
#ifdef THR_DEBUG
|
||||
printf("Mutex created thread: %d\n",mythreadid());
|
||||
#endif
|
||||
return( mutexp );
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* Free a mutex */
|
||||
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
if (mutexp) {
|
||||
#ifdef TSRM_WIN32
|
||||
DeleteCriticalSection(mutexp);
|
||||
free(mutexp);
|
||||
#else
|
||||
pthread_mutex_destroy(mutexp);
|
||||
free(mutexp);
|
||||
#endif
|
||||
}
|
||||
#ifdef THR_DEBUG
|
||||
printf("Mutex freed thread: %d\n",mythreadid());
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/*
|
||||
Lock a mutex.
|
||||
A return value of 0 indicates success
|
||||
*/
|
||||
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id()));
|
||||
#ifdef TSRM_WIN32
|
||||
EnterCriticalSection(mutexp);
|
||||
return 0;
|
||||
#else
|
||||
return pthread_mutex_lock(mutexp);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/*
|
||||
Unlock a mutex.
|
||||
A return value of 0 indicates success
|
||||
*/
|
||||
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id()));
|
||||
#ifdef TSRM_WIN32
|
||||
LeaveCriticalSection(mutexp);
|
||||
return 0;
|
||||
#else
|
||||
return pthread_mutex_unlock(mutexp);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
/*
|
||||
Changes the signal mask of the calling thread
|
||||
*/
|
||||
#ifdef HAVE_SIGPROCMASK
|
||||
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id()));
|
||||
|
||||
return pthread_sigmask(how, set, oldset);
|
||||
}/*}}}*/
|
||||
#endif
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)
|
||||
{/*{{{*/
|
||||
void *retval = (void *) tsrm_new_thread_begin_handler;
|
||||
|
||||
tsrm_new_thread_begin_handler = new_thread_begin_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)
|
||||
{/*{{{*/
|
||||
void *retval = (void *) tsrm_new_thread_end_handler;
|
||||
|
||||
tsrm_new_thread_end_handler = new_thread_end_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)
|
||||
{/*{{{*/
|
||||
void *retval = (void *) tsrm_shutdown_handler;
|
||||
|
||||
tsrm_shutdown_handler = shutdown_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/*
|
||||
* Debug support
|
||||
*/
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
int tsrm_error(int level, const char *format, ...)
|
||||
{/*{{{*/
|
||||
if (level<=tsrm_error_level) {
|
||||
va_list args;
|
||||
int size;
|
||||
|
||||
fprintf(tsrm_error_file, "TSRM: ");
|
||||
va_start(args, format);
|
||||
size = vfprintf(tsrm_error_file, format, args);
|
||||
va_end(args);
|
||||
fprintf(tsrm_error_file, "\n");
|
||||
fflush(tsrm_error_file);
|
||||
return size;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}/*}}}*/
|
||||
#endif
|
||||
|
||||
|
||||
void tsrm_error_set(int level, const char *debug_filename)
|
||||
{/*{{{*/
|
||||
tsrm_error_level = level;
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
if (tsrm_error_file!=stderr) { /* close files opened earlier */
|
||||
fclose(tsrm_error_file);
|
||||
}
|
||||
|
||||
if (debug_filename) {
|
||||
tsrm_error_file = fopen(debug_filename, "w");
|
||||
if (!tsrm_error_file) {
|
||||
tsrm_error_file = stderr;
|
||||
}
|
||||
} else {
|
||||
tsrm_error_file = stderr;
|
||||
}
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API void *tsrm_get_ls_cache(void)
|
||||
{/*{{{*/
|
||||
return tsrm_tls_get();
|
||||
}/*}}}*/
|
||||
|
||||
/* Returns offset of tsrm_ls_cache slot from Thread Control Block address */
|
||||
TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
|
||||
{/*{{{*/
|
||||
#if defined(__APPLE__) && defined(__x86_64__)
|
||||
// TODO: Implement support for fast JIT ZTS code ???
|
||||
return 0;
|
||||
#elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__)
|
||||
size_t ret;
|
||||
|
||||
asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
|
||||
: "=r" (ret));
|
||||
return ret;
|
||||
#elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__)
|
||||
size_t ret;
|
||||
|
||||
asm ("leal _tsrm_ls_cache@ntpoff,%0"
|
||||
: "=r" (ret));
|
||||
return ret;
|
||||
#elif defined(__aarch64__)
|
||||
size_t ret;
|
||||
|
||||
# ifdef __APPLE__
|
||||
// Points to struct TLVDecriptor for _tsrm_ls_cache in macOS.
|
||||
asm("adrp %0, #__tsrm_ls_cache@TLVPPAGE\n\t"
|
||||
"ldr %0, [%0, #__tsrm_ls_cache@TLVPPAGEOFF]"
|
||||
: "=r" (ret));
|
||||
# else
|
||||
asm("mov %0, xzr\n\t"
|
||||
"add %0, %0, #:tprel_hi12:_tsrm_ls_cache, lsl #12\n\t"
|
||||
"add %0, %0, #:tprel_lo12_nc:_tsrm_ls_cache"
|
||||
: "=r" (ret));
|
||||
# endif
|
||||
return ret;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API uint8_t tsrm_is_main_thread(void)
|
||||
{/*{{{*/
|
||||
return in_main_thread;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API uint8_t tsrm_is_shutdown(void)
|
||||
{/*{{{*/
|
||||
return is_thread_shutdown;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API const char *tsrm_api_name(void)
|
||||
{/*{{{*/
|
||||
#ifdef TSRM_WIN32
|
||||
return "Windows Threads";
|
||||
#else
|
||||
return "POSIX Threads";
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
#endif /* ZTS */
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Thread Safe Resource Manager |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski |
|
||||
| This source file is subject to the TSRM license, that is bundled |
|
||||
| with this package in the file LICENSE |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Zeev Suraski <zeev@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef TSRM_H
|
||||
#define TSRM_H
|
||||
|
||||
#if !defined(__CYGWIN__) && defined(WIN32)
|
||||
# define TSRM_WIN32
|
||||
# include "Zend/zend_config.w32.h"
|
||||
#else
|
||||
# include "main/php_config.h"
|
||||
#endif
|
||||
|
||||
#include "main/php_stdint.h"
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# ifdef TSRM_EXPORTS
|
||||
# define TSRM_API __declspec(dllexport)
|
||||
# else
|
||||
# define TSRM_API __declspec(dllimport)
|
||||
# endif
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define TSRM_API __attribute__ ((visibility("default")))
|
||||
#else
|
||||
# define TSRM_API
|
||||
#endif
|
||||
|
||||
typedef intptr_t tsrm_intptr_t;
|
||||
typedef uintptr_t tsrm_uintptr_t;
|
||||
|
||||
/* Only compile multi-threading functions if we're in ZTS mode */
|
||||
#ifdef ZTS
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
#else
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if SIZEOF_SIZE_T == 4
|
||||
# define TSRM_ALIGNED_SIZE(size) \
|
||||
(((size) + INT32_C(15)) & ~INT32_C(15))
|
||||
#else
|
||||
# define TSRM_ALIGNED_SIZE(size) \
|
||||
(((size) + INT64_C(15)) & ~INT64_C(15))
|
||||
#endif
|
||||
|
||||
typedef int ts_rsrc_id;
|
||||
|
||||
/* Define THREAD_T and MUTEX_T */
|
||||
#ifdef TSRM_WIN32
|
||||
# define THREAD_T DWORD
|
||||
# define MUTEX_T CRITICAL_SECTION *
|
||||
#else
|
||||
# define THREAD_T pthread_t
|
||||
# define MUTEX_T pthread_mutex_t *
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
typedef void (*ts_allocate_ctor)(void *);
|
||||
typedef void (*ts_allocate_dtor)(void *);
|
||||
|
||||
#define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* startup/shutdown */
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, const char *debug_filename);
|
||||
TSRM_API void tsrm_shutdown(void);
|
||||
|
||||
/* environ lock API */
|
||||
TSRM_API void tsrm_env_lock(void);
|
||||
TSRM_API void tsrm_env_unlock(void);
|
||||
|
||||
/* allocates a new thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
|
||||
|
||||
/* Fast resource in reserved (pre-allocated) space */
|
||||
TSRM_API void tsrm_reserve(size_t size);
|
||||
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
|
||||
|
||||
/* fetches the requested resource for the current thread */
|
||||
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id);
|
||||
#define ts_resource(id) ts_resource_ex(id, NULL)
|
||||
|
||||
/* frees all resources allocated for the current thread */
|
||||
TSRM_API void ts_free_thread(void);
|
||||
|
||||
/* deallocates all occurrences of a given id */
|
||||
TSRM_API void ts_free_id(ts_rsrc_id id);
|
||||
|
||||
|
||||
/* Debug support */
|
||||
#define TSRM_ERROR_LEVEL_ERROR 1
|
||||
#define TSRM_ERROR_LEVEL_CORE 2
|
||||
#define TSRM_ERROR_LEVEL_INFO 3
|
||||
|
||||
typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id);
|
||||
typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id);
|
||||
typedef void (*tsrm_shutdown_func_t)(void);
|
||||
|
||||
|
||||
TSRM_API int tsrm_error(int level, const char *format, ...);
|
||||
TSRM_API void tsrm_error_set(int level, const char *debug_filename);
|
||||
|
||||
/* utility functions */
|
||||
TSRM_API THREAD_T tsrm_thread_id(void);
|
||||
TSRM_API MUTEX_T tsrm_mutex_alloc(void);
|
||||
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp);
|
||||
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp);
|
||||
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp);
|
||||
#ifdef HAVE_SIGPROCMASK
|
||||
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset);
|
||||
#endif
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler);
|
||||
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler);
|
||||
TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler);
|
||||
|
||||
TSRM_API void *tsrm_get_ls_cache(void);
|
||||
TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void);
|
||||
TSRM_API uint8_t tsrm_is_main_thread(void);
|
||||
TSRM_API uint8_t tsrm_is_shutdown(void);
|
||||
TSRM_API const char *tsrm_api_name(void);
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# define TSRM_TLS __declspec(thread)
|
||||
#else
|
||||
# define TSRM_TLS __thread
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if !__has_attribute(tls_model) || defined(__FreeBSD__) || defined(__MUSL__)
|
||||
# define TSRM_TLS_MODEL_ATTR
|
||||
#elif __PIC__
|
||||
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec")))
|
||||
#else
|
||||
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("local-exec")))
|
||||
#endif
|
||||
|
||||
#define TSRM_SHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)+1)
|
||||
#define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1)
|
||||
|
||||
#define TSRMG(id, type, element) (TSRMG_BULK(id, type)->element)
|
||||
#define TSRMG_BULK(id, type) ((type) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(id)])
|
||||
#define TSRMG_FAST(offset, type, element) (TSRMG_FAST_BULK(offset, type)->element)
|
||||
#define TSRMG_FAST_BULK(offset, type) ((type) (((char*) tsrm_get_ls_cache())+(offset)))
|
||||
|
||||
#define TSRMG_STATIC(id, type, element) (TSRMG_BULK_STATIC(id, type)->element)
|
||||
#define TSRMG_BULK_STATIC(id, type) ((type) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(id)])
|
||||
#define TSRMG_FAST_STATIC(offset, type, element) (TSRMG_FAST_BULK_STATIC(offset, type)->element)
|
||||
#define TSRMG_FAST_BULK_STATIC(offset, type) ((type) (((char*) TSRMLS_CACHE)+(offset)))
|
||||
#define TSRMLS_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR;
|
||||
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
|
||||
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache()
|
||||
#define TSRMLS_CACHE _tsrm_ls_cache
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* non ZTS */
|
||||
|
||||
#define tsrm_env_lock()
|
||||
#define tsrm_env_unlock()
|
||||
|
||||
#define TSRMG_STATIC(id, type, element)
|
||||
#define TSRMLS_CACHE_EXTERN()
|
||||
#define TSRMLS_CACHE_DEFINE()
|
||||
#define TSRMLS_CACHE_UPDATE()
|
||||
#define TSRMLS_CACHE
|
||||
|
||||
#define TSRM_TLS
|
||||
|
||||
#endif /* ZTS */
|
||||
|
||||
#endif /* TSRM_H */
|
||||
@@ -1,4 +0,0 @@
|
||||
// vim:ft=javascript
|
||||
|
||||
ADD_SOURCES("TSRM", "TSRM.c tsrm_win32.c");
|
||||
ADD_FLAG("CFLAGS_BD_TSRM", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
dnl Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved.
|
||||
dnl
|
||||
dnl Redistribution and use in source and binary forms, with or without
|
||||
dnl modification, are permitted provided that the following conditions
|
||||
dnl are met:
|
||||
dnl
|
||||
dnl 1. Redistributions of source code must retain the above copyright
|
||||
dnl notice, this list of conditions and the following disclaimer.
|
||||
dnl
|
||||
dnl 2. Redistributions in binary form must reproduce the above copyright
|
||||
dnl notice, this list of conditions and the following disclaimer in
|
||||
dnl the documentation and/or other materials provided with the
|
||||
dnl distribution.
|
||||
dnl
|
||||
dnl THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY
|
||||
dnl EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
dnl IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
dnl PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SASCHA SCHUMANN OR
|
||||
dnl HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
dnl NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
dnl LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
dnl HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
dnl STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
dnl ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
dnl OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
dnl
|
||||
dnl PTHREADS_FLAGS
|
||||
dnl
|
||||
dnl Set some magic defines to achieve POSIX threads conformance.
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_FLAGS],[
|
||||
if test -z "$host_alias" && test -n "$host"; then
|
||||
host_alias=$host
|
||||
fi
|
||||
if test -z "$host_alias"; then
|
||||
AC_MSG_ERROR(host_alias is not set. Make sure to run config.guess)
|
||||
fi
|
||||
case $host_alias in
|
||||
*solaris*)
|
||||
PTHREAD_FLAGS="-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT";;
|
||||
*freebsd*)
|
||||
PTHREAD_FLAGS="-D_REENTRANT -D_THREAD_SAFE";;
|
||||
*linux*)
|
||||
PTHREAD_FLAGS=-D_REENTRANT;;
|
||||
*aix*)
|
||||
PTHREAD_FLAGS=-D_THREAD_SAFE;;
|
||||
*irix*)
|
||||
PTHREAD_FLAGS=-D_POSIX_THREAD_SAFE_FUNCTIONS;;
|
||||
*hpux*)
|
||||
PTHREAD_FLAGS=-D_REENTRANT;;
|
||||
*sco*)
|
||||
PTHREAD_FLAGS=-D_REENTRANT;;
|
||||
esac
|
||||
|
||||
if test -n "$PTHREAD_FLAGS"; then
|
||||
CPPFLAGS="$CPPFLAGS $PTHREAD_FLAGS"
|
||||
fi
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl PTHREADS_CHECK_COMPILE
|
||||
dnl
|
||||
dnl Check whether the current setup can use POSIX threads calls.
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_CHECK_COMPILE], [
|
||||
AC_LINK_IFELSE([ AC_LANG_SOURCE([
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void *thread_routine(void *data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
int main() {
|
||||
pthread_t thd;
|
||||
pthread_mutexattr_t mattr;
|
||||
int data = 1;
|
||||
pthread_mutexattr_init(&mattr);
|
||||
return pthread_create(&thd, NULL, thread_routine, &data);
|
||||
} ]) ], [
|
||||
pthreads_checked=yes
|
||||
], [
|
||||
pthreads_checked=no
|
||||
]
|
||||
) ] )
|
||||
|
||||
dnl
|
||||
dnl PTHREADS_CHECK
|
||||
dnl
|
||||
dnl Try to find a way to enable POSIX threads.
|
||||
dnl
|
||||
dnl Magic flags
|
||||
dnl -kthread gcc (FreeBSD)
|
||||
dnl -Kthread UDK cc (UnixWare)
|
||||
dnl -mt WorkShop cc (Solaris)
|
||||
dnl -mthreads gcc (AIX)
|
||||
dnl -pthread gcc (Linux, FreeBSD, NetBSD, OpenBSD)
|
||||
dnl -pthreads gcc (Solaris)
|
||||
dnl -qthreaded AIX cc V5
|
||||
dnl -threads gcc (HP-UX)
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_CHECK],[
|
||||
AC_CACHE_CHECK(for pthreads_cflags,ac_cv_pthreads_cflags,[
|
||||
ac_cv_pthreads_cflags=
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
for flag in -kthread -pthread -pthreads -mthreads -Kthread -threads -mt -qthreaded; do
|
||||
ac_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $flag"
|
||||
PTHREADS_CHECK_COMPILE
|
||||
CFLAGS=$ac_save
|
||||
if test "$pthreads_checked" = "yes"; then
|
||||
ac_cv_pthreads_cflags=$flag
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK(for pthreads_lib, ac_cv_pthreads_lib,[
|
||||
ac_cv_pthreads_lib=
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
for lib in pthread pthreads c_r; do
|
||||
ac_save=$LIBS
|
||||
LIBS="$LIBS -l$lib"
|
||||
PTHREADS_CHECK_COMPILE
|
||||
LIBS=$ac_save
|
||||
if test "$pthreads_checked" = "yes"; then
|
||||
ac_cv_pthreads_lib=$lib
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
if test "x$ac_cv_pthreads_cflags" != "x" -o "x$ac_cv_pthreads_lib" != "x"; then
|
||||
pthreads_working="yes"
|
||||
fi
|
||||
])
|
||||
@@ -1,17 +0,0 @@
|
||||
dnl This file contains TSRM specific autoconf macros.
|
||||
|
||||
dnl
|
||||
dnl TSRM_CHECK_PTHREADS
|
||||
dnl
|
||||
AC_DEFUN([TSRM_CHECK_PTHREADS],[
|
||||
PTHREADS_CHECK
|
||||
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
AC_MSG_ERROR(Your system seems to lack POSIX threads.)
|
||||
fi
|
||||
|
||||
AC_DEFINE(PTHREADS, 1, Whether to use Pthreads)
|
||||
|
||||
AC_MSG_CHECKING(for POSIX threads)
|
||||
AC_MSG_RESULT(yes)
|
||||
])
|
||||
@@ -1,825 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Daniel Beulshausen <daniel@php4win.de> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
|
||||
#include "SAPI.h"
|
||||
#include "TSRM.h"
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
#include <Sddl.h>
|
||||
#include "tsrm_win32.h"
|
||||
#include "zend_virtual_cwd.h"
|
||||
#include "win32/ioutil.h"
|
||||
|
||||
#ifdef ZTS
|
||||
static ts_rsrc_id win32_globals_id;
|
||||
#else
|
||||
static tsrm_win32_globals win32_globals;
|
||||
#endif
|
||||
|
||||
static void tsrm_win32_ctor(tsrm_win32_globals *globals)
|
||||
{/*{{{*/
|
||||
#ifdef ZTS
|
||||
TSRMLS_CACHE_UPDATE();
|
||||
#endif
|
||||
globals->process = NULL;
|
||||
globals->shm = NULL;
|
||||
globals->process_size = 0;
|
||||
globals->shm_size = 0;
|
||||
globals->comspec = _strdup("cmd.exe");
|
||||
|
||||
/* Set it to INVALID_HANDLE_VALUE
|
||||
* It will be initialized correctly in tsrm_win32_access or set to
|
||||
* NULL if no impersonation has been done.
|
||||
* the impersonated token can't be set here as the impersonation
|
||||
* will happen later, in fcgi_accept_request (or whatever is the
|
||||
* SAPI being used).
|
||||
*/
|
||||
globals->impersonation_token = INVALID_HANDLE_VALUE;
|
||||
globals->impersonation_token_sid = NULL;
|
||||
}/*}}}*/
|
||||
|
||||
static void tsrm_win32_dtor(tsrm_win32_globals *globals)
|
||||
{/*{{{*/
|
||||
shm_pair *ptr;
|
||||
|
||||
if (globals->process) {
|
||||
free(globals->process);
|
||||
}
|
||||
|
||||
if (globals->shm) {
|
||||
for (ptr = globals->shm; ptr < (globals->shm + globals->shm_size); ptr++) {
|
||||
UnmapViewOfFile(ptr->addr);
|
||||
CloseHandle(ptr->segment);
|
||||
UnmapViewOfFile(ptr->descriptor);
|
||||
CloseHandle(ptr->info);
|
||||
}
|
||||
free(globals->shm);
|
||||
}
|
||||
|
||||
free(globals->comspec);
|
||||
|
||||
if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE ) {
|
||||
CloseHandle(globals->impersonation_token);
|
||||
}
|
||||
if (globals->impersonation_token_sid) {
|
||||
free(globals->impersonation_token_sid);
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API void tsrm_win32_startup(void)
|
||||
{/*{{{*/
|
||||
#ifdef ZTS
|
||||
ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_ctor)tsrm_win32_dtor);
|
||||
#else
|
||||
tsrm_win32_ctor(&win32_globals);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API void tsrm_win32_shutdown(void)
|
||||
{/*{{{*/
|
||||
#ifndef ZTS
|
||||
tsrm_win32_dtor(&win32_globals);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
const char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len)
|
||||
{/*{{{*/
|
||||
PSID pSid = TWG(impersonation_token_sid);
|
||||
char *ptcSid = NULL;
|
||||
char *bucket_key = NULL;
|
||||
size_t ptc_sid_len;
|
||||
|
||||
if (!pSid) {
|
||||
*key_len = pathname_len;
|
||||
return pathname;
|
||||
}
|
||||
|
||||
if (!ConvertSidToStringSid(pSid, &ptcSid)) {
|
||||
*key_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ptc_sid_len = strlen(ptcSid);
|
||||
*key_len = pathname_len + ptc_sid_len;
|
||||
bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *key_len + 1);
|
||||
if (!bucket_key) {
|
||||
LocalFree(ptcSid);
|
||||
*key_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(bucket_key, ptcSid, ptc_sid_len);
|
||||
memcpy(bucket_key + ptc_sid_len, pathname, pathname_len + 1);
|
||||
|
||||
LocalFree(ptcSid);
|
||||
return bucket_key;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
PSID tsrm_win32_get_token_sid(HANDLE hToken)
|
||||
{/*{{{*/
|
||||
DWORD dwLength = 0;
|
||||
PTOKEN_USER pTokenUser = NULL;
|
||||
DWORD sid_len;
|
||||
PSID pResultSid = NULL;
|
||||
|
||||
/* Get the actual size of the TokenUser structure */
|
||||
if (!GetTokenInformation(
|
||||
hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength)) {
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
|
||||
if (pTokenUser == NULL) {
|
||||
goto Finished;
|
||||
}
|
||||
}
|
||||
|
||||
/* and fetch it now */
|
||||
if (!GetTokenInformation(
|
||||
hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
sid_len = GetLengthSid(pTokenUser->User.Sid);
|
||||
|
||||
/* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
|
||||
pResultSid = malloc(sid_len);
|
||||
if (!pResultSid) {
|
||||
goto Finished;
|
||||
}
|
||||
if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) {
|
||||
goto Finished;
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
|
||||
return pResultSid;
|
||||
|
||||
Finished:
|
||||
if (pResultSid) {
|
||||
free(pResultSid);
|
||||
}
|
||||
/* Free the buffer for the token groups. */
|
||||
if (pTokenUser != NULL) {
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
|
||||
}
|
||||
return NULL;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode)
|
||||
{/*{{{*/
|
||||
time_t t;
|
||||
HANDLE thread_token = NULL;
|
||||
PSID token_sid;
|
||||
SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
|
||||
GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
|
||||
DWORD priv_set_length = sizeof(PRIVILEGE_SET);
|
||||
|
||||
PRIVILEGE_SET privilege_set = {0};
|
||||
DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0;
|
||||
BYTE * psec_desc = NULL;
|
||||
BOOL fAccess = FALSE;
|
||||
|
||||
realpath_cache_bucket * bucket = NULL;
|
||||
char real_path[MAXPATHLEN] = {0};
|
||||
|
||||
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
|
||||
if(tsrm_realpath(pathname, real_path) == NULL) {
|
||||
SET_ERRNO_FROM_WIN32_CODE(ERROR_FILE_NOT_FOUND);
|
||||
return -1;
|
||||
}
|
||||
pathname = real_path;
|
||||
}
|
||||
|
||||
PHP_WIN32_IOUTIL_INIT_W(pathname)
|
||||
if (!pathw) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Either access call failed, or the mode was asking for a specific check.*/
|
||||
int ret = php_win32_ioutil_access_w(pathw, mode);
|
||||
if (0 > ret || X_OK == mode || F_OK == mode) {
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Only in NTS when impersonate==1 (aka FastCGI) */
|
||||
|
||||
/*
|
||||
AccessCheck() requires an impersonation token. We first get a primary
|
||||
token and then create a duplicate impersonation token. The
|
||||
impersonation token is not actually assigned to the thread, but is
|
||||
used in the call to AccessCheck. Thus, this function itself never
|
||||
impersonates, but does use the identity of the thread. If the thread
|
||||
was impersonating already, this function uses that impersonation context.
|
||||
*/
|
||||
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
||||
if (GetLastError() == ERROR_NO_TOKEN) {
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
|
||||
TWG(impersonation_token) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* token_sid will be freed in tsrmwin32_dtor */
|
||||
token_sid = tsrm_win32_get_token_sid(thread_token);
|
||||
if (!token_sid) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
/* Different identity, we need a new impersonated token as well */
|
||||
if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = token_sid;
|
||||
|
||||
/* Duplicate the token as impersonated token */
|
||||
if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
|
||||
goto Finished;
|
||||
}
|
||||
} else {
|
||||
/* we already have it, free it then */
|
||||
free(token_sid);
|
||||
}
|
||||
|
||||
if (CWDG(realpath_cache_size_limit)) {
|
||||
t = time(0);
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||
if(bucket == NULL && !real_path[0]) {
|
||||
/* We used the pathname directly. Call tsrm_realpath */
|
||||
/* so that entry is created in realpath cache */
|
||||
if(tsrm_realpath(pathname, real_path) != NULL) {
|
||||
pathname = real_path;
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||
PHP_WIN32_IOUTIL_REINIT_W(pathname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a full access check because access() will only check read-only attribute */
|
||||
if(mode == 0 || mode > 6) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ;
|
||||
} else if(mode <= 2) {
|
||||
if(bucket != NULL && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_writable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_WRITE;
|
||||
} else if(mode <= 4) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
|
||||
} else { // if(mode <= 6)
|
||||
if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_readable & bucket->is_writable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if(TWG(impersonation_token) == NULL) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
/* Get size of security buffer. Call is expected to fail */
|
||||
if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
psec_desc = (BYTE *)malloc(sec_desc_length);
|
||||
if(psec_desc == NULL ||
|
||||
!GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
MapGenericMask(&desired_access, &gen_map);
|
||||
|
||||
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
|
||||
goto Finished_Impersonate;
|
||||
}
|
||||
|
||||
/* Keep the result in realpath_cache */
|
||||
if(bucket != NULL) {
|
||||
if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
}
|
||||
else if(desired_access == FILE_GENERIC_WRITE) {
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
}
|
||||
}
|
||||
|
||||
Finished_Impersonate:
|
||||
if(psec_desc != NULL) {
|
||||
free(psec_desc);
|
||||
psec_desc = NULL;
|
||||
}
|
||||
|
||||
Finished:
|
||||
if(thread_token != NULL) {
|
||||
CloseHandle(thread_token);
|
||||
}
|
||||
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
if(fAccess == FALSE) {
|
||||
errno = EACCES;
|
||||
return errno;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
static process_pair *process_get(FILE *stream)
|
||||
{/*{{{*/
|
||||
process_pair *ptr;
|
||||
process_pair *newptr;
|
||||
|
||||
for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) {
|
||||
if (ptr->stream == stream) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr < (TWG(process) + TWG(process_size))) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair));
|
||||
if (newptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TWG(process) = newptr;
|
||||
ptr = newptr + TWG(process_size);
|
||||
TWG(process_size)++;
|
||||
return ptr;
|
||||
}/*}}}*/
|
||||
|
||||
static shm_pair *shm_get(key_t key, void *addr)
|
||||
{/*{{{*/
|
||||
shm_pair *ptr;
|
||||
shm_pair *newptr;
|
||||
|
||||
for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
|
||||
if (!ptr->descriptor) {
|
||||
continue;
|
||||
}
|
||||
if (!addr && ptr->descriptor->shm_perm.key == key) {
|
||||
break;
|
||||
} else if (ptr->addr == addr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr < (TWG(shm) + TWG(shm_size))) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair));
|
||||
if (newptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TWG(shm) = newptr;
|
||||
ptr = newptr + TWG(shm_size);
|
||||
TWG(shm_size)++;
|
||||
memset(ptr, 0, sizeof(*ptr));
|
||||
return ptr;
|
||||
}/*}}}*/
|
||||
|
||||
static HANDLE dupHandle(HANDLE fh, BOOL inherit)
|
||||
{/*{{{*/
|
||||
HANDLE copy, self = GetCurrentProcess();
|
||||
if (!DuplicateHandle(self, fh, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
|
||||
return NULL;
|
||||
}
|
||||
return copy;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API FILE *popen(const char *command, const char *type)
|
||||
{/*{{{*/
|
||||
|
||||
return popen_ex(command, type, NULL, NULL);
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, const char *env)
|
||||
{/*{{{*/
|
||||
FILE *stream = NULL;
|
||||
int fno, type_len, read, mode;
|
||||
STARTUPINFOW startup;
|
||||
PROCESS_INFORMATION process;
|
||||
SECURITY_ATTRIBUTES security;
|
||||
HANDLE in, out;
|
||||
DWORD dwCreateFlags = 0;
|
||||
BOOL res;
|
||||
process_pair *proc;
|
||||
char *cmd = NULL;
|
||||
wchar_t *cmdw = NULL, *cwdw = NULL, *envw = NULL;
|
||||
char *ptype = (char *)type;
|
||||
HANDLE thread_token = NULL;
|
||||
HANDLE token_user = NULL;
|
||||
BOOL asuser = TRUE;
|
||||
|
||||
if (!type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
type_len = (int)strlen(type);
|
||||
if (type_len < 1 || type_len > 2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ptype[0] != 'r' && ptype[0] != 'w') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type_len > 1 && (ptype[1] != 'b' && ptype[1] != 't')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /s /c ")+2);
|
||||
if (!cmd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(cmd, "%s /s /c \"%s\"", TWG(comspec), command);
|
||||
cmdw = php_win32_cp_any_to_w(cmd);
|
||||
if (!cmdw) {
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cwd) {
|
||||
cwdw = php_win32_ioutil_any_to_w(cwd);
|
||||
if (!cwdw) {
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security.bInheritHandle = TRUE;
|
||||
security.lpSecurityDescriptor = NULL;
|
||||
|
||||
if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&startup, 0, sizeof(STARTUPINFOW));
|
||||
memset(&process, 0, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
startup.cb = sizeof(STARTUPINFOW);
|
||||
startup.dwFlags = STARTF_USESTDHANDLES;
|
||||
startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
read = (type[0] == 'r') ? TRUE : FALSE;
|
||||
mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
|
||||
|
||||
if (read) {
|
||||
in = dupHandle(in, FALSE);
|
||||
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
startup.hStdOutput = out;
|
||||
} else {
|
||||
out = dupHandle(out, FALSE);
|
||||
startup.hStdInput = in;
|
||||
startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
|
||||
dwCreateFlags = NORMAL_PRIORITY_CLASS;
|
||||
if (strcmp(sapi_module.name, "cli") != 0) {
|
||||
dwCreateFlags |= CREATE_NO_WINDOW;
|
||||
}
|
||||
|
||||
/* Get a token with the impersonated user. */
|
||||
if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
||||
DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
|
||||
} else {
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_NO_TOKEN) {
|
||||
asuser = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
envw = php_win32_cp_env_any_to_w(env);
|
||||
if (envw) {
|
||||
dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
} else {
|
||||
if (env) {
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (asuser) {
|
||||
res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
|
||||
CloseHandle(token_user);
|
||||
} else {
|
||||
res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
|
||||
}
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
free(envw);
|
||||
|
||||
if (!res) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CloseHandle(process.hThread);
|
||||
proc = process_get(NULL);
|
||||
|
||||
if (read) {
|
||||
fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
|
||||
CloseHandle(out);
|
||||
} else {
|
||||
fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
|
||||
CloseHandle(in);
|
||||
}
|
||||
|
||||
stream = _fdopen(fno, type);
|
||||
proc->prochnd = process.hProcess;
|
||||
proc->stream = stream;
|
||||
return stream;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API int pclose(FILE *stream)
|
||||
{/*{{{*/
|
||||
DWORD termstat = 0;
|
||||
process_pair *process;
|
||||
|
||||
if ((process = process_get(stream)) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fflush(process->stream);
|
||||
fclose(process->stream);
|
||||
|
||||
WaitForSingleObject(process->prochnd, INFINITE);
|
||||
GetExitCodeProcess(process->prochnd, &termstat);
|
||||
process->stream = NULL;
|
||||
CloseHandle(process->prochnd);
|
||||
|
||||
return termstat;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API int shmget(key_t key, size_t size, int flags)
|
||||
{/*{{{*/
|
||||
shm_pair *shm;
|
||||
char shm_segment[26], shm_info[29];
|
||||
HANDLE shm_handle = NULL, info_handle = NULL;
|
||||
BOOL created = FALSE;
|
||||
|
||||
if (key != IPC_PRIVATE) {
|
||||
snprintf(shm_segment, sizeof(shm_segment), "TSRM_SHM_SEGMENT:%d", key);
|
||||
snprintf(shm_info, sizeof(shm_info), "TSRM_SHM_DESCRIPTOR:%d", key);
|
||||
|
||||
shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
|
||||
info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
|
||||
}
|
||||
|
||||
if (!shm_handle && !info_handle) {
|
||||
if (flags & IPC_CREAT) {
|
||||
#if SIZEOF_SIZE_T == 8
|
||||
DWORD high = size >> 32;
|
||||
DWORD low = (DWORD)size;
|
||||
#else
|
||||
DWORD high = 0;
|
||||
DWORD low = size;
|
||||
#endif
|
||||
shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, key == IPC_PRIVATE ? NULL : shm_segment);
|
||||
info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), key == IPC_PRIVATE ? NULL : shm_info);
|
||||
created = TRUE;
|
||||
}
|
||||
if (!shm_handle || !info_handle) {
|
||||
if (shm_handle) {
|
||||
CloseHandle(shm_handle);
|
||||
}
|
||||
if (info_handle) {
|
||||
CloseHandle(info_handle);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (flags & IPC_EXCL) {
|
||||
if (shm_handle) {
|
||||
CloseHandle(shm_handle);
|
||||
}
|
||||
if (info_handle) {
|
||||
CloseHandle(info_handle);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
shm = shm_get(key, NULL);
|
||||
if (!shm) {
|
||||
CloseHandle(shm_handle);
|
||||
CloseHandle(info_handle);
|
||||
return -1;
|
||||
}
|
||||
shm->segment = shm_handle;
|
||||
shm->info = info_handle;
|
||||
shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (NULL != shm->descriptor && created) {
|
||||
shm->descriptor->shm_perm.key = key;
|
||||
shm->descriptor->shm_segsz = size;
|
||||
shm->descriptor->shm_ctime = time(NULL);
|
||||
shm->descriptor->shm_cpid = getpid();
|
||||
shm->descriptor->shm_perm.mode = flags;
|
||||
|
||||
shm->descriptor->shm_perm.cuid = shm->descriptor->shm_perm.cgid= 0;
|
||||
shm->descriptor->shm_perm.gid = shm->descriptor->shm_perm.uid = 0;
|
||||
shm->descriptor->shm_atime = shm->descriptor->shm_dtime = 0;
|
||||
shm->descriptor->shm_lpid = shm->descriptor->shm_nattch = 0;
|
||||
shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0;
|
||||
}
|
||||
|
||||
if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
|
||||
if (NULL != shm->segment) {
|
||||
CloseHandle(shm->segment);
|
||||
}
|
||||
UnmapViewOfFile(shm->descriptor);
|
||||
CloseHandle(shm->info);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return key;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API void *shmat(int key, const void *shmaddr, int flags)
|
||||
{/*{{{*/
|
||||
shm_pair *shm = shm_get(key, NULL);
|
||||
|
||||
if (!shm->segment) {
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (NULL == shm->addr) {
|
||||
int err = GetLastError();
|
||||
SET_ERRNO_FROM_WIN32_CODE(err);
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
shm->descriptor->shm_atime = time(NULL);
|
||||
shm->descriptor->shm_lpid = getpid();
|
||||
shm->descriptor->shm_nattch++;
|
||||
|
||||
return shm->addr;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API int shmdt(const void *shmaddr)
|
||||
{/*{{{*/
|
||||
shm_pair *shm = shm_get(0, (void*)shmaddr);
|
||||
int ret;
|
||||
|
||||
if (!shm->segment) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
shm->descriptor->shm_dtime = time(NULL);
|
||||
shm->descriptor->shm_lpid = getpid();
|
||||
shm->descriptor->shm_nattch--;
|
||||
|
||||
ret = UnmapViewOfFile(shm->addr) ? 0 : -1;
|
||||
if (!ret && shm->descriptor->shm_nattch <= 0) {
|
||||
ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
|
||||
shm->descriptor = NULL;
|
||||
}
|
||||
return ret;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
|
||||
{/*{{{*/
|
||||
shm_pair *shm = shm_get(key, NULL);
|
||||
|
||||
if (!shm->segment) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case IPC_STAT:
|
||||
memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
|
||||
return 0;
|
||||
|
||||
case IPC_SET:
|
||||
shm->descriptor->shm_ctime = time(NULL);
|
||||
shm->descriptor->shm_perm.uid = buf->shm_perm.uid;
|
||||
shm->descriptor->shm_perm.gid = buf->shm_perm.gid;
|
||||
shm->descriptor->shm_perm.mode = buf->shm_perm.mode;
|
||||
return 0;
|
||||
|
||||
case IPC_RMID:
|
||||
if (shm->descriptor->shm_nattch < 1) {
|
||||
shm->descriptor->shm_perm.key = -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
|
||||
{
|
||||
// Note that LONGLONG is a 64-bit value
|
||||
LONGLONG ll;
|
||||
|
||||
ll = t * 10000000LL + 116444736000000000LL;
|
||||
pft->dwLowDateTime = (DWORD)ll;
|
||||
pft->dwHighDateTime = ll >> 32;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
|
||||
{
|
||||
FILETIME mtime, atime;
|
||||
HANDLE hFile;
|
||||
PHP_WIN32_IOUTIL_INIT_W(filename)
|
||||
|
||||
if (!pathw) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
|
||||
/* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
|
||||
the CreateFile operation succeeds */
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
SetLastError(0);
|
||||
}
|
||||
|
||||
if ( hFile == INVALID_HANDLE_VALUE ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
SYSTEMTIME st;
|
||||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st, &mtime);
|
||||
atime = mtime;
|
||||
} else {
|
||||
UnixTimeToFileTime(buf->modtime, &mtime);
|
||||
UnixTimeToFileTime(buf->actime, &atime);
|
||||
}
|
||||
if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
|
||||
CloseHandle(hFile);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Daniel Beulshausen <daniel@php4win.de> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef TSRM_WIN32_H
|
||||
#define TSRM_WIN32_H
|
||||
|
||||
#include "TSRM.h"
|
||||
#include <windows.h>
|
||||
#include <sys/utime.h>
|
||||
#include "win32/ipc.h"
|
||||
|
||||
struct ipc_perm {
|
||||
key_t key;
|
||||
unsigned short uid;
|
||||
unsigned short gid;
|
||||
unsigned short cuid;
|
||||
unsigned short cgid;
|
||||
unsigned short mode;
|
||||
unsigned short seq;
|
||||
};
|
||||
|
||||
struct shmid_ds {
|
||||
struct ipc_perm shm_perm;
|
||||
size_t shm_segsz;
|
||||
time_t shm_atime;
|
||||
time_t shm_dtime;
|
||||
time_t shm_ctime;
|
||||
unsigned short shm_cpid;
|
||||
unsigned short shm_lpid;
|
||||
short shm_nattch;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FILE *stream;
|
||||
HANDLE prochnd;
|
||||
} process_pair;
|
||||
|
||||
typedef struct {
|
||||
void *addr;
|
||||
HANDLE info;
|
||||
HANDLE segment;
|
||||
struct shmid_ds *descriptor;
|
||||
} shm_pair;
|
||||
|
||||
typedef struct {
|
||||
process_pair *process;
|
||||
shm_pair *shm;
|
||||
int process_size;
|
||||
int shm_size;
|
||||
char *comspec;
|
||||
HANDLE impersonation_token;
|
||||
PSID impersonation_token_sid;
|
||||
} tsrm_win32_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
# define TWG(v) TSRMG_STATIC(win32_globals_id, tsrm_win32_globals *, v)
|
||||
TSRMLS_CACHE_EXTERN()
|
||||
#else
|
||||
# define TWG(v) (win32_globals.v)
|
||||
#endif
|
||||
|
||||
#define IPC_PRIVATE 0
|
||||
#define IPC_CREAT 00001000
|
||||
#define IPC_EXCL 00002000
|
||||
#define IPC_NOWAIT 00004000
|
||||
|
||||
#define IPC_RMID 0
|
||||
#define IPC_SET 1
|
||||
#define IPC_STAT 2
|
||||
#define IPC_INFO 3
|
||||
|
||||
#define SHM_R PAGE_READONLY
|
||||
#define SHM_W PAGE_READWRITE
|
||||
|
||||
#define SHM_RDONLY FILE_MAP_READ
|
||||
#define SHM_RND FILE_MAP_WRITE
|
||||
#define SHM_REMAP FILE_MAP_COPY
|
||||
|
||||
const char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len);
|
||||
|
||||
TSRM_API void tsrm_win32_startup(void);
|
||||
TSRM_API void tsrm_win32_shutdown(void);
|
||||
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, const char *env);
|
||||
TSRM_API FILE *popen(const char *command, const char *type);
|
||||
TSRM_API int pclose(FILE *stream);
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode);
|
||||
TSRM_API int win32_utime(const char *filename, struct utimbuf *buf);
|
||||
|
||||
TSRM_API int shmget(key_t key, size_t size, int flags);
|
||||
TSRM_API void *shmat(int key, const void *shmaddr, int flags);
|
||||
TSRM_API int shmdt(const void *shmaddr);
|
||||
TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf);
|
||||
#endif
|
||||
@@ -1,444 +0,0 @@
|
||||
PHP 8.1 UPGRADE NOTES
|
||||
|
||||
1. Backward Incompatible Changes
|
||||
2. New Features
|
||||
3. Changes in SAPI modules
|
||||
4. Deprecated Functionality
|
||||
5. Changed Functions
|
||||
6. New Functions
|
||||
7. New Classes and Interfaces
|
||||
8. Removed Extensions and SAPIs
|
||||
9. Other Changes to Extensions
|
||||
10. New Global Constants
|
||||
11. Changes to INI File Handling
|
||||
12. Windows Support
|
||||
13. Other Changes
|
||||
14. Performance Improvements
|
||||
|
||||
========================================
|
||||
1. Backward Incompatible Changes
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Access to the $GLOBALS array is now subject to a number of restrictions.
|
||||
Read and write access to individual array elements like $GLOBALS['var']
|
||||
continues to work as-is. Read-only access to the entire $GLOBALS array also
|
||||
continues to be supported. However, write access to the entire $GLOBALS
|
||||
array is no longer supported. For example, array_pop($GLOBALS) will result
|
||||
in an error.
|
||||
RFC: https://wiki.php.net/rfc/restrict_globals_usage
|
||||
. Passing null to a non-nullable argument of a built-in function is
|
||||
deprecated. This matches the behavior of user-defined functions, where null
|
||||
is never accepted by non-nullable arguments.
|
||||
user-defined functions.
|
||||
|
||||
var_dump(str_contains("foobar", null));
|
||||
// Deprecated: Passing null to parameter #2 ($needle) of type string
|
||||
// is deprecated
|
||||
|
||||
RFC: https://wiki.php.net/rfc/deprecate_null_to_scalar_internal_arg
|
||||
. When a method using static variables is inherited (but not overridden), the
|
||||
inherited method will now share static variables with the parent method.
|
||||
|
||||
class A {
|
||||
public static function counter() {
|
||||
static $counter = 0;
|
||||
$counter++;
|
||||
return $counter;
|
||||
}
|
||||
}
|
||||
class B extends A {}
|
||||
|
||||
var_dump(A::counter()); // int(1)
|
||||
var_dump(A::counter()); // int(2)
|
||||
var_dump(B::counter()); // int(3), previously int(1)
|
||||
var_dump(B::counter()); // int(4), previously int(2)
|
||||
|
||||
This means that static variables in methods now behave the same way as
|
||||
static properties.
|
||||
RFC: https://wiki.php.net/rfc/static_variable_inheritance
|
||||
. Most non-final internal methods now require overriding methods to declare a
|
||||
compatible return type, otherwise a deprecated notice is emitted during
|
||||
inheritance validation.
|
||||
RFC: https://wiki.php.net/rfc/internal_method_return_types
|
||||
|
||||
- Fileinfo:
|
||||
. The fileinfo functions now accept and return, respectively, finfo objects
|
||||
instead of resources.
|
||||
|
||||
- FTP:
|
||||
. The FTP functions now accept and return, respectively, FTP\Connection objects
|
||||
instead of resources.
|
||||
|
||||
- IMAP:
|
||||
. The IMAP functions now accept and return, respectively, IMAP\Connection objects
|
||||
instead of resources.
|
||||
|
||||
- LDAP:
|
||||
. The LDAP functions now accept and return, respectively, LDAP\Connection objects
|
||||
instead of "ldap link" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The LDAP functions now accept and return, respectively, LDAP\Result objects
|
||||
instead of "ldap result" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The LDAP functions now accept and return, respectively, LDAP\ResultEntry
|
||||
objects instead of "ldap result entry" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
|
||||
- MySQLi:
|
||||
. mysqli_fetch_fields() and mysqli_fetch_field_direct() will now always return
|
||||
zero for max_length. You can compute this information by iterating over the
|
||||
result set and taking the maximum length. This is what PHP was doing
|
||||
internally previously.
|
||||
. The MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH option no longer has an effect.
|
||||
. The MYSQLI_STORE_RESULT_COPY_DATA option no longer has an effect.
|
||||
. The default error handling mode has been changed from "silent" to
|
||||
"exceptions". See https://www.php.net/manual/en/mysqli-driver.report-mode.php
|
||||
for details of behavior changes and how to explicitly set this attribute. To
|
||||
keep the old behavior, use mysqli_report(MYSQLI_REPORT_OFF);
|
||||
RFC: https://wiki.php.net/rfc/mysqli_default_errmode
|
||||
. Classes extending mysqli_stmt::execute() will be required to specify the
|
||||
additional parameter now.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_bind_in_execute
|
||||
. mysqli::connect() will now return true instead of null on success.
|
||||
|
||||
- MySQLnd:
|
||||
. The mysqlnd.fetch_copy_data ini setting has been removed. However, this
|
||||
should not result in user-visible behavior changes.
|
||||
|
||||
- PDO:
|
||||
. PDO::ATTR_STRINGIFY_FETCHES now also stringifies values of type bool to
|
||||
"0" or "1". Previously booleans were not stringified.
|
||||
. Calling bindColumn() with PDO::PARAM_LOB (and assuming stringification is
|
||||
not enabled) will now consistently bind a stream result, as documented.
|
||||
Previously the result would be either a stream or a string depending on the
|
||||
used database driver and the time the binding is performed.
|
||||
|
||||
- PDO MySQL:
|
||||
. Integers and floats in result sets will now be returned using native PHP
|
||||
types instead of strings when using emulated prepared statements. This
|
||||
matches the behavior of native prepared statements. You can restore the
|
||||
previous behavior by enabling the PDO::ATTR_STRINGIFY_FETCHES option.
|
||||
|
||||
- PDO SQLite:
|
||||
. Integers and floats in results sets will now be returned using native PHP
|
||||
types. You can restore the previous behavior by enabling the
|
||||
PDO::ATTR_STRINGIFY_FETCHES option.
|
||||
|
||||
- PgSQL:
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Connection
|
||||
objects instead of "pgsql link" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Result
|
||||
objects instead of "pgsql result" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Lob
|
||||
objects instead of "pgsql large object" resources. Return value checks
|
||||
using is_resource() should be replaced with checks for `false`.
|
||||
|
||||
- PSpell:
|
||||
. The PSpell functions now accept and return, respectively, PSpell\Dictionary objects
|
||||
instead of "pspell" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The PSpell functions now accept and return, respectively, PSpell\Config objects
|
||||
instead of "pspell config" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
|
||||
- Standard:
|
||||
. version_compare() no longer accepts undocumented operator abbreviations.
|
||||
. htmlspecialchars(), htmlentities(), htmlspecialchars_decode(),
|
||||
html_entity_decode() and get_html_translation_table() now use
|
||||
ENT_QUOTES | ENT_SUBSTITUTE rather than ENT_COMPAT by default. This means
|
||||
that ' is escaped to ' while previously it was left alone.
|
||||
Additionally, malformed UTF-8 will be replaced by a Unicode substitution
|
||||
character, instead of resulting in an empty string.
|
||||
. debug_zval_dump() will now print reference wrappers with their refcount,
|
||||
instead of only prepending a "&" to the value. This more accurately models
|
||||
reference representation since PHP 7.0.
|
||||
. debug_zval_dump() will not print "interned" instead of a dummy refcount of
|
||||
one for interned strings and immutable arrays.
|
||||
. fputcsv() now accepts a new "eol" argument which allow to define a custom
|
||||
eol sequence, the default remains the same and is "\n".
|
||||
|
||||
- SPL:
|
||||
. SplFileObject::fputcsv() now accepts a new "eol" argument which allow to
|
||||
define a custom eol sequence, the default remains the same and is "\n".
|
||||
|
||||
========================================
|
||||
2. New Features
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. It is now possible to specify octal integer by using the explicit "0o"/"0O"
|
||||
prefix similar to hexadecimal ("0x"/"0X) and binary ("0b"/"0B") integer
|
||||
literals.
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
. Added support for array unpacking with strings keys.
|
||||
RFC: https://wiki.php.net/rfc/array_unpacking_string_keys
|
||||
. Added support for enumerations.
|
||||
RFC: https://wiki.php.net/rfc/enumerations
|
||||
. Added support for never return type
|
||||
RFC: https://wiki.php.net/rfc/noreturn_type
|
||||
. Added support for fibers.
|
||||
RFC: https://wiki.php.net/rfc/fibers
|
||||
. File uploads now provide an additional full_path key, which contains the
|
||||
full path (rather than just the basename) of the uploaded file. This is
|
||||
intended for use in conjunction with "upload webkitdirectory".
|
||||
|
||||
- Curl:
|
||||
. Added CURLOPT_DOH_URL option.
|
||||
. Added CURLStringFile, which can be used to post a file from a string rather
|
||||
than a file:
|
||||
|
||||
$file = new CURLStringFile($data, 'filename.txt', 'text/plain');
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, ['file' => $file]);
|
||||
|
||||
- FPM:
|
||||
. Added openmetrics status format. It can be used by Prometheus to fetch FPM
|
||||
metrics.
|
||||
. Added new pool option for the dynamic process manager called
|
||||
pm.max_spawn_rate. It allows to start number of children in a faster rate
|
||||
when dynamic pm is selected. The default value is 32 which was the previous
|
||||
hard coded value.
|
||||
|
||||
- hash:
|
||||
. The following functions have changed signatures:
|
||||
|
||||
- function hash(string $algo, string $data, bool $binary = false, array $options = []): string|false {}
|
||||
- function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): string|false {}
|
||||
- function hash_init(string $algo, int $flags = 0, string $key = "", array $options = []): HashContext {}
|
||||
|
||||
The additional `$options` argument can be used to pass algorithm specific data.
|
||||
|
||||
. Added MurmurHash3 with streaming support. The following variants are implemented:
|
||||
|
||||
- murmur3a, 32-bit hash
|
||||
- murmur3c, 128-bit hash for x86
|
||||
- murmur3f, 128-bit hash for x64
|
||||
|
||||
The initial hash state can be passed through the `seed` key in the `$options` array, for example:
|
||||
|
||||
```php
|
||||
$h = hash("murmur3f", $data, options: ["seed" => 42]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
A valid seed value is within the range from 0 to the platform defined UINT_MAX, usually 4294967295.
|
||||
|
||||
. Added xxHash. The implementation brings in the following arguments
|
||||
|
||||
- xxh32, 32-bit hash
|
||||
- xxh64, 64-bit hash
|
||||
- xxh3, 64-bit hash
|
||||
- xxh128, 128-bit hash
|
||||
|
||||
The initial hash state can be passed through the `seed` key in the `$options` array, for example:
|
||||
|
||||
```php
|
||||
$h = hash("xxh3", $data, options: ["seed" => 42]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
Secret usage is supported through passing the `secret` key in the `$options` array, too:
|
||||
|
||||
```php
|
||||
$h = hash("xxh3", $data, options: ["secret" => "at least 136 bytes long secret here"]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
Note, that the quality of the custom secret is crucial for the quality of the resulting hash. It is
|
||||
highly recommended for the secret to use the best possible entropy.
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli.local_infile_directory ini setting has been added, which can be
|
||||
used to specify a directory from which files are allowed to be loaded. It
|
||||
is only meaningful if mysqli.allow_local_infile is not enabled, as all
|
||||
directories are allowed in that case.
|
||||
. Binding in execute has been added to mysqli prepared statements.
|
||||
Parameters can now be passed to mysqli_stmt::execute as an array.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_bind_in_execute
|
||||
. A new function has been added to mysqli_result called mysqli_fetch_column().
|
||||
It allows for fetching single scalar values from the result set.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_fetch_column
|
||||
|
||||
- PDO MySQL:
|
||||
. The PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY attribute has been added, which
|
||||
can be used to specify a directory from which files are allowed to be
|
||||
loaded. It is only meaningful if PDO::MYSQL_ATTR_LOCAL_INFILE is not
|
||||
enabled, as all directories are allowed in that case.
|
||||
|
||||
- PDO SQLite:
|
||||
. SQLite's "file:" DSN syntax is now supported, which allows specifying
|
||||
additional flags. This feature is not available if open_basedir is set.
|
||||
Example:
|
||||
|
||||
new PDO('sqlite:file:path/to/sqlite.db?mode=ro')
|
||||
|
||||
- Posix:
|
||||
. Added POSIX_RLIMIT_KQUEUES and POSIX_RLIMIT_NPTS. These rlimits are only
|
||||
available on FreeBSD.
|
||||
|
||||
========================================
|
||||
3. Changes in SAPI modules
|
||||
========================================
|
||||
|
||||
- CLI:
|
||||
. Using -a without the readline extension will now result in an error.
|
||||
Previously, -a without readline had the same behavior as calling php without
|
||||
any arguments, apart from printing an additional "Interactive mode enabled"
|
||||
message. This mode was not, in fact, interactive.
|
||||
|
||||
========================================
|
||||
4. Deprecated Functionality
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Implementing the Serializable interface without also implementing
|
||||
__serialize() and __unserialize() has been deprecated. You should either
|
||||
implement the new methods (if you only support PHP 7.4 and higher) or
|
||||
implement both (if you support older PHP versions as well).
|
||||
RFC: https://wiki.php.net/rfc/phase_out_serializable
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli_driver::$driver_version property has been deprecated. The driver
|
||||
version is meaningless as it hasn't been updated in more than a decade. Use
|
||||
PHP_VERSION_ID instead.
|
||||
. Calling mysqli::get_client_info in OO style or passing $mysqli argument to
|
||||
mysqli_get_client_info() function has been deprecated. Use
|
||||
mysqli_get_client_info() without any arguments to obtain the client
|
||||
library version information.
|
||||
|
||||
- PDO:
|
||||
. The PDO::FETCH_SERIALIZE mode has been deprecated.
|
||||
RFC: https://wiki.php.net/rfc/phase_out_serializable
|
||||
|
||||
========================================
|
||||
5. Changed Functions
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Properties order used in foreach, var_dump(), serialize(), object comparison
|
||||
etc. was changed. Now properties are naturally ordered according to their
|
||||
declaration and inheritance. Properties declared in a base class are going
|
||||
to be before the child properties. This order is consistent with internal
|
||||
layout of properties in zend_object structure and repeats the order in
|
||||
default_properties_table[] and properties_info_table[]. The old order was
|
||||
not documented and was caused by class inheritance implementation details.
|
||||
|
||||
- Filter:
|
||||
. The FILTER_FLAG_ALLOW_OCTAL flag of the FILTER_VALIDATE_INT filter now accept
|
||||
octal string with the leading octal prefix ("0o"/"0O")
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
|
||||
- GMP:
|
||||
. All GMP function now accept octal string with the leading octal prefix ("0o"/"0O")
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
|
||||
- PDO ODBC:
|
||||
. PDO::getAttributes() with PDO::ATTR_SERVER_INFO and PDO::ATTR_SERVER_VERSION
|
||||
now return values instead of throwing PDOException.
|
||||
|
||||
========================================
|
||||
6. New Functions
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Added array_is_list(array $array), which will return true if the array keys are 0 .. count($array)-1 in that order.
|
||||
RFC: https://wiki.php.net/rfc/is_list
|
||||
|
||||
- pcntl:
|
||||
. Added pcntl_rfork for FreeBSD variants
|
||||
|
||||
- Reflection:
|
||||
. Added ReflectionFunctionAbstract::getClosureUsedVariables
|
||||
|
||||
- Standard:
|
||||
. Added fsync() and fdatasync(), which instruct the operating system to
|
||||
flush its buffers to physical storage.
|
||||
RFC: https://wiki.php.net/rfc/fsync_function
|
||||
|
||||
- Sodium:
|
||||
. Added the XChaCha20 stream cipher interface functions:
|
||||
|
||||
- sodium_crypto_stream_xchacha20()
|
||||
- sodium_crypto_stream_xchacha20_keygen()
|
||||
- sodium_crypto_stream_xchacha20_xor()
|
||||
|
||||
. Added the Ristretto255 functions, which are available in libsodium 1.0.18.
|
||||
|
||||
Ristretto is a technique for constructing prime order elliptic curve groups with non-malleable encodings.
|
||||
Ristretto255 implements Ristretto atop Curve25519.
|
||||
|
||||
- sodium_crypto_core_ristretto255_add()
|
||||
- sodium_crypto_core_ristretto255_from_hash()
|
||||
- sodium_crypto_core_ristretto255_is_valid_point()
|
||||
- sodium_crypto_core_ristretto255_random()
|
||||
- sodium_crypto_core_ristretto255_scalar_add()
|
||||
- sodium_crypto_core_ristretto255_scalar_complement()
|
||||
- sodium_crypto_core_ristretto255_scalar_invert()
|
||||
- sodium_crypto_core_ristretto255_scalar_mul()
|
||||
- sodium_crypto_core_ristretto255_scalar_negate()
|
||||
- sodium_crypto_core_ristretto255_scalar_random()
|
||||
- sodium_crypto_core_ristretto255_scalar_reduce()
|
||||
- sodium_crypto_core_ristretto255_scalar_sub()
|
||||
- sodium_crypto_core_ristretto255_sub()
|
||||
- sodium_crypto_scalarmult_ristretto255()
|
||||
- sodium_crypto_scalarmult_ristretto255_base()
|
||||
|
||||
========================================
|
||||
7. New Classes and Interfaces
|
||||
========================================
|
||||
|
||||
========================================
|
||||
8. Removed Extensions and SAPIs
|
||||
========================================
|
||||
|
||||
========================================
|
||||
9. Other Changes to Extensions
|
||||
========================================
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli_stmt::next_result() and mysqli::fetch_all() methods are now
|
||||
available when linking against libmysqlclient.
|
||||
|
||||
- OpenSSL:
|
||||
. The OpenSSL extension now requires at least OpenSSL version 1.0.2.
|
||||
|
||||
- Sockets:
|
||||
. FreeBSD defines SO_ACCEPTFILTER
|
||||
|
||||
- Standard:
|
||||
. --with-password-argon2 now uses pkg-config to detect libargon2. As such,
|
||||
an alternative libargon2 location should now be specified using
|
||||
PKG_CONFIG_PATH.
|
||||
|
||||
========================================
|
||||
10. New Global Constants
|
||||
========================================
|
||||
|
||||
- MySQLi:
|
||||
. MYSQLI_REFRESH_REPLICA has been added as a replacement for
|
||||
MYSQLI_REFRESH_SLAVE, in line with an upstream change in MySQL. The old
|
||||
constant is still available for backwards-compatibility reasons, but may
|
||||
be deprecated/removed in the future.
|
||||
|
||||
========================================
|
||||
11. Changes to INI File Handling
|
||||
========================================
|
||||
|
||||
- The log_errors_max_len ini setting has been removed. It no longer had an
|
||||
effect since PHP 8.0.
|
||||
|
||||
========================================
|
||||
12. Windows Support
|
||||
========================================
|
||||
|
||||
. The macro IGNORE_URL_WIN has been removed; it had no effect as of PHP 5.0.0.
|
||||
|
||||
========================================
|
||||
13. Other Changes
|
||||
========================================
|
||||
|
||||
========================================
|
||||
14. Performance Improvements
|
||||
========================================
|
||||
@@ -1,110 +0,0 @@
|
||||
PHP 8.1 INTERNALS UPGRADE NOTES
|
||||
|
||||
1. Internal API changes
|
||||
a. Removed Zend APIs
|
||||
b. Zend Stream API
|
||||
c. zend_get_opcode_id()
|
||||
|
||||
2. Build system changes
|
||||
a. New compiler flags
|
||||
|
||||
3. Module changes
|
||||
a. ext/hash
|
||||
b. ext/pdo
|
||||
c. ext/standard
|
||||
d. ext/pgsql
|
||||
e. ext/pcre
|
||||
|
||||
========================
|
||||
1. Internal API changes
|
||||
========================
|
||||
a. The following APIs have been removed from the Zend Engine:
|
||||
- The spl_ce_Aggregate, spl_ce_ArrayAccess, spl_ce_Countable, spl_ce_Iterator, spl_ce_Serializable,
|
||||
spl_ce_Stringable, spl_ce_Traversable alias class entries have been removed in favor of zend_ce_aggregate,
|
||||
zend_ce_arrayaccess, zend_ce_countable, zend_ce_iterator, zend_ce_serializable, zend_ce_stringable,
|
||||
zend_ce_traversable.
|
||||
b. Zend Stream API has been changed to use "zend_string*" instead of "char*"
|
||||
- zend_file_handle.filename now is zend_string*
|
||||
- zend_file_handle.free_filename is removed. Now zend_file_handle.filename is always released.
|
||||
- added zend_file_handle.primary_script flag. SAPIs should set it for main executed script.
|
||||
- added zend_file_handle.in_list flag, which is set when a file_handle is added into CG(open_files)
|
||||
- added zend_stream_init_filename_ex() function, that takes filename as zend_string*
|
||||
- the "filename" parameter of functons zend_stream_open(), php_stream_open_for_zend_ex() and
|
||||
callback zend_stream_open_function() has been removed (it's now passed as a "filename" field of the
|
||||
file_handle parameter)
|
||||
- in zend_fopen() and zend_resolve_path() callbacks filename now passed as zend_string*
|
||||
- file_handles should be destroyed by zend_destroy_file_handle() function (usually in the same function
|
||||
the same function where they were created by zend_stream_init_*()). Previously there were two different
|
||||
destructors zend_destroy_file_handle() and zend_file_handle_dtor().
|
||||
- zend_ini_scanner_globals.filename now is zend_string*
|
||||
c. Added the zend_get_opcode_id() function, which is intended to get opcode id from name.
|
||||
|
||||
========================
|
||||
2. Build system changes
|
||||
========================
|
||||
a. New compiler flags
|
||||
- [-Wimplicit-fallthrough=1] is now enabled by default.
|
||||
It is possible to use the new ZEND_FALLTHROUGH macro keyword to suppress the warning.
|
||||
========================
|
||||
3. Module changes
|
||||
========================
|
||||
|
||||
a. ext/hash
|
||||
- The init signatures are extended with an additional `HashTable*`
|
||||
argument. The passed HT is to contain the algorithm specific
|
||||
configuration. If an algorithm doesn't make use of any additional
|
||||
configuration, the argument is to be marked with ZEND_ATTRIBUTE_UNUSED.
|
||||
|
||||
b. ext/pdo
|
||||
- The "preparer" callback now accepts a zend_string* instead of
|
||||
char* + size_t pair the query string. Similarly, the query_string and
|
||||
active_query_string members of pdo_stmt_t are now zend_string*.
|
||||
- The way in which drivers provide results has changed: Previously,
|
||||
the "describer" callback populated the "pdo_type" member in the
|
||||
pdo_column_data structure, and the "get_col" callback then had to return
|
||||
pointers to data of appropriate type.
|
||||
|
||||
In PHP 8.1, the "describer" callback no longer determines the pdo_type
|
||||
(and this member has been removed from pdo_column_data). Instead, the
|
||||
"get_col" callback accepts a zval pointer that may be populated with a
|
||||
value of arbitrary type. This gives drivers more flexibility in
|
||||
determining result types (e.g. based on whether a specific integer fits
|
||||
the PHP integer type) and avoid awkward juggling of temporary buffers.
|
||||
|
||||
As the "describer" no longer determines pdo_type, the "get_column_meta"
|
||||
function is now responsible for providing this information for use by
|
||||
getColumnMeta(). The type provided here does not need to match the type
|
||||
returned by get_col (in fact no corresponding type might exist, e.g. for
|
||||
floats). It should be the closest logical equivalent for the column type.
|
||||
- The transaction, set_attribute, and preparer handler's return type
|
||||
has been formalized to bool instead of int.
|
||||
- The check_liveness handler's return type has been formalized to zend_return
|
||||
instead of int.
|
||||
- The closer, and fetch_error handlers have been voidified.
|
||||
- The quoter handler now returns the quoted string as zend_string* instead
|
||||
of returning a boolean, and the quoted string as a pair of out params.
|
||||
Similarly the unquoted string is now a zend_string* instead of a pair of
|
||||
char* and size_t length.
|
||||
- The doer handler now accepts a zend_string* instead of char* + size_t
|
||||
pair for the SQL statement.
|
||||
- The last_id handler now returns a zend_string* instead of returning a
|
||||
char* and the length as an out param, and accepts a zend_string* instead
|
||||
of char* for the optional sequence/table name.
|
||||
- The php_pdo_str_tolower_dup() PDO_API has been removed use zend_str_tolower_dup()
|
||||
or zend_string_tolower_ex().
|
||||
|
||||
c. ext/standard
|
||||
- The PHP API php_fputcsv() now takes an extra zend_string* argument at the end
|
||||
for a custom EOL sequence, passing NULL provides the old default of "\n".
|
||||
|
||||
d. ext/pgsql
|
||||
- The functions php_pgsql_meta_data(), php_pgsql_convert(), php_pgsql_insert(),
|
||||
php_pgsql_update(), php_pgsql_delete(), and php_pgsql_select() have had
|
||||
their return type formalized to zend_result.
|
||||
- The functions php_pgsql_meta_data(), php_pgsql_convert(), php_pgsql_insert(),
|
||||
php_pgsql_update(), php_pgsql_delete(), and php_pgsql_select() now accept a
|
||||
zend_string* instead of a char* for the table name.
|
||||
|
||||
e. ext/pcre
|
||||
- Bundled PCRE2 is at version 10.37. The most important diff to the previous
|
||||
version is the JIT support on the S390 platform.
|
||||
@@ -1,56 +0,0 @@
|
||||
--------------------------------------------------------------------
|
||||
The Zend Engine License, Version 2.00
|
||||
Copyright (c) 1999-2006 Zend Technologies Ltd. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
3. The names "Zend" and "Zend Engine" must not be used to endorse
|
||||
or promote products derived from this software without prior
|
||||
permission from Zend Technologies Ltd. For written permission,
|
||||
please contact license@zend.com.
|
||||
|
||||
4. Zend Technologies Ltd. may publish revised and/or new versions
|
||||
of the license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
Once covered code has been published under a particular version
|
||||
of the license, you may always continue to use it under the
|
||||
terms of that version. You may also choose to use such covered
|
||||
code under the terms of any subsequent version of the license
|
||||
published by Zend Technologies Ltd. No one other than Zend
|
||||
Technologies Ltd. has the right to modify the terms applicable
|
||||
to covered code created under this License.
|
||||
|
||||
5. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes the Zend Engine, freely available at
|
||||
http://www.zend.com"
|
||||
|
||||
6. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"The Zend Engine is freely available at http://www.zend.com"
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND
|
||||
TECHNOLOGIES LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
@@ -1,42 +0,0 @@
|
||||
#
|
||||
# Zend
|
||||
#
|
||||
|
||||
$(builddir)/zend_language_scanner.lo: $(srcdir)/zend_language_parser.h
|
||||
$(builddir)/zend_ini_scanner.lo: $(srcdir)/zend_ini_parser.h
|
||||
|
||||
$(srcdir)/zend_language_scanner.c: $(srcdir)/zend_language_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_language_scanner_defs.h -oZend/zend_language_scanner.c Zend/zend_language_scanner.l)
|
||||
|
||||
$(srcdir)/zend_language_parser.h: $(srcdir)/zend_language_parser.c
|
||||
$(srcdir)/zend_language_parser.c: $(srcdir)/zend_language_parser.y
|
||||
# Tweak zendparse to be exported through ZEND_API. This has to be revisited once
|
||||
# bison supports foreign skeletons and that bison version is used. Read
|
||||
# https://git.savannah.gnu.org/cgit/bison.git/tree/data/README.md for more.
|
||||
@$(YACC) $(YFLAGS) -v -d $(srcdir)/zend_language_parser.y -o $@
|
||||
@$(SED) -e 's,^int zendparse\(.*\),ZEND_API int zendparse\1,g' < $@ \
|
||||
> $@.tmp && \
|
||||
mv $@.tmp $@
|
||||
@$(SED) -e 's,^int zendparse\(.*\),ZEND_API int zendparse\1,g' < $(srcdir)/zend_language_parser.h \
|
||||
> $(srcdir)/zend_language_parser.h.tmp && \
|
||||
mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h
|
||||
@nlinit=`echo 'nl="'; echo '"'`; eval "$$nlinit"; \
|
||||
$(SED) -e "s/^#ifndef YYTOKENTYPE/#include \"zend.h\"\\$${nl}#ifndef YYTOKENTYPE/" < $(srcdir)/zend_language_parser.h \
|
||||
> $(srcdir)/zend_language_parser.h.tmp && \
|
||||
mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h
|
||||
|
||||
$(srcdir)/zend_ini_parser.h: $(srcdir)/zend_ini_parser.c
|
||||
$(srcdir)/zend_ini_parser.c: $(srcdir)/zend_ini_parser.y
|
||||
$(YACC) $(YFLAGS) -v -d $(srcdir)/zend_ini_parser.y -o $@
|
||||
|
||||
$(srcdir)/zend_ini_scanner.c: $(srcdir)/zend_ini_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_ini_scanner_defs.h -oZend/zend_ini_scanner.c Zend/zend_ini_scanner.l)
|
||||
|
||||
$(srcdir)/zend_vm_execute.h $(srcdir)/zend_vm_opcodes.c: $(srcdir)/zend_vm_def.h $(srcdir)/zend_vm_execute.skl $(srcdir)/zend_vm_gen.php
|
||||
@if test ! -z "$(PHP)"; then \
|
||||
$(PHP) $(srcdir)/zend_vm_gen.php; \
|
||||
fi;
|
||||
|
||||
$(builddir)/zend_highlight.lo $(builddir)/zend_compile.lo: $(srcdir)/zend_language_parser.h
|
||||
|
||||
Zend/zend_execute.lo: $(srcdir)/zend_vm_execute.h $(srcdir)/zend_vm_opcodes.h
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,864 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 11
|
||||
* - compact literals table
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
#include "zend_extensions.h"
|
||||
|
||||
#define DEBUG_COMPACT_LITERALS 0
|
||||
|
||||
#define LITERAL_VALUE 0x0100
|
||||
#define LITERAL_FUNC 0x0200
|
||||
#define LITERAL_CLASS 0x0300
|
||||
#define LITERAL_CONST 0x0400
|
||||
#define LITERAL_CLASS_CONST 0x0500
|
||||
#define LITERAL_STATIC_METHOD 0x0600
|
||||
#define LITERAL_STATIC_PROPERTY 0x0700
|
||||
#define LITERAL_METHOD 0x0800
|
||||
#define LITERAL_PROPERTY 0x0900
|
||||
#define LITERAL_GLOBAL 0x0A00
|
||||
|
||||
#define LITERAL_KIND_MASK 0x0f00
|
||||
#define LITERAL_NUM_RELATED_MASK 0x000f
|
||||
|
||||
#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
|
||||
|
||||
typedef struct _literal_info {
|
||||
uint32_t flags; /* bitmask (see defines above) */
|
||||
} literal_info;
|
||||
|
||||
#define LITERAL_INFO(n, kind, related) do { \
|
||||
info[n].flags = ((kind) | (related)); \
|
||||
} while (0)
|
||||
|
||||
static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
|
||||
{
|
||||
zend_arg_info *arg_info;
|
||||
if (arg_num > 0) {
|
||||
if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
|
||||
return 0;
|
||||
}
|
||||
if (EXPECTED(arg_num <= op_array->num_args)) {
|
||||
arg_info = &op_array->arg_info[arg_num-1];
|
||||
} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
|
||||
arg_info = &op_array->arg_info[op_array->num_args];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
arg_info = op_array->arg_info - 1;
|
||||
}
|
||||
|
||||
if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
|
||||
if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
|
||||
return ZEND_TYPE_LIST(arg_info->type)->num_types;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t add_static_slot(HashTable *hash,
|
||||
zend_op_array *op_array,
|
||||
uint32_t op1,
|
||||
uint32_t op2,
|
||||
uint32_t kind,
|
||||
int *cache_size)
|
||||
{
|
||||
uint32_t ret;
|
||||
zval *class_name = &op_array->literals[op1];
|
||||
zval *prop_name = &op_array->literals[op2];
|
||||
zval *pos, tmp;
|
||||
|
||||
zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
|
||||
ZSTR_H(key) = zend_string_hash_func(key);
|
||||
ZSTR_H(key) += kind;
|
||||
|
||||
pos = zend_hash_find(hash, key);
|
||||
if (pos) {
|
||||
ret = Z_LVAL_P(pos);
|
||||
} else {
|
||||
ret = *cache_size;
|
||||
*cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
|
||||
ZVAL_LONG(&tmp, ret);
|
||||
zend_hash_add(hash, key, &tmp);
|
||||
}
|
||||
zend_string_release_ex(key, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zend_string *create_str_cache_key(zval *literal, uint32_t flags)
|
||||
{
|
||||
ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
|
||||
uint32_t num_related = LITERAL_NUM_RELATED(flags);
|
||||
if (num_related == 1) {
|
||||
return zend_string_copy(Z_STR_P(literal));
|
||||
}
|
||||
if ((flags & LITERAL_KIND_MASK) == LITERAL_VALUE) {
|
||||
/* Don't merge LITERAL_VALUE that has related literals */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Concatenate all the related literals for the cache key. */
|
||||
zend_string *key;
|
||||
if (num_related == 2) {
|
||||
ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
|
||||
key = zend_string_concat2(
|
||||
Z_STRVAL_P(literal), Z_STRLEN_P(literal),
|
||||
Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
|
||||
} else if (num_related == 3) {
|
||||
ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
|
||||
key = zend_string_concat3(
|
||||
Z_STRVAL_P(literal), Z_STRLEN_P(literal),
|
||||
Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
|
||||
Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
|
||||
} else {
|
||||
ZEND_ASSERT(0 && "Currently not needed");
|
||||
}
|
||||
|
||||
/* Add a bias to the hash so we can distinguish keys
|
||||
* that would otherwise be the same after concatenation. */
|
||||
ZSTR_H(key) = zend_string_hash_val(key) + num_related - 1;
|
||||
return key;
|
||||
}
|
||||
|
||||
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline, *end;
|
||||
int i, j, n, *map, cache_size;
|
||||
zval zv, *pos;
|
||||
literal_info *info;
|
||||
int l_null = -1;
|
||||
int l_false = -1;
|
||||
int l_true = -1;
|
||||
int l_empty_arr = -1;
|
||||
HashTable hash, double_hash;
|
||||
zend_string *key = NULL;
|
||||
void *checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
|
||||
|
||||
if (op_array->last_literal) {
|
||||
info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
|
||||
|
||||
/* Mark literals of specific types */
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
|
||||
break;
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
|
||||
break;
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
|
||||
break;
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1);
|
||||
break;
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
|
||||
} else {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_FETCH_STATIC_PROP_R:
|
||||
case ZEND_FETCH_STATIC_PROP_W:
|
||||
case ZEND_FETCH_STATIC_PROP_RW:
|
||||
case ZEND_FETCH_STATIC_PROP_IS:
|
||||
case ZEND_FETCH_STATIC_PROP_UNSET:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_UNSET_STATIC_PROP:
|
||||
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
|
||||
case ZEND_PRE_INC_STATIC_PROP:
|
||||
case ZEND_PRE_DEC_STATIC_PROP:
|
||||
case ZEND_POST_INC_STATIC_PROP:
|
||||
case ZEND_POST_DEC_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS:
|
||||
case ZEND_INSTANCEOF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_NEW:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_W:
|
||||
case ZEND_FETCH_OBJ_RW:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
case ZEND_FETCH_OBJ_UNSET:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_BIND_GLOBAL:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
|
||||
break;
|
||||
case ZEND_RECV_INIT:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
break;
|
||||
case ZEND_DECLARE_CLASS:
|
||||
case ZEND_DECLARE_CLASS_DELAYED:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_R:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
|
||||
} else {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
|
||||
#if DEBUG_COMPACT_LITERALS
|
||||
{
|
||||
int i, use_copy;
|
||||
fprintf(stderr, "File %s func %s\n", op_array->filename->val,
|
||||
op_array->function_name ? op_array->function_name->val : "main");
|
||||
fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
|
||||
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
zval zv;
|
||||
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
|
||||
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
|
||||
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
|
||||
if (use_copy) {
|
||||
zval_ptr_dtor_nogc(&zv);
|
||||
}
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Merge equal constants */
|
||||
j = 0;
|
||||
zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
|
||||
/* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
|
||||
zend_hash_init(&double_hash, 0, NULL, NULL, 0);
|
||||
map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
|
||||
memset(map, 0, op_array->last_literal * sizeof(int));
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
if (!info[i].flags) {
|
||||
/* unset literal */
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
continue;
|
||||
}
|
||||
switch (Z_TYPE(op_array->literals[i])) {
|
||||
case IS_NULL:
|
||||
if (l_null < 0) {
|
||||
l_null = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_null;
|
||||
break;
|
||||
case IS_FALSE:
|
||||
if (l_false < 0) {
|
||||
l_false = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_false;
|
||||
break;
|
||||
case IS_TRUE:
|
||||
if (l_true < 0) {
|
||||
l_true = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_true;
|
||||
break;
|
||||
case IS_LONG:
|
||||
if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
|
||||
if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
|
||||
key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
|
||||
ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
|
||||
LITERAL_NUM_RELATED(info[i].flags) - 1;
|
||||
if ((pos = zend_hash_find(&hash, key)) != NULL
|
||||
&& LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i+1]);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_add_new(&hash, key, &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
op_array->literals[j+1] = op_array->literals[i+1];
|
||||
info[j+1] = info[i+1];
|
||||
}
|
||||
j += 2;
|
||||
}
|
||||
zend_string_release_ex(key, 0);
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case IS_DOUBLE:
|
||||
if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
case IS_STRING: {
|
||||
key = create_str_cache_key(&op_array->literals[i], info[i].flags);
|
||||
if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
|
||||
Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
|
||||
LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
|
||||
(LITERAL_NUM_RELATED(info[i].flags) != 2 ||
|
||||
((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
|
||||
(info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
|
||||
zend_string_release_ex(key, 0);
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
n = LITERAL_NUM_RELATED(info[i].flags);
|
||||
while (n > 1) {
|
||||
i++;
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
if (key) {
|
||||
zend_hash_add_new(&hash, key, &zv);
|
||||
zend_string_release_ex(key, 0);
|
||||
}
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
n = LITERAL_NUM_RELATED(info[i].flags);
|
||||
while (n > 1) {
|
||||
i++;
|
||||
if (i != j) op_array->literals[j] = op_array->literals[i];
|
||||
j++;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IS_ARRAY:
|
||||
if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
|
||||
if (l_empty_arr < 0) {
|
||||
l_empty_arr = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
} else {
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
}
|
||||
map[i] = l_empty_arr;
|
||||
break;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
default:
|
||||
/* don't merge other types */
|
||||
map[i] = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only clean "hash", as it will be reused in the loop below. */
|
||||
zend_hash_clean(&hash);
|
||||
zend_hash_destroy(&double_hash);
|
||||
op_array->last_literal = j;
|
||||
|
||||
const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
|
||||
memset(const_slot, -1, j * 6 * sizeof(int));
|
||||
class_slot = const_slot + j;
|
||||
func_slot = class_slot + j;
|
||||
bind_var_slot = func_slot + j;
|
||||
property_slot = bind_var_slot + j;
|
||||
method_slot = property_slot + j;
|
||||
|
||||
/* Update opcodes to use new literals table */
|
||||
cache_size = zend_op_array_extension_handles * sizeof(void*);
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
while (opline < end) {
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
opline->op1.constant = map[opline->op1.constant];
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->op2.constant = map[opline->op2.constant];
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_RECV_INIT:
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_VARIADIC:
|
||||
{
|
||||
size_t num_classes = type_num_classes(op_array, opline->op1.num);
|
||||
if (num_classes) {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += num_classes * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
{
|
||||
size_t num_classes = type_num_classes(op_array, 0);
|
||||
if (num_classes) {
|
||||
opline->op2.num = cache_size;
|
||||
cache_size += num_classes * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 static property
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
(opline+1)->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op2.constant,
|
||||
opline->op1.constant,
|
||||
LITERAL_STATIC_PROPERTY,
|
||||
&cache_size);
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
cache_size += 3 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
(opline+1)->extended_value = class_slot[opline->op2.constant];
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
class_slot[opline->op2.constant] = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
(opline+1)->extended_value = property_slot[opline->op2.constant];
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = (opline+1)->extended_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_W:
|
||||
case ZEND_FETCH_OBJ_RW:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
case ZEND_FETCH_OBJ_UNSET:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
// op2 func
|
||||
if (func_slot[opline->op2.constant] >= 0) {
|
||||
opline->result.num = func_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
func_slot[opline->op2.constant] = opline->result.num;
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 method
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
method_slot[opline->op2.constant] >= 0) {
|
||||
opline->result.num = method_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
method_slot[opline->op2.constant] = opline->result.num;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 static method
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
opline->result.num = add_static_slot(&hash, op_array,
|
||||
opline->op1.constant,
|
||||
opline->op2.constant,
|
||||
LITERAL_STATIC_METHOD,
|
||||
&cache_size);
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->result.num = class_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->result.num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
// op1 const
|
||||
if (const_slot[opline->op1.constant] >= 0) {
|
||||
opline->extended_value = const_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
const_slot[opline->op1.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
// op2 const
|
||||
if (const_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = const_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
const_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1/op2 class_const
|
||||
opline->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op1.constant,
|
||||
opline->op2.constant,
|
||||
LITERAL_CLASS_CONST,
|
||||
&cache_size);
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_FETCH_STATIC_PROP_R:
|
||||
case ZEND_FETCH_STATIC_PROP_W:
|
||||
case ZEND_FETCH_STATIC_PROP_RW:
|
||||
case ZEND_FETCH_STATIC_PROP_IS:
|
||||
case ZEND_FETCH_STATIC_PROP_UNSET:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_UNSET_STATIC_PROP:
|
||||
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
|
||||
case ZEND_PRE_INC_STATIC_PROP:
|
||||
case ZEND_PRE_DEC_STATIC_PROP:
|
||||
case ZEND_POST_INC_STATIC_PROP:
|
||||
case ZEND_POST_DEC_STATIC_PROP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 static property
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op2.constant,
|
||||
opline->op1.constant,
|
||||
LITERAL_STATIC_PROPERTY,
|
||||
&cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
class_slot[opline->op2.constant] = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS:
|
||||
case ZEND_INSTANCEOF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_NEW:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->op2.num = class_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->op2.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->op2.num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_BIND_GLOBAL:
|
||||
// op2 bind var
|
||||
if (bind_var_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = bind_var_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
bind_var_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_CLASS_DELAYED:
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
op_array->cache_size = cache_size;
|
||||
zend_hash_destroy(&hash);
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
|
||||
if (1) {
|
||||
opline = op_array->opcodes;
|
||||
while (1) {
|
||||
if (opline->opcode == ZEND_RECV_INIT) {
|
||||
zval *val = &op_array->literals[opline->op2.constant];
|
||||
|
||||
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
||||
/* Ensure zval is aligned to 8 bytes */
|
||||
op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
|
||||
Z_CACHE_SLOT_P(val) = op_array->cache_size;
|
||||
op_array->cache_size += sizeof(zval);
|
||||
}
|
||||
} else if (opline->opcode != ZEND_RECV) {
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_COMPACT_LITERALS
|
||||
{
|
||||
int i, use_copy;
|
||||
fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
|
||||
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
zval zv;
|
||||
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
|
||||
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
|
||||
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
|
||||
if (use_copy) {
|
||||
zval_ptr_dtor_nogc(&zv);
|
||||
}
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Removing unused variables |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
|
||||
* This pass does not operate on SSA form anymore. */
|
||||
void zend_optimizer_compact_vars(zend_op_array *op_array) {
|
||||
int i;
|
||||
|
||||
ALLOCA_FLAG(use_heap1);
|
||||
ALLOCA_FLAG(use_heap2);
|
||||
uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
|
||||
zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
|
||||
uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
|
||||
uint32_t num_cvs, num_tmps;
|
||||
|
||||
/* Determine which CVs are used */
|
||||
zend_bitset_clear(used_vars, used_vars_len);
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
|
||||
if (opline->opcode == ZEND_ROPE_INIT) {
|
||||
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
num_cvs = 0;
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
if (zend_bitset_in(used_vars, i)) {
|
||||
vars_map[i] = num_cvs++;
|
||||
} else {
|
||||
vars_map[i] = (uint32_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
num_tmps = 0;
|
||||
for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
|
||||
if (zend_bitset_in(used_vars, i)) {
|
||||
vars_map[i] = num_cvs + num_tmps++;
|
||||
} else {
|
||||
vars_map[i] = (uint32_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(used_vars, use_heap1);
|
||||
if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
|
||||
free_alloca(vars_map, use_heap2);
|
||||
return;
|
||||
}
|
||||
|
||||
ZEND_ASSERT(num_cvs <= op_array->last_var);
|
||||
ZEND_ASSERT(num_tmps <= op_array->T);
|
||||
|
||||
/* Update CV and TMP references in opcodes */
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
|
||||
}
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update CV name table */
|
||||
if (num_cvs != op_array->last_var) {
|
||||
if (num_cvs) {
|
||||
zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
if (vars_map[i] != (uint32_t) -1) {
|
||||
names[vars_map[i]] = op_array->vars[i];
|
||||
} else {
|
||||
zend_string_release_ex(op_array->vars[i], 0);
|
||||
}
|
||||
}
|
||||
efree(op_array->vars);
|
||||
op_array->vars = names;
|
||||
} else {
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
zend_string_release_ex(op_array->vars[i], 0);
|
||||
}
|
||||
efree(op_array->vars);
|
||||
op_array->vars = NULL;
|
||||
}
|
||||
op_array->last_var = num_cvs;
|
||||
}
|
||||
|
||||
op_array->T = num_tmps;
|
||||
|
||||
free_alloca(vars_map, use_heap2);
|
||||
}
|
||||
@@ -1,651 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DCE - Dead Code Elimination |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Nikita Popov <nikic@php.net> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "Optimizer/zend_inference.h"
|
||||
#include "Optimizer/zend_ssa.h"
|
||||
#include "Optimizer/zend_func_info.h"
|
||||
#include "Optimizer/zend_call_graph.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes
|
||||
* that all instructions and phis are dead. Instructions with immediate side-effects are then marked
|
||||
* as live. We then recursively (using a worklist) propagate liveness to the instructions that def
|
||||
* the used operands.
|
||||
*
|
||||
* Notes:
|
||||
* * This pass does not perform unreachable code elimination. This happens as part of the SCCP
|
||||
* pass.
|
||||
* * The DCE is performed without taking control-dependence into account, i.e. all conditional
|
||||
* branches are assumed to be live. It's possible to take control-dependence into account using
|
||||
* the DCE algorithm described by Cytron et al., however it requires the construction of a
|
||||
* postdominator tree and of postdominance frontiers, which does not seem worthwhile at this
|
||||
* point.
|
||||
* * We separate intrinsic side-effects from potential side-effects in the form of notices thrown
|
||||
* by the instruction (in case we want to make this configurable). See may_have_side_effects() and
|
||||
* zend_may_throw().
|
||||
* * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same
|
||||
* order. There is an optimization option to allow reordering of dtor effects.
|
||||
* * The algorithm is able to eliminate dead modifications of non-escaping arrays
|
||||
* and objects as well as dead arrays and objects allocations.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
zend_ssa *ssa;
|
||||
zend_op_array *op_array;
|
||||
zend_bitset instr_dead;
|
||||
zend_bitset phi_dead;
|
||||
zend_bitset instr_worklist;
|
||||
zend_bitset phi_worklist;
|
||||
zend_bitset phi_worklist_no_val;
|
||||
uint32_t instr_worklist_len;
|
||||
uint32_t phi_worklist_len;
|
||||
unsigned reorder_dtor_effects : 1;
|
||||
} context;
|
||||
|
||||
static inline bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
|
||||
if (def < 0) {
|
||||
/* This modification is not tracked by SSA, assume the worst */
|
||||
return 1;
|
||||
}
|
||||
if (ssa->var_info[use].type & MAY_BE_REF) {
|
||||
/* Modification of reference may have side-effect */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool may_have_side_effects(
|
||||
zend_op_array *op_array, zend_ssa *ssa,
|
||||
const zend_op *opline, const zend_ssa_op *ssa_op,
|
||||
bool reorder_dtor_effects) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_NOP:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_FREE:
|
||||
case ZEND_FE_FREE:
|
||||
case ZEND_TYPE_CHECK:
|
||||
case ZEND_DEFINED:
|
||||
case ZEND_ADD:
|
||||
case ZEND_SUB:
|
||||
case ZEND_MUL:
|
||||
case ZEND_POW:
|
||||
case ZEND_BW_OR:
|
||||
case ZEND_BW_AND:
|
||||
case ZEND_BW_XOR:
|
||||
case ZEND_CONCAT:
|
||||
case ZEND_FAST_CONCAT:
|
||||
case ZEND_DIV:
|
||||
case ZEND_MOD:
|
||||
case ZEND_BOOL_XOR:
|
||||
case ZEND_BOOL:
|
||||
case ZEND_BOOL_NOT:
|
||||
case ZEND_BW_NOT:
|
||||
case ZEND_SL:
|
||||
case ZEND_SR:
|
||||
case ZEND_IS_EQUAL:
|
||||
case ZEND_IS_NOT_EQUAL:
|
||||
case ZEND_IS_SMALLER:
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
case ZEND_CAST:
|
||||
case ZEND_ROPE_INIT:
|
||||
case ZEND_ROPE_ADD:
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
case ZEND_SPACESHIP:
|
||||
case ZEND_STRLEN:
|
||||
case ZEND_COUNT:
|
||||
case ZEND_GET_TYPE:
|
||||
case ZEND_ISSET_ISEMPTY_THIS:
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_ISSET_ISEMPTY_CV:
|
||||
case ZEND_ISSET_ISEMPTY_VAR:
|
||||
case ZEND_FETCH_IS:
|
||||
case ZEND_IN_ARRAY:
|
||||
case ZEND_FUNC_NUM_ARGS:
|
||||
case ZEND_FUNC_GET_ARGS:
|
||||
case ZEND_ARRAY_KEY_EXISTS:
|
||||
/* No side effects */
|
||||
return 0;
|
||||
case ZEND_ROPE_END:
|
||||
/* TODO: Rope dce optimization, see #76446 */
|
||||
return 1;
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
/* For our purposes a jumps and branches are side effects. */
|
||||
return 1;
|
||||
case ZEND_BEGIN_SILENCE:
|
||||
case ZEND_END_SILENCE:
|
||||
case ZEND_ECHO:
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_EXT_STMT:
|
||||
case ZEND_EXT_FCALL_BEGIN:
|
||||
case ZEND_EXT_FCALL_END:
|
||||
case ZEND_TICKS:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
/* Intrinsic side effects */
|
||||
return 1;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
/* For now assume all calls have side effects */
|
||||
return 1;
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_INIT:
|
||||
/* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
|
||||
* due to the prologue skipping code. */
|
||||
return 1;
|
||||
case ZEND_ASSIGN_REF:
|
||||
return 1;
|
||||
case ZEND_ASSIGN:
|
||||
{
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
|
||||
return 1;
|
||||
}
|
||||
if (!reorder_dtor_effects) {
|
||||
if (opline->op2_type != IS_CONST
|
||||
&& (OP2_INFO() & MAY_HAVE_DTOR)
|
||||
&& ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
/* DCE might shorten lifetime */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case ZEND_UNSET_VAR:
|
||||
return 1;
|
||||
case ZEND_UNSET_CV:
|
||||
{
|
||||
uint32_t t1 = OP1_INFO();
|
||||
if (t1 & MAY_BE_REF) {
|
||||
/* We don't consider uses as the LHS of an assignment as real uses during DCE, so
|
||||
* an unset may be considered dead even if there is a later assignment to the
|
||||
* variable. Removing the unset in this case would not be correct if the variable
|
||||
* is a reference, because unset breaks references. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_DEC:
|
||||
return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def);
|
||||
case ZEND_ASSIGN_OP:
|
||||
return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
return 1;
|
||||
}
|
||||
if (!reorder_dtor_effects) {
|
||||
opline++;
|
||||
ssa_op++;
|
||||
if (opline->op1_type != IS_CONST
|
||||
&& (OP1_INFO() & MAY_HAVE_DTOR)) {
|
||||
/* DCE might shorten lifetime */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_BIND_STATIC:
|
||||
if (op_array->static_variables) {
|
||||
/* Implicit and Explicit bind static is effectively prologue of closure so
|
||||
report it has side effects like RECV, RECV_INIT; This allows us to
|
||||
reflect on the closure and discover used variable at runtime */
|
||||
if ((opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((opline->extended_value & ZEND_BIND_REF) != 0) {
|
||||
zval *value =
|
||||
(zval*)((char*)op_array->static_variables->arData +
|
||||
(opline->extended_value & ~ZEND_BIND_REF));
|
||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||
/* AST may contain undefined constants */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_CHECK_VAR:
|
||||
return (OP1_INFO() & MAY_BE_UNDEF) != 0;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
/* Model as not having side-effects -- let the side-effect be introduced by
|
||||
* FE_FETCH if the array is not known to be non-empty. */
|
||||
return (OP1_INFO() & MAY_BE_ANY) != MAY_BE_ARRAY;
|
||||
default:
|
||||
/* For everything we didn't handle, assume a side-effect */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition >= 0) {
|
||||
if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
|
||||
zend_bitset_incl(ctx->instr_worklist, var->definition);
|
||||
}
|
||||
} else if (var->definition_phi) {
|
||||
if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
|
||||
zend_bitset_incl(ctx->phi_worklist, var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
|
||||
zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) {
|
||||
if (ssa_op->result_use >= 0) {
|
||||
add_to_worklists(ctx, ssa_op->result_use, check);
|
||||
}
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)
|
||||
|| (opline->opcode == ZEND_ASSIGN
|
||||
&& (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) != 0)) {
|
||||
add_to_worklists(ctx, ssa_op->op1_use, check);
|
||||
} else {
|
||||
add_to_phi_worklist_no_val(ctx, ssa_op->op1_use);
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)
|
||||
|| (opline->opcode == ZEND_FE_FETCH_R
|
||||
&& (ssa->var_info[ssa_op->op2_use].type & MAY_BE_REF) != 0)) {
|
||||
add_to_worklists(ctx, ssa_op->op2_use, check);
|
||||
} else {
|
||||
add_to_phi_worklist_no_val(ctx, ssa_op->op2_use);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
add_to_worklists(ctx, source, check);
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
}
|
||||
|
||||
static inline bool is_var_dead(context *ctx, int var_num) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition_phi) {
|
||||
return zend_bitset_in(ctx->phi_dead, var_num);
|
||||
} else if (var->definition >= 0) {
|
||||
return zend_bitset_in(ctx->instr_dead, var->definition);
|
||||
} else {
|
||||
/* Variable has no definition, so either the definition has already been removed (var is
|
||||
* dead) or this is one of the implicit variables at the start of the function (for our
|
||||
* purposes live) */
|
||||
return var_num >= ctx->op_array->last_var;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes we can mark the var as EXT_UNUSED
|
||||
static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
|
||||
if (use_chain >= 0) {
|
||||
return 0;
|
||||
}
|
||||
zend_ssa_var *var = &ctx->ssa->vars[free_var];
|
||||
int def = var->definition;
|
||||
|
||||
if (def >= 0) {
|
||||
zend_ssa_op *def_op = &ctx->ssa->ops[def];
|
||||
|
||||
if (def_op->result_def == free_var
|
||||
&& var->phi_use_chain == NULL
|
||||
&& var->use_chain == (opline - ctx->op_array->opcodes)) {
|
||||
zend_op *def_opline = &ctx->op_array->opcodes[def];
|
||||
|
||||
switch (def_opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_ASSIGN_REF:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
def_opline->result_type = IS_UNUSED;
|
||||
def_opline->result.var = 0;
|
||||
def_op->result_def = -1;
|
||||
var->definition = -1;
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_always_inline zend_bool may_be_refcounted(uint32_t type) {
|
||||
return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
|
||||
}
|
||||
|
||||
static inline bool is_free_of_live_var(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FREE:
|
||||
/* It is always safe to remove FREEs of non-refcounted values, even if they are live. */
|
||||
if (!may_be_refcounted(ctx->ssa->var_info[ssa_op->op1_use].type)) {
|
||||
return 0;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_FE_FREE:
|
||||
return !is_var_dead(ctx, ssa_op->op1_use);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns whether the instruction has been DCEd */
|
||||
static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
int free_var = -1;
|
||||
zend_uchar free_var_type;
|
||||
|
||||
if (opline->opcode == ZEND_NOP) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
|
||||
if (is_free_of_live_var(ctx, opline, ssa_op)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
|
||||
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
|
||||
if (may_be_refcounted(ssa->var_info[ssa_op->op1_use].type)
|
||||
&& opline->opcode != ZEND_CASE && opline->opcode != ZEND_CASE_STRICT) {
|
||||
free_var = ssa_op->op1_use;
|
||||
free_var_type = opline->op1_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
|
||||
if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
|
||||
if (may_be_refcounted(ssa->var_info[ssa_op->op2_use].type)) {
|
||||
if (free_var >= 0) {
|
||||
// TODO: We can't free two vars. Keep instruction alive.
|
||||
zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
|
||||
return 0;
|
||||
}
|
||||
free_var = ssa_op->op2_use;
|
||||
free_var_type = opline->op2_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
|
||||
zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
|
||||
|
||||
if (free_var >= 0) {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->op1.var = EX_NUM_TO_VAR(ssa->vars[free_var].var);
|
||||
opline->op1_type = free_var_type;
|
||||
|
||||
ssa_op->op1_use = free_var;
|
||||
ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
|
||||
ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
|
||||
int common_source = -1;
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
if (common_source == -1) {
|
||||
common_source = source;
|
||||
} else if (common_source != source && source != phi->ssa_var) {
|
||||
return -1;
|
||||
}
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
ZEND_ASSERT(common_source != -1);
|
||||
return common_source;
|
||||
}
|
||||
|
||||
static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
if (phi->pi < 0) {
|
||||
/* Phi assignment with identical source operands */
|
||||
int common_source = get_common_phi_source(ssa, phi);
|
||||
if (common_source >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}
|
||||
} else {
|
||||
/* Pi assignment that is only used in Phi/Pi assignments */
|
||||
// TODO What if we want to rerun type inference after DCE? Maybe separate this?
|
||||
/*ZEND_ASSERT(phi->sources[0] != -1);
|
||||
if (ssa->vars[phi->ssa_var].use_chain < 0) {
|
||||
zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
|
||||
if (ssa_op->op1_def >= 0
|
||||
&& ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
if (ssa_op->op2_def >= 0
|
||||
&& ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
if (ssa_op->result_def >= 0
|
||||
&& ssa->vars[ssa_op->result_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects) {
|
||||
int i;
|
||||
zend_ssa_phi *phi;
|
||||
int removed_ops = 0;
|
||||
|
||||
/* DCE of CV operations that changes arguments may affect vararg functions. */
|
||||
bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0;
|
||||
|
||||
context ctx;
|
||||
ctx.ssa = ssa;
|
||||
ctx.op_array = op_array;
|
||||
ctx.reorder_dtor_effects = reorder_dtor_effects;
|
||||
|
||||
/* We have no dedicated phi vector, so we use the whole ssa var vector instead */
|
||||
ctx.instr_worklist_len = zend_bitset_len(op_array->last);
|
||||
ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
|
||||
ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
|
||||
/* Optimistically assume all instructions and phis to be dead */
|
||||
ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
|
||||
/* Mark non-CV phis as live. Even if the result is unused, we generally cannot remove one
|
||||
* of the producing instructions, as it combines producing the result with control flow.
|
||||
* This can be made more precise if there are any cases where this is not the case. */
|
||||
FOREACH_PHI(phi) {
|
||||
if (phi->var >= op_array->last_var
|
||||
&& may_be_refcounted(ssa->var_info[phi->ssa_var].type)) {
|
||||
zend_bitset_excl(ctx.phi_dead, phi->ssa_var);
|
||||
add_phi_sources_to_worklists(&ctx, phi, 0);
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
/* Mark reachable instruction without side effects as dead */
|
||||
int b = ssa->cfg.blocks_count;
|
||||
while (b > 0) {
|
||||
int op_data = -1;
|
||||
|
||||
b--;
|
||||
zend_basic_block *block = &ssa->cfg.blocks[b];
|
||||
if (!(block->flags & ZEND_BB_REACHABLE)) {
|
||||
continue;
|
||||
}
|
||||
i = block->start + block->len;
|
||||
while (i > block->start) {
|
||||
i--;
|
||||
|
||||
if (op_array->opcodes[i].opcode == ZEND_OP_DATA) {
|
||||
op_data = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zend_bitset_in(ctx.instr_worklist, i)) {
|
||||
zend_bitset_excl(ctx.instr_worklist, i);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
|
||||
if (op_data >= 0) {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
|
||||
}
|
||||
} else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects)
|
||||
|| zend_may_throw(&op_array->opcodes[i], &ssa->ops[i], op_array, ssa)
|
||||
|| (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) {
|
||||
if (op_array->opcodes[i].opcode == ZEND_NEW
|
||||
&& op_array->opcodes[i+1].opcode == ZEND_DO_FCALL
|
||||
&& ssa->ops[i].result_def >= 0
|
||||
&& ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
zend_bitset_incl(ctx.instr_dead, i);
|
||||
zend_bitset_incl(ctx.instr_dead, i+1);
|
||||
} else {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
|
||||
if (op_data >= 0) {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zend_bitset_incl(ctx.instr_dead, i);
|
||||
if (op_data >= 0) {
|
||||
zend_bitset_incl(ctx.instr_dead, op_data);
|
||||
}
|
||||
}
|
||||
op_data = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Propagate liveness backwards to all definitions of used vars */
|
||||
while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len)
|
||||
|| !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) {
|
||||
while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) {
|
||||
zend_bitset_excl(ctx.instr_dead, i);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1);
|
||||
if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) {
|
||||
zend_bitset_excl(ctx.instr_dead, i+1);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1);
|
||||
}
|
||||
}
|
||||
while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) {
|
||||
zend_bitset_excl(ctx.phi_dead, i);
|
||||
zend_bitset_excl(ctx.phi_worklist_no_val, i);
|
||||
add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Eliminate dead instructions */
|
||||
ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
|
||||
removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
|
||||
} ZEND_BITSET_FOREACH_END();
|
||||
|
||||
/* Improper uses don't count as "uses" for the purpose of instruction elimination,
|
||||
* but we have to retain phis defining them.
|
||||
* Propagate this information backwards, marking any phi with an improperly used
|
||||
* target as non-dead. */
|
||||
while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) {
|
||||
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
||||
int source;
|
||||
zend_bitset_excl(ctx.phi_dead, i);
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
add_to_phi_worklist_no_val(&ctx, source);
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
}
|
||||
|
||||
/* Now collect the actually dead phis */
|
||||
FOREACH_PHI(phi) {
|
||||
if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) {
|
||||
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
} else {
|
||||
/* Remove trivial phis (phis with identical source operands) */
|
||||
try_remove_trivial_phi(&ctx, phi);
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
return removed_ops;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,538 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache, Escape Analysis |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_bitset.h"
|
||||
#include "zend_cfg.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_dump.h"
|
||||
|
||||
/*
|
||||
* T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic
|
||||
* compilation and deoptimization. In Proceedings of the International
|
||||
* Conference on Virtual Execution Environments, pages 111-120, Chicago,
|
||||
* June 2005
|
||||
*/
|
||||
|
||||
static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
parent[i] = i;
|
||||
size[i] = 1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
|
||||
{
|
||||
int p = parent[i];
|
||||
|
||||
while (i != p) {
|
||||
p = parent[p];
|
||||
parent[i] = p;
|
||||
i = p;
|
||||
p = parent[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
|
||||
{
|
||||
int r1 = union_find_root(parent, i);
|
||||
int r2 = union_find_root(parent, j);
|
||||
|
||||
if (r1 != r2) {
|
||||
if (size[r1] < size[r2]) {
|
||||
parent[r1] = r2;
|
||||
size[r2] += size[r1];
|
||||
} else {
|
||||
parent[r2] = r1;
|
||||
size[r1] += size[r2];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
|
||||
{
|
||||
zend_ssa_var *ssa_vars = ssa->vars;
|
||||
int ssa_vars_count = ssa->vars_count;
|
||||
zend_ssa_phi *p;
|
||||
int i, j;
|
||||
int *size;
|
||||
ALLOCA_FLAG(use_heap)
|
||||
|
||||
size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
|
||||
if (!size) {
|
||||
return FAILURE;
|
||||
}
|
||||
union_find_init(parent, size, ssa_vars_count);
|
||||
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].definition_phi) {
|
||||
p = ssa_vars[i].definition_phi;
|
||||
if (p->pi >= 0) {
|
||||
union_find_unite(parent, size, i, p->sources[0]);
|
||||
} else {
|
||||
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
|
||||
union_find_unite(parent, size, i, p->sources[j]);
|
||||
}
|
||||
}
|
||||
} else if (ssa_vars[i].definition >= 0) {
|
||||
int def = ssa_vars[i].definition;
|
||||
zend_ssa_op *op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (op->op1_def >= 0) {
|
||||
if (op->op1_use >= 0) {
|
||||
if (opline->opcode != ZEND_ASSIGN) {
|
||||
union_find_unite(parent, size, op->op1_def, op->op1_use);
|
||||
}
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->op1_def, op->op2_use);
|
||||
}
|
||||
}
|
||||
if (op->op2_def >= 0) {
|
||||
if (op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->op2_def, op->op2_use);
|
||||
}
|
||||
}
|
||||
if (op->result_def >= 0) {
|
||||
if (op->result_use >= 0) {
|
||||
if (opline->opcode != ZEND_QM_ASSIGN) {
|
||||
union_find_unite(parent, size, op->result_def, op->result_use);
|
||||
}
|
||||
}
|
||||
if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op1_use);
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op2_use);
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op1_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
parent[i] = union_find_root(parent, i);
|
||||
}
|
||||
|
||||
free_alloca(size, use_heap);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */
|
||||
{
|
||||
zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
|
||||
if (ce) {
|
||||
return ce;
|
||||
}
|
||||
|
||||
ce = zend_hash_find_ptr(CG(class_table), lcname);
|
||||
if (ce && ce->type == ZEND_INTERNAL_CLASS) {
|
||||
return ce;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *ssa_op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (ssa_op->result_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_ARRAY:
|
||||
return 1;
|
||||
case ZEND_NEW:
|
||||
/* objects with destructors should escape */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
|
||||
uint32_t forbidden_flags =
|
||||
/* These flags will always cause an exception */
|
||||
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
|
||||
| ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
|
||||
if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
|
||||
!ce->destructor && !ce->__get && !ce->__set &&
|
||||
!(ce->ce_flags & forbidden_flags) &&
|
||||
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_QM_ASSIGN:
|
||||
if (opline->op1_type == IS_CONST
|
||||
&& Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
|
||||
return 1;
|
||||
}
|
||||
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (ssa_op->op1_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op2_type == IS_CONST
|
||||
&& Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
|
||||
return 1;
|
||||
}
|
||||
if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
|
||||
/* implicit object/array allocation */
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (op->result_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_ASSIGN:
|
||||
return 1;
|
||||
case ZEND_NEW:
|
||||
/* objects with destructors should escape */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
|
||||
if (ce && !ce->create_object && !ce->constructor &&
|
||||
!ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (op->op1_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *ssa_op = ssa->ops + use;
|
||||
zend_op *opline = op_array->opcodes + use;
|
||||
|
||||
if (ssa_op->op1_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
/* no_val */
|
||||
break;
|
||||
case ZEND_QM_ASSIGN:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
return 1;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
break;
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
break;
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
|
||||
return 1;
|
||||
}
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
/* reference dependencies processed separately */
|
||||
break;
|
||||
case ZEND_OP_DATA:
|
||||
if ((opline-1)->opcode != ZEND_ASSIGN_DIM
|
||||
&& (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
|
||||
return 1;
|
||||
}
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
opline--;
|
||||
ssa_op--;
|
||||
if (opline->op1_type != IS_CV
|
||||
|| (OP1_INFO() & MAY_BE_REF)
|
||||
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
|
||||
/* assignment into escaping structure */
|
||||
return 1;
|
||||
}
|
||||
/* reference dependencies processed separately */
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->op2_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op1_type != IS_CV
|
||||
|| (OP1_INFO() & MAY_BE_REF)
|
||||
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
|
||||
/* assignment into escaping variable */
|
||||
return 1;
|
||||
}
|
||||
if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
|
||||
if (OP2_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->result_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
|
||||
{
|
||||
zend_ssa_var *ssa_vars = ssa->vars;
|
||||
int ssa_vars_count = ssa->vars_count;
|
||||
int i, root, use;
|
||||
int *ees;
|
||||
bool has_allocations;
|
||||
int num_non_escaped;
|
||||
ALLOCA_FLAG(use_heap)
|
||||
|
||||
if (!ssa_vars) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
has_allocations = 0;
|
||||
for (i = op_array->last_var; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].definition >= 0
|
||||
&& (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
|
||||
&& is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
has_allocations = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_allocations) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* 1. Build EES (Equi-Escape Sets) */
|
||||
ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
|
||||
if (!ees) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* 2. Identify Allocations */
|
||||
num_non_escaped = 0;
|
||||
for (i = op_array->last_var; i < ssa_vars_count; i++) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
|
||||
/* already escape. skip */
|
||||
} else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
}
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else if (ssa_vars[i].definition >= 0
|
||||
&& (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
|
||||
if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
}
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
|
||||
&& is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
|
||||
num_non_escaped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Mark escaped EES */
|
||||
if (num_non_escaped) {
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].use_chain >= 0) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
FOREACH_USE(ssa_vars + i, use) {
|
||||
if (is_escape_use(op_array, ssa, use, i)) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
num_non_escaped--;
|
||||
if (num_non_escaped == 0) {
|
||||
i = ssa_vars_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 4. Process referential dependencies */
|
||||
if (num_non_escaped) {
|
||||
bool changed;
|
||||
|
||||
do {
|
||||
changed = 0;
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].use_chain >= 0) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
FOREACH_USE(ssa_vars + i, use) {
|
||||
zend_ssa_op *op = ssa->ops + use;
|
||||
zend_op *opline = op_array->opcodes + use;
|
||||
int enclosing_root;
|
||||
|
||||
if (opline->opcode == ZEND_OP_DATA &&
|
||||
((opline-1)->opcode == ZEND_ASSIGN_DIM ||
|
||||
(opline-1)->opcode == ZEND_ASSIGN_OBJ ||
|
||||
(opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) &&
|
||||
op->op1_use == i &&
|
||||
(op-1)->op1_use >= 0) {
|
||||
enclosing_root = ees[(op-1)->op1_use];
|
||||
} else if ((opline->opcode == ZEND_INIT_ARRAY ||
|
||||
opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
|
||||
op->op1_use == i &&
|
||||
op->result_def >= 0) {
|
||||
enclosing_root = ees[op->result_def];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
|
||||
ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
|
||||
if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else {
|
||||
ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
|
||||
}
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
if (num_non_escaped == 0) {
|
||||
changed = 0;
|
||||
} else {
|
||||
changed = 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
/* 5. Propagate values of escape sets to variables */
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
root = ees[i];
|
||||
if (i != root) {
|
||||
ssa_vars[i].escape_state = ssa_vars[root].escape_state;
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(ees, use_heap);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 10:
|
||||
* - remove NOPs
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *end, *opline;
|
||||
uint32_t new_count, i, shift;
|
||||
int j;
|
||||
uint32_t *shiftlist;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
|
||||
i = new_count = shift = 0;
|
||||
end = op_array->opcodes + op_array->last;
|
||||
for (opline = op_array->opcodes; opline < end; opline++) {
|
||||
|
||||
/* Kill JMP-over-NOP-s */
|
||||
if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
|
||||
/* check if there are only NOPs under the branch */
|
||||
zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 1;
|
||||
|
||||
while (target->opcode == ZEND_NOP) {
|
||||
target--;
|
||||
}
|
||||
if (target == opline) {
|
||||
/* only NOPs */
|
||||
opline->opcode = ZEND_NOP;
|
||||
}
|
||||
}
|
||||
|
||||
shiftlist[i++] = shift;
|
||||
if (opline->opcode == ZEND_NOP) {
|
||||
shift++;
|
||||
} else {
|
||||
if (shift) {
|
||||
zend_op *new_opline = op_array->opcodes + new_count;
|
||||
|
||||
*new_opline = *opline;
|
||||
zend_optimizer_migrate_jump(op_array, new_opline, opline);
|
||||
}
|
||||
new_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (shift) {
|
||||
op_array->last = new_count;
|
||||
end = op_array->opcodes + op_array->last;
|
||||
|
||||
/* update JMPs */
|
||||
for (opline = op_array->opcodes; opline<end; opline++) {
|
||||
zend_optimizer_shift_jump(op_array, opline, shiftlist);
|
||||
}
|
||||
|
||||
/* update try/catch array */
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
|
||||
op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
|
||||
op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
|
||||
}
|
||||
}
|
||||
|
||||
/* update early binding list */
|
||||
if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
|
||||
uint32_t *opline_num = &ctx->script->first_early_binding_opline;
|
||||
|
||||
ZEND_ASSERT(op_array == &ctx->script->main_op_array);
|
||||
do {
|
||||
*opline_num -= shiftlist[*opline_num];
|
||||
opline_num = &op_array->opcodes[*opline_num].result.opline_num;
|
||||
} while (*opline_num != (uint32_t)-1);
|
||||
}
|
||||
}
|
||||
free_alloca(shiftlist, use_heap);
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 4
|
||||
* - optimize INIT_FCALL_BY_NAME to DO_FCALL
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
#define ZEND_OP1_IS_CONST_STRING(opline) \
|
||||
(opline->op1_type == IS_CONST && \
|
||||
Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
|
||||
#define ZEND_OP2_IS_CONST_STRING(opline) \
|
||||
(opline->op2_type == IS_CONST && \
|
||||
Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
|
||||
|
||||
typedef struct _optimizer_call_info {
|
||||
zend_function *func;
|
||||
zend_op *opline;
|
||||
bool is_prototype;
|
||||
bool try_inline;
|
||||
uint32_t func_arg_num;
|
||||
} optimizer_call_info;
|
||||
|
||||
static void zend_delete_call_instructions(zend_op *opline)
|
||||
{
|
||||
int call = 0;
|
||||
|
||||
while (1) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL:
|
||||
if (call == 0) {
|
||||
MAKE_NOP(opline);
|
||||
return;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call--;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
call++;
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
if (call == 0) {
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
MAKE_NOP(opline);
|
||||
} else if (opline->op1_type == IS_CV) {
|
||||
opline->opcode = ZEND_CHECK_VAR;
|
||||
opline->extended_value = 0;
|
||||
opline->result.var = 0;
|
||||
} else {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->extended_value = 0;
|
||||
opline->result.var = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline--;
|
||||
}
|
||||
}
|
||||
|
||||
static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
|
||||
{
|
||||
if (func->type == ZEND_USER_FUNCTION
|
||||
&& !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
|
||||
/* TODO: function copied from trait may be inconsistent ??? */
|
||||
&& !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
|
||||
&& fcall->extended_value >= func->op_array.required_num_args
|
||||
&& func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
|
||||
|
||||
zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
|
||||
|
||||
if (ret_opline->op1_type == IS_CONST) {
|
||||
uint32_t i, num_args = func->op_array.num_args;
|
||||
num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
|
||||
|
||||
if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|
||||
&& !(func->op_array.fn_flags & ZEND_ACC_STATIC)) {
|
||||
/* Don't inline static call to instance method. */
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_args; i++) {
|
||||
/* Don't inline functions with by-reference arguments. This would require
|
||||
* correct handling of INDIRECT arguments. */
|
||||
if (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fcall->extended_value < func->op_array.num_args) {
|
||||
/* don't inline functions with named constants in default arguments */
|
||||
i = fcall->extended_value;
|
||||
|
||||
do {
|
||||
if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) {
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
} while (i < func->op_array.num_args);
|
||||
}
|
||||
|
||||
if (RETURN_VALUE_USED(opline)) {
|
||||
zval zv;
|
||||
|
||||
ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, ret_opline->op1));
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->op1_type = IS_CONST;
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
SET_UNUSED(opline->op2);
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
|
||||
zend_delete_call_instructions(opline-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
int call = 0;
|
||||
void *checkpoint;
|
||||
optimizer_call_info *call_stack;
|
||||
|
||||
if (op_array->last < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_NEW:
|
||||
/* The argument passing optimizations are valid for prototypes as well,
|
||||
* as inheritance cannot change between ref <-> non-ref arguments. */
|
||||
call_stack[call].func = zend_optimizer_get_called_func(
|
||||
ctx->script, op_array, opline, &call_stack[call].is_prototype);
|
||||
call_stack[call].try_inline =
|
||||
!call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call_stack[call].opline = opline;
|
||||
call_stack[call].func_arg_num = (uint32_t)-1;
|
||||
call++;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
call--;
|
||||
if (call_stack[call].func && call_stack[call].opline) {
|
||||
zend_op *fcall = call_stack[call].opline;
|
||||
|
||||
if (fcall->opcode == ZEND_INIT_FCALL) {
|
||||
/* nothing to do */
|
||||
} else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
|
||||
fcall->opcode = ZEND_INIT_FCALL;
|
||||
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(fcall));
|
||||
fcall->op2.constant = fcall->op2.constant + 1;
|
||||
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
|
||||
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
|
||||
fcall->opcode = ZEND_INIT_FCALL;
|
||||
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
|
||||
literal_dtor(&op_array->literals[fcall->op2.constant]);
|
||||
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
|
||||
fcall->op2.constant = fcall->op2.constant + 1;
|
||||
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
|
||||
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|
||||
|| fcall->opcode == ZEND_INIT_METHOD_CALL
|
||||
|| fcall->opcode == ZEND_NEW) {
|
||||
/* We don't have specialized opcodes for this, do nothing */
|
||||
} else {
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
|
||||
if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
|
||||
&& call_stack[call].try_inline) {
|
||||
zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
|
||||
}
|
||||
}
|
||||
call_stack[call].func = NULL;
|
||||
call_stack[call].opline = NULL;
|
||||
call_stack[call].try_inline = 0;
|
||||
call_stack[call].func_arg_num = (uint32_t)-1;
|
||||
break;
|
||||
case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
if (call_stack[call - 1].func
|
||||
&& call_stack[call - 1].func_arg_num != (uint32_t)-1) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
|
||||
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
|
||||
opline->opcode -= 9;
|
||||
} else {
|
||||
opline->opcode = ZEND_FETCH_STATIC_PROP_W;
|
||||
}
|
||||
} else {
|
||||
if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
|
||||
&& opline->op2_type == IS_UNUSED) {
|
||||
/* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
|
||||
* Performing the replacement would create an invalid opcode. */
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
|
||||
opline->opcode -= 12;
|
||||
} else {
|
||||
opline->opcode = ZEND_FETCH_STATIC_PROP_R;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAL_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
/* We won't convert it into_DO_FCALL to emit error at run-time */
|
||||
call_stack[call - 1].opline = NULL;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = opline->op2.num;
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_REF;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_VAR_NO_REF;
|
||||
} else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_VAL;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_REF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_ARRAY:
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
#define GET_AVAILABLE_T() \
|
||||
for (i = 0; i < T; i++) { \
|
||||
if (!zend_bitset_in(taken_T, i)) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
zend_bitset_incl(taken_T, i); \
|
||||
if (i > max) { \
|
||||
max = i; \
|
||||
}
|
||||
|
||||
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
int T = op_array->T;
|
||||
int offset = op_array->last_var;
|
||||
uint32_t bitset_len;
|
||||
zend_bitset taken_T; /* T index in use */
|
||||
zend_op **start_of_T; /* opline where T is first used */
|
||||
zend_bitset valid_T; /* Is the map_T valid */
|
||||
int *map_T; /* Map's the T to its new index */
|
||||
zend_op *opline, *end;
|
||||
int currT;
|
||||
int i;
|
||||
int max = -1;
|
||||
void *checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
|
||||
bitset_len = zend_bitset_len(T);
|
||||
taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
||||
start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
|
||||
valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
||||
map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
|
||||
|
||||
end = op_array->opcodes;
|
||||
opline = &op_array->opcodes[op_array->last - 1];
|
||||
|
||||
/* Find T definition points */
|
||||
while (opline >= end) {
|
||||
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
|
||||
start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
|
||||
}
|
||||
opline--;
|
||||
}
|
||||
|
||||
zend_bitset_clear(valid_T, bitset_len);
|
||||
zend_bitset_clear(taken_T, bitset_len);
|
||||
|
||||
end = op_array->opcodes;
|
||||
opline = &op_array->opcodes[op_array->last - 1];
|
||||
|
||||
while (opline >= end) {
|
||||
if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
|
||||
currT = VAR_NUM(opline->op1.var) - offset;
|
||||
if (opline->opcode == ZEND_ROPE_END) {
|
||||
int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
int var;
|
||||
|
||||
var = max;
|
||||
while (var >= 0 && !zend_bitset_in(taken_T, var)) {
|
||||
var--;
|
||||
}
|
||||
max = MAX(max, var + num);
|
||||
var = var + 1;
|
||||
map_T[currT] = var;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
zend_bitset_incl(taken_T, var);
|
||||
opline->op1.var = NUM_VAR(var + offset);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_incl(taken_T, var + num);
|
||||
}
|
||||
} else {
|
||||
if (!zend_bitset_in(valid_T, currT)) {
|
||||
int use_new_var = 0;
|
||||
|
||||
/* Code in "finally" blocks may modify temporary variables.
|
||||
* We allocate new temporaries for values that need to
|
||||
* relive FAST_CALLs.
|
||||
*/
|
||||
if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
|
||||
(opline->opcode == ZEND_RETURN ||
|
||||
opline->opcode == ZEND_GENERATOR_RETURN ||
|
||||
opline->opcode == ZEND_RETURN_BY_REF ||
|
||||
opline->opcode == ZEND_FREE ||
|
||||
opline->opcode == ZEND_FE_FREE)) {
|
||||
zend_op *curr = opline;
|
||||
|
||||
while (--curr >= end) {
|
||||
if (curr->opcode == ZEND_FAST_CALL) {
|
||||
use_new_var = 1;
|
||||
break;
|
||||
} else if (curr->opcode != ZEND_FREE &&
|
||||
curr->opcode != ZEND_FE_FREE &&
|
||||
curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
|
||||
curr->opcode != ZEND_DISCARD_EXCEPTION) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (use_new_var) {
|
||||
i = ++max;
|
||||
zend_bitset_incl(taken_T, i);
|
||||
} else {
|
||||
GET_AVAILABLE_T();
|
||||
}
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
}
|
||||
opline->op1.var = NUM_VAR(map_T[currT] + offset);
|
||||
}
|
||||
}
|
||||
|
||||
if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
|
||||
currT = VAR_NUM(opline->op2.var) - offset;
|
||||
if (!zend_bitset_in(valid_T, currT)) {
|
||||
GET_AVAILABLE_T();
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
}
|
||||
opline->op2.var = NUM_VAR(map_T[currT] + offset);
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
|
||||
currT = VAR_NUM(opline->result.var) - offset;
|
||||
if (zend_bitset_in(valid_T, currT)) {
|
||||
if (start_of_T[currT] == opline) {
|
||||
/* ZEND_FAST_CALL can not share temporary var with others
|
||||
* since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
|
||||
* which could be ahead of it */
|
||||
if (opline->opcode != ZEND_FAST_CALL) {
|
||||
zend_bitset_excl(taken_T, map_T[currT]);
|
||||
}
|
||||
}
|
||||
opline->result.var = NUM_VAR(map_T[currT] + offset);
|
||||
if (opline->opcode == ZEND_ROPE_INIT) {
|
||||
if (start_of_T[currT] == opline) {
|
||||
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_excl(taken_T, map_T[currT]+num);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Code which gets here is using a wrongly built opcode such as RECV() */
|
||||
GET_AVAILABLE_T();
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
opline->result.var = NUM_VAR(i + offset);
|
||||
}
|
||||
}
|
||||
|
||||
opline--;
|
||||
}
|
||||
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
op_array->T = max + 1;
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 1 (Simple local optimizations)
|
||||
* - persistent constant substitution (true, false, null, etc)
|
||||
* - constant casting (ADD expects numbers, CONCAT strings, etc)
|
||||
* - constant expression evaluation
|
||||
* - optimize constant conditional JMPs
|
||||
* - pre-evaluate constant function calls
|
||||
* - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
|
||||
*/
|
||||
|
||||
#include "php.h"
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
|
||||
(op_array == &ctx->script->main_op_array) : 0;
|
||||
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_CONCAT:
|
||||
case ZEND_FAST_CONCAT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP1_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
goto constant_binary_op;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_ADD:
|
||||
case ZEND_SUB:
|
||||
case ZEND_MUL:
|
||||
case ZEND_DIV:
|
||||
case ZEND_POW:
|
||||
case ZEND_MOD:
|
||||
case ZEND_SL:
|
||||
case ZEND_SR:
|
||||
case ZEND_BW_OR:
|
||||
case ZEND_BW_AND:
|
||||
case ZEND_BW_XOR:
|
||||
case ZEND_IS_EQUAL:
|
||||
case ZEND_IS_NOT_EQUAL:
|
||||
case ZEND_IS_SMALLER:
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
case ZEND_BOOL_XOR:
|
||||
case ZEND_SPACESHIP:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
opline->op2_type == IS_CONST) {
|
||||
/* binary operation with constant operands */
|
||||
zval result;
|
||||
|
||||
constant_binary_op:
|
||||
if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_ASSIGN_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (opline->extended_value == ZEND_ADD
|
||||
|| opline->extended_value == ZEND_SUB
|
||||
|| opline->extended_value == ZEND_MUL
|
||||
|| opline->extended_value == ZEND_DIV
|
||||
|| opline->extended_value == ZEND_POW) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
/* don't optimize if it should produce a runtime numeric string error */
|
||||
if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
|
||||
convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
} else if (opline->extended_value == ZEND_MOD
|
||||
|| opline->extended_value == ZEND_SL
|
||||
|| opline->extended_value == ZEND_SR) {
|
||||
zval *op2 = &ZEND_OP2_LITERAL(opline);
|
||||
if (Z_TYPE_P(op2) != IS_LONG) {
|
||||
if (!zend_is_op_long_compatible(op2)) {
|
||||
break;
|
||||
}
|
||||
convert_to_long(op2);
|
||||
}
|
||||
} else if (opline->extended_value == ZEND_CONCAT) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_CAST:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
/* cast of constant operand */
|
||||
zval result;
|
||||
|
||||
if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_BW_NOT:
|
||||
case ZEND_BOOL_NOT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
/* unary operation on constant operand */
|
||||
zval result;
|
||||
|
||||
if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
if (opline->op2_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
|
||||
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 &&
|
||||
memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) {
|
||||
/* substitute __COMPILER_HALT_OFFSET__ constant */
|
||||
zend_execute_data *orig_execute_data = EG(current_execute_data);
|
||||
zend_execute_data fake_execute_data;
|
||||
zval *offset;
|
||||
|
||||
memset(&fake_execute_data, 0, sizeof(zend_execute_data));
|
||||
fake_execute_data.func = (zend_function*)op_array;
|
||||
EG(current_execute_data) = &fake_execute_data;
|
||||
if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, offset);
|
||||
}
|
||||
}
|
||||
EG(current_execute_data) = orig_execute_data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->op2_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
/* substitute persistent constants */
|
||||
zval c;
|
||||
|
||||
if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) {
|
||||
if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Z_TYPE(c) == IS_CONSTANT_AST) {
|
||||
break;
|
||||
}
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op2_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
|
||||
zend_class_entry *ce = NULL;
|
||||
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
|
||||
/* for A::B */
|
||||
if (op_array->scope &&
|
||||
zend_string_equals_ci(Z_STR(ZEND_OP1_LITERAL(opline)), op_array->scope->name)) {
|
||||
ce = op_array->scope;
|
||||
} else {
|
||||
if ((ce = zend_hash_find_ptr(EG(class_table),
|
||||
Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL ||
|
||||
(ce->type == ZEND_INTERNAL_CLASS &&
|
||||
ce->info.internal.module->type != MODULE_PERSISTENT) ||
|
||||
(ce->type == ZEND_USER_CLASS &&
|
||||
ce->info.user.filename != op_array->filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (op_array->scope &&
|
||||
opline->op1_type == IS_UNUSED &&
|
||||
(opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
|
||||
/* for self::B */
|
||||
ce = op_array->scope;
|
||||
} else if (op_array->scope &&
|
||||
opline->op1_type == IS_VAR &&
|
||||
(opline - 1)->opcode == ZEND_FETCH_CLASS &&
|
||||
((opline - 1)->op2_type == IS_UNUSED &&
|
||||
((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
|
||||
(opline - 1)->result.var == opline->op1.var) {
|
||||
/* for self::B */
|
||||
ce = op_array->scope;
|
||||
}
|
||||
|
||||
if (ce) {
|
||||
zend_class_constant *cc;
|
||||
zval *c, t;
|
||||
|
||||
if ((cc = zend_hash_find_ptr(&ce->constants_table,
|
||||
Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
|
||||
(ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
|
||||
c = &cc->value;
|
||||
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
|
||||
zend_ast *ast = Z_ASTVAL_P(c);
|
||||
if (ast->kind != ZEND_AST_CONSTANT
|
||||
|| !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1)
|
||||
|| Z_TYPE(t) == IS_CONSTANT_AST) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ZVAL_COPY_OR_DUP(&t, c);
|
||||
}
|
||||
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
} else if (opline->op1_type == IS_VAR) {
|
||||
MAKE_NOP((opline - 1));
|
||||
}
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_DO_ICALL: {
|
||||
zend_op *send1_opline = opline - 1;
|
||||
zend_op *send2_opline = NULL;
|
||||
zend_op *init_opline = NULL;
|
||||
|
||||
while (send1_opline->opcode == ZEND_NOP) {
|
||||
send1_opline--;
|
||||
}
|
||||
if (send1_opline->opcode != ZEND_SEND_VAL ||
|
||||
send1_opline->op1_type != IS_CONST) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
if (send1_opline->op2.num == 2) {
|
||||
send2_opline = send1_opline;
|
||||
send1_opline--;
|
||||
while (send1_opline->opcode == ZEND_NOP) {
|
||||
send1_opline--;
|
||||
}
|
||||
if (send1_opline->opcode != ZEND_SEND_VAL ||
|
||||
send1_opline->op1_type != IS_CONST) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
init_opline = send1_opline - 1;
|
||||
while (init_opline->opcode == ZEND_NOP) {
|
||||
init_opline--;
|
||||
}
|
||||
if (init_opline->opcode != ZEND_INIT_FCALL ||
|
||||
init_opline->op2_type != IS_CONST ||
|
||||
Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* define("name", scalar); */
|
||||
if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 &&
|
||||
zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) {
|
||||
|
||||
if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING &&
|
||||
send2_opline &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) {
|
||||
|
||||
if (collect_constants) {
|
||||
zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
|
||||
}
|
||||
|
||||
if (RESULT_UNUSED(opline) &&
|
||||
!zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
|
||||
|
||||
opline->opcode = ZEND_DECLARE_CONST;
|
||||
opline->op1_type = IS_CONST;
|
||||
opline->op2_type = IS_CONST;
|
||||
opline->result_type = IS_UNUSED;
|
||||
opline->op1.constant = send1_opline->op1.constant;
|
||||
opline->op2.constant = send2_opline->op1.constant;
|
||||
opline->result.num = 0;
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
MAKE_NOP(send1_opline);
|
||||
MAKE_NOP(send2_opline);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* pre-evaluate constant functions:
|
||||
constant(x)
|
||||
function_exists(x)
|
||||
is_callable(x)
|
||||
extension_loaded(x)
|
||||
*/
|
||||
if (!send2_opline &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) {
|
||||
if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"function_exists", sizeof("function_exists")-1)) ||
|
||||
(Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"is_callable", sizeof("is_callable")))) {
|
||||
zend_internal_function *func;
|
||||
zend_string *lc_name = zend_string_tolower(
|
||||
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
|
||||
|
||||
if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL
|
||||
&& func->type == ZEND_INTERNAL_FUNCTION
|
||||
&& func->module->type == MODULE_PERSISTENT
|
||||
#ifdef ZEND_WIN32
|
||||
&& func->module->handle == NULL
|
||||
#endif
|
||||
) {
|
||||
zval t;
|
||||
ZVAL_TRUE(&t);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
zend_string_release_ex(lc_name, 0);
|
||||
break;
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"extension_loaded", sizeof("extension_loaded")-1)) {
|
||||
zval t;
|
||||
zend_string *lc_name = zend_string_tolower(
|
||||
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
|
||||
zend_module_entry *m = zend_hash_find_ptr(&module_registry,
|
||||
lc_name);
|
||||
|
||||
zend_string_release_ex(lc_name, 0);
|
||||
if (!m) {
|
||||
if (PG(enable_dl)) {
|
||||
break;
|
||||
} else {
|
||||
ZVAL_FALSE(&t);
|
||||
}
|
||||
} else {
|
||||
if (m->type == MODULE_PERSISTENT
|
||||
#ifdef ZEND_WIN32
|
||||
&& m->handle == NULL
|
||||
#endif
|
||||
) {
|
||||
ZVAL_TRUE(&t);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
break;
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"constant", sizeof("constant")-1)) {
|
||||
zval t;
|
||||
|
||||
if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"dirname", sizeof("dirname") - 1) &&
|
||||
IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
|
||||
zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0);
|
||||
ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
|
||||
if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) {
|
||||
zval t;
|
||||
|
||||
ZVAL_STR(&t, dirname);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
} else {
|
||||
zend_string_release_ex(dirname, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* don't collect constants after any other function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
case ZEND_STRLEN:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval t;
|
||||
|
||||
if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
{
|
||||
zval c;
|
||||
if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) {
|
||||
break;
|
||||
}
|
||||
ZVAL_TRUE(&c);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DECLARE_CONST:
|
||||
if (collect_constants &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) {
|
||||
zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
/* see ext/opcache/tests/bug78961.phpt */
|
||||
// case ZEND_FETCH_R:
|
||||
case ZEND_FETCH_W:
|
||||
// case ZEND_FETCH_RW:
|
||||
case ZEND_FETCH_IS:
|
||||
// case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_UNSET:
|
||||
/* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */
|
||||
if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 &&
|
||||
opline->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
|
||||
zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") &&
|
||||
((opline + 1)->opcode == opline->opcode + 1 ||
|
||||
((opline + 1)->opcode == ZEND_UNSET_DIM &&
|
||||
opline->opcode == ZEND_FETCH_UNSET) ||
|
||||
((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ &&
|
||||
opline->opcode == ZEND_FETCH_IS)) &&
|
||||
(opline + 1)->op1_type == opline->result_type &&
|
||||
(opline + 1)->op1.var == opline->result.var &&
|
||||
((opline + 1)->op2_type != IS_CONST ||
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) {
|
||||
|
||||
if ((opline + 1)->opcode == ZEND_UNSET_DIM) {
|
||||
(opline + 1)->opcode = ZEND_UNSET_VAR;
|
||||
(opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
|
||||
} else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) {
|
||||
(opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR;
|
||||
(opline + 1)->extended_value |= ZEND_FETCH_GLOBAL;
|
||||
} else {
|
||||
(opline + 1)->opcode = opline->opcode;
|
||||
(opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
|
||||
}
|
||||
(opline + 1)->op1_type = (opline + 1)->op2_type;
|
||||
(opline + 1)->op1 = (opline + 1)->op2;
|
||||
if ((opline + 1)->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) {
|
||||
|
||||
convert_to_string(&ZEND_OP1_LITERAL(opline + 1));
|
||||
zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1)));
|
||||
}
|
||||
SET_UNUSED((opline + 1)->op2);
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
|
||||
in case we know it wouldn't jump */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
if (opline->opcode == ZEND_JMPZ_EX) {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
|
||||
ZVAL_TRUE(&ZEND_OP1_LITERAL(opline));
|
||||
opline->op2.num = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (opline->opcode == ZEND_JMPNZ_EX) {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
|
||||
ZVAL_FALSE(&ZEND_OP1_LITERAL(opline));
|
||||
opline->op2.num = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
|
||||
|
||||
if (opline->opcode == ZEND_JMPZ) {
|
||||
should_jmp = !should_jmp;
|
||||
}
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
opline->op1_type = IS_UNUSED;
|
||||
if (should_jmp) {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
opline->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_JMPZNZ:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_op *target_opline;
|
||||
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
|
||||
} else {
|
||||
target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
|
||||
}
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
opline->op1_type = IS_UNUSED;
|
||||
opline->opcode = ZEND_JMP;
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_JMP:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 3: (Jump optimization)
|
||||
* - optimize series of JMPs
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
|
||||
static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < jmp_hitlist_count; i++) {
|
||||
if (jmp_hitlist[i] == target) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CHECK_LOOP(target) \
|
||||
if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
|
||||
jmp_hitlist[jmp_hitlist_count++] = target; \
|
||||
} else { \
|
||||
break; \
|
||||
}
|
||||
|
||||
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline;
|
||||
zend_op *end;
|
||||
zend_op *target;
|
||||
zend_op **jmp_hitlist;
|
||||
int jmp_hitlist_count;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
|
||||
while (opline < end) {
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP1_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
|
||||
}
|
||||
|
||||
if (target == opline + 1) {
|
||||
/* convert L: JMP L+1 to NOP */
|
||||
MAKE_NOP(opline);
|
||||
} else if (target->opcode == ZEND_JMPZNZ) {
|
||||
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
|
||||
*opline = *target;
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval zv;
|
||||
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
}
|
||||
goto optimize_jmpznz;
|
||||
} else if ((target->opcode == ZEND_RETURN ||
|
||||
target->opcode == ZEND_RETURN_BY_REF ||
|
||||
target->opcode == ZEND_GENERATOR_RETURN ||
|
||||
target->opcode == ZEND_EXIT) &&
|
||||
!(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
|
||||
/* JMP L, L: RETURN to immediate RETURN */
|
||||
*opline = *target;
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval zv;
|
||||
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
}
|
||||
} else if (opline > op_array->opcodes &&
|
||||
((opline-1)->opcode == ZEND_JMPZ ||
|
||||
(opline-1)->opcode == ZEND_JMPNZ)) {
|
||||
if (ZEND_OP2_JMP_ADDR(opline-1) == target) {
|
||||
/* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
|
||||
if ((opline-1)->op1_type == IS_CV) {
|
||||
(opline-1)->opcode = ZEND_CHECK_VAR;
|
||||
(opline-1)->op2.num = 0;
|
||||
} else if ((opline-1)->op1_type & (IS_TMP_VAR|IS_VAR)) {
|
||||
(opline-1)->opcode = ZEND_FREE;
|
||||
(opline-1)->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline-1);
|
||||
}
|
||||
} else {
|
||||
/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
|
||||
if ((opline-1)->opcode == ZEND_JMPZ) {
|
||||
(opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), target);
|
||||
} else {
|
||||
(opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), ZEND_OP2_JMP_ADDR(opline-1));
|
||||
ZEND_SET_OP_JMP_ADDR((opline-1), (opline-1)->op2, target);
|
||||
}
|
||||
(opline-1)->opcode = ZEND_JMPZNZ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* plain JMP */
|
||||
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
/* same opcode and same var as this opcode */
|
||||
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == INV_COND(opline->opcode) &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
/* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
|
||||
JMPZ(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
target = (opline->opcode == ZEND_JMPZ) ?
|
||||
ZEND_OP2_JMP_ADDR(target) :
|
||||
ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
/* convert L: JMPZ L+1 to NOP */
|
||||
if (target == opline + 1) {
|
||||
if (opline->op1_type == IS_CV) {
|
||||
opline->opcode = ZEND_CHECK_VAR;
|
||||
opline->op2.num = 0;
|
||||
} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* plain JMP */
|
||||
/* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode-3 &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
|
||||
JMPZ_EX(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode &&
|
||||
target->result.var == opline->result.var &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
|
||||
JMPZ_EX(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* Check for JMPZNZ with same cond variable */
|
||||
target = (opline->opcode == ZEND_JMPZ_EX) ?
|
||||
ZEND_OP2_JMP_ADDR(target) :
|
||||
ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == INV_EX_COND(opline->opcode) &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
|
||||
JMPZ_EX(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
|
||||
target->result.var == opline->result.var &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
|
||||
JMPZ_EX(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_BOOL &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
|
||||
Z = JMPZ_EX(X,L1+1) */
|
||||
|
||||
/* NOTE: This optimization pattern is not safe, but works, */
|
||||
/* because result of JMPZ_EX instruction */
|
||||
/* is not used on the following path and */
|
||||
/* should be used once on the branch path. */
|
||||
/* */
|
||||
/* The pattern works well only if jumps processed in */
|
||||
/* direct order, otherwise it breaks JMPZ_EX */
|
||||
/* sequences too early. */
|
||||
opline->result.var = target->result.var;
|
||||
target = target + 1;
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
|
||||
if (target == opline + 1) {
|
||||
opline->opcode = ZEND_BOOL;
|
||||
opline->op2.num = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZNZ:
|
||||
optimize_jmpznz:
|
||||
jmp_hitlist_count = 0;
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
jmp_hitlist_count = 0;
|
||||
target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target);
|
||||
}
|
||||
|
||||
if (ZEND_OP2_JMP_ADDR(opline) == target &&
|
||||
!(opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
|
||||
/* JMPZNZ(?,L,L) -> JMP(L) */
|
||||
opline->opcode = ZEND_JMP;
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
|
||||
SET_UNUSED(opline->op1);
|
||||
SET_UNUSED(opline->op2);
|
||||
opline->extended_value = 0;
|
||||
}
|
||||
/* Don't convert JMPZNZ back to JMPZ/JMPNZ, because the
|
||||
following JMP is not removed yet. */
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
free_alloca(jmp_hitlist, use_heap);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Sparse Conditional Data Flow Propagation Framework |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "Optimizer/scdf.h"
|
||||
|
||||
/* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is
|
||||
* based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a
|
||||
* generalized implementation as described in chapter 8.3 of the SSA book.
|
||||
*
|
||||
* Every SSA variable is associated with an element on a finite-height lattice, those value can only
|
||||
* ever be lowered during the operation of the algorithm. If a value is lowered all instructions and
|
||||
* phis using that value need to be reconsidered (this is done by adding the variable to a
|
||||
* worklist). For phi functions the result is computed by applying the meet operation to the
|
||||
* operands. This continues until a fixed point is reached.
|
||||
*
|
||||
* The algorithm is control-flow sensitive: All blocks except the start block are initially assumed
|
||||
* to be unreachable. When considering a branch instruction, we determine the feasible successors
|
||||
* based on the current state of the variable lattice. If a new edge becomes feasible we either have
|
||||
* to mark the successor block executable and consider all instructions in it, or, if the target is
|
||||
* already executable, we only have to reconsider the phi functions (as we only consider phi
|
||||
* operands which are associated with a feasible edge).
|
||||
*
|
||||
* The generic framework requires the definition of three functions:
|
||||
* * visit_instr() should recompute the lattice values of all SSA variables defined by an
|
||||
* instruction.
|
||||
* * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While
|
||||
* doing this it should only consider operands for which scfg_is_edge_feasible() returns true.
|
||||
* * get_feasible_successors() should determine the feasible successors for a branch instruction.
|
||||
* Note that this callback only needs to handle conditional branches (with two successors).
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
|
||||
uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
|
||||
|
||||
if (zend_bitset_in(scdf->feasible_edges, edge)) {
|
||||
/* We already handled this edge */
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
|
||||
zend_bitset_incl(scdf->feasible_edges, edge);
|
||||
|
||||
if (!zend_bitset_in(scdf->executable_blocks, to)) {
|
||||
if (!zend_bitset_in(scdf->block_worklist, to)) {
|
||||
DEBUG_PRINT("Adding block %d to worklist\n", to);
|
||||
}
|
||||
zend_bitset_incl(scdf->block_worklist, to);
|
||||
} else {
|
||||
/* Block is already executable, only a new edge became feasible.
|
||||
* Reevaluate phi nodes to account for changed source operands. */
|
||||
zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
|
||||
zend_ssa_phi *phi;
|
||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||
zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) {
|
||||
scdf->op_array = op_array;
|
||||
scdf->ssa = ssa;
|
||||
|
||||
scdf->instr_worklist_len = zend_bitset_len(op_array->last);
|
||||
scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count);
|
||||
scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count);
|
||||
|
||||
scdf->instr_worklist = zend_arena_calloc(&ctx->arena,
|
||||
scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count),
|
||||
sizeof(zend_ulong));
|
||||
|
||||
scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len;
|
||||
scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len;
|
||||
scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len;
|
||||
scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len;
|
||||
|
||||
zend_bitset_incl(scdf->block_worklist, 0);
|
||||
zend_bitset_incl(scdf->executable_blocks, 0);
|
||||
}
|
||||
|
||||
void scdf_solve(scdf_ctx *scdf, const char *name) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
DEBUG_PRINT("Start SCDF solve (%s)\n", name);
|
||||
while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
|
||||
|| !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
|
||||
|| !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
|
||||
) {
|
||||
int i;
|
||||
while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
|
||||
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
||||
ZEND_ASSERT(phi);
|
||||
if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
|
||||
int block_num = ssa->cfg.map[i];
|
||||
if (zend_bitset_in(scdf->executable_blocks, block_num)) {
|
||||
zend_basic_block *block = &ssa->cfg.blocks[block_num];
|
||||
zend_op *opline = &scdf->op_array->opcodes[i];
|
||||
zend_ssa_op *ssa_op = &ssa->ops[i];
|
||||
if (opline->opcode == ZEND_OP_DATA) {
|
||||
opline--;
|
||||
ssa_op--;
|
||||
}
|
||||
scdf->handlers.visit_instr(scdf, opline, ssa_op);
|
||||
if (i == block->start + block->len - 1) {
|
||||
if (block->successors_count == 1) {
|
||||
scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
|
||||
} else if (block->successors_count > 1) {
|
||||
scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
|
||||
/* This block is now live. Interpret phis and instructions in it. */
|
||||
zend_basic_block *block = &ssa->cfg.blocks[i];
|
||||
zend_ssa_block *ssa_block = &ssa->blocks[i];
|
||||
|
||||
DEBUG_PRINT("Pop block %d from worklist\n", i);
|
||||
zend_bitset_incl(scdf->executable_blocks, i);
|
||||
|
||||
{
|
||||
zend_ssa_phi *phi;
|
||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||
zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
|
||||
if (block->len == 0) {
|
||||
/* Zero length blocks don't have a last instruction that would normally do this */
|
||||
scdf_mark_edge_feasible(scdf, i, block->successors[0]);
|
||||
} else {
|
||||
zend_op *opline = NULL;
|
||||
int j, end = block->start + block->len;
|
||||
for (j = block->start; j < end; j++) {
|
||||
opline = &scdf->op_array->opcodes[j];
|
||||
zend_bitset_excl(scdf->instr_worklist, j);
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
|
||||
}
|
||||
}
|
||||
if (block->successors_count == 1) {
|
||||
scdf_mark_edge_feasible(scdf, i, block->successors[0]);
|
||||
} else if (block->successors_count > 1) {
|
||||
ZEND_ASSERT(opline && "Should have opline in non-empty block");
|
||||
if (opline->opcode == ZEND_OP_DATA) {
|
||||
opline--;
|
||||
j--;
|
||||
}
|
||||
scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If a live range starts in a reachable block and ends in an unreachable block, we should
|
||||
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
|
||||
* is necessary for the correctness of temporary compaction. */
|
||||
static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) {
|
||||
uint32_t i;
|
||||
const zend_op_array *op_array = scdf->op_array;
|
||||
const zend_cfg *cfg = &scdf->ssa->cfg;
|
||||
const zend_basic_block *block = &cfg->blocks[block_idx];
|
||||
if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = block->start; i < block->start + block->len; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (zend_optimizer_is_loop_var_free(opline)) {
|
||||
int ssa_var = scdf->ssa->ops[i].op1_use;
|
||||
if (ssa_var >= 0) {
|
||||
int op_num = scdf->ssa->vars[ssa_var].definition;
|
||||
uint32_t def_block;
|
||||
ZEND_ASSERT(op_num >= 0);
|
||||
def_block = cfg->map[op_num];
|
||||
if (zend_bitset_in(scdf->executable_blocks, def_block)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
|
||||
* blocks, as well as remove them from the successor / predecessor lists and mark them
|
||||
* unreachable. Blocks already marked unreachable are not removed. */
|
||||
int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
int i;
|
||||
int removed_ops = 0;
|
||||
for (i = 0; i < ssa->cfg.blocks_count; i++) {
|
||||
if (!zend_bitset_in(scdf->executable_blocks, i)
|
||||
&& (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
|
||||
&& !kept_alive_by_loop_var_free(scdf, i)) {
|
||||
removed_ops += ssa->cfg.blocks[i].len;
|
||||
zend_ssa_remove_block(scdf->op_array, ssa, i);
|
||||
}
|
||||
}
|
||||
return removed_ops;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef _SCDF_H
|
||||
#define _SCDF_H
|
||||
|
||||
#include "zend_bitset.h"
|
||||
|
||||
typedef struct _scdf_ctx {
|
||||
zend_op_array *op_array;
|
||||
zend_ssa *ssa;
|
||||
zend_bitset instr_worklist;
|
||||
/* Represent phi-instructions through the defining var */
|
||||
zend_bitset phi_var_worklist;
|
||||
zend_bitset block_worklist;
|
||||
zend_bitset executable_blocks;
|
||||
/* 1 bit per edge, see scdf_edge(cfg, from, to) */
|
||||
zend_bitset feasible_edges;
|
||||
uint32_t instr_worklist_len;
|
||||
uint32_t phi_var_worklist_len;
|
||||
uint32_t block_worklist_len;
|
||||
|
||||
struct {
|
||||
void (*visit_instr)(
|
||||
struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
|
||||
void (*visit_phi)(
|
||||
struct _scdf_ctx *scdf, zend_ssa_phi *phi);
|
||||
void (*mark_feasible_successors)(
|
||||
struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
|
||||
zend_op *opline, zend_ssa_op *ssa_op);
|
||||
} handlers;
|
||||
} scdf_ctx;
|
||||
|
||||
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa);
|
||||
void scdf_solve(scdf_ctx *scdf, const char *name);
|
||||
|
||||
int scdf_remove_unreachable_blocks(scdf_ctx *scdf);
|
||||
|
||||
/* Add uses to worklist */
|
||||
static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
zend_ssa_var *var = &ssa->vars[var_num];
|
||||
int use;
|
||||
zend_ssa_phi *phi;
|
||||
FOREACH_USE(var, use) {
|
||||
zend_bitset_incl(scdf->instr_worklist, use);
|
||||
} FOREACH_USE_END();
|
||||
FOREACH_PHI_USE(var, phi) {
|
||||
zend_bitset_incl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
} FOREACH_PHI_USE_END();
|
||||
}
|
||||
|
||||
/* This should usually not be necessary, however it's used for type narrowing. */
|
||||
static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
|
||||
zend_ssa_var *var = &scdf->ssa->vars[var_num];
|
||||
if (var->definition >= 0) {
|
||||
zend_bitset_incl(scdf->instr_worklist, var->definition);
|
||||
} else if (var->definition_phi) {
|
||||
zend_bitset_incl(scdf->phi_var_worklist, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t scdf_edge(zend_cfg *cfg, int from, int to) {
|
||||
zend_basic_block *to_block = cfg->blocks + to;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < to_block->predecessors_count; i++) {
|
||||
uint32_t edge = to_block->predecessor_offset + i;
|
||||
|
||||
if (cfg->predecessors[edge] == from) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
|
||||
static inline bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to) {
|
||||
uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
|
||||
return zend_bitset_in(scdf->feasible_edges, edge);
|
||||
}
|
||||
|
||||
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to);
|
||||
|
||||
#endif
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, SSA validation |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
|
||||
/* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and
|
||||
* CFG are upheld and prints messages to stderr if this is not the case. */
|
||||
|
||||
static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
|
||||
int use;
|
||||
FOREACH_USE(&ssa->vars[var], use) {
|
||||
if (use == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
|
||||
zend_ssa_phi *phi;
|
||||
FOREACH_PHI_USE(&ssa->vars[var], phi) {
|
||||
if (phi == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_PHI_USE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[op];
|
||||
return (ssa_op->op1_use == check)
|
||||
|| (ssa_op->op2_use == check)
|
||||
|| (ssa_op->result_use == check);
|
||||
}
|
||||
|
||||
static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[op];
|
||||
return (ssa_op->op1_def == check)
|
||||
|| (ssa_op->op2_def == check)
|
||||
|| (ssa_op->result_def == check);
|
||||
}
|
||||
|
||||
static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) {
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
if (source == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
|
||||
int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
|
||||
for (i = 0; i < block->predecessors_count; i++) {
|
||||
if (predecessors[i] == check) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_successors(zend_basic_block *block, int check) {
|
||||
int s;
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
if (block->successors[s] == check) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_var_type(zend_uchar type) {
|
||||
return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0;
|
||||
}
|
||||
|
||||
#define FAIL(...) do { \
|
||||
if (status == SUCCESS) { \
|
||||
fprintf(stderr, "\nIn function %s::%s (%s):\n", \
|
||||
op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \
|
||||
op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \
|
||||
} \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
status = FAILURE; \
|
||||
} while (0)
|
||||
|
||||
#define VARFMT "%d (%s%s)"
|
||||
#define VAR(i) \
|
||||
(i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \
|
||||
(ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "")
|
||||
|
||||
#define INSTRFMT "%d (%s)"
|
||||
#define INSTR(i) \
|
||||
(i), (zend_get_opcode_name(op_array->opcodes[i].opcode))
|
||||
|
||||
int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) {
|
||||
zend_cfg *cfg = &ssa->cfg;
|
||||
zend_ssa_phi *phi;
|
||||
int i, status = SUCCESS;
|
||||
|
||||
/* Vars */
|
||||
for (i = 0; i < ssa->vars_count; i++) {
|
||||
zend_ssa_var *var = &ssa->vars[i];
|
||||
int use, c;
|
||||
uint32_t type = ssa->var_info[i].type;
|
||||
|
||||
if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
|
||||
if (var->use_chain >= 0) {
|
||||
FAIL("var " VARFMT " without def has op uses\n", VAR(i));
|
||||
}
|
||||
if (var->phi_use_chain) {
|
||||
FAIL("var " VARFMT " without def has phi uses\n", VAR(i));
|
||||
}
|
||||
}
|
||||
if (var->definition >= 0 && var->definition_phi) {
|
||||
FAIL("var " VARFMT " has both def and def_phi\n", VAR(i));
|
||||
}
|
||||
if (var->definition >= 0) {
|
||||
if (!is_defined_by_op(ssa, var->definition, i)) {
|
||||
FAIL("var " VARFMT " not defined by op " INSTRFMT "\n",
|
||||
VAR(i), INSTR(var->definition));
|
||||
}
|
||||
}
|
||||
if (var->definition_phi) {
|
||||
if (var->definition_phi->ssa_var != i) {
|
||||
FAIL("var " VARFMT " not defined by given phi\n", VAR(i));
|
||||
}
|
||||
}
|
||||
|
||||
c = 0;
|
||||
FOREACH_USE(var, use) {
|
||||
if (++c > 10000) {
|
||||
FAIL("cycle in uses of " VARFMT "\n", VAR(i));
|
||||
return status;
|
||||
}
|
||||
if (!is_used_by_op(ssa, use, i)) {
|
||||
fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use);
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
|
||||
c = 0;
|
||||
FOREACH_PHI_USE(var, phi) {
|
||||
if (++c > 10000) {
|
||||
FAIL("cycle in phi uses of " VARFMT "\n", VAR(i));
|
||||
return status;
|
||||
}
|
||||
if (!is_in_phi_sources(ssa, phi, i)) {
|
||||
FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var);
|
||||
}
|
||||
} FOREACH_PHI_USE_END();
|
||||
|
||||
if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
|
||||
FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
|
||||
}
|
||||
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
|
||||
FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Instructions */
|
||||
FOREACH_INSTR_NUM(i) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[i];
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (is_var_type(opline->op1_type)) {
|
||||
if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) {
|
||||
FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) {
|
||||
FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
if (is_var_type(opline->op2_type)) {
|
||||
if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) {
|
||||
FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) {
|
||||
FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
if (is_var_type(opline->result_type)) {
|
||||
if (ssa_op->result_use < 0 && ssa_op->result_def < 0) {
|
||||
FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) {
|
||||
FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
if (ssa_op->op1_use >= ssa->vars_count) {
|
||||
FAIL("op1 use %d out of range\n", ssa_op->op1_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) {
|
||||
FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->op1_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) {
|
||||
FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
if (ssa_op->op2_use >= ssa->vars_count) {
|
||||
FAIL("op2 use %d out of range\n", ssa_op->op2_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) {
|
||||
FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->op2_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) {
|
||||
FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->result_use >= 0) {
|
||||
if (ssa_op->result_use >= ssa->vars_count) {
|
||||
FAIL("result use %d out of range\n", ssa_op->result_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->result_use, i)) {
|
||||
FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->result_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) {
|
||||
FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op1_def >= 0) {
|
||||
if (ssa_op->op1_def >= ssa->vars_count) {
|
||||
FAIL("op1 def %d out of range\n", ssa_op->op1_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->op1_def].definition != i) {
|
||||
FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->op1_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) {
|
||||
FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_def >= 0) {
|
||||
if (ssa_op->op2_def >= ssa->vars_count) {
|
||||
FAIL("op2 def %d out of range\n", ssa_op->op2_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->op2_def].definition != i) {
|
||||
FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->op2_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) {
|
||||
FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->result_def >= 0) {
|
||||
if (ssa_op->result_def >= ssa->vars_count) {
|
||||
FAIL("result def %d out of range\n", ssa_op->result_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->result_def].definition != i) {
|
||||
FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->result_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) {
|
||||
FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
} FOREACH_INSTR_NUM_END();
|
||||
|
||||
/* Phis */
|
||||
FOREACH_PHI(phi) {
|
||||
unsigned num_sources = NUM_PHI_SOURCES(phi);
|
||||
for (i = 0; i < num_sources; i++) {
|
||||
int source = phi->sources[i];
|
||||
if (source < 0) {
|
||||
FAIL(VARFMT " negative source\n", VAR(phi->ssa_var));
|
||||
}
|
||||
if (!is_in_phi_use_chain(ssa, source, phi)) {
|
||||
FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source);
|
||||
}
|
||||
if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) {
|
||||
FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var));
|
||||
}
|
||||
if (phi->use_chains[i]) {
|
||||
int j;
|
||||
for (j = i + 1; j < num_sources; j++) {
|
||||
if (phi->sources[j] == source && phi->use_chains[j]) {
|
||||
FAIL("use chain for source " VARFMT " of phi " VARFMT
|
||||
" at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ssa->vars[phi->ssa_var].definition_phi != phi) {
|
||||
FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var));
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
/* Blocks */
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
zend_basic_block *block = &cfg->blocks[i];
|
||||
int *predecessors = &cfg->predecessors[block->predecessor_offset];
|
||||
int s, j;
|
||||
|
||||
if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
|
||||
FAIL("Block %d start %d smaller previous end %d\n",
|
||||
i, block->start, (block-1)->start + (block-1)->len);
|
||||
}
|
||||
if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) {
|
||||
FAIL("Block %d end %d greater next start %d\n",
|
||||
i, block->start + block->len, (block+1)->start);
|
||||
}
|
||||
|
||||
for (j = block->start; j < block->start + block->len; j++) {
|
||||
if (cfg->map[j] != i) {
|
||||
FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(block->flags & ZEND_BB_REACHABLE)) {
|
||||
if (ssa->blocks[i].phis) {
|
||||
FAIL("Unreachable block %d has phis\n", i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
zend_basic_block *next_block;
|
||||
if (block->successors[s] < 0) {
|
||||
FAIL("Successor number %d of %d negative", s, i);
|
||||
}
|
||||
next_block = &cfg->blocks[block->successors[s]];
|
||||
if (!(next_block->flags & ZEND_BB_REACHABLE)) {
|
||||
FAIL("Successor %d of %d not reachable\n", block->successors[s], i);
|
||||
}
|
||||
if (!is_in_predecessors(cfg, next_block, i)) {
|
||||
FAIL("Block %d predecessors missing %d\n", block->successors[s], i);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < block->predecessors_count; j++) {
|
||||
if (predecessors[j] >= 0) {
|
||||
int k;
|
||||
zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
|
||||
if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
|
||||
FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
|
||||
}
|
||||
if (!is_in_successors(prev_block, i)) {
|
||||
FAIL("Block %d successors missing %d\n", predecessors[j], i);
|
||||
}
|
||||
for (k = 0; k < block->predecessors_count; k++) {
|
||||
if (k != j && predecessors[k] == predecessors[j]) {
|
||||
FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
|
||||
static void zend_op_array_calc(zend_op_array *op_array, void *context)
|
||||
{
|
||||
zend_call_graph *call_graph = context;
|
||||
call_graph->op_arrays_count++;
|
||||
}
|
||||
|
||||
static void zend_op_array_collect(zend_op_array *op_array, void *context)
|
||||
{
|
||||
zend_call_graph *call_graph = context;
|
||||
zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
|
||||
|
||||
ZEND_SET_FUNC_INFO(op_array, func_info);
|
||||
call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
|
||||
func_info->num = call_graph->op_arrays_count;
|
||||
call_graph->op_arrays_count++;
|
||||
}
|
||||
|
||||
ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
zend_function *func;
|
||||
zend_call_info *call_info;
|
||||
int call = 0;
|
||||
zend_call_info **call_stack;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
bool is_prototype;
|
||||
|
||||
call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
|
||||
call_info = NULL;
|
||||
while (opline != end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
call_stack[call] = call_info;
|
||||
func = zend_optimizer_get_called_func(
|
||||
script, op_array, opline, &is_prototype);
|
||||
if (func) {
|
||||
call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
|
||||
call_info->caller_op_array = op_array;
|
||||
call_info->caller_init_opline = opline;
|
||||
call_info->caller_call_opline = NULL;
|
||||
call_info->callee_func = func;
|
||||
call_info->num_args = opline->extended_value;
|
||||
call_info->next_callee = func_info->callee_info;
|
||||
call_info->is_prototype = is_prototype;
|
||||
func_info->callee_info = call_info;
|
||||
|
||||
if (build_flags & ZEND_CALL_TREE) {
|
||||
call_info->next_caller = NULL;
|
||||
} else if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
call_info->next_caller = NULL;
|
||||
} else {
|
||||
zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
|
||||
if (callee_func_info) {
|
||||
call_info->next_caller = callee_func_info->caller_info;
|
||||
callee_func_info->caller_info = call_info;
|
||||
} else {
|
||||
call_info->next_caller = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call_info = NULL;
|
||||
}
|
||||
call++;
|
||||
break;
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call_stack[call] = call_info;
|
||||
call_info = NULL;
|
||||
call++;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
func_info->flags |= ZEND_FUNC_HAS_CALLS;
|
||||
if (call_info) {
|
||||
call_info->caller_call_opline = opline;
|
||||
}
|
||||
call--;
|
||||
call_info = call_stack[call];
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_USER:
|
||||
if (call_info) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_info->named_args = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t num = opline->op2.num;
|
||||
if (num > 0) {
|
||||
num--;
|
||||
}
|
||||
call_info->arg_info[num].opline = opline;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_UNPACK:
|
||||
if (call_info) {
|
||||
call_info->send_unpack = 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_EXIT:
|
||||
/* In this case the DO_CALL opcode may have been dropped
|
||||
* and caller_call_opline will be NULL. */
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
free_alloca(call_stack, use_heap);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
|
||||
{
|
||||
zend_func_info *func_info;
|
||||
zend_call_info *call_info;
|
||||
int ret = 0;
|
||||
|
||||
if (op_array == root) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
func_info = ZEND_FUNC_INFO(op_array);
|
||||
if (zend_bitset_in(visited, func_info->num)) {
|
||||
return 0;
|
||||
}
|
||||
zend_bitset_incl(visited, func_info->num);
|
||||
call_info = func_info->caller_info;
|
||||
while (call_info) {
|
||||
if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
|
||||
call_info->recursive = 1;
|
||||
ret = 1;
|
||||
}
|
||||
call_info = call_info->next_caller;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void zend_analyze_recursion(zend_call_graph *call_graph)
|
||||
{
|
||||
zend_op_array *op_array;
|
||||
zend_func_info *func_info;
|
||||
zend_call_info *call_info;
|
||||
int i;
|
||||
int set_len = zend_bitset_len(call_graph->op_arrays_count);
|
||||
zend_bitset visited;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
|
||||
for (i = 0; i < call_graph->op_arrays_count; i++) {
|
||||
op_array = call_graph->op_arrays[i];
|
||||
func_info = call_graph->func_infos + i;
|
||||
call_info = func_info->caller_info;
|
||||
for (; call_info; call_info = call_info->next_caller) {
|
||||
if (call_info->is_prototype) {
|
||||
/* Might be calling an overridden child method and not actually recursive. */
|
||||
continue;
|
||||
}
|
||||
if (call_info->caller_op_array == op_array) {
|
||||
call_info->recursive = 1;
|
||||
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
|
||||
} else {
|
||||
memset(visited, 0, sizeof(zend_ulong) * set_len);
|
||||
if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
|
||||
call_info->recursive = 1;
|
||||
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(visited, use_heap);
|
||||
}
|
||||
|
||||
static void zend_sort_op_arrays(zend_call_graph *call_graph)
|
||||
{
|
||||
(void) call_graph;
|
||||
|
||||
// TODO: perform topological sort of cyclic call graph
|
||||
}
|
||||
|
||||
ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
|
||||
{
|
||||
call_graph->op_arrays_count = 0;
|
||||
zend_foreach_op_array(script, zend_op_array_calc, call_graph);
|
||||
|
||||
call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
|
||||
call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
|
||||
call_graph->op_arrays_count = 0;
|
||||
zend_foreach_op_array(script, zend_op_array_collect, call_graph);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < call_graph->op_arrays_count; i++) {
|
||||
zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i);
|
||||
}
|
||||
zend_analyze_recursion(call_graph);
|
||||
zend_sort_op_arrays(call_graph);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */
|
||||
{
|
||||
zend_call_info **map, *call;
|
||||
if (!info->callee_info) {
|
||||
/* Don't build call map if function contains no calls */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
|
||||
for (call = info->callee_info; call; call = call->next_callee) {
|
||||
int i;
|
||||
map[call->caller_init_opline - op_array->opcodes] = call;
|
||||
if (call->caller_call_opline) {
|
||||
map[call->caller_call_opline - op_array->opcodes] = call;
|
||||
}
|
||||
for (i = 0; i < call->num_args; i++) {
|
||||
if (call->arg_info[i].opline) {
|
||||
map[call->arg_info[i].opline - op_array->opcodes] = call;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_CALL_GRAPH_H
|
||||
#define ZEND_CALL_GRAPH_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_optimizer.h"
|
||||
|
||||
typedef struct _zend_send_arg_info {
|
||||
zend_op *opline;
|
||||
} zend_send_arg_info;
|
||||
|
||||
struct _zend_call_info {
|
||||
zend_op_array *caller_op_array;
|
||||
zend_op *caller_init_opline;
|
||||
zend_op *caller_call_opline;
|
||||
zend_function *callee_func;
|
||||
zend_call_info *next_caller;
|
||||
zend_call_info *next_callee;
|
||||
bool recursive;
|
||||
bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
|
||||
bool named_args; /* Function has named arguments */
|
||||
bool is_prototype; /* An overridden child method may be called */
|
||||
int num_args;
|
||||
zend_send_arg_info arg_info[1];
|
||||
};
|
||||
|
||||
struct _zend_func_info {
|
||||
int num;
|
||||
uint32_t flags;
|
||||
zend_ssa ssa; /* Static Single Assignment Form */
|
||||
zend_call_info *caller_info; /* where this function is called from */
|
||||
zend_call_info *callee_info; /* which functions are called from this one */
|
||||
zend_call_info **call_map; /* Call info associated with init/call/send opnum */
|
||||
zend_ssa_var_info return_info;
|
||||
};
|
||||
|
||||
typedef struct _zend_call_graph {
|
||||
int op_arrays_count;
|
||||
zend_op_array **op_arrays;
|
||||
zend_func_info *func_infos;
|
||||
} zend_call_graph;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
|
||||
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
|
||||
ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array);
|
||||
ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_CALL_GRAPH_H */
|
||||
@@ -1,912 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, CFG - Control Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_cfg.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_worklist.h"
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_sort.h"
|
||||
|
||||
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
|
||||
while (1) {
|
||||
int i;
|
||||
|
||||
b->flags |= ZEND_BB_REACHABLE;
|
||||
if (b->successors_count == 0) {
|
||||
b->flags |= ZEND_BB_EXIT;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < b->successors_count; i++) {
|
||||
zend_basic_block *succ = blocks + b->successors[i];
|
||||
|
||||
if (b->len != 0) {
|
||||
zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
|
||||
if (b->successors_count == 1) {
|
||||
if (opcode == ZEND_JMP) {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
|
||||
if ((cfg->flags & ZEND_CFG_STACKLESS)) {
|
||||
if (opcode == ZEND_INCLUDE_OR_EVAL ||
|
||||
opcode == ZEND_GENERATOR_CREATE ||
|
||||
opcode == ZEND_YIELD ||
|
||||
opcode == ZEND_YIELD_FROM ||
|
||||
opcode == ZEND_DO_FCALL ||
|
||||
opcode == ZEND_DO_UCALL ||
|
||||
opcode == ZEND_DO_FCALL_BY_NAME) {
|
||||
succ->flags |= ZEND_BB_ENTRY;
|
||||
}
|
||||
}
|
||||
if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
|
||||
if (opcode == ZEND_RECV ||
|
||||
opcode == ZEND_RECV_INIT) {
|
||||
succ->flags |= ZEND_BB_RECV_ENTRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (b->successors_count == 2) {
|
||||
if (i == 0 || opcode == ZEND_JMPZNZ) {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(
|
||||
opcode == ZEND_SWITCH_LONG
|
||||
|| opcode == ZEND_SWITCH_STRING
|
||||
|| opcode == ZEND_MATCH
|
||||
);
|
||||
if (i == b->successors_count - 1) {
|
||||
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
}
|
||||
|
||||
if (i == b->successors_count - 1) {
|
||||
/* Tail call optimization */
|
||||
if (succ->flags & ZEND_BB_REACHABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
b = succ;
|
||||
break;
|
||||
} else {
|
||||
/* Recursively check reachability */
|
||||
if (!(succ->flags & ZEND_BB_REACHABLE)) {
|
||||
zend_mark_reachable(opcodes, cfg, succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
|
||||
blocks[start].flags = ZEND_BB_START;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
|
||||
|
||||
if (op_array->last_try_catch) {
|
||||
zend_basic_block *b;
|
||||
int j, changed;
|
||||
uint32_t *block_map = cfg->map;
|
||||
|
||||
do {
|
||||
changed = 0;
|
||||
|
||||
/* Add exception paths */
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
|
||||
/* check for jumps into the middle of try block */
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
zend_basic_block *end;
|
||||
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
|
||||
while (b != end) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
op_array->try_catch_array[j].try_op = b->start;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
}
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
|
||||
while (b != end) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
b->flags |= ZEND_BB_TRY;
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
|
||||
b->flags |= ZEND_BB_CATCH;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
|
||||
b->flags |= ZEND_BB_FINALLY;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
|
||||
b->flags |= ZEND_BB_FINALLY_END;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) {
|
||||
zend_basic_block *b;
|
||||
int j;
|
||||
uint32_t *block_map = cfg->map;
|
||||
|
||||
/* Mark blocks that are unreachable, but free a loop var created in a reachable block. */
|
||||
for (b = blocks; b < blocks + cfg->blocks_count; b++) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = b->start; j < b->start + b->len; j++) {
|
||||
zend_op *opline = &op_array->opcodes[j];
|
||||
if (zend_optimizer_is_loop_var_free(opline)) {
|
||||
zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
|
||||
if (def_opline) {
|
||||
uint32_t def_block = block_map[def_opline - op_array->opcodes];
|
||||
if (blocks[def_block].flags & ZEND_BB_REACHABLE) {
|
||||
b->flags |= ZEND_BB_UNREACHABLE_FREE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int i;
|
||||
int start = 0;
|
||||
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
if (blocks[i].flags & ZEND_BB_REACHABLE) {
|
||||
start = i;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* clear all flags */
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
blocks[i].flags = 0;
|
||||
}
|
||||
|
||||
zend_mark_reachable_blocks(op_array, cfg, start);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void initialize_block(zend_basic_block *block) {
|
||||
block->flags = 0;
|
||||
block->successors = block->successors_storage;
|
||||
block->successors_count = 0;
|
||||
block->predecessors_count = 0;
|
||||
block->predecessor_offset = -1;
|
||||
block->idom = -1;
|
||||
block->loop_header = -1;
|
||||
block->level = -1;
|
||||
block->children = -1;
|
||||
block->next_child = -1;
|
||||
}
|
||||
|
||||
#define BB_START(i) do { \
|
||||
if (!block_map[i]) { blocks_count++;} \
|
||||
block_map[i]++; \
|
||||
} while (0)
|
||||
|
||||
ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
uint32_t i;
|
||||
int j;
|
||||
uint32_t *block_map;
|
||||
zend_function *fn;
|
||||
int blocks_count = 0;
|
||||
zend_basic_block *blocks;
|
||||
zval *zv;
|
||||
bool extra_entry_block = 0;
|
||||
|
||||
cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
|
||||
|
||||
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
|
||||
|
||||
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
|
||||
BB_START(0);
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = op_array->opcodes + i;
|
||||
switch (opline->opcode) {
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_INIT:
|
||||
if (build_flags & ZEND_CFG_RECV_ENTRY) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_THROW:
|
||||
/* Don't treat THROW as terminator if it's used in expression context,
|
||||
* as we may lose live ranges when eliminating unreachable code. */
|
||||
if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_GENERATOR_CREATE:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
if (build_flags & ZEND_CFG_STACKLESS) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
flags |= ZEND_FUNC_HAS_CALLS;
|
||||
if (build_flags & ZEND_CFG_STACKLESS) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_DO_ICALL:
|
||||
flags |= ZEND_FUNC_HAS_CALLS;
|
||||
break;
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
zv = CRT_CONSTANT(opline->op2);
|
||||
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
|
||||
/* The third literal is the lowercased unqualified name */
|
||||
zv += 2;
|
||||
}
|
||||
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
|
||||
if (fn->type == ZEND_INTERNAL_FUNCTION) {
|
||||
flags |= zend_optimizer_classify_function(
|
||||
Z_STR_P(zv), opline->extended_value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_FAST_CALL:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FAST_RET:
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMP:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
}
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_SWITCH_LONG:
|
||||
case ZEND_SWITCH_STRING:
|
||||
case ZEND_MATCH:
|
||||
{
|
||||
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
|
||||
zval *zv;
|
||||
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
}
|
||||
case ZEND_FETCH_R:
|
||||
case ZEND_FETCH_W:
|
||||
case ZEND_FETCH_RW:
|
||||
case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_IS:
|
||||
case ZEND_FETCH_UNSET:
|
||||
case ZEND_UNSET_VAR:
|
||||
case ZEND_ISSET_ISEMPTY_VAR:
|
||||
if (opline->extended_value & ZEND_FETCH_LOCAL) {
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
} else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
|
||||
!op_array->function_name) {
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
}
|
||||
break;
|
||||
case ZEND_FUNC_GET_ARGS:
|
||||
flags |= ZEND_FUNC_VARARG;
|
||||
break;
|
||||
case ZEND_EXT_STMT:
|
||||
flags |= ZEND_FUNC_HAS_EXTENDED_STMT;
|
||||
break;
|
||||
case ZEND_EXT_FCALL_BEGIN:
|
||||
case ZEND_EXT_FCALL_END:
|
||||
flags |= ZEND_FUNC_HAS_EXTENDED_FCALL;
|
||||
break;
|
||||
case ZEND_FREE:
|
||||
if (opline->extended_value == ZEND_FREE_SWITCH) {
|
||||
flags |= ZEND_FUNC_FREE_LOOP_VAR;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FREE:
|
||||
flags |= ZEND_FUNC_FREE_LOOP_VAR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the entry block has predecessors, we may need to split it */
|
||||
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
|
||||
&& op_array->last > 0 && block_map[0] > 1) {
|
||||
extra_entry_block = 1;
|
||||
}
|
||||
|
||||
if (op_array->last_try_catch) {
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
BB_START(op_array->try_catch_array[j].try_op);
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
BB_START(op_array->try_catch_array[j].catch_op);
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
BB_START(op_array->try_catch_array[j].finally_op);
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
BB_START(op_array->try_catch_array[j].finally_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks_count += extra_entry_block;
|
||||
cfg->blocks_count = blocks_count;
|
||||
|
||||
/* Build CFG, Step 2: Build Array of Basic Blocks */
|
||||
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
|
||||
|
||||
blocks_count = -1;
|
||||
|
||||
if (extra_entry_block) {
|
||||
initialize_block(&blocks[0]);
|
||||
blocks[0].start = 0;
|
||||
blocks[0].len = 0;
|
||||
blocks_count++;
|
||||
}
|
||||
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
if (block_map[i]) {
|
||||
if (blocks_count >= 0) {
|
||||
blocks[blocks_count].len = i - blocks[blocks_count].start;
|
||||
}
|
||||
blocks_count++;
|
||||
initialize_block(&blocks[blocks_count]);
|
||||
blocks[blocks_count].start = i;
|
||||
}
|
||||
block_map[i] = blocks_count;
|
||||
}
|
||||
|
||||
blocks[blocks_count].len = i - blocks[blocks_count].start;
|
||||
blocks_count++;
|
||||
|
||||
/* Build CFG, Step 3: Calculate successors */
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_basic_block *block = &blocks[j];
|
||||
zend_op *opline;
|
||||
if (block->len == 0) {
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
opline = op_array->opcodes + block->start + block->len - 1;
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
break;
|
||||
case ZEND_JMP:
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
break;
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
} else {
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_FAST_CALL:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_SWITCH_LONG:
|
||||
case ZEND_SWITCH_STRING:
|
||||
case ZEND_MATCH:
|
||||
{
|
||||
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
|
||||
zval *zv;
|
||||
uint32_t s = 0;
|
||||
|
||||
block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable);
|
||||
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
|
||||
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
if (opline->opcode != ZEND_MATCH) {
|
||||
block->successors[s++] = j + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
|
||||
cfg->flags |= flags;
|
||||
zend_mark_reachable_blocks(op_array, cfg, 0);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
int j, s, edges;
|
||||
zend_basic_block *b;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
zend_basic_block *end = blocks + cfg->blocks_count;
|
||||
int *predecessors;
|
||||
|
||||
edges = 0;
|
||||
for (b = blocks; b < end; b++) {
|
||||
b->predecessors_count = 0;
|
||||
}
|
||||
for (b = blocks; b < end; b++) {
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
b->successors_count = 0;
|
||||
b->predecessors_count = 0;
|
||||
} else {
|
||||
for (s = 0; s < b->successors_count; s++) {
|
||||
edges++;
|
||||
blocks[b->successors[s]].predecessors_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg->edges_count = edges;
|
||||
cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
|
||||
|
||||
edges = 0;
|
||||
for (b = blocks; b < end; b++) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
b->predecessor_offset = edges;
|
||||
edges += b->predecessors_count;
|
||||
b->predecessors_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < cfg->blocks_count; j++) {
|
||||
if (blocks[j].flags & ZEND_BB_REACHABLE) {
|
||||
/* SWITCH_STRING/LONG may have few identical successors */
|
||||
for (s = 0; s < blocks[j].successors_count; s++) {
|
||||
int duplicate = 0;
|
||||
int p;
|
||||
|
||||
for (p = 0; p < s; p++) {
|
||||
if (blocks[j].successors[p] == blocks[j].successors[s]) {
|
||||
duplicate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!duplicate) {
|
||||
zend_basic_block *b = blocks + blocks[j].successors[s];
|
||||
|
||||
predecessors[b->predecessor_offset + b->predecessors_count] = j;
|
||||
b->predecessors_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Computes a postorder numbering of the CFG */
|
||||
static void compute_postnum_recursive(
|
||||
int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
|
||||
{
|
||||
int s;
|
||||
zend_basic_block *block = &cfg->blocks[block_num];
|
||||
if (postnum[block_num] != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
postnum[block_num] = -2; /* Marker for "currently visiting" */
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
|
||||
}
|
||||
postnum[block_num] = (*cur)++;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
|
||||
* Cooper, Harvey and Kennedy. */
|
||||
ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int blocks_count = cfg->blocks_count;
|
||||
int j, k, changed;
|
||||
|
||||
ALLOCA_FLAG(use_heap)
|
||||
int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
|
||||
memset(postnum, -1, sizeof(int) * cfg->blocks_count);
|
||||
j = 0;
|
||||
compute_postnum_recursive(postnum, &j, cfg, 0);
|
||||
|
||||
/* FIXME: move declarations */
|
||||
blocks[0].idom = 0;
|
||||
do {
|
||||
changed = 0;
|
||||
/* Iterating in RPO here would converge faster */
|
||||
for (j = 1; j < blocks_count; j++) {
|
||||
int idom = -1;
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
|
||||
|
||||
if (idom < 0) {
|
||||
if (blocks[pred].idom >= 0)
|
||||
idom = pred;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blocks[pred].idom >= 0) {
|
||||
while (idom != pred) {
|
||||
while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
|
||||
while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idom >= 0 && blocks[j].idom != idom) {
|
||||
blocks[j].idom = idom;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
blocks[0].idom = -1;
|
||||
|
||||
for (j = 1; j < blocks_count; j++) {
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (blocks[j].idom >= 0) {
|
||||
/* Sort by block number to traverse children in pre-order */
|
||||
if (blocks[blocks[j].idom].children < 0 ||
|
||||
j < blocks[blocks[j].idom].children) {
|
||||
blocks[j].next_child = blocks[blocks[j].idom].children;
|
||||
blocks[blocks[j].idom].children = j;
|
||||
} else {
|
||||
int k = blocks[blocks[j].idom].children;
|
||||
while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
|
||||
k = blocks[k].next_child;
|
||||
}
|
||||
blocks[j].next_child = blocks[k].next_child;
|
||||
blocks[k].next_child = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
int idom = blocks[j].idom, level = 0;
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
while (idom >= 0) {
|
||||
level++;
|
||||
if (blocks[idom].level >= 0) {
|
||||
level += blocks[idom].level;
|
||||
break;
|
||||
} else {
|
||||
idom = blocks[idom].idom;
|
||||
}
|
||||
}
|
||||
blocks[j].level = level;
|
||||
}
|
||||
|
||||
free_alloca(postnum, use_heap);
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
|
||||
{
|
||||
while (blocks[b].level > blocks[a].level) {
|
||||
b = blocks[b].idom;
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
int level;
|
||||
} block_info;
|
||||
static int compare_block_level(const block_info *a, const block_info *b) {
|
||||
return b->level - a->level;
|
||||
}
|
||||
static void swap_blocks(block_info *a, block_info *b) {
|
||||
block_info tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
int i, j, k, n;
|
||||
int time;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int *entry_times, *exit_times;
|
||||
zend_worklist work;
|
||||
int flag = ZEND_FUNC_NO_LOOPS;
|
||||
block_info *sorted_blocks;
|
||||
ALLOCA_FLAG(list_use_heap)
|
||||
ALLOCA_FLAG(tree_use_heap)
|
||||
ALLOCA_FLAG(sorted_blocks_use_heap)
|
||||
|
||||
ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
|
||||
|
||||
/* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
|
||||
* queries. These are implemented by checking entry/exit times of the DFS search. */
|
||||
entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
|
||||
exit_times = entry_times + cfg->blocks_count;
|
||||
memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
|
||||
|
||||
zend_worklist_push(&work, 0);
|
||||
time = 0;
|
||||
while (zend_worklist_len(&work)) {
|
||||
next:
|
||||
i = zend_worklist_peek(&work);
|
||||
if (entry_times[i] == -1) {
|
||||
entry_times[i] = time++;
|
||||
}
|
||||
/* Visit blocks immediately dominated by i. */
|
||||
for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
|
||||
if (zend_worklist_push(&work, j)) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
/* Visit join edges. */
|
||||
for (j = 0; j < blocks[i].successors_count; j++) {
|
||||
int succ = blocks[i].successors[j];
|
||||
if (blocks[succ].idom == i) {
|
||||
continue;
|
||||
} else if (zend_worklist_push(&work, succ)) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
exit_times[i] = time++;
|
||||
zend_worklist_pop(&work);
|
||||
}
|
||||
|
||||
/* Sort blocks by decreasing level, which is the order in which we want to process them */
|
||||
sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
sorted_blocks[i].id = i;
|
||||
sorted_blocks[i].level = blocks[i].level;
|
||||
}
|
||||
zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
|
||||
(compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
|
||||
|
||||
/* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
|
||||
Graphs". */
|
||||
|
||||
for (n = 0; n < cfg->blocks_count; n++) {
|
||||
i = sorted_blocks[n].id;
|
||||
|
||||
zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
|
||||
for (j = 0; j < blocks[i].predecessors_count; j++) {
|
||||
int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
|
||||
|
||||
/* A join edge is one for which the predecessor does not
|
||||
immediately dominate the successor. */
|
||||
if (blocks[i].idom == pred) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In a loop back-edge (back-join edge), the successor dominates
|
||||
the predecessor. */
|
||||
if (dominates(blocks, i, pred)) {
|
||||
blocks[i].flags |= ZEND_BB_LOOP_HEADER;
|
||||
flag &= ~ZEND_FUNC_NO_LOOPS;
|
||||
zend_worklist_push(&work, pred);
|
||||
} else {
|
||||
/* Otherwise it's a cross-join edge. See if it's a branch
|
||||
to an ancestor on the DJ spanning tree. */
|
||||
if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
|
||||
blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
|
||||
flag |= ZEND_FUNC_IRREDUCIBLE;
|
||||
flag &= ~ZEND_FUNC_NO_LOOPS;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (zend_worklist_len(&work)) {
|
||||
j = zend_worklist_pop(&work);
|
||||
while (blocks[j].loop_header >= 0) {
|
||||
j = blocks[j].loop_header;
|
||||
}
|
||||
if (j != i) {
|
||||
blocks[j].loop_header = i;
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(sorted_blocks, sorted_blocks_use_heap);
|
||||
free_alloca(entry_times, tree_use_heap);
|
||||
ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
|
||||
|
||||
cfg->flags |= flag;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, CFG - Control Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_CFG_H
|
||||
#define ZEND_CFG_H
|
||||
|
||||
/* zend_basic_block.flags */
|
||||
#define ZEND_BB_START (1<<0) /* first block */
|
||||
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
|
||||
#define ZEND_BB_TARGET (1<<2) /* jump target */
|
||||
#define ZEND_BB_EXIT (1<<3) /* without successors */
|
||||
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
|
||||
#define ZEND_BB_TRY (1<<5) /* start of try block */
|
||||
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
|
||||
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
|
||||
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
|
||||
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
|
||||
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
|
||||
|
||||
#define ZEND_BB_LOOP_HEADER (1<<16)
|
||||
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
|
||||
|
||||
#define ZEND_BB_REACHABLE (1U<<31)
|
||||
|
||||
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE)
|
||||
|
||||
typedef struct _zend_basic_block {
|
||||
int *successors; /* successor block indices */
|
||||
uint32_t flags;
|
||||
uint32_t start; /* first opcode number */
|
||||
uint32_t len; /* number of opcodes */
|
||||
int successors_count; /* number of successors */
|
||||
int predecessors_count; /* number of predecessors */
|
||||
int predecessor_offset; /* offset of 1-st predecessor */
|
||||
int idom; /* immediate dominator block */
|
||||
int loop_header; /* closest loop header, or -1 */
|
||||
int level; /* steps away from the entry in the dom. tree */
|
||||
int children; /* list of dominated blocks */
|
||||
int next_child; /* next dominated block */
|
||||
int successors_storage[2]; /* up to 2 successor blocks */
|
||||
} zend_basic_block;
|
||||
|
||||
/*
|
||||
+------------+---+---+---+---+---+
|
||||
| |OP1|OP2|EXT| 0 | 1 |
|
||||
+------------+---+---+---+---+---+
|
||||
|JMP |ADR| | |OP1| - |
|
||||
|JMPZ | |ADR| |OP2|FOL|
|
||||
|JMPNZ | |ADR| |OP2|FOL|
|
||||
|JMPZNZ | |ADR|ADR|OP2|EXT|
|
||||
|JMPZ_EX | |ADR| |OP2|FOL|
|
||||
|JMPNZ_EX | |ADR| |OP2|FOL|
|
||||
|JMP_SET | |ADR| |OP2|FOL|
|
||||
|COALESCE | |ADR| |OP2|FOL|
|
||||
|ASSERT_CHK | |ADR| |OP2|FOL|
|
||||
|NEW | |ADR| |OP2|FOL|
|
||||
|DCL_ANON* |ADR| | |OP1|FOL|
|
||||
|FE_RESET_* | |ADR| |OP2|FOL|
|
||||
|FE_FETCH_* | | |ADR|EXT|FOL|
|
||||
|CATCH | | |ADR|EXT|FOL|
|
||||
|FAST_CALL |ADR| | |OP1|FOL|
|
||||
|FAST_RET | | | | - | - |
|
||||
|RETURN* | | | | - | - |
|
||||
|EXIT | | | | - | - |
|
||||
|THROW | | | | - | - |
|
||||
|* | | | |FOL| - |
|
||||
+------------+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
typedef struct _zend_cfg {
|
||||
int blocks_count; /* number of basic blocks */
|
||||
int edges_count; /* number of edges */
|
||||
zend_basic_block *blocks; /* array of basic blocks */
|
||||
int *predecessors;
|
||||
uint32_t *map;
|
||||
uint32_t flags;
|
||||
} zend_cfg;
|
||||
|
||||
/* Build Flags */
|
||||
#define ZEND_CFG_STACKLESS (1<<30)
|
||||
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
|
||||
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
|
||||
#define ZEND_SSA_RC_INFERENCE (1<<27)
|
||||
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
|
||||
#define ZEND_CFG_RECV_ENTRY (1<<24)
|
||||
#define ZEND_CALL_TREE (1<<23)
|
||||
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
|
||||
|
||||
#define CRT_CONSTANT_EX(op_array, opline, node) \
|
||||
(((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \
|
||||
RT_CONSTANT(opline, (node)) \
|
||||
: \
|
||||
CT_CONSTANT_EX(op_array, (node).constant) \
|
||||
)
|
||||
|
||||
#define CRT_CONSTANT(node) \
|
||||
CRT_CONSTANT_EX(op_array, opline, node)
|
||||
|
||||
#define RETURN_VALUE_USED(opline) \
|
||||
((opline)->result_type != IS_UNUSED)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
|
||||
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_CFG_H */
|
||||
@@ -1,332 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DFG - Data Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_dfg.h"
|
||||
|
||||
static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
uint32_t var_num;
|
||||
const zend_op *next;
|
||||
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
|
||||
&& opline->opcode != ZEND_FE_FETCH_R
|
||||
&& opline->opcode != ZEND_FE_FETCH_RW)
|
||||
|| (opline->op2_type == IS_CV)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op2.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV
|
||||
&& opline->opcode != ZEND_RECV) {
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
add_op1_def:
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_REF:
|
||||
if (opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_FE_RESET_R:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_UNPACK:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_INIT_ARRAY:
|
||||
if (((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
|
||||
&& opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_YIELD:
|
||||
if (opline->op1_type == IS_CV
|
||||
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
|
||||
|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_UNSET_CV:
|
||||
goto add_op1_def;
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
#if 0
|
||||
/* This special case was handled above the switch */
|
||||
if (opline->op2_type != IS_CV) {
|
||||
op2_use = -1; /* not used */
|
||||
}
|
||||
#endif
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
break;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
|
||||
{
|
||||
int set_size;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int blocks_count = cfg->blocks_count;
|
||||
zend_bitset tmp, def, use, in, out;
|
||||
int k;
|
||||
int j;
|
||||
|
||||
set_size = dfg->size;
|
||||
tmp = dfg->tmp;
|
||||
def = dfg->def;
|
||||
use = dfg->use;
|
||||
in = dfg->in;
|
||||
out = dfg->out;
|
||||
|
||||
/* Collect "def" and "use" sets */
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_op *opline, *end;
|
||||
zend_bitset b_use, b_def;
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
opline = op_array->opcodes + blocks[j].start;
|
||||
end = opline + blocks[j].len;
|
||||
b_use = DFG_BITSET(use, set_size, j);
|
||||
b_def = DFG_BITSET(def, set_size, j);
|
||||
for (; opline < end; opline++) {
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate "in" and "out" sets */
|
||||
{
|
||||
uint32_t worklist_len = zend_bitset_len(blocks_count);
|
||||
zend_bitset worklist;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
|
||||
memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_bitset_incl(worklist, j);
|
||||
}
|
||||
while (!zend_bitset_empty(worklist, worklist_len)) {
|
||||
/* We use the last block on the worklist, because predecessors tend to be located
|
||||
* before the succeeding block, so this converges faster. */
|
||||
j = zend_bitset_last(worklist, worklist_len);
|
||||
zend_bitset_excl(worklist, j);
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (blocks[j].successors_count != 0) {
|
||||
zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
|
||||
for (k = 1; k < blocks[j].successors_count; k++) {
|
||||
zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
|
||||
}
|
||||
} else {
|
||||
zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
|
||||
}
|
||||
zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
|
||||
if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
|
||||
zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
|
||||
|
||||
/* Add predecessors of changed block to worklist */
|
||||
{
|
||||
int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
zend_bitset_incl(worklist, predecessors[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(worklist, use_heap);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DFG - Data Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_DFG_H
|
||||
#define ZEND_DFG_H
|
||||
|
||||
#include "zend_bitset.h"
|
||||
#include "zend_cfg.h"
|
||||
|
||||
typedef struct _zend_dfg {
|
||||
int vars;
|
||||
uint32_t size;
|
||||
zend_bitset tmp;
|
||||
zend_bitset def;
|
||||
zend_bitset use;
|
||||
zend_bitset in;
|
||||
zend_bitset out;
|
||||
} zend_dfg;
|
||||
|
||||
#define DFG_BITSET(set, set_size, block_num) \
|
||||
((set) + ((block_num) * (set_size)))
|
||||
|
||||
#define DFG_SET(set, set_size, block_num, var_num) \
|
||||
zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
|
||||
|
||||
#define DFG_ISSET(set, set_size, block_num, var_num) \
|
||||
zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
|
||||
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_DFG_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Bytecode Visualisation |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_DUMP_H
|
||||
#define ZEND_DUMP_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_dfg.h"
|
||||
|
||||
#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
|
||||
#define ZEND_DUMP_RC_INFERENCE (1<<1)
|
||||
#define ZEND_DUMP_CFG (1<<2)
|
||||
#define ZEND_DUMP_SSA (1<<3)
|
||||
#define ZEND_DUMP_LIVE_RANGES (1<<4)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
|
||||
ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op);
|
||||
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
|
||||
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
|
||||
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
|
||||
void zend_dump_variables(const zend_op_array *op_array);
|
||||
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
|
||||
ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags);
|
||||
ZEND_API void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
|
||||
void zend_dump_op_array_name(const zend_op_array *op_array);
|
||||
void zend_dump_const(const zval *zv);
|
||||
void zend_dump_ht(HashTable *ht);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_DUMP_H */
|
||||
@@ -1,924 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Func Info |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_inference.h"
|
||||
#ifdef _WIN32
|
||||
#include "win32/ioutil.h"
|
||||
#endif
|
||||
|
||||
typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
|
||||
|
||||
typedef struct _func_info_t {
|
||||
const char *name;
|
||||
int name_len;
|
||||
uint32_t info;
|
||||
info_func_t info_func;
|
||||
} func_info_t;
|
||||
|
||||
#define F0(name, info) \
|
||||
{name, sizeof(name)-1, (info), NULL}
|
||||
#define F1(name, info) \
|
||||
{name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
|
||||
#define FN(name, info) \
|
||||
{name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
|
||||
#define FC(name, callback) \
|
||||
{name, sizeof(name)-1, 0, callback}
|
||||
|
||||
static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
|
||||
{
|
||||
if (!call_info->send_unpack
|
||||
&& (call_info->num_args == 2 || call_info->num_args == 3)
|
||||
&& ssa
|
||||
&& !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
|
||||
zend_op_array *op_array = call_info->caller_op_array;
|
||||
uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
|
||||
&ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
|
||||
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
|
||||
&ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
|
||||
uint32_t t3 = 0;
|
||||
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED;
|
||||
|
||||
if (call_info->num_args == 3) {
|
||||
t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
|
||||
&ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
|
||||
}
|
||||
if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
|
||||
tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
|
||||
}
|
||||
if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
|
||||
|| (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
|
||||
|| (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
|
||||
tmp |= MAY_BE_ARRAY_OF_DOUBLE;
|
||||
}
|
||||
if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
|
||||
if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) {
|
||||
tmp |= MAY_BE_ARRAY_OF_LONG;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
} else {
|
||||
/* May throw */
|
||||
return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
static const func_info_t func_infos[] = {
|
||||
/* zend */
|
||||
F1("zend_version", MAY_BE_STRING),
|
||||
FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
|
||||
F0("restore_error_handler", MAY_BE_TRUE),
|
||||
F0("restore_exception_handler", MAY_BE_TRUE),
|
||||
F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("get_resource_type", MAY_BE_STRING),
|
||||
F1("get_defined_constants", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("debug_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/standard */
|
||||
FN("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("bin2hex", MAY_BE_STRING),
|
||||
F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_NANOSLEEP
|
||||
F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#if HAVE_STRPTIME
|
||||
F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("wordwrap", MAY_BE_STRING),
|
||||
F1("htmlspecialchars", MAY_BE_STRING),
|
||||
F1("htmlentities", MAY_BE_STRING),
|
||||
F1("get_html_translation_table", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sha1", MAY_BE_STRING),
|
||||
F1("sha1_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("md5", MAY_BE_STRING),
|
||||
F1("md5_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iptcparse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("iptcembed", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("getimagesize", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("getimagesizefromstring", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("image_type_to_mime_type", MAY_BE_STRING),
|
||||
F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F0("phpinfo", MAY_BE_TRUE),
|
||||
F1("phpversion", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F0("phpcredits", MAY_BE_TRUE),
|
||||
F1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("php_uname", MAY_BE_STRING),
|
||||
F1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strtok", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strrev", MAY_BE_STRING),
|
||||
F1("hebrev", MAY_BE_STRING),
|
||||
F1("basename", MAY_BE_STRING),
|
||||
F1("dirname", MAY_BE_STRING),
|
||||
F1("pathinfo", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stripslashes", MAY_BE_STRING),
|
||||
F1("stripcslashes", MAY_BE_STRING),
|
||||
F1("strstr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stristr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strrchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("str_shuffle", MAY_BE_STRING),
|
||||
F1("str_word_count", MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("str_split", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
FN("substr_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("quotemeta", MAY_BE_STRING),
|
||||
F1("ucwords", MAY_BE_STRING),
|
||||
F1("addcslashes", MAY_BE_STRING),
|
||||
FN("str_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
|
||||
FN("str_ireplace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("str_repeat", MAY_BE_STRING),
|
||||
F1("count_chars", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("chunk_split", MAY_BE_STRING),
|
||||
F1("strip_tags", MAY_BE_STRING),
|
||||
F1("explode", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("localeconv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#if HAVE_NL_LANGINFO
|
||||
F1("nl_langinfo", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("soundex", MAY_BE_STRING),
|
||||
F1("chr", MAY_BE_STRING),
|
||||
F1("str_getcsv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("strchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sprintf", MAY_BE_STRING),
|
||||
F1("vsprintf", MAY_BE_STRING),
|
||||
F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("urlencode", MAY_BE_STRING),
|
||||
F1("urldecode", MAY_BE_STRING),
|
||||
F1("rawurlencode", MAY_BE_STRING),
|
||||
F1("rawurldecode", MAY_BE_STRING),
|
||||
F1("http_build_query", MAY_BE_STRING),
|
||||
#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
|
||||
F1("readlink", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("system", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("escapeshellcmd", MAY_BE_STRING),
|
||||
F1("escapeshellarg", MAY_BE_STRING),
|
||||
F0("passthru", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#ifdef PHP_CAN_SUPPORT_PROC_OPEN
|
||||
F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("proc_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("random_bytes", MAY_BE_STRING),
|
||||
#if HAVE_GETSERVBYPORT
|
||||
F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_GETPROTOBYNUMBER
|
||||
F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("base64_encode", MAY_BE_STRING),
|
||||
F1("password_hash", MAY_BE_STRING),
|
||||
F1("password_get_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("convert_uuencode", MAY_BE_STRING),
|
||||
F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pow", MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT),
|
||||
F1("decbin", MAY_BE_STRING),
|
||||
F1("decoct", MAY_BE_STRING),
|
||||
F1("dechex", MAY_BE_STRING),
|
||||
F1("base_convert", MAY_BE_STRING),
|
||||
F1("number_format", MAY_BE_STRING),
|
||||
#ifdef HAVE_INET_NTOP
|
||||
F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#ifdef HAVE_INET_PTON
|
||||
F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("long2ip", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_PUTENV
|
||||
#endif
|
||||
F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#ifdef HAVE_GETLOADAVG
|
||||
F1("sys_getloadavg", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
F1("microtime", MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("gettimeofday", MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
F1("getrusage", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
F1("uniqid", MAY_BE_STRING),
|
||||
#endif
|
||||
F1("quoted_printable_decode", MAY_BE_STRING),
|
||||
F1("quoted_printable_encode", MAY_BE_STRING),
|
||||
F1("get_current_user", MAY_BE_STRING),
|
||||
F1("get_cfg_var", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("serialize", MAY_BE_STRING),
|
||||
F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("print_r", MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("show_source", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("php_strip_whitespace", MAY_BE_STRING),
|
||||
F1("ini_get_all", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("ini_alter", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("set_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#if ZEND_DEBUG
|
||||
F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#endif
|
||||
F1("gethostbyaddr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gethostbyname", MAY_BE_STRING),
|
||||
F1("gethostbynamel", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_GETHOSTNAME
|
||||
F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
|
||||
# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
|
||||
F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
# endif
|
||||
#endif
|
||||
F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_context_create", MAY_BE_RESOURCE),
|
||||
F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_SOCKETPAIR
|
||||
F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
|
||||
#endif
|
||||
F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fgetcsv", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_get_meta_data", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_get_wrappers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_get_transports", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("realpath", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("pack", MAY_BE_STRING),
|
||||
F1("unpack", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("crypt", MAY_BE_STRING),
|
||||
FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("readdir", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("scandir", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_GLOB
|
||||
F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("filetype", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("lstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
F0("syslog", MAY_BE_TRUE),
|
||||
F0("closelog", MAY_BE_TRUE),
|
||||
#endif
|
||||
F1("metaphone", MAY_BE_STRING),
|
||||
F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("ob_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F0("array_walk", MAY_BE_TRUE),
|
||||
F0("array_walk_recursive", MAY_BE_TRUE),
|
||||
F0("arsort", MAY_BE_TRUE),
|
||||
F0("asort", MAY_BE_TRUE),
|
||||
F0("krsort", MAY_BE_TRUE),
|
||||
F0("ksort", MAY_BE_TRUE),
|
||||
F0("shuffle", MAY_BE_TRUE),
|
||||
F0("sort", MAY_BE_TRUE),
|
||||
F0("usort", MAY_BE_TRUE),
|
||||
F0("uasort", MAY_BE_TRUE),
|
||||
F0("uksort", MAY_BE_TRUE),
|
||||
F1("compact", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_fill", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_fill_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FC("range", zend_range_info),
|
||||
F1("array_splice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_slice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_replace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_replace_recursive", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("array_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("array_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_count_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("array_column", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_reverse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_flip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("array_change_key_case", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("array_rand", MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("array_intersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_chunk", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_combine", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("str_rot13", MAY_BE_STRING),
|
||||
F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("stream_bucket_new", MAY_BE_OBJECT),
|
||||
F1("sys_get_temp_dir", MAY_BE_STRING),
|
||||
|
||||
/* ext/date */
|
||||
F1("date", MAY_BE_STRING),
|
||||
F1("gmdate", MAY_BE_STRING),
|
||||
F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("localtime", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("getdate", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_parse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("date_parse_from_format", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("date_format", MAY_BE_STRING),
|
||||
F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_diff", MAY_BE_OBJECT),
|
||||
F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("timezone_name_get", MAY_BE_STRING),
|
||||
F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("timezone_identifiers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("timezone_version_get", MAY_BE_STRING),
|
||||
F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_interval_format", MAY_BE_STRING),
|
||||
F1("date_default_timezone_get", MAY_BE_STRING),
|
||||
F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("date_sun_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
|
||||
|
||||
/* ext/preg */
|
||||
FN("preg_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("preg_filter", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("preg_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
|
||||
/* ext/mysqli */
|
||||
F1("mysqli_connect", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F0("mysqli_close", MAY_BE_TRUE),
|
||||
F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mysqli_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("mysqli_get_links_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mysqli_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
|
||||
F1("mysqli_get_charset", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_object", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("mysqli_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_character_set_name", MAY_BE_STRING),
|
||||
F0("mysqli_debug", MAY_BE_TRUE),
|
||||
F1("mysqli_error", MAY_BE_STRING),
|
||||
F1("mysqli_reap_async_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("mysqli_stmt_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_field", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_fields", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("mysqli_fetch_field_direct", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_lengths", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mysqli_fetch_row", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_get_client_info", MAY_BE_STRING),
|
||||
F1("mysqli_get_host_info", MAY_BE_STRING),
|
||||
F1("mysqli_get_server_info", MAY_BE_STRING),
|
||||
F1("mysqli_info", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("mysqli_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_insert_id", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_num_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_real_escape_string", MAY_BE_STRING),
|
||||
F1("mysqli_stmt_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_insert_id", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_num_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_sqlstate", MAY_BE_STRING),
|
||||
F0("mysqli_ssl_set", MAY_BE_TRUE),
|
||||
F1("mysqli_stat", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_error", MAY_BE_STRING),
|
||||
F1("mysqli_stmt_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_result_metadata", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_sqlstate", MAY_BE_STRING),
|
||||
F1("mysqli_store_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_use_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
|
||||
/* ext/curl */
|
||||
F1("curl_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("curl_copy_handle", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("curl_version", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("curl_error", MAY_BE_STRING),
|
||||
F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("curl_escape", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("curl_unescape", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("curl_multi_init", MAY_BE_OBJECT),
|
||||
F1("curl_multi_info_read", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("curl_share_init", MAY_BE_OBJECT),
|
||||
F1("curl_file_create", MAY_BE_OBJECT),
|
||||
|
||||
/* ext/mbstring */
|
||||
F1("mb_convert_case", MAY_BE_STRING),
|
||||
F1("mb_strtoupper", MAY_BE_STRING),
|
||||
F1("mb_strtolower", MAY_BE_STRING),
|
||||
F1("mb_language", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_http_input", MAY_BE_FALSE | MAY_BE_STRING| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_http_output", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_detect_order", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_substitute_character", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mb_output_handler", MAY_BE_STRING),
|
||||
F1("mb_preferred_mime_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strstr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strrchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_stristr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strrichr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_substr", MAY_BE_STRING),
|
||||
F1("mb_strcut", MAY_BE_STRING),
|
||||
F1("mb_strimwidth", MAY_BE_STRING),
|
||||
F1("mb_convert_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mb_detect_encoding", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_encoding_aliases", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_convert_kana", MAY_BE_STRING),
|
||||
F1("mb_encode_mimeheader", MAY_BE_STRING),
|
||||
F1("mb_decode_mimeheader", MAY_BE_STRING),
|
||||
F1("mb_convert_variables", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_encode_numericentity", MAY_BE_STRING),
|
||||
F1("mb_decode_numericentity", MAY_BE_STRING),
|
||||
F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
F1("mb_regex_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_regex_set_options", MAY_BE_STRING),
|
||||
F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_ereg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_ereg_search_pos", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mb_ereg_search_regs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/iconv */
|
||||
F1("iconv", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_get_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
/* ext/json */
|
||||
F1("json_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("json_last_error_msg", MAY_BE_STRING),
|
||||
|
||||
/* ext/xml */
|
||||
F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("xml_parser_get_option", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("utf8_encode", MAY_BE_STRING),
|
||||
F1("utf8_decode", MAY_BE_STRING),
|
||||
|
||||
/* ext/zlib */
|
||||
F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzread", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzdeflate", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzinflate", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzencode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzdecode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_get_coding_type", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/hash */
|
||||
F1("hash", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hmac", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hmac_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("hash_hmac_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hkdf", MAY_BE_STRING),
|
||||
F1("hash_init", MAY_BE_OBJECT),
|
||||
F1("hash_final", MAY_BE_STRING),
|
||||
F1("hash_copy", MAY_BE_OBJECT),
|
||||
F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("hash_pbkdf2", MAY_BE_STRING),
|
||||
F1("mhash_keygen_s2k", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mhash_get_hash_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mhash", MAY_BE_FALSE | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/sodium */
|
||||
F1("sodium_crypto_shorthash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash_init", MAY_BE_STRING),
|
||||
F0("sodium_crypto_generichash_update", MAY_BE_TRUE),
|
||||
F1("sodium_crypto_generichash_final", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seal", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seal_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_detached", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream_xor", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_str", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_bin2hex", MAY_BE_STRING),
|
||||
F1("sodium_hex2bin", MAY_BE_STRING),
|
||||
F1("sodium_crypto_scalarmult", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_client_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sodium_crypto_kx_server_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sodium_crypto_auth", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_auth_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kdf_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_shorthash_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kdf_derive_from_key", MAY_BE_STRING),
|
||||
F1("sodium_pad", MAY_BE_STRING),
|
||||
F1("sodium_unpad", MAY_BE_STRING),
|
||||
|
||||
F1("sodium_crypto_box_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_publickey_from_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_publickey_from_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_scryptsalsa208sha256", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_scryptsalsa208sha256_str", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_ed25519_sk_to_curve25519", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_ed25519_pk_to_curve25519", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING),
|
||||
|
||||
/* ext/session */
|
||||
F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("session_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_module_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_save_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_create_id", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_cache_limiter", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/pgsql */
|
||||
F1("pg_dbname", MAY_BE_STRING),
|
||||
F1("pg_options", MAY_BE_STRING),
|
||||
F1("pg_port", MAY_BE_STRING),
|
||||
F1("pg_tty", MAY_BE_STRING),
|
||||
F1("pg_host", MAY_BE_STRING),
|
||||
F1("pg_version", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_NULL),
|
||||
F1("pg_parameter_status", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_query", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_query_params", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_execute", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
FN("pg_last_notice", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY ),
|
||||
F1("pg_field_name", MAY_BE_STRING),
|
||||
F1("pg_field_type_oid", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_fetch_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_fetch_row", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_assoc", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_object", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("pg_fetch_all_columns", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_last_oid", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_lo_create", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_lo_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_lo_read", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_lo_import", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_copy_to", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_escape_string", MAY_BE_STRING),
|
||||
F1("pg_escape_bytea", MAY_BE_STRING),
|
||||
F1("pg_unescape_bytea", MAY_BE_STRING),
|
||||
F1("pg_escape_literal", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_escape_identifier", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_result_error", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_result_error_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("pg_socket", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT | MAY_BE_STRING),
|
||||
F1("pg_update", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("pg_delete", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("pg_select", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
/* ext/bcmath */
|
||||
F1("bcadd", MAY_BE_STRING),
|
||||
F1("bcsub", MAY_BE_STRING),
|
||||
F1("bcmul", MAY_BE_STRING),
|
||||
F1("bcdiv", MAY_BE_STRING),
|
||||
F1("bcmod", MAY_BE_STRING),
|
||||
F1("bcpowmod", MAY_BE_STRING),
|
||||
F1("bcpow", MAY_BE_STRING),
|
||||
F1("bcsqrt", MAY_BE_STRING),
|
||||
|
||||
/* ext/exif */
|
||||
F1("exif_tagname", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("exif_read_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("exif_thumbnail", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/filter */
|
||||
F1("filter_input_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("filter_var_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/gettext */
|
||||
F1("textdomain", MAY_BE_STRING),
|
||||
F1("gettext", MAY_BE_STRING),
|
||||
F1("_", MAY_BE_STRING),
|
||||
F1("dgettext", MAY_BE_STRING),
|
||||
F1("dcgettext", MAY_BE_STRING),
|
||||
F1("bindtextdomain", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_NGETTEXT
|
||||
F1("ngettext", MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_DNGETTEXT
|
||||
F1("dcngettext", MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_BIND_TEXTDOMAIN_CODESET
|
||||
F1("bind_textdomain_codeset", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
|
||||
/* ext/fileinfo */
|
||||
F1("finfo_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("finfo_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("finfo_buffer", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mime_content_type", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/gd */
|
||||
F1("gd_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE),
|
||||
F1("imagecreatetruecolor", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#ifdef PHP_WIN32
|
||||
F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagerotate", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreate", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromstring", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgif", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#ifdef HAVE_GD_JPG
|
||||
F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
#ifdef HAVE_GD_PNG
|
||||
F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
#ifdef HAVE_GD_WEBP
|
||||
F1("imagecreatefromwebp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagecreatefromxbm", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#if defined(HAVE_GD_XPM)
|
||||
F1("imagecreatefromxpm", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagecreatefromwbmp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd2", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd2part", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#if defined(HAVE_GD_BMP)
|
||||
F1("imagecreatefrombmp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("imagecolorsforindex", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imageftbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagefttext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagettfbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagettftext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagecrop", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecropauto", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagescale", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imageaffine", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imageaffinematrixget", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
F1("imageaffinematrixconcat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
F1("imageresolution", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
|
||||
/* ext/spl */
|
||||
F1("class_implements", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("class_parents", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("class_uses", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("iterator_to_array", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("spl_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("spl_object_hash", MAY_BE_STRING),
|
||||
|
||||
};
|
||||
|
||||
static HashTable func_info;
|
||||
ZEND_API int zend_func_info_rid = -1;
|
||||
|
||||
static uint32_t get_internal_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa) {
|
||||
zend_function *callee_func = call_info->callee_func;
|
||||
if (callee_func->common.scope) {
|
||||
/* This is a method, not a function. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
zval *zv = zend_hash_find_ex(&func_info, callee_func->common.function_name, 1);
|
||||
if (!zv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
func_info_t *info = Z_PTR_P(zv);
|
||||
if (info->info_func) {
|
||||
return info->info_func(call_info, ssa);
|
||||
} else {
|
||||
return info->info;
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API uint32_t zend_get_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
const zend_function *callee_func = call_info->callee_func;
|
||||
*ce = NULL;
|
||||
*ce_is_instanceof = 0;
|
||||
|
||||
if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
uint32_t internal_ret = get_internal_func_info(call_info, ssa);
|
||||
#if !ZEND_DEBUG
|
||||
if (internal_ret) {
|
||||
return internal_ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = zend_get_return_info_from_signature_only(
|
||||
callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
|
||||
|
||||
#if ZEND_DEBUG
|
||||
if (internal_ret) {
|
||||
zend_string *name = callee_func->common.function_name;
|
||||
/* Check whether the func_info information is a subset of the information we can
|
||||
* compute from the specified return type, otherwise it contains redundant types. */
|
||||
if (internal_ret & ~ret) {
|
||||
fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
/* Check whether the func info is completely redundant with arginfo. */
|
||||
if (internal_ret == ret) {
|
||||
fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
/* If the return type is not mixed, check that the types match exactly if we exclude
|
||||
* RC and array information. */
|
||||
uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY;
|
||||
if (ret_any != MAY_BE_ANY) {
|
||||
uint32_t diff = internal_ret_any ^ ret_any;
|
||||
/* Func info may contain "true" types as well as isolated "null" and "false". */
|
||||
if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE))
|
||||
&& (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
|
||||
fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
}
|
||||
return internal_ret;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (!call_info->is_prototype) {
|
||||
// FIXME: the order of functions matters!!!
|
||||
zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
|
||||
if (info) {
|
||||
ret = info->return_info.type;
|
||||
*ce = info->return_info.ce;
|
||||
*ce_is_instanceof = info->return_info.is_instanceof;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
ret = zend_get_return_info_from_signature_only(
|
||||
callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int zend_func_info_startup(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (zend_func_info_rid == -1) {
|
||||
zend_func_info_rid = zend_get_resource_handle("Zend Optimizer");
|
||||
if (zend_func_info_rid < 0) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
|
||||
for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
|
||||
zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
|
||||
|
||||
if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
|
||||
fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
|
||||
}
|
||||
zend_string_release_ex(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int zend_func_info_shutdown(void)
|
||||
{
|
||||
if (zend_func_info_rid != -1) {
|
||||
zend_hash_destroy(&func_info);
|
||||
zend_func_info_rid = -1;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Func Info |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_FUNC_INFO_H
|
||||
#define ZEND_FUNC_INFO_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
|
||||
/* func/cfg flags */
|
||||
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
|
||||
#define ZEND_FUNC_HAS_CALLS (1<<1)
|
||||
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
|
||||
#define ZEND_FUNC_NO_LOOPS (1<<3)
|
||||
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
|
||||
#define ZEND_FUNC_FREE_LOOP_VAR (1<<5)
|
||||
#define ZEND_FUNC_RECURSIVE (1<<7)
|
||||
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
|
||||
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
|
||||
#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10)
|
||||
#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
|
||||
#define ZEND_SSA_TSSA (1<<12) /* used by tracing JIT */
|
||||
|
||||
#define ZEND_FUNC_JIT_ON_FIRST_EXEC (1<<13) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */
|
||||
|
||||
|
||||
typedef struct _zend_func_info zend_func_info;
|
||||
typedef struct _zend_call_info zend_call_info;
|
||||
|
||||
#define ZEND_FUNC_INFO(op_array) \
|
||||
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
|
||||
|
||||
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
|
||||
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
|
||||
*pinfo = info; \
|
||||
} while (0)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
extern ZEND_API int zend_func_info_rid;
|
||||
|
||||
ZEND_API uint32_t zend_get_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof);
|
||||
|
||||
int zend_func_info_startup(void);
|
||||
int zend_func_info_shutdown(void);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_FUNC_INFO_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, e-SSA based Type & Range Inference |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_INFERENCE_H
|
||||
#define ZEND_INFERENCE_H
|
||||
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* Bitmask for type inference (zend_ssa_var_info.type) */
|
||||
#include "zend_type_info.h"
|
||||
|
||||
#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
|
||||
#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
|
||||
#define MAY_BE_GUARD (1<<28) /* needs type guard */
|
||||
//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
|
||||
|
||||
//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
|
||||
#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
|
||||
#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
|
||||
|
||||
#define MAY_HAVE_DTOR \
|
||||
(MAY_BE_OBJECT|MAY_BE_RESOURCE \
|
||||
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
|
||||
|
||||
#define DEFINE_SSA_OP_HAS_RANGE(opN) \
|
||||
static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
|
||||
} else { \
|
||||
return (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_MIN_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG) { \
|
||||
return Z_LVAL_P(zv); \
|
||||
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
|
||||
return 1; \
|
||||
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
|
||||
return 0; \
|
||||
} else if (Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.min; \
|
||||
} \
|
||||
return ZEND_LONG_MIN; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_MAX_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG) { \
|
||||
return Z_LVAL_P(zv); \
|
||||
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
|
||||
return 1; \
|
||||
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
|
||||
return 0; \
|
||||
} else if (Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.max; \
|
||||
} \
|
||||
return ZEND_LONG_MAX; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.underflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.overflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
|
||||
DEFINE_SSA_OP_HAS_RANGE(op1)
|
||||
DEFINE_SSA_OP_MIN_RANGE(op1)
|
||||
DEFINE_SSA_OP_MAX_RANGE(op1)
|
||||
DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
|
||||
DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
|
||||
DEFINE_SSA_OP_HAS_RANGE(op2)
|
||||
DEFINE_SSA_OP_MIN_RANGE(op2)
|
||||
DEFINE_SSA_OP_MAX_RANGE(op2)
|
||||
DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
|
||||
DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
|
||||
|
||||
#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline uint32_t _const_op_type(const zval *zv) {
|
||||
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
|
||||
return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
|
||||
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
|
||||
HashTable *ht = Z_ARRVAL_P(zv);
|
||||
uint32_t tmp = MAY_BE_ARRAY;
|
||||
zend_string *str;
|
||||
zval *val;
|
||||
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
tmp |= MAY_BE_RC1 | MAY_BE_RCN;
|
||||
} else {
|
||||
tmp |= MAY_BE_RCN;
|
||||
}
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
|
||||
if (str) {
|
||||
tmp |= MAY_BE_ARRAY_KEY_STRING;
|
||||
} else {
|
||||
tmp |= MAY_BE_ARRAY_KEY_LONG;
|
||||
}
|
||||
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
if (HT_IS_PACKED(ht)) {
|
||||
tmp &= ~MAY_BE_ARRAY_HASH;
|
||||
}
|
||||
return tmp;
|
||||
} else {
|
||||
uint32_t tmp = (1 << Z_TYPE_P(zv));
|
||||
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
tmp |= MAY_BE_RC1 | MAY_BE_RCN;
|
||||
} else if (Z_TYPE_P(zv) == IS_STRING) {
|
||||
tmp |= MAY_BE_RCN;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
|
||||
{
|
||||
if (ssa->var_info && ssa_var_num >= 0) {
|
||||
return ssa->var_info[ssa_var_num].type;
|
||||
} else {
|
||||
return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
return _const_op_type(CRT_CONSTANT(opline->opN)); \
|
||||
} else { \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \
|
||||
} \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_DEF_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \
|
||||
} \
|
||||
|
||||
|
||||
DEFINE_SSA_OP_INFO(op1)
|
||||
DEFINE_SSA_OP_INFO(op2)
|
||||
DEFINE_SSA_OP_INFO(result)
|
||||
DEFINE_SSA_OP_DEF_INFO(op1)
|
||||
DEFINE_SSA_OP_DEF_INFO(op2)
|
||||
DEFINE_SSA_OP_DEF_INFO(result)
|
||||
|
||||
#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) {
|
||||
return (b > 0 && a > ZEND_LONG_MAX - b)
|
||||
|| (b < 0 && a < ZEND_LONG_MIN - b);
|
||||
}
|
||||
static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) {
|
||||
return (b > 0 && a < ZEND_LONG_MIN + b)
|
||||
|| (b < 0 && a > ZEND_LONG_MAX + b);
|
||||
}
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
|
||||
|
||||
ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert);
|
||||
|
||||
int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
|
||||
ZEND_API int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
|
||||
void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow);
|
||||
int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
|
||||
int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
|
||||
void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
|
||||
|
||||
int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level);
|
||||
|
||||
ZEND_API uint32_t zend_fetch_arg_info_type(
|
||||
const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce);
|
||||
ZEND_API void zend_init_func_return_info(
|
||||
const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
|
||||
uint32_t zend_get_return_info_from_signature_only(
|
||||
const zend_function *func, const zend_script *script,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info);
|
||||
void zend_func_return_info(const zend_op_array *op_array,
|
||||
const zend_script *script,
|
||||
int recursive,
|
||||
int widening,
|
||||
zend_ssa_var_info *ret);
|
||||
|
||||
ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2);
|
||||
ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
ZEND_API int zend_update_type_info(
|
||||
const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script,
|
||||
zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
|
||||
zend_long optimization_level);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_INFERENCE_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_OPTIMIZER_H
|
||||
#define ZEND_OPTIMIZER_H
|
||||
|
||||
#include "zend.h"
|
||||
#include "zend_compile.h"
|
||||
|
||||
#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */
|
||||
#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */
|
||||
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
|
||||
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
|
||||
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
|
||||
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
|
||||
#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */
|
||||
#define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */
|
||||
#define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */
|
||||
#define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */
|
||||
#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */
|
||||
#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */
|
||||
|
||||
#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */
|
||||
|
||||
#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
|
||||
|
||||
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF"
|
||||
|
||||
|
||||
#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
|
||||
#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
|
||||
#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
|
||||
#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
|
||||
#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
|
||||
#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
|
||||
#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
|
||||
#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
|
||||
#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
|
||||
#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
|
||||
#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
|
||||
#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
|
||||
#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
|
||||
#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
|
||||
|
||||
#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
|
||||
#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
|
||||
|
||||
#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
|
||||
#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
|
||||
#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
|
||||
|
||||
#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
|
||||
#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
|
||||
#define ZEND_DUMP_DFA_CFG (1<<23)
|
||||
#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
|
||||
#define ZEND_DUMP_DFA_LIVENESS (1<<25)
|
||||
#define ZEND_DUMP_DFA_PHI (1<<26)
|
||||
#define ZEND_DUMP_DFA_SSA (1<<27)
|
||||
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
|
||||
#define ZEND_DUMP_SCCP (1<<29)
|
||||
|
||||
typedef struct _zend_script {
|
||||
zend_string *filename;
|
||||
zend_op_array main_op_array;
|
||||
HashTable function_table;
|
||||
HashTable class_table;
|
||||
uint32_t first_early_binding_opline; /* the linked list of delayed declarations */
|
||||
} zend_script;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
|
||||
int zend_optimizer_startup(void);
|
||||
int zend_optimizer_shutdown(void);
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_OPTIMIZER_INTERNAL_H
|
||||
#define ZEND_OPTIMIZER_INTERNAL_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_func_info.h"
|
||||
|
||||
#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
|
||||
#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
|
||||
#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
|
||||
#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
|
||||
|
||||
#define VAR_NUM(v) EX_VAR_TO_NUM(v)
|
||||
#define NUM_VAR(v) EX_NUM_TO_VAR(v)
|
||||
|
||||
#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ)
|
||||
#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ)
|
||||
#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
|
||||
#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
|
||||
|
||||
#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
|
||||
#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
|
||||
|
||||
typedef struct _zend_optimizer_ctx {
|
||||
zend_arena *arena;
|
||||
zend_script *script;
|
||||
HashTable *constants;
|
||||
zend_long optimization_level;
|
||||
zend_long debug_level;
|
||||
} zend_optimizer_ctx;
|
||||
|
||||
#define LITERAL_LONG(op, val) do { \
|
||||
zval _c; \
|
||||
ZVAL_LONG(&_c, val); \
|
||||
op.constant = zend_optimizer_add_literal(op_array, &_c); \
|
||||
} while (0)
|
||||
|
||||
#define LITERAL_BOOL(op, val) do { \
|
||||
zval _c; \
|
||||
ZVAL_BOOL(&_c, val); \
|
||||
op.constant = zend_optimizer_add_literal(op_array, &_c); \
|
||||
} while (0)
|
||||
|
||||
#define literal_dtor(zv) do { \
|
||||
zval_ptr_dtor_nogc(zv); \
|
||||
ZVAL_NULL(zv); \
|
||||
} while (0)
|
||||
|
||||
#define COPY_NODE(target, src) do { \
|
||||
target ## _type = src ## _type; \
|
||||
target = src; \
|
||||
} while (0)
|
||||
|
||||
static inline bool zend_optimizer_is_loop_var_free(const zend_op *opline) {
|
||||
return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN)
|
||||
|| (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH);
|
||||
}
|
||||
|
||||
int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv);
|
||||
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy);
|
||||
void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value);
|
||||
int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value);
|
||||
int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2);
|
||||
int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1);
|
||||
int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1);
|
||||
int zend_optimizer_eval_strlen(zval *result, zval *op1);
|
||||
int zend_optimizer_update_op1_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zval *val);
|
||||
int zend_optimizer_update_op2_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zval *val);
|
||||
int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zend_uchar type,
|
||||
uint32_t var,
|
||||
zval *val);
|
||||
zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline);
|
||||
|
||||
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
|
||||
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map);
|
||||
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_compact_vars(zend_op_array *op_array);
|
||||
zend_function *zend_optimizer_get_called_func(
|
||||
zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype);
|
||||
uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
|
||||
void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
|
||||
void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
|
||||
int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map);
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects);
|
||||
int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
typedef void (*zend_op_array_func_t)(zend_op_array *, void *context);
|
||||
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, SSA - Static Single Assignment Form |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_SSA_H
|
||||
#define ZEND_SSA_H
|
||||
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_cfg.h"
|
||||
|
||||
typedef struct _zend_ssa_range {
|
||||
zend_long min;
|
||||
zend_long max;
|
||||
bool underflow;
|
||||
bool overflow;
|
||||
} zend_ssa_range;
|
||||
|
||||
typedef enum _zend_ssa_negative_lat {
|
||||
NEG_NONE = 0,
|
||||
NEG_INIT = 1,
|
||||
NEG_INVARIANT = 2,
|
||||
NEG_USE_LT = 3,
|
||||
NEG_USE_GT = 4,
|
||||
NEG_UNKNOWN = 5
|
||||
} zend_ssa_negative_lat;
|
||||
|
||||
/* Special kind of SSA Phi function used in eSSA */
|
||||
typedef struct _zend_ssa_range_constraint {
|
||||
zend_ssa_range range; /* simple range constraint */
|
||||
int min_var;
|
||||
int max_var;
|
||||
int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
|
||||
int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
|
||||
zend_ssa_negative_lat negative;
|
||||
} zend_ssa_range_constraint;
|
||||
|
||||
typedef struct _zend_ssa_type_constraint {
|
||||
uint32_t type_mask; /* Type mask to intersect with */
|
||||
zend_class_entry *ce; /* Class entry for instanceof constraints */
|
||||
} zend_ssa_type_constraint;
|
||||
|
||||
typedef union _zend_ssa_pi_constraint {
|
||||
zend_ssa_range_constraint range;
|
||||
zend_ssa_type_constraint type;
|
||||
} zend_ssa_pi_constraint;
|
||||
|
||||
/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
|
||||
typedef struct _zend_ssa_phi zend_ssa_phi;
|
||||
struct _zend_ssa_phi {
|
||||
zend_ssa_phi *next; /* next Phi in the same BB */
|
||||
int pi; /* if >= 0 this is actually a e-SSA Pi */
|
||||
zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
|
||||
int var; /* Original CV, VAR or TMP variable index */
|
||||
int ssa_var; /* SSA variable index */
|
||||
int block; /* current BB index */
|
||||
int visited : 1; /* flag to avoid recursive processing */
|
||||
int has_range_constraint : 1;
|
||||
zend_ssa_phi **use_chains;
|
||||
zend_ssa_phi *sym_use_chain;
|
||||
int *sources; /* Array of SSA IDs that produce this var.
|
||||
As many as this block has
|
||||
predecessors. */
|
||||
};
|
||||
|
||||
typedef struct _zend_ssa_block {
|
||||
zend_ssa_phi *phis;
|
||||
} zend_ssa_block;
|
||||
|
||||
typedef struct _zend_ssa_op {
|
||||
int op1_use;
|
||||
int op2_use;
|
||||
int result_use;
|
||||
int op1_def;
|
||||
int op2_def;
|
||||
int result_def;
|
||||
int op1_use_chain;
|
||||
int op2_use_chain;
|
||||
int res_use_chain;
|
||||
} zend_ssa_op;
|
||||
|
||||
typedef enum _zend_ssa_alias_kind {
|
||||
NO_ALIAS,
|
||||
SYMTABLE_ALIAS,
|
||||
HTTP_RESPONSE_HEADER_ALIAS
|
||||
} zend_ssa_alias_kind;
|
||||
|
||||
typedef enum _zend_ssa_escape_state {
|
||||
ESCAPE_STATE_UNKNOWN,
|
||||
ESCAPE_STATE_NO_ESCAPE,
|
||||
ESCAPE_STATE_FUNCTION_ESCAPE,
|
||||
ESCAPE_STATE_GLOBAL_ESCAPE
|
||||
} zend_ssa_escape_state;
|
||||
|
||||
typedef struct _zend_ssa_var {
|
||||
int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
|
||||
int scc; /* strongly connected component */
|
||||
int definition; /* opcode that defines this value */
|
||||
zend_ssa_phi *definition_phi; /* phi that defines this value */
|
||||
int use_chain; /* uses of this value, linked through opN_use_chain */
|
||||
zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
|
||||
zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */
|
||||
unsigned int no_val : 1; /* value doesn't matter (used as op1 in ZEND_ASSIGN) */
|
||||
unsigned int scc_entry : 1;
|
||||
unsigned int alias : 2; /* value may be changed indirectly */
|
||||
unsigned int escape_state : 2;
|
||||
} zend_ssa_var;
|
||||
|
||||
typedef struct _zend_ssa_var_info {
|
||||
uint32_t type; /* inferred type (see zend_inference.h) */
|
||||
zend_ssa_range range;
|
||||
zend_class_entry *ce;
|
||||
unsigned int has_range : 1;
|
||||
unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
|
||||
unsigned int recursive : 1;
|
||||
unsigned int use_as_double : 1;
|
||||
unsigned int delayed_fetch_this : 1;
|
||||
unsigned int avoid_refcounting : 1;
|
||||
unsigned int guarded_reference : 1;
|
||||
unsigned int indirect_reference : 1; /* IS_INDIRECT returned by FETCH_DIM_W/FETCH_OBJ_W */
|
||||
} zend_ssa_var_info;
|
||||
|
||||
typedef struct _zend_ssa {
|
||||
zend_cfg cfg; /* control flow graph */
|
||||
int vars_count; /* number of SSA variables */
|
||||
int sccs; /* number of SCCs */
|
||||
zend_ssa_block *blocks; /* array of SSA blocks */
|
||||
zend_ssa_op *ops; /* array of SSA instructions */
|
||||
zend_ssa_var *vars; /* use/def chain of SSA variables */
|
||||
zend_ssa_var_info *var_info;
|
||||
} zend_ssa;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
|
||||
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
|
||||
|
||||
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
|
||||
void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
|
||||
void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
|
||||
void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
|
||||
void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
|
||||
void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types);
|
||||
|
||||
static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var)
|
||||
{
|
||||
ZEND_ASSERT(var->definition >= 0);
|
||||
ZEND_ASSERT(var->use_chain < 0);
|
||||
ZEND_ASSERT(!var->phi_use_chain);
|
||||
var->definition = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_result_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->result_def = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_op1_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->op1_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->op1_def = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_op2_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->op2_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->op2_def = -1;
|
||||
}
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
|
||||
{
|
||||
ssa_op += use;
|
||||
if (ssa_op->op1_use == var) {
|
||||
return ssa_op->op1_use_chain;
|
||||
} else if (ssa_op->op2_use == var) {
|
||||
return ssa_op->op2_use_chain;
|
||||
} else {
|
||||
return ssa_op->res_use_chain;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p)
|
||||
{
|
||||
if (p->pi >= 0) {
|
||||
return p->use_chains[0];
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
|
||||
if (p->sources[j] == var) {
|
||||
return p->use_chains[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static zend_always_inline bool zend_ssa_is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
|
||||
{
|
||||
if (opline->opcode == ZEND_ASSIGN
|
||||
|| opline->opcode == ZEND_UNSET_CV
|
||||
|| opline->opcode == ZEND_BIND_GLOBAL
|
||||
|| opline->opcode == ZEND_BIND_STATIC) {
|
||||
return ssa_op->op1_use == var && ssa_op->op2_use != var;
|
||||
}
|
||||
if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
|
||||
return ssa_op->op2_use == var && ssa_op->op1_use != var;
|
||||
}
|
||||
if (ssa_op->result_use == var
|
||||
&& opline->opcode != ZEND_ADD_ARRAY_ELEMENT
|
||||
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
|
||||
return ssa_op->op1_use != var && ssa_op->op2_use != var;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) {
|
||||
/* Rename def to use if possible. Mark variable as not defined otherwise. */
|
||||
if (ssa_op->op1_def >= 0) {
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->op1_def].definition = -1;
|
||||
ssa_op->op1_def = -1;
|
||||
}
|
||||
if (ssa_op->op2_def >= 0) {
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->op2_def].definition = -1;
|
||||
ssa_op->op2_def = -1;
|
||||
}
|
||||
if (ssa_op->result_def >= 0) {
|
||||
if (ssa_op->result_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->result_def].definition = -1;
|
||||
ssa_op->result_def = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_PHI_SOURCES(phi) \
|
||||
((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count))
|
||||
|
||||
/* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue"
|
||||
* and changing the use chain of the current element */
|
||||
#define FOREACH_USE(var, use) do { \
|
||||
int _var_num = (var) - ssa->vars, next; \
|
||||
for (use = (var)->use_chain; use >= 0; use = next) { \
|
||||
next = zend_ssa_next_use(ssa->ops, _var_num, use);
|
||||
#define FOREACH_USE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI_USE(var, phi) do { \
|
||||
int _var_num = (var) - ssa->vars; \
|
||||
zend_ssa_phi *next_phi; \
|
||||
for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \
|
||||
next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi);
|
||||
#define FOREACH_PHI_USE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI_SOURCE(phi, source) do { \
|
||||
zend_ssa_phi *_phi = (phi); \
|
||||
int _i, _end = NUM_PHI_SOURCES(phi); \
|
||||
for (_i = 0; _i < _end; _i++) { \
|
||||
ZEND_ASSERT(_phi->sources[_i] >= 0); \
|
||||
source = _phi->sources[_i];
|
||||
#define FOREACH_PHI_SOURCE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI(phi) do { \
|
||||
int _i; \
|
||||
for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
|
||||
phi = ssa->blocks[_i].phis; \
|
||||
for (; phi; phi = phi->next) {
|
||||
#define FOREACH_PHI_END() \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_BLOCK(block) do { \
|
||||
int _i; \
|
||||
for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
|
||||
(block) = &ssa->cfg.blocks[_i]; \
|
||||
if (!((block)->flags & ZEND_BB_REACHABLE)) { \
|
||||
continue; \
|
||||
}
|
||||
#define FOREACH_BLOCK_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Does not support "break" */
|
||||
#define FOREACH_INSTR_NUM(i) do { \
|
||||
zend_basic_block *_block; \
|
||||
FOREACH_BLOCK(_block) { \
|
||||
uint32_t _end = _block->start + _block->len; \
|
||||
for ((i) = _block->start; (i) < _end; (i)++) {
|
||||
#define FOREACH_INSTR_NUM_END() \
|
||||
} \
|
||||
} FOREACH_BLOCK_END(); \
|
||||
} while (0)
|
||||
|
||||
#endif /* ZEND_SSA_H */
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andy Wingo <wingo@igalia.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef _ZEND_WORKLIST_H_
|
||||
#define _ZEND_WORKLIST_H_
|
||||
|
||||
#include "zend_arena.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
typedef struct _zend_worklist_stack {
|
||||
int *buf;
|
||||
int len;
|
||||
int capacity;
|
||||
} zend_worklist_stack;
|
||||
|
||||
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
|
||||
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
|
||||
(s)->len = 0; \
|
||||
(s)->capacity = _len; \
|
||||
} while (0)
|
||||
|
||||
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
|
||||
free_alloca((s)->buf, use_heap)
|
||||
|
||||
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
|
||||
{
|
||||
ZEND_ASSERT(len >= 0);
|
||||
|
||||
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
|
||||
stack->len = 0;
|
||||
stack->capacity = len;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
|
||||
{
|
||||
ZEND_ASSERT(stack->len < stack->capacity);
|
||||
stack->buf[stack->len++] = i;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
|
||||
{
|
||||
ZEND_ASSERT(stack->len);
|
||||
return stack->buf[stack->len - 1];
|
||||
}
|
||||
|
||||
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
|
||||
{
|
||||
ZEND_ASSERT(stack->len);
|
||||
return stack->buf[--stack->len];
|
||||
}
|
||||
|
||||
typedef struct _zend_worklist {
|
||||
zend_bitset visited;
|
||||
zend_worklist_stack stack;
|
||||
} zend_worklist;
|
||||
|
||||
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
|
||||
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
|
||||
(w)->stack.len = 0; \
|
||||
(w)->stack.capacity = _len; \
|
||||
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
|
||||
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
|
||||
} while (0)
|
||||
|
||||
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
|
||||
free_alloca((w)->stack.buf, use_heap)
|
||||
|
||||
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
|
||||
{
|
||||
ZEND_ASSERT(len >= 0);
|
||||
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
|
||||
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
|
||||
}
|
||||
|
||||
static inline int zend_worklist_len(zend_worklist *worklist)
|
||||
{
|
||||
return worklist->stack.len;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_push(zend_worklist *worklist, int i)
|
||||
{
|
||||
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
|
||||
|
||||
if (zend_bitset_in(worklist->visited, i)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
zend_bitset_incl(worklist->visited, i);
|
||||
zend_worklist_stack_push(&worklist->stack, i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_peek(zend_worklist *worklist)
|
||||
{
|
||||
return zend_worklist_stack_peek(&worklist->stack);
|
||||
}
|
||||
|
||||
static inline int zend_worklist_pop(zend_worklist *worklist)
|
||||
{
|
||||
/* Does not clear visited flag */
|
||||
return zend_worklist_stack_pop(&worklist->stack);
|
||||
}
|
||||
|
||||
#endif /* _ZEND_WORKLIST_H_ */
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
# Zend Engine
|
||||
|
||||
## Zend memory manager
|
||||
|
||||
### General
|
||||
|
||||
The goal of the new memory manager (available since PHP 5.2) is to reduce memory
|
||||
allocation overhead and speedup memory management.
|
||||
|
||||
### Debugging
|
||||
|
||||
Normal:
|
||||
|
||||
```bash
|
||||
sapi/cli/php -r 'leak();'
|
||||
```
|
||||
|
||||
Zend MM disabled:
|
||||
|
||||
```bash
|
||||
USE_ZEND_ALLOC=0 valgrind --leak-check=full sapi/cli/php -r 'leak();'
|
||||
```
|
||||
|
||||
### Shared extensions
|
||||
|
||||
Since PHP 5.3.11 it is possible to prevent shared extensions from unloading so
|
||||
that valgrind can correctly track the memory leaks in shared extensions. For
|
||||
this there is the `ZEND_DONT_UNLOAD_MODULES` environment variable. If set, then
|
||||
`DL_UNLOAD()` is skipped during the shutdown of shared extensions.
|
||||
|
||||
## ZEND_VM
|
||||
|
||||
`ZEND_VM` architecture allows specializing opcode handlers according to
|
||||
`op_type` fields and using different execution methods (call threading, switch
|
||||
threading and direct threading). As a result ZE2 got more than 20% speedup on
|
||||
raw PHP code execution (with specialized executor and direct threading execution
|
||||
method). As in most PHP applications raw execution speed isn't the limiting
|
||||
factor but system calls and database calls are, your mileage with this patch
|
||||
will vary.
|
||||
|
||||
Most parts of the old zend_execute.c go into `zend_vm_def.h`. Here you can find
|
||||
opcode handlers and helpers. The typical opcode handler template looks like
|
||||
this:
|
||||
|
||||
```c
|
||||
ZEND_VM_HANDLER(<OPCODE-NUMBER>, <OPCODE>, <OP1_TYPES>, <OP2_TYPES>)
|
||||
{
|
||||
<HANDLER'S CODE>
|
||||
}
|
||||
```
|
||||
|
||||
`<OPCODE-NUMBER>` is a opcode number (0, 1, ...)
|
||||
`<OPCODE>` is an opcode name (ZEN_NOP, ZEND_ADD, :)
|
||||
`<OP1_TYPES>` and `<OP2_TYPES>` are masks for allowed operand op_types.
|
||||
Specializer will generate code only for defined combination of types. You can
|
||||
use any combination of the following op_types UNUSED, CONST, VAR, TMP and CV
|
||||
also you can use ANY mask to disable specialization according operand's op_type.
|
||||
`<HANDLER'S CODE>` is a handler's code itself. For most handlers it stills the
|
||||
same as in old `zend_execute.c`, but now it uses macros to access opcode
|
||||
operands and some internal executor data.
|
||||
|
||||
You can see the conformity of new macros to old code in the following list:
|
||||
|
||||
```c
|
||||
EXECUTE_DATA
|
||||
execute_data
|
||||
ZEND_VM_DISPATCH_TO_HANDLER(<OP>)
|
||||
return <OP>_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
||||
ZEND_VM_DISPATCH_TO_HELPER(<NAME>)
|
||||
return <NAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
||||
ZEND_VM_DISPATCH_TO_HELPER_EX(<NAME>,<PARAM>,<VAL>)
|
||||
return <NAME>(<VAL>, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
||||
ZEND_VM_CONTINUE()
|
||||
return 0
|
||||
ZEND_VM_NEXT_OPCODE()
|
||||
NEXT_OPCODE()
|
||||
ZEND_VM_SET_OPCODE(<TARGET>
|
||||
SET_OPCODE(<TARGET>
|
||||
ZEND_VM_INC_OPCODE()
|
||||
INC_OPCOD()
|
||||
ZEND_VM_RETURN_FROM_EXECUTE_LOOP()
|
||||
RETURN_FROM_EXECUTE_LOOP()
|
||||
ZEND_VM_C_LABEL(<LABEL>):
|
||||
<LABEL>:
|
||||
ZEND_VM_C_GOTO(<LABEL>)
|
||||
goto <LABEL>
|
||||
OP<X>_TYPE
|
||||
opline->op<X>.op_type
|
||||
GET_OP<X>_ZVAL_PTR(<TYPE>)
|
||||
get_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
||||
GET_OP<X>_ZVAL_PTR_PTR(<TYPE>)
|
||||
get_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
||||
GET_OP<X>_OBJ_ZVAL_PTR(<TYPE>)
|
||||
get_obj_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
||||
GET_OP<X>_OBJ_ZVAL_PTR_PTR(<TYPE>)
|
||||
get_obj_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
||||
IS_OP<X>_TMP_FREE()
|
||||
IS_TMP_FREE(free_op<X>)
|
||||
FREE_OP<X>()
|
||||
FREE_OP(free_op<X>)
|
||||
FREE_OP<X>_IF_VAR()
|
||||
FREE_VAR(free_op<X>)
|
||||
FREE_OP<X>_VAR_PTR()
|
||||
FREE_VAR_PTR(free_op<X>)
|
||||
```
|
||||
|
||||
Executor's helpers can be defined without parameters or with one parameter. This
|
||||
is done with the following constructs:
|
||||
|
||||
```c
|
||||
ZEND_VM_HELPER(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>)
|
||||
{
|
||||
<HELPER'S CODE>
|
||||
}
|
||||
|
||||
ZEND_VM_HELPER_EX(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>, <PARAM_SPEC>)
|
||||
{
|
||||
<HELPER'S CODE>
|
||||
}
|
||||
```
|
||||
|
||||
Executor's code is generated by PHP script zend_vm_gen.php it uses
|
||||
`zend_vm_def.h` and `zend_vm_execute.skl` as input and produces
|
||||
`zend_vm_opcodes.h` and `zend_vm_execute.h`. The first file is a list of opcode
|
||||
definitions. It is included from `zend_compile.h`. The second one is an executor
|
||||
code itself. It is included from `zend_execute.c`.
|
||||
|
||||
`zend_vm_gen.php` can produce different kind of executors. You can select
|
||||
different opcode threading model using `--with-vm-kind=CALL|SWITCH|GOTO`. You
|
||||
can disable opcode specialization using `--without-specializer`. You can include
|
||||
or exclude old executor together with specialized one using
|
||||
`--without-old-executor`. At last you can debug executor using original
|
||||
`zend_vm_def.h` or generated file `zend_vm_execute.h`. Debugging with original
|
||||
file requires `--with-lines` option. By default ZE2 uses the following command
|
||||
to generate executor:
|
||||
|
||||
```bash
|
||||
php zend_vm_gen.php --with-vm-kind=CALL
|
||||
```
|
||||
-388
@@ -1,388 +0,0 @@
|
||||
dnl This file contains Zend specific autoconf macros.
|
||||
|
||||
dnl
|
||||
dnl ZEND_CHECK_FLOAT_PRECISION
|
||||
dnl
|
||||
dnl x87 floating point internal precision control checks
|
||||
dnl See: http://wiki.php.net/rfc/rounding
|
||||
dnl
|
||||
AC_DEFUN([ZEND_CHECK_FLOAT_PRECISION],[
|
||||
AC_MSG_CHECKING([for usable _FPU_SETCW])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <fpu_control.h>
|
||||
]],[[
|
||||
fpu_control_t fpu_oldcw, fpu_cw;
|
||||
volatile double result;
|
||||
double a = 2877.0;
|
||||
volatile double b = 1000000.0;
|
||||
|
||||
_FPU_GETCW(fpu_oldcw);
|
||||
fpu_cw = (fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE;
|
||||
_FPU_SETCW(fpu_cw);
|
||||
result = a / b;
|
||||
_FPU_SETCW(fpu_oldcw);
|
||||
]])],[ac_cfp_have__fpu_setcw=yes],[ac_cfp_have__fpu_setcw=no])
|
||||
if test "$ac_cfp_have__fpu_setcw" = "yes" ; then
|
||||
AC_DEFINE(HAVE__FPU_SETCW, 1, [whether _FPU_SETCW is present and usable])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for usable fpsetprec])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <machine/ieeefp.h>
|
||||
]],[[
|
||||
fp_prec_t fpu_oldprec;
|
||||
volatile double result;
|
||||
double a = 2877.0;
|
||||
volatile double b = 1000000.0;
|
||||
|
||||
fpu_oldprec = fpgetprec();
|
||||
fpsetprec(FP_PD);
|
||||
result = a / b;
|
||||
fpsetprec(fpu_oldprec);
|
||||
]])], [ac_cfp_have_fpsetprec=yes], [ac_cfp_have_fpsetprec=no])
|
||||
if test "$ac_cfp_have_fpsetprec" = "yes" ; then
|
||||
AC_DEFINE(HAVE_FPSETPREC, 1, [whether fpsetprec is present and usable])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for usable _controlfp])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <float.h>
|
||||
]],[[
|
||||
unsigned int fpu_oldcw;
|
||||
volatile double result;
|
||||
double a = 2877.0;
|
||||
volatile double b = 1000000.0;
|
||||
|
||||
fpu_oldcw = _controlfp(0, 0);
|
||||
_controlfp(_PC_53, _MCW_PC);
|
||||
result = a / b;
|
||||
_controlfp(fpu_oldcw, _MCW_PC);
|
||||
]])], [ac_cfp_have__controlfp=yes], [ac_cfp_have__controlfp=no])
|
||||
if test "$ac_cfp_have__controlfp" = "yes" ; then
|
||||
AC_DEFINE(HAVE__CONTROLFP, 1, [whether _controlfp is present usable])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for usable _controlfp_s])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <float.h>
|
||||
]],[[
|
||||
unsigned int fpu_oldcw, fpu_cw;
|
||||
volatile double result;
|
||||
double a = 2877.0;
|
||||
volatile double b = 1000000.0;
|
||||
|
||||
_controlfp_s(&fpu_cw, 0, 0);
|
||||
fpu_oldcw = fpu_cw;
|
||||
_controlfp_s(&fpu_cw, _PC_53, _MCW_PC);
|
||||
result = a / b;
|
||||
_controlfp_s(&fpu_cw, fpu_oldcw, _MCW_PC);
|
||||
]])], [ac_cfp_have__controlfp_s=yes], [ac_cfp_have__controlfp_s=no])
|
||||
if test "$ac_cfp_have__controlfp_s" = "yes" ; then
|
||||
AC_DEFINE(HAVE__CONTROLFP_S, 1, [whether _controlfp_s is present and usable])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([whether FPU control word can be manipulated by inline assembler])
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
/* nothing */
|
||||
]],[[
|
||||
unsigned int oldcw, cw;
|
||||
volatile double result;
|
||||
double a = 2877.0;
|
||||
volatile double b = 1000000.0;
|
||||
|
||||
__asm__ __volatile__ ("fnstcw %0" : "=m" (*&oldcw));
|
||||
cw = (oldcw & ~0x0 & ~0x300) | 0x200;
|
||||
__asm__ __volatile__ ("fldcw %0" : : "m" (*&cw));
|
||||
|
||||
result = a / b;
|
||||
|
||||
__asm__ __volatile__ ("fldcw %0" : : "m" (*&oldcw));
|
||||
]])], [ac_cfp_have_fpu_inline_asm_x86=yes], [ac_cfp_have_fpu_inline_asm_x86=no])
|
||||
if test "$ac_cfp_have_fpu_inline_asm_x86" = "yes" ; then
|
||||
AC_DEFINE(HAVE_FPU_INLINE_ASM_X86, 1, [whether FPU control word can be manipulated by inline assembler])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl LIBZEND_BASIC_CHECKS
|
||||
dnl
|
||||
dnl Basic checks specific for the Zend engine library.
|
||||
dnl
|
||||
AC_DEFUN([LIBZEND_BASIC_CHECKS],[
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
|
||||
AC_CHECK_HEADERS([cpuid.h])
|
||||
|
||||
dnl
|
||||
dnl LIBZEND_DLSYM_CHECK
|
||||
dnl
|
||||
dnl Ugly hack to check if dlsym() requires a leading underscore in symbol name.
|
||||
dnl
|
||||
AC_DEFUN([LIBZEND_DLSYM_CHECK],[
|
||||
AC_MSG_CHECKING([whether dlsym() requires a leading underscore in symbol names])
|
||||
_LT_AC_TRY_DLOPEN_SELF([
|
||||
AC_MSG_RESULT(no)
|
||||
], [
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(DLSYM_NEEDS_UNDERSCORE, 1, [Define if dlsym() requires a leading underscore in symbol names. ])
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
], [])
|
||||
])
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS(getpid kill sigsetjmp)
|
||||
|
||||
ZEND_CHECK_FLOAT_PRECISION
|
||||
|
||||
dnl Test whether double cast to long preserves least significant bits.
|
||||
AC_MSG_CHECKING(whether double cast to long preserves least significant bits)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (sizeof(long) == 4) {
|
||||
double d = (double) LONG_MIN * LONG_MIN + 2e9;
|
||||
|
||||
if ((long) d == 2e9 && (long) -d == -2e9) {
|
||||
return 0;
|
||||
}
|
||||
} else if (sizeof(long) == 8) {
|
||||
double correct = 18e18 - ((double) LONG_MIN * -2); /* Subtract ULONG_MAX + 1 */
|
||||
|
||||
if ((long) 18e18 == correct) { /* On 64-bit, only check between LONG_MAX and ULONG_MAX */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
]])], [
|
||||
AC_DEFINE([ZEND_DVAL_TO_LVAL_CAST_OK], 1, [Define if double cast to long preserves least significant bits])
|
||||
AC_MSG_RESULT(yes)
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
], [
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
dnl
|
||||
dnl LIBZEND_OTHER_CHECKS
|
||||
dnl
|
||||
AC_DEFUN([LIBZEND_OTHER_CHECKS],[
|
||||
|
||||
AC_MSG_CHECKING(whether to enable thread-safety)
|
||||
AC_MSG_RESULT($ZEND_ZTS)
|
||||
|
||||
AC_MSG_CHECKING(whether to enable Zend debugging)
|
||||
AC_MSG_RESULT($ZEND_DEBUG)
|
||||
|
||||
if test "$ZEND_DEBUG" = "yes"; then
|
||||
AC_DEFINE(ZEND_DEBUG,1,[ ])
|
||||
echo " $CFLAGS" | grep ' -g' >/dev/null || DEBUG_CFLAGS="-g"
|
||||
if test "$CFLAGS" = "-g -O2"; then
|
||||
CFLAGS=-g
|
||||
fi
|
||||
else
|
||||
AC_DEFINE(ZEND_DEBUG,0,[ ])
|
||||
fi
|
||||
|
||||
test -n "$GCC" && CFLAGS="-Wall -Wextra -Wno-strict-aliasing -Wno-unused-parameter -Wno-sign-compare $CFLAGS"
|
||||
dnl Check if compiler supports -Wno-clobbered (only GCC)
|
||||
AX_CHECK_COMPILE_FLAG([-Wno-clobbered], CFLAGS="-Wno-clobbered $CFLAGS", , [-Werror])
|
||||
dnl Check for support for implicit fallthrough level 1, also add after previous CFLAGS as level 3 is enabled in -Wextra
|
||||
AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough=1], CFLAGS="$CFLAGS -Wimplicit-fallthrough=1", , [-Werror])
|
||||
AX_CHECK_COMPILE_FLAG([-Wduplicated-cond], CFLAGS="-Wduplicated-cond $CFLAGS", , [-Werror])
|
||||
AX_CHECK_COMPILE_FLAG([-Wlogical-op], CFLAGS="-Wlogical-op $CFLAGS", , [-Werror])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-truncation], CFLAGS="-Wformat-truncation $CFLAGS", , [-Werror])
|
||||
AX_CHECK_COMPILE_FLAG([-Wstrict-prototypes], CFLAGS="-Wstrict-prototypes $CFLAGS", , [-Werror])
|
||||
AX_CHECK_COMPILE_FLAG([-fno-common], CFLAGS="-fno-common $CFLAGS", , [-Werror])
|
||||
|
||||
test -n "$DEBUG_CFLAGS" && CFLAGS="$CFLAGS $DEBUG_CFLAGS"
|
||||
|
||||
if test "$ZEND_ZTS" = "yes"; then
|
||||
AC_DEFINE(ZTS,1,[ ])
|
||||
CFLAGS="$CFLAGS -DZTS"
|
||||
fi
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
AC_MSG_CHECKING(target system is Darwin)
|
||||
if echo "$target" | grep "darwin" > /dev/null; then
|
||||
AC_DEFINE([DARWIN], 1, [Define if the target system is darwin])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
dnl Test and set the alignment define for ZEND_MM. This also does the
|
||||
dnl logarithmic test for ZEND_MM.
|
||||
AC_MSG_CHECKING(for MM alignment and log values)
|
||||
|
||||
AC_RUN_IFELSE([AC_LANG_SOURCE([[
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef union _mm_align_test {
|
||||
void *ptr;
|
||||
double dbl;
|
||||
long lng;
|
||||
} mm_align_test;
|
||||
|
||||
#if (defined (__GNUC__) && __GNUC__ >= 2)
|
||||
#define ZEND_MM_ALIGNMENT (__alignof__ (mm_align_test))
|
||||
#else
|
||||
#define ZEND_MM_ALIGNMENT (sizeof(mm_align_test))
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
int i = ZEND_MM_ALIGNMENT;
|
||||
int zeros = 0;
|
||||
FILE *fp;
|
||||
|
||||
while (i & ~0x1) {
|
||||
zeros++;
|
||||
i = i >> 1;
|
||||
}
|
||||
|
||||
fp = fopen("conftest.zend", "w");
|
||||
fprintf(fp, "%d %d\n", ZEND_MM_ALIGNMENT, zeros);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
]])], [
|
||||
LIBZEND_MM_ALIGN=`cat conftest.zend | cut -d ' ' -f 1`
|
||||
LIBZEND_MM_ALIGN_LOG2=`cat conftest.zend | cut -d ' ' -f 2`
|
||||
AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT, $LIBZEND_MM_ALIGN, [ ])
|
||||
AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT_LOG2, $LIBZEND_MM_ALIGN_LOG2, [ ])
|
||||
], [], [
|
||||
dnl Cross compilation needs something here.
|
||||
AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT, 8, [ ])
|
||||
AC_DEFINE_UNQUOTED(ZEND_MM_ALIGNMENT_LOG2, 3, [ ])
|
||||
])
|
||||
|
||||
AC_MSG_RESULT(done)
|
||||
|
||||
AC_CHECK_FUNCS(mremap)
|
||||
|
||||
AC_ARG_ENABLE([zend-signals],
|
||||
[AS_HELP_STRING([--disable-zend-signals],
|
||||
[whether to enable zend signal handling])],
|
||||
[ZEND_SIGNALS=$enableval],
|
||||
[ZEND_SIGNALS=yes])
|
||||
|
||||
AC_CHECK_FUNCS([sigaction], [], [
|
||||
ZEND_SIGNALS=no
|
||||
])
|
||||
if test "$ZEND_SIGNALS" = "yes"; then
|
||||
AC_DEFINE(ZEND_SIGNALS, 1, [Use zend signal handling])
|
||||
CFLAGS="$CFLAGS -DZEND_SIGNALS"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(whether to enable zend signal handling)
|
||||
AC_MSG_RESULT($ZEND_SIGNALS)
|
||||
|
||||
])
|
||||
|
||||
AC_MSG_CHECKING(whether /dev/urandom exists)
|
||||
if test -r "/dev/urandom" && test -c "/dev/urandom"; then
|
||||
AC_DEFINE([HAVE_DEV_URANDOM], 1, [Define if the target system has /dev/urandom device])
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([gcc-global-regs],
|
||||
[AS_HELP_STRING([--disable-gcc-global-regs],
|
||||
[whether to enable GCC global register variables])],
|
||||
[ZEND_GCC_GLOBAL_REGS=$enableval],
|
||||
[ZEND_GCC_GLOBAL_REGS=yes])
|
||||
|
||||
AC_MSG_CHECKING(for global register variables support)
|
||||
if test "$ZEND_GCC_GLOBAL_REGS" != "no"; then
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#if defined(__GNUC__)
|
||||
# define ZEND_GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
|
||||
#else
|
||||
# define ZEND_GCC_VERSION 0
|
||||
#endif
|
||||
#if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386)
|
||||
# define ZEND_VM_FP_GLOBAL_REG "%esi"
|
||||
# define ZEND_VM_IP_GLOBAL_REG "%edi"
|
||||
#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__x86_64__)
|
||||
# define ZEND_VM_FP_GLOBAL_REG "%r14"
|
||||
# define ZEND_VM_IP_GLOBAL_REG "%r15"
|
||||
#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__powerpc64__)
|
||||
# define ZEND_VM_FP_GLOBAL_REG "r28"
|
||||
# define ZEND_VM_IP_GLOBAL_REG "r29"
|
||||
#elif defined(__IBMC__) && ZEND_GCC_VERSION >= 4002 && defined(__powerpc64__)
|
||||
# define ZEND_VM_FP_GLOBAL_REG "r28"
|
||||
# define ZEND_VM_IP_GLOBAL_REG "r29"
|
||||
#elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__aarch64__)
|
||||
# define ZEND_VM_FP_GLOBAL_REG "x27"
|
||||
# define ZEND_VM_IP_GLOBAL_REG "x28"
|
||||
#else
|
||||
# error "global register variables are not supported"
|
||||
#endif
|
||||
typedef int (*opcode_handler_t)(void);
|
||||
register void *FP __asm__(ZEND_VM_FP_GLOBAL_REG);
|
||||
register const opcode_handler_t *IP __asm__(ZEND_VM_IP_GLOBAL_REG);
|
||||
int emu(const opcode_handler_t *ip, void *fp) {
|
||||
const opcode_handler_t *orig_ip = IP;
|
||||
void *orig_fp = FP;
|
||||
IP = ip;
|
||||
FP = fp;
|
||||
while ((*ip)());
|
||||
FP = orig_fp;
|
||||
IP = orig_ip;
|
||||
}
|
||||
]], [[
|
||||
]])], [
|
||||
ZEND_GCC_GLOBAL_REGS=yes
|
||||
], [
|
||||
ZEND_GCC_GLOBAL_REGS=no
|
||||
])
|
||||
fi
|
||||
if test "$ZEND_GCC_GLOBAL_REGS" = "yes"; then
|
||||
AC_DEFINE([HAVE_GCC_GLOBAL_REGS], 1, [Define if the target system has support for global register variables])
|
||||
else
|
||||
HAVE_GCC_GLOBAL_REGS=no
|
||||
fi
|
||||
AC_MSG_RESULT($ZEND_GCC_GLOBAL_REGS)
|
||||
|
||||
dnl Check whether __cpuid_count is available.
|
||||
AC_CACHE_CHECK(whether __cpuid_count is available, ac_cv_cpuid_count_available, [
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <cpuid.h>
|
||||
]], [[
|
||||
unsigned eax, ebx, ecx, edx;
|
||||
__cpuid_count(0, 0, eax, ebx, ecx, edx);
|
||||
]])], [
|
||||
ac_cv_cpuid_count_available=yes
|
||||
], [
|
||||
ac_cv_cpuid_count_available=no
|
||||
])])
|
||||
if test "$ac_cv_cpuid_count_available" = "yes"; then
|
||||
AC_DEFINE([HAVE_CPUID_COUNT], 1, [whether __cpuid_count is available])
|
||||
fi
|
||||
@@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
Copyright Edward Nevill + Oliver Kowalke 2015
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d8 | d9 | d10 | d11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d12 | d13 | d14 | d15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x19 | x20 | x21 | x22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x23 | x24 | x25 | x26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x27 | x28 | FP | LR | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0xa0| 0xa4| 0xa8| 0xac| | | *
|
||||
* ------------------------------------------------- *
|
||||
* | PC | align | | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "jump_arm64_aapcs_elf_gas.S"
|
||||
.text
|
||||
.align 2
|
||||
.global jump_fcontext
|
||||
.type jump_fcontext, %function
|
||||
jump_fcontext:
|
||||
# prepare stack for GP + FPU
|
||||
sub sp, sp, #0xb0
|
||||
|
||||
# save d8 - d15
|
||||
stp d8, d9, [sp, #0x00]
|
||||
stp d10, d11, [sp, #0x10]
|
||||
stp d12, d13, [sp, #0x20]
|
||||
stp d14, d15, [sp, #0x30]
|
||||
|
||||
# save x19-x30
|
||||
stp x19, x20, [sp, #0x40]
|
||||
stp x21, x22, [sp, #0x50]
|
||||
stp x23, x24, [sp, #0x60]
|
||||
stp x25, x26, [sp, #0x70]
|
||||
stp x27, x28, [sp, #0x80]
|
||||
stp x29, x30, [sp, #0x90]
|
||||
|
||||
# save LR as PC
|
||||
str x30, [sp, #0xa0]
|
||||
|
||||
# store RSP (pointing to context-data) in X0
|
||||
mov x4, sp
|
||||
|
||||
# restore RSP (pointing to context-data) from X1
|
||||
mov sp, x0
|
||||
|
||||
# load d8 - d15
|
||||
ldp d8, d9, [sp, #0x00]
|
||||
ldp d10, d11, [sp, #0x10]
|
||||
ldp d12, d13, [sp, #0x20]
|
||||
ldp d14, d15, [sp, #0x30]
|
||||
|
||||
# load x19-x30
|
||||
ldp x19, x20, [sp, #0x40]
|
||||
ldp x21, x22, [sp, #0x50]
|
||||
ldp x23, x24, [sp, #0x60]
|
||||
ldp x25, x26, [sp, #0x70]
|
||||
ldp x27, x28, [sp, #0x80]
|
||||
ldp x29, x30, [sp, #0x90]
|
||||
|
||||
# return transfer_t from jump
|
||||
# pass transfer_t as first arg in context function
|
||||
# X0 == FCTX, X1 == DATA
|
||||
mov x0, x4
|
||||
|
||||
# load pc
|
||||
ldr x4, [sp, #0xa0]
|
||||
|
||||
# restore stack from GP + FPU
|
||||
add sp, sp, #0xb0
|
||||
|
||||
ret x4
|
||||
.size jump_fcontext,.-jump_fcontext
|
||||
# Mark that we don't need executable stack.
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
Copyright Edward Nevill + Oliver Kowalke 2015
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d8 | d9 | d10 | d11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d12 | d13 | d14 | d15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x19 | x20 | x21 | x22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x23 | x24 | x25 | x26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x27 | x28 | FP | LR | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0xa0| 0xa4| 0xa8| 0xac| | | *
|
||||
* ------------------------------------------------- *
|
||||
* | PC | align | | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _jump_fcontext
|
||||
.balign 16
|
||||
_jump_fcontext:
|
||||
; prepare stack for GP + FPU
|
||||
sub sp, sp, #0xb0
|
||||
|
||||
; save d8 - d15
|
||||
stp d8, d9, [sp, #0x00]
|
||||
stp d10, d11, [sp, #0x10]
|
||||
stp d12, d13, [sp, #0x20]
|
||||
stp d14, d15, [sp, #0x30]
|
||||
|
||||
; save x19-x30
|
||||
stp x19, x20, [sp, #0x40]
|
||||
stp x21, x22, [sp, #0x50]
|
||||
stp x23, x24, [sp, #0x60]
|
||||
stp x25, x26, [sp, #0x70]
|
||||
stp x27, x28, [sp, #0x80]
|
||||
stp fp, lr, [sp, #0x90]
|
||||
|
||||
; save LR as PC
|
||||
str lr, [sp, #0xa0]
|
||||
|
||||
; store RSP (pointing to context-data) in X0
|
||||
mov x4, sp
|
||||
|
||||
; restore RSP (pointing to context-data) from X1
|
||||
mov sp, x0
|
||||
|
||||
; load d8 - d15
|
||||
ldp d8, d9, [sp, #0x00]
|
||||
ldp d10, d11, [sp, #0x10]
|
||||
ldp d12, d13, [sp, #0x20]
|
||||
ldp d14, d15, [sp, #0x30]
|
||||
|
||||
; load x19-x30
|
||||
ldp x19, x20, [sp, #0x40]
|
||||
ldp x21, x22, [sp, #0x50]
|
||||
ldp x23, x24, [sp, #0x60]
|
||||
ldp x25, x26, [sp, #0x70]
|
||||
ldp x27, x28, [sp, #0x80]
|
||||
ldp fp, lr, [sp, #0x90]
|
||||
|
||||
; return transfer_t from jump
|
||||
; pass transfer_t as first arg in context function
|
||||
; X0 == FCTX, X1 == DATA
|
||||
mov x0, x4
|
||||
|
||||
; load pc
|
||||
ldr x4, [sp, #0xa0]
|
||||
|
||||
; restore stack from GP + FPU
|
||||
add sp, sp, #0xb0
|
||||
|
||||
ret x4
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | v8 | lr | pc | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "jump_arm_aapcs_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.align 2
|
||||
.type jump_fcontext,%function
|
||||
.syntax unified
|
||||
jump_fcontext:
|
||||
@ save LR as PC
|
||||
push {lr}
|
||||
@ save hidden,V1-V8,LR
|
||||
push {a1,v1-v8,lr}
|
||||
|
||||
@ prepare stack for FPU
|
||||
sub sp, sp, #64
|
||||
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
||||
@ save S16-S31
|
||||
vstmia sp, {d8-d15}
|
||||
#endif
|
||||
|
||||
@ store RSP (pointing to context-data) in A1
|
||||
mov a1, sp
|
||||
|
||||
@ restore RSP (pointing to context-data) from A2
|
||||
mov sp, a2
|
||||
|
||||
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
||||
@ restore S16-S31
|
||||
vldmia sp, {d8-d15}
|
||||
#endif
|
||||
@ prepare stack for FPU
|
||||
add sp, sp, #64
|
||||
|
||||
@ restore hidden,V1-V8,LR
|
||||
pop {a4,v1-v8,lr}
|
||||
|
||||
@ return transfer_t from jump
|
||||
str a1, [a4, #0]
|
||||
str a3, [a4, #4]
|
||||
@ pass transfer_t as first arg in context function
|
||||
@ A1 == FCTX, A2 == DATA
|
||||
mov a2, a3
|
||||
|
||||
@ restore PC
|
||||
pop {pc}
|
||||
.size jump_fcontext,.-jump_fcontext
|
||||
|
||||
@ Mark that we don't need executable stack.
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | sjlj|hiddn| v1 | v2 | v3 | v4 | v5 | v6 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | v7 | v8 | lr | pc | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _jump_fcontext
|
||||
.align 2
|
||||
_jump_fcontext:
|
||||
@ save LR as PC
|
||||
push {lr}
|
||||
@ save hidden,V1-V8,LR
|
||||
push {a1,v1-v8,lr}
|
||||
|
||||
@ locate TLS to save/restore SjLj handler
|
||||
mrc p15, 0, v2, c13, c0, #3
|
||||
bic v2, v2, #3
|
||||
|
||||
@ load TLS[__PTK_LIBC_DYLD_Unwind_SjLj_Key]
|
||||
ldr v1, [v2, #8]
|
||||
@ save SjLj handler
|
||||
push {v1}
|
||||
|
||||
@ prepare stack for FPU
|
||||
sub sp, sp, #64
|
||||
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
||||
@ save S16-S31
|
||||
vstmia sp, {d8-d15}
|
||||
#endif
|
||||
|
||||
@ store RSP (pointing to context-data) in A1
|
||||
mov a1, sp
|
||||
|
||||
@ restore RSP (pointing to context-data) from A2
|
||||
mov sp, a2
|
||||
|
||||
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
||||
@ restore S16-S31
|
||||
vldmia sp, {d8-d15}
|
||||
#endif
|
||||
@ prepare stack for FPU
|
||||
add sp, sp, #64
|
||||
|
||||
@ r#estore SjLj handler
|
||||
pop {v1}
|
||||
@ store SjLj handler in TLS
|
||||
str v1, [v2, #8]
|
||||
|
||||
@ restore hidden,V1-V8,LR
|
||||
pop {a4,v1-v8,lr}
|
||||
|
||||
@ return transfer_t from jump
|
||||
str a1, [a4, #0]
|
||||
str a3, [a4, #4]
|
||||
@ pass transfer_t as first arg in context function
|
||||
@ A1 == FCTX, A2 == DATA
|
||||
mov a2, a3
|
||||
|
||||
@ restore PC
|
||||
pop {pc}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
Copyright Sergue E. Leontiev 2013.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Stub file for universal binary
|
||||
|
||||
#if defined(__i386__)
|
||||
#include "jump_i386_sysv_macho_gas.S"
|
||||
#elif defined(__x86_64__)
|
||||
#include "jump_x86_64_sysv_macho_gas.S"
|
||||
#elif defined(__ppc__)
|
||||
#include "jump_ppc32_sysv_macho_gas.S"
|
||||
#elif defined(__ppc64__)
|
||||
#include "jump_ppc64_sysv_macho_gas.S"
|
||||
#elif defined(__arm__)
|
||||
#include "jump_arm_aapcs_macho_gas.S"
|
||||
#elif defined(__arm64__)
|
||||
#include "jump_arm64_aapcs_macho_gas.S"
|
||||
#else
|
||||
#error "No arch's"
|
||||
#endif
|
||||
@@ -1,116 +0,0 @@
|
||||
|
||||
; Copyright Oliver Kowalke 2009.
|
||||
; Distributed under the Boost Software License, Version 1.0.
|
||||
; (See accompanying file LICENSE_1_0.txt or copy at
|
||||
; http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | ESI | EBX | EBP | EIP | to | data | EH NXT |SEH HNDLR|
|
||||
; ---------------------------------------------------------------------------------
|
||||
|
||||
.386
|
||||
.XMM
|
||||
.model flat, c
|
||||
.code
|
||||
|
||||
jump_fcontext PROC BOOST_CONTEXT_EXPORT
|
||||
; prepare stack
|
||||
lea esp, [esp-02ch]
|
||||
|
||||
IFNDEF BOOST_USE_TSX
|
||||
; save MMX control- and status-word
|
||||
stmxcsr [esp]
|
||||
; save x87 control-word
|
||||
fnstcw [esp+04h]
|
||||
ENDIF
|
||||
|
||||
assume fs:nothing
|
||||
; load NT_TIB into ECX
|
||||
mov edx, fs:[018h]
|
||||
assume fs:error
|
||||
; load fiber local storage
|
||||
mov eax, [edx+010h]
|
||||
mov [esp+08h], eax
|
||||
; load current deallocation stack
|
||||
mov eax, [edx+0e0ch]
|
||||
mov [esp+0ch], eax
|
||||
; load current stack limit
|
||||
mov eax, [edx+08h]
|
||||
mov [esp+010h], eax
|
||||
; load current stack base
|
||||
mov eax, [edx+04h]
|
||||
mov [esp+014h], eax
|
||||
; load current SEH exception list
|
||||
mov eax, [edx]
|
||||
mov [esp+018h], eax
|
||||
|
||||
mov [esp+01ch], edi ; save EDI
|
||||
mov [esp+020h], esi ; save ESI
|
||||
mov [esp+024h], ebx ; save EBX
|
||||
mov [esp+028h], ebp ; save EBP
|
||||
|
||||
; store ESP (pointing to context-data) in EAX
|
||||
mov eax, esp
|
||||
|
||||
; firstarg of jump_fcontext() == fcontext to jump to
|
||||
mov ecx, [esp+030h]
|
||||
|
||||
; restore ESP (pointing to context-data) from ECX
|
||||
mov esp, ecx
|
||||
|
||||
IFNDEF BOOST_USE_TSX
|
||||
; restore MMX control- and status-word
|
||||
ldmxcsr [esp]
|
||||
; restore x87 control-word
|
||||
fldcw [esp+04h]
|
||||
ENDIF
|
||||
|
||||
assume fs:nothing
|
||||
; load NT_TIB into EDX
|
||||
mov edx, fs:[018h]
|
||||
assume fs:error
|
||||
; restore fiber local storage
|
||||
mov ecx, [esp+08h]
|
||||
mov [edx+010h], ecx
|
||||
; restore current deallocation stack
|
||||
mov ecx, [esp+0ch]
|
||||
mov [edx+0e0ch], ecx
|
||||
; restore current stack limit
|
||||
mov ecx, [esp+010h]
|
||||
mov [edx+08h], ecx
|
||||
; restore current stack base
|
||||
mov ecx, [esp+014h]
|
||||
mov [edx+04h], ecx
|
||||
; restore current SEH exception list
|
||||
mov ecx, [esp+018h]
|
||||
mov [edx], ecx
|
||||
|
||||
mov ecx, [esp+02ch] ; restore EIP
|
||||
|
||||
mov edi, [esp+01ch] ; restore EDI
|
||||
mov esi, [esp+020h] ; restore ESI
|
||||
mov ebx, [esp+024h] ; restore EBX
|
||||
mov ebp, [esp+028h] ; restore EBP
|
||||
|
||||
; prepare stack
|
||||
lea esp, [esp+030h]
|
||||
|
||||
; return transfer_t
|
||||
; FCTX == EAX, DATA == EDX
|
||||
mov edx, [eax+034h]
|
||||
|
||||
; jump to context
|
||||
jmp ecx
|
||||
jump_fcontext ENDP
|
||||
END
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | to | data | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.file "jump_i386_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.align 2
|
||||
.type jump_fcontext,@function
|
||||
jump_fcontext:
|
||||
leal -0x18(%esp), %esp /* prepare stack */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
stmxcsr (%esp) /* save MMX control- and status-word */
|
||||
fnstcw 0x4(%esp) /* save x87 control-word */
|
||||
#endif
|
||||
|
||||
movl %edi, 0x8(%esp) /* save EDI */
|
||||
movl %esi, 0xc(%esp) /* save ESI */
|
||||
movl %ebx, 0x10(%esp) /* save EBX */
|
||||
movl %ebp, 0x14(%esp) /* save EBP */
|
||||
|
||||
/* store ESP (pointing to context-data) in ECX */
|
||||
movl %esp, %ecx
|
||||
|
||||
/* first arg of jump_fcontext() == fcontext to jump to */
|
||||
movl 0x20(%esp), %eax
|
||||
|
||||
/* second arg of jump_fcontext() == data to be transferred */
|
||||
movl 0x24(%esp), %edx
|
||||
|
||||
/* restore ESP (pointing to context-data) from EAX */
|
||||
movl %eax, %esp
|
||||
|
||||
/* address of returned transport_t */
|
||||
movl 0x1c(%esp), %eax
|
||||
/* return parent fcontext_t */
|
||||
movl %ecx, (%eax)
|
||||
/* return data */
|
||||
movl %edx, 0x4(%eax)
|
||||
|
||||
movl 0x18(%esp), %ecx /* restore EIP */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
ldmxcsr (%esp) /* restore MMX control- and status-word */
|
||||
fldcw 0x4(%esp) /* restore x87 control-word */
|
||||
#endif
|
||||
|
||||
movl 0x8(%esp), %edi /* restore EDI */
|
||||
movl 0xc(%esp), %esi /* restore ESI */
|
||||
movl 0x10(%esp), %ebx /* restore EBX */
|
||||
movl 0x14(%esp), %ebp /* restore EBP */
|
||||
|
||||
leal 0x20(%esp), %esp /* prepare stack */
|
||||
|
||||
/* jump to context */
|
||||
jmp *%ecx
|
||||
.size jump_fcontext,.-jump_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | to | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | data | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.text
|
||||
.globl _jump_fcontext
|
||||
.align 2
|
||||
_jump_fcontext:
|
||||
leal -0x18(%esp), %esp /* prepare stack */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
stmxcsr (%esp) /* save MMX control- and status-word */
|
||||
fnstcw 0x4(%esp) /* save x87 control-word */
|
||||
#endif
|
||||
|
||||
movl %edi, 0x8(%esp) /* save EDI */
|
||||
movl %esi, 0xc(%esp) /* save ESI */
|
||||
movl %ebx, 0x10(%esp) /* save EBX */
|
||||
movl %ebp, 0x14(%esp) /* save EBP */
|
||||
|
||||
/* store ESP (pointing to context-data) in ECX */
|
||||
movl %esp, %ecx
|
||||
|
||||
/* first arg of jump_fcontext() == fcontext to jump to */
|
||||
movl 0x1c(%esp), %eax
|
||||
|
||||
/* second arg of jump_fcontext() == data to be transferred */
|
||||
movl 0x20(%esp), %edx
|
||||
|
||||
/* restore ESP (pointing to context-data) from EAX */
|
||||
movl %eax, %esp
|
||||
|
||||
/* return parent fcontext_t */
|
||||
movl %ecx, %eax
|
||||
/* returned data is stored in EDX */
|
||||
|
||||
movl 0x18(%esp), %ecx /* restore EIP */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
ldmxcsr (%esp) /* restore MMX control- and status-word */
|
||||
fldcw 0x4(%esp) /* restore x87 control-word */
|
||||
#endif
|
||||
|
||||
movl 0x8(%esp), %edi /* restore EDI */
|
||||
movl 0xc(%esp), %esi /* restore ESI */
|
||||
movl 0x10(%esp), %ebx /* restore EBX */
|
||||
movl 0x14(%esp), %ebp /* restore EBP */
|
||||
|
||||
leal 0x1c(%esp), %esp /* prepare stack */
|
||||
|
||||
/* jump to context */
|
||||
jmp *%ecx
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F20 | F22 | F24 | F26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F28 | F30 | S0 | S1 | S2 | S3 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S4 | S5 | S6 | S7 | FP |hiddn| RA | PC | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | ABI ARGS | GP | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
* *****************************************************/
|
||||
|
||||
.file "jump_mips32_o32_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.align 2
|
||||
.type jump_fcontext,@function
|
||||
.ent jump_fcontext
|
||||
jump_fcontext:
|
||||
# reserve space on stack
|
||||
addiu $sp, $sp, -96
|
||||
|
||||
sw $s0, 48($sp) # save S0
|
||||
sw $s1, 52($sp) # save S1
|
||||
sw $s2, 56($sp) # save S2
|
||||
sw $s3, 60($sp) # save S3
|
||||
sw $s4, 64($sp) # save S4
|
||||
sw $s5, 68($sp) # save S5
|
||||
sw $s6, 72($sp) # save S6
|
||||
sw $s7, 76($sp) # save S7
|
||||
sw $fp, 80($sp) # save FP
|
||||
sw $a0, 84($sp) # save hidden, address of returned transfer_t
|
||||
sw $ra, 88($sp) # save RA
|
||||
sw $ra, 92($sp) # save RA as PC
|
||||
|
||||
#if defined(__mips_hard_float)
|
||||
s.d $f20, ($sp) # save F20
|
||||
s.d $f22, 8($sp) # save F22
|
||||
s.d $f24, 16($sp) # save F24
|
||||
s.d $f26, 24($sp) # save F26
|
||||
s.d $f28, 32($sp) # save F28
|
||||
s.d $f30, 40($sp) # save F30
|
||||
#endif
|
||||
|
||||
# store SP (pointing to context-data) in A0
|
||||
move $a0, $sp
|
||||
|
||||
# restore SP (pointing to context-data) from A1
|
||||
move $sp, $a1
|
||||
|
||||
#if defined(__mips_hard_float)
|
||||
l.d $f20, ($sp) # restore F20
|
||||
l.d $f22, 8($sp) # restore F22
|
||||
l.d $f24, 16($sp) # restore F24
|
||||
l.d $f26, 24($sp) # restore F26
|
||||
l.d $f28, 32($sp) # restore F28
|
||||
l.d $f30, 40($sp) # restore F30
|
||||
#endif
|
||||
|
||||
lw $s0, 48($sp) # restore S0
|
||||
lw $s1, 52($sp) # restore S1
|
||||
lw $s2, 56($sp) # restore S2
|
||||
lw $s3, 60($sp) # restore S3
|
||||
lw $s4, 64($sp) # restore S4
|
||||
lw $s5, 68($sp) # restore S5
|
||||
lw $s6, 72($sp) # restore S6
|
||||
lw $s7, 76($sp) # restore S7
|
||||
lw $fp, 80($sp) # restore FP
|
||||
lw $v0, 84($sp) # restore hidden, address of returned transfer_t
|
||||
lw $ra, 88($sp) # restore RA
|
||||
|
||||
# load PC
|
||||
lw $t9, 92($sp)
|
||||
|
||||
# adjust stack
|
||||
addiu $sp, $sp, 96
|
||||
|
||||
# return transfer_t from jump
|
||||
sw $a0, ($v0) # fctx of transfer_t
|
||||
sw $a2, 4($v0) # data of transfer_t
|
||||
# pass transfer_t as first arg in context function
|
||||
# A0 == fctx, A1 == data
|
||||
move $a1, $a2
|
||||
|
||||
# jump to context
|
||||
jr $t9
|
||||
.end jump_fcontext
|
||||
.size jump_fcontext, .-jump_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
Copyright Jiaxun Yang 2018.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 8 | 16 | 24 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F24 | F25 | F26 | F27 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 40 | 48 | 56 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F28 | F29 | F30 | F31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 72 | 80 | 88 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S0 | S1 | S2 | S3 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S4 | S5 | S6 | S7 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | FP | GP | RA | PC | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
* *****************************************************/
|
||||
|
||||
.file "jump_mips64_n64_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.align 3
|
||||
.type jump_fcontext,@function
|
||||
.ent jump_fcontext
|
||||
jump_fcontext:
|
||||
# reserve space on stack
|
||||
daddiu $sp, $sp, -160
|
||||
|
||||
sd $s0, 64($sp) # save S0
|
||||
sd $s1, 72($sp) # save S1
|
||||
sd $s2, 80($sp) # save S2
|
||||
sd $s3, 88($sp) # save S3
|
||||
sd $s4, 96($sp) # save S4
|
||||
sd $s5, 104($sp) # save S5
|
||||
sd $s6, 112($sp) # save S6
|
||||
sd $s7, 120($sp) # save S7
|
||||
sd $fp, 128($sp) # save FP
|
||||
sd $ra, 144($sp) # save RA
|
||||
sd $ra, 152($sp) # save RA as PC
|
||||
|
||||
#if defined(__mips_hard_float)
|
||||
s.d $f24, 0($sp) # save F24
|
||||
s.d $f25, 8($sp) # save F25
|
||||
s.d $f26, 16($sp) # save F26
|
||||
s.d $f27, 24($sp) # save F27
|
||||
s.d $f28, 32($sp) # save F28
|
||||
s.d $f29, 40($sp) # save F29
|
||||
s.d $f30, 48($sp) # save F30
|
||||
s.d $f31, 56($sp) # save F31
|
||||
#endif
|
||||
|
||||
# store SP (pointing to old context-data) in v0 as return
|
||||
move $v0, $sp
|
||||
|
||||
# get SP (pointing to new context-data) from a0 param
|
||||
move $sp, $a0
|
||||
|
||||
#if defined(__mips_hard_float)
|
||||
l.d $f24, 0($sp) # restore F24
|
||||
l.d $f25, 8($sp) # restore F25
|
||||
l.d $f26, 16($sp) # restore F26
|
||||
l.d $f27, 24($sp) # restore F27
|
||||
l.d $f28, 32($sp) # restore F28
|
||||
l.d $f29, 40($sp) # restore F29
|
||||
l.d $f30, 48($sp) # restore F30
|
||||
l.d $f31, 56($sp) # restore F31
|
||||
#endif
|
||||
|
||||
ld $s0, 64($sp) # restore S0
|
||||
ld $s1, 72($sp) # restore S1
|
||||
ld $s2, 80($sp) # restore S2
|
||||
ld $s3, 88($sp) # restore S3
|
||||
ld $s4, 96($sp) # restore S4
|
||||
ld $s5, 104($sp) # restore S5
|
||||
ld $s6, 112($sp) # restore S6
|
||||
ld $s7, 120($sp) # restore S7
|
||||
ld $fp, 128($sp) # restore FP
|
||||
ld $ra, 144($sp) # restore RAa
|
||||
|
||||
# load PC
|
||||
ld $t9, 152($sp)
|
||||
|
||||
# adjust stack
|
||||
daddiu $sp, $sp, 160
|
||||
|
||||
move $a0, $v0 # move old sp from v0 to a0 as param
|
||||
move $v1, $a1 # move *data from a1 to v1 as return
|
||||
|
||||
# jump to context
|
||||
jr $t9
|
||||
.end jump_fcontext
|
||||
.size jump_fcontext, .-jump_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* |bchai|hiddn| fpscr | PC | CR | R14 | R15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R16 | R17 | R18 | R19 | R20 | R21 | R22 | R23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F14 | F15 | F16 | F17 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F18 | F19 | F20 | F21 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F22 | F23 | F24 | F25 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F26 | F27 | F28 | F29 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------|------------ *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | *
|
||||
* ------------------------|------------ *
|
||||
* | F30 | F31 |bchai| LR | *
|
||||
* ------------------------|------------ *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "jump_ppc32_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.align 2
|
||||
.type jump_fcontext,@function
|
||||
jump_fcontext:
|
||||
# Linux: jump_fcontext( hidden transfer_t * R3, R4, R5)
|
||||
# Other: transfer_t R3:R4 = jump_fcontext( R3, R4)
|
||||
|
||||
mflr %r0 # return address from LR
|
||||
mffs %f0 # FPSCR
|
||||
mfcr %r8 # condition register
|
||||
|
||||
stwu %r1, -240(%r1) # allocate stack space, R1 % 16 == 0
|
||||
stw %r0, 244(%r1) # save LR in caller's frame
|
||||
|
||||
#ifdef __linux__
|
||||
stw %r3, 4(%r1) # hidden pointer
|
||||
#endif
|
||||
|
||||
stfd %f0, 8(%r1) # FPSCR
|
||||
stw %r0, 16(%r1) # LR as PC
|
||||
stw %r8, 20(%r1) # CR
|
||||
|
||||
# Save registers R14 to R31.
|
||||
# Don't change R2, the thread-local storage pointer.
|
||||
# Don't change R13, the small data pointer.
|
||||
stw %r14, 24(%r1)
|
||||
stw %r15, 28(%r1)
|
||||
stw %r16, 32(%r1)
|
||||
stw %r17, 36(%r1)
|
||||
stw %r18, 40(%r1)
|
||||
stw %r19, 44(%r1)
|
||||
stw %r20, 48(%r1)
|
||||
stw %r21, 52(%r1)
|
||||
stw %r22, 56(%r1)
|
||||
stw %r23, 60(%r1)
|
||||
stw %r24, 64(%r1)
|
||||
stw %r25, 68(%r1)
|
||||
stw %r26, 72(%r1)
|
||||
stw %r27, 76(%r1)
|
||||
stw %r28, 80(%r1)
|
||||
stw %r29, 84(%r1)
|
||||
stw %r30, 88(%r1)
|
||||
stw %r31, 92(%r1)
|
||||
|
||||
# Save registers F14 to F31 in slots with 8-byte alignment.
|
||||
# 4-byte alignment may stall the pipeline of some processors.
|
||||
# Less than 4 may cause alignment traps.
|
||||
stfd %f14, 96(%r1)
|
||||
stfd %f15, 104(%r1)
|
||||
stfd %f16, 112(%r1)
|
||||
stfd %f17, 120(%r1)
|
||||
stfd %f18, 128(%r1)
|
||||
stfd %f19, 136(%r1)
|
||||
stfd %f20, 144(%r1)
|
||||
stfd %f21, 152(%r1)
|
||||
stfd %f22, 160(%r1)
|
||||
stfd %f23, 168(%r1)
|
||||
stfd %f24, 176(%r1)
|
||||
stfd %f25, 184(%r1)
|
||||
stfd %f26, 192(%r1)
|
||||
stfd %f27, 200(%r1)
|
||||
stfd %f28, 208(%r1)
|
||||
stfd %f29, 216(%r1)
|
||||
stfd %f30, 224(%r1)
|
||||
stfd %f31, 232(%r1)
|
||||
|
||||
# store RSP (pointing to context-data) in R7/R6
|
||||
# restore RSP (pointing to context-data) from R4/R3
|
||||
#ifdef __linux__
|
||||
mr %r7, %r1
|
||||
mr %r1, %r4
|
||||
lwz %r3, 4(%r1) # hidden pointer
|
||||
#else
|
||||
mr %r6, %r1
|
||||
mr %r1, %r3
|
||||
#endif
|
||||
|
||||
lfd %f0, 8(%r1) # FPSCR
|
||||
lwz %r0, 16(%r1) # PC
|
||||
lwz %r8, 20(%r1) # CR
|
||||
|
||||
mtfsf 0xff, %f0 # restore FPSCR
|
||||
mtctr %r0 # load CTR with PC
|
||||
mtcr %r8 # restore CR
|
||||
|
||||
# restore R14 to R31
|
||||
lwz %r14, 24(%r1)
|
||||
lwz %r15, 28(%r1)
|
||||
lwz %r16, 32(%r1)
|
||||
lwz %r17, 36(%r1)
|
||||
lwz %r18, 40(%r1)
|
||||
lwz %r19, 44(%r1)
|
||||
lwz %r20, 48(%r1)
|
||||
lwz %r21, 52(%r1)
|
||||
lwz %r22, 56(%r1)
|
||||
lwz %r23, 60(%r1)
|
||||
lwz %r24, 64(%r1)
|
||||
lwz %r25, 68(%r1)
|
||||
lwz %r26, 72(%r1)
|
||||
lwz %r27, 76(%r1)
|
||||
lwz %r28, 80(%r1)
|
||||
lwz %r29, 84(%r1)
|
||||
lwz %r30, 88(%r1)
|
||||
lwz %r31, 92(%r1)
|
||||
|
||||
# restore F14 to F31
|
||||
lfd %f14, 96(%r1)
|
||||
lfd %f15, 104(%r1)
|
||||
lfd %f16, 112(%r1)
|
||||
lfd %f17, 120(%r1)
|
||||
lfd %f18, 128(%r1)
|
||||
lfd %f19, 136(%r1)
|
||||
lfd %f20, 144(%r1)
|
||||
lfd %f21, 152(%r1)
|
||||
lfd %f22, 160(%r1)
|
||||
lfd %f23, 168(%r1)
|
||||
lfd %f24, 176(%r1)
|
||||
lfd %f25, 184(%r1)
|
||||
lfd %f26, 192(%r1)
|
||||
lfd %f27, 200(%r1)
|
||||
lfd %f28, 208(%r1)
|
||||
lfd %f29, 216(%r1)
|
||||
lfd %f30, 224(%r1)
|
||||
lfd %f31, 232(%r1)
|
||||
|
||||
# restore LR from caller's frame
|
||||
lwz %r0, 244(%r1)
|
||||
mtlr %r0
|
||||
|
||||
# adjust stack
|
||||
addi %r1, %r1, 240
|
||||
|
||||
# return transfer_t
|
||||
#ifdef __linux__
|
||||
stw %r7, 0(%r3)
|
||||
stw %r5, 4(%r3)
|
||||
#else
|
||||
mr %r3, %r6
|
||||
# %r4, %r4
|
||||
#endif
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
.size jump_fcontext, .-jump_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F14 | F15 | F16 | F17 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F18 | F19 | F20 | F21 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F22 | F23 | F24 | F25 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F26 | F27 | F28 | F29 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F30 | F31 | fpscr | R13 | R14 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R31 |hiddn| CR | LR | PC |bchai|linkr| FCTX| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 256 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _jump_fcontext
|
||||
.align 2
|
||||
_jump_fcontext:
|
||||
; reserve space on stack
|
||||
subi r1, r1, 244
|
||||
|
||||
stfd f14, 0(r1) # save F14
|
||||
stfd f15, 8(r1) # save F15
|
||||
stfd f16, 16(r1) # save F16
|
||||
stfd f17, 24(r1) # save F17
|
||||
stfd f18, 32(r1) # save F18
|
||||
stfd f19, 40(r1) # save F19
|
||||
stfd f20, 48(r1) # save F20
|
||||
stfd f21, 56(r1) # save F21
|
||||
stfd f22, 64(r1) # save F22
|
||||
stfd f23, 72(r1) # save F23
|
||||
stfd f24, 80(r1) # save F24
|
||||
stfd f25, 88(r1) # save F25
|
||||
stfd f26, 96(r1) # save F26
|
||||
stfd f27, 104(r1) # save F27
|
||||
stfd f28, 112(r1) # save F28
|
||||
stfd f29, 120(r1) # save F29
|
||||
stfd f30, 128(r1) # save F30
|
||||
stfd f31, 136(r1) # save F31
|
||||
mffs f0 # load FPSCR
|
||||
stfd f0, 144(r1) # save FPSCR
|
||||
|
||||
stw r13, 152(r1) # save R13
|
||||
stw r14, 156(r1) # save R14
|
||||
stw r15, 160(r1) # save R15
|
||||
stw r16, 164(r1) # save R16
|
||||
stw r17, 168(r1) # save R17
|
||||
stw r18, 172(r1) # save R18
|
||||
stw r19, 176(r1) # save R19
|
||||
stw r20, 180(r1) # save R20
|
||||
stw r21, 184(r1) # save R21
|
||||
stw r22, 188(r1) # save R22
|
||||
stw r23, 192(r1) # save R23
|
||||
stw r24, 196(r1) # save R24
|
||||
stw r25, 200(r1) # save R25
|
||||
stw r26, 204(r1) # save R26
|
||||
stw r27, 208(r1) # save R27
|
||||
stw r28, 212(r1) # save R28
|
||||
stw r29, 216(r1) # save R29
|
||||
stw r30, 220(r1) # save R30
|
||||
stw r31, 224(r1) # save R31
|
||||
stw r3, 228(r1) # save hidden
|
||||
|
||||
# save CR
|
||||
mfcr r0
|
||||
stw r0, 232(r1)
|
||||
# save LR
|
||||
mflr r0
|
||||
stw r0, 236(r1)
|
||||
# save LR as PC
|
||||
stw r0, 240(r1)
|
||||
|
||||
# store RSP (pointing to context-data) in R6
|
||||
mr r6, r1
|
||||
|
||||
# restore RSP (pointing to context-data) from R4
|
||||
mr r1, r4
|
||||
|
||||
lfd f14, 0(r1) # restore F14
|
||||
lfd f15, 8(r1) # restore F15
|
||||
lfd f16, 16(r1) # restore F16
|
||||
lfd f17, 24(r1) # restore F17
|
||||
lfd f18, 32(r1) # restore F18
|
||||
lfd f19, 40(r1) # restore F19
|
||||
lfd f20, 48(r1) # restore F20
|
||||
lfd f21, 56(r1) # restore F21
|
||||
lfd f22, 64(r1) # restore F22
|
||||
lfd f23, 72(r1) # restore F23
|
||||
lfd f24, 80(r1) # restore F24
|
||||
lfd f25, 88(r1) # restore F25
|
||||
lfd f26, 96(r1) # restore F26
|
||||
lfd f27, 104(r1) # restore F27
|
||||
lfd f28, 112(r1) # restore F28
|
||||
lfd f29, 120(r1) # restore F29
|
||||
lfd f30, 128(r1) # restore F30
|
||||
lfd f31, 136(r1) # restore F31
|
||||
lfd f0, 144(r1) # load FPSCR
|
||||
mtfsf 0xff, f0 # restore FPSCR
|
||||
|
||||
lwz r13, 152(r1) # restore R13
|
||||
lwz r14, 156(r1) # restore R14
|
||||
lwz r15, 160(r1) # restore R15
|
||||
lwz r16, 164(r1) # restore R16
|
||||
lwz r17, 168(r1) # restore R17
|
||||
lwz r18, 172(r1) # restore R18
|
||||
lwz r19, 176(r1) # restore R19
|
||||
lwz r20, 180(r1) # restore R20
|
||||
lwz r21, 184(r1) # restore R21
|
||||
lwz r22, 188(r1) # restore R22
|
||||
lwz r23, 192(r1) # restore R23
|
||||
lwz r24, 196(r1) # restore R24
|
||||
lwz r25, 200(r1) # restore R25
|
||||
lwz r26, 204(r1) # restore R26
|
||||
lwz r27, 208(r1) # restore R27
|
||||
lwz r28, 212(r1) # restore R28
|
||||
lwz r29, 216(r1) # restore R29
|
||||
lwz r30, 220(r1) # restore R30
|
||||
lwz r31, 224(r1) # restore R31
|
||||
lwz r3, 228(r1) # restore hidden
|
||||
|
||||
# restore CR
|
||||
lwz r0, 232(r1)
|
||||
mtcr r0
|
||||
# restore LR
|
||||
lwz r0, 236(r1)
|
||||
mtlr r0
|
||||
# load PC
|
||||
lwz r0, 240(r1)
|
||||
# restore CTR
|
||||
mtctr r0
|
||||
|
||||
# adjust stack
|
||||
addi r1, r1, 244
|
||||
|
||||
# return transfer_t
|
||||
stw r6, 0(r3)
|
||||
stw r5, 4(r3)
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC | R14 | R15 | R16 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R17 | R18 | R19 | R20 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R21 | R22 | R23 | R24 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R25 | R26 | R27 | R28 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R29 | R30 | R31 | hidden | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | CR | LR | PC | back-chain| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | cr saved | lr saved | compiler | linker | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC saved | FCTX | DATA | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "jump_ppc64_sysv_elf_gas.S"
|
||||
.globl jump_fcontext
|
||||
#if _CALL_ELF == 2
|
||||
.text
|
||||
.align 2
|
||||
jump_fcontext:
|
||||
addis %r2, %r12, .TOC.-jump_fcontext@ha
|
||||
addi %r2, %r2, .TOC.-jump_fcontext@l
|
||||
.localentry jump_fcontext, . - jump_fcontext
|
||||
#else
|
||||
.section ".opd","aw"
|
||||
.align 3
|
||||
jump_fcontext:
|
||||
# ifdef _CALL_LINUX
|
||||
.quad .L.jump_fcontext,.TOC.@tocbase,0
|
||||
.type jump_fcontext,@function
|
||||
.text
|
||||
.align 2
|
||||
.L.jump_fcontext:
|
||||
# else
|
||||
.hidden .jump_fcontext
|
||||
.globl .jump_fcontext
|
||||
.quad .jump_fcontext,.TOC.@tocbase,0
|
||||
.size jump_fcontext,24
|
||||
.type .jump_fcontext,@function
|
||||
.text
|
||||
.align 2
|
||||
.jump_fcontext:
|
||||
# endif
|
||||
#endif
|
||||
# reserve space on stack
|
||||
subi %r1, %r1, 184
|
||||
|
||||
#if _CALL_ELF != 2
|
||||
std %r2, 0(%r1) # save TOC
|
||||
#endif
|
||||
std %r14, 8(%r1) # save R14
|
||||
std %r15, 16(%r1) # save R15
|
||||
std %r16, 24(%r1) # save R16
|
||||
std %r17, 32(%r1) # save R17
|
||||
std %r18, 40(%r1) # save R18
|
||||
std %r19, 48(%r1) # save R19
|
||||
std %r20, 56(%r1) # save R20
|
||||
std %r21, 64(%r1) # save R21
|
||||
std %r22, 72(%r1) # save R22
|
||||
std %r23, 80(%r1) # save R23
|
||||
std %r24, 88(%r1) # save R24
|
||||
std %r25, 96(%r1) # save R25
|
||||
std %r26, 104(%r1) # save R26
|
||||
std %r27, 112(%r1) # save R27
|
||||
std %r28, 120(%r1) # save R28
|
||||
std %r29, 128(%r1) # save R29
|
||||
std %r30, 136(%r1) # save R30
|
||||
std %r31, 144(%r1) # save R31
|
||||
#if _CALL_ELF != 2
|
||||
std %r3, 152(%r1) # save hidden
|
||||
#endif
|
||||
|
||||
# save CR
|
||||
mfcr %r0
|
||||
std %r0, 160(%r1)
|
||||
# save LR
|
||||
mflr %r0
|
||||
std %r0, 168(%r1)
|
||||
# save LR as PC
|
||||
std %r0, 176(%r1)
|
||||
|
||||
# store RSP (pointing to context-data) in R6
|
||||
mr %r6, %r1
|
||||
|
||||
#if _CALL_ELF == 2
|
||||
# restore RSP (pointing to context-data) from R3
|
||||
mr %r1, %r3
|
||||
#else
|
||||
# restore RSP (pointing to context-data) from R4
|
||||
mr %r1, %r4
|
||||
|
||||
ld %r2, 0(%r1) # restore TOC
|
||||
#endif
|
||||
ld %r14, 8(%r1) # restore R14
|
||||
ld %r15, 16(%r1) # restore R15
|
||||
ld %r16, 24(%r1) # restore R16
|
||||
ld %r17, 32(%r1) # restore R17
|
||||
ld %r18, 40(%r1) # restore R18
|
||||
ld %r19, 48(%r1) # restore R19
|
||||
ld %r20, 56(%r1) # restore R20
|
||||
ld %r21, 64(%r1) # restore R21
|
||||
ld %r22, 72(%r1) # restore R22
|
||||
ld %r23, 80(%r1) # restore R23
|
||||
ld %r24, 88(%r1) # restore R24
|
||||
ld %r25, 96(%r1) # restore R25
|
||||
ld %r26, 104(%r1) # restore R26
|
||||
ld %r27, 112(%r1) # restore R27
|
||||
ld %r28, 120(%r1) # restore R28
|
||||
ld %r29, 128(%r1) # restore R29
|
||||
ld %r30, 136(%r1) # restore R30
|
||||
ld %r31, 144(%r1) # restore R31
|
||||
#if _CALL_ELF != 2
|
||||
ld %r3, 152(%r1) # restore hidden
|
||||
#endif
|
||||
|
||||
# restore CR
|
||||
ld %r0, 160(%r1)
|
||||
mtcr %r0
|
||||
# restore LR
|
||||
ld %r0, 168(%r1)
|
||||
mtlr %r0
|
||||
|
||||
# load PC
|
||||
ld %r12, 176(%r1)
|
||||
# restore CTR
|
||||
mtctr %r12
|
||||
|
||||
# adjust stack
|
||||
addi %r1, %r1, 184
|
||||
|
||||
#if _CALL_ELF == 2
|
||||
# copy transfer_t into transfer_fn arg registers
|
||||
mr %r3, %r6
|
||||
# arg pointer already in %r4
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
.size jump_fcontext, .-jump_fcontext
|
||||
#else
|
||||
# zero in r3 indicates first jump to context-function
|
||||
cmpdi %r3, 0
|
||||
beq use_entry_arg
|
||||
|
||||
# return transfer_t
|
||||
std %r6, 0(%r3)
|
||||
std %r5, 8(%r3)
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
|
||||
use_entry_arg:
|
||||
# copy transfer_t into transfer_fn arg registers
|
||||
mr %r3, %r6
|
||||
mr %r4, %r5
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
# ifdef _CALL_LINUX
|
||||
.size .jump_fcontext, .-.L.jump_fcontext
|
||||
# else
|
||||
.size .jump_fcontext, .-.jump_fcontext
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC | R14 | R15 | R16 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R17 | R18 | R19 | R20 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R21 | R22 | R23 | R24 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R25 | R26 | R27 | R28 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R29 | R30 | R31 | hidden | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | CR | LR | PC | back-chain| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | cr saved | lr saved | compiler | linker | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC saved | FCTX | DATA | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.align 2
|
||||
.globl _jump_fcontext
|
||||
|
||||
_jump_fcontext:
|
||||
; reserve space on stack
|
||||
subi r1, r1, 184
|
||||
|
||||
std r14, 8(r1) ; save R14
|
||||
std r15, 16(r1) ; save R15
|
||||
std r16, 24(r1) ; save R16
|
||||
std r17, 32(r1) ; save R17
|
||||
std r18, 40(r1) ; save R18
|
||||
std r19, 48(r1) ; save R19
|
||||
std r20, 56(r1) ; save R20
|
||||
std r21, 64(r1) ; save R21
|
||||
std r22, 72(r1) ; save R22
|
||||
std r23, 80(r1) ; save R23
|
||||
std r24, 88(r1) ; save R24
|
||||
std r25, 96(r1) ; save R25
|
||||
std r26, 104(r1) ; save R26
|
||||
std r27, 112(r1) ; save R27
|
||||
std r28, 120(r1) ; save R28
|
||||
std r29, 128(r1) ; save R29
|
||||
std r30, 136(r1) ; save R30
|
||||
std r31, 144(r1) ; save R31
|
||||
std r3, 152(r1) ; save hidden
|
||||
|
||||
; save CR
|
||||
mfcr r0
|
||||
std r0, 160(r1)
|
||||
; save LR
|
||||
mflr r0
|
||||
std r0, 168(r1)
|
||||
; save LR as PC
|
||||
std r0, 176(r1)
|
||||
|
||||
; store RSP (pointing to context-data) in R6
|
||||
mr r6, r1
|
||||
|
||||
; restore RSP (pointing to context-data) from R4
|
||||
mr r1, r4
|
||||
|
||||
ld r14, 8(r1) ; restore R14
|
||||
ld r15, 16(r1) ; restore R15
|
||||
ld r16, 24(r1) ; restore R16
|
||||
ld r17, 32(r1) ; restore R17
|
||||
ld r18, 40(r1) ; restore R18
|
||||
ld r19, 48(r1) ; restore R19
|
||||
ld r20, 56(r1) ; restore R20
|
||||
ld r21, 64(r1) ; restore R21
|
||||
ld r22, 72(r1) ; restore R22
|
||||
ld r23, 80(r1) ; restore R23
|
||||
ld r24, 88(r1) ; restore R24
|
||||
ld r25, 96(r1) ; restore R25
|
||||
ld r26, 104(r1) ; restore R26
|
||||
ld r27, 112(r1) ; restore R27
|
||||
ld r28, 120(r1) ; restore R28
|
||||
ld r29, 128(r1) ; restore R29
|
||||
ld r30, 136(r1) ; restore R30
|
||||
ld r31, 144(r1) ; restore R31
|
||||
ld r3, 152(r1) ; restore hidden
|
||||
|
||||
; restore CR
|
||||
ld r0, 160(r1)
|
||||
mtcr r0
|
||||
; restore LR
|
||||
ld r0, 168(r1)
|
||||
mtlr r0
|
||||
|
||||
; load PC
|
||||
ld r12, 176(r1)
|
||||
# restore CTR
|
||||
mtctr r12
|
||||
|
||||
# adjust stack
|
||||
addi r1, r1, 184
|
||||
|
||||
# zero in r3 indicates first jump to context-function
|
||||
cmpdi r3, 0
|
||||
beq use_entry_arg
|
||||
|
||||
# return transfer_t
|
||||
std r6, 0(r3)
|
||||
std r5, 8(r3)
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
|
||||
use_entry_arg:
|
||||
# copy transfer_t into transfer_fn arg registers
|
||||
mr r3, r6
|
||||
mr r4, r5
|
||||
|
||||
# jump to context
|
||||
bctr
|
||||
@@ -1,156 +0,0 @@
|
||||
/*******************************************************
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 8 | 16 | 24 | *
|
||||
* ------------------------------------------------- *
|
||||
* | t.fctx | t.data | r2 | r6 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 40 | 48 | 56 | *
|
||||
* ------------------------------------------------- *
|
||||
* | r7 | r8 | r9 | r10 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 72 | 80 | 88 | *
|
||||
* ------------------------------------------------- *
|
||||
* | r11 | r12 | r13 | r14 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 104 | 112 | 120 | *
|
||||
* ------------------------------------------------- *
|
||||
* | f8 | f9 | f10 | f11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 136 | 144 | 152 | *
|
||||
* ------------------------------------------------- *
|
||||
* | f12 | f13 | f14 | f15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 168 | 176 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | fpc | pc | | | *
|
||||
* ------------------------------------------------- *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.align 8
|
||||
.global jump_fcontext
|
||||
.type jump_fcontext, @function
|
||||
|
||||
#define ARG_OFFSET 0
|
||||
#define GR_OFFSET 16
|
||||
#define FP_OFFSET 96
|
||||
#define FPC_OFFSET 160
|
||||
#define PC_OFFSET 168
|
||||
#define CONTEXT_SIZE 176
|
||||
|
||||
#define REG_SAVE_AREA_SIZE 160
|
||||
|
||||
/*
|
||||
|
||||
typedef void* fcontext_t;
|
||||
|
||||
struct transfer_t {
|
||||
fcontext_t fctx;
|
||||
void * data;
|
||||
};
|
||||
|
||||
transfer_t jump_fcontext( fcontext_t const to,
|
||||
void * data);
|
||||
|
||||
Incoming args
|
||||
r2 - Hidden argument to the location where the return transfer_t needs to be returned
|
||||
r3 - Context we want to switch to
|
||||
r4 - Data pointer
|
||||
|
||||
*/
|
||||
|
||||
jump_fcontext:
|
||||
.machine "z10"
|
||||
/* Reserve stack space to store the current context. */
|
||||
aghi %r15,-CONTEXT_SIZE
|
||||
|
||||
/* Save the argument register holding the location of the return value. */
|
||||
stg %r2,GR_OFFSET(%r15)
|
||||
|
||||
/* Save the call-saved general purpose registers. */
|
||||
stmg %r6,%r14,GR_OFFSET+8(%r15)
|
||||
|
||||
/* Save call-saved floating point registers. */
|
||||
std %f8,FP_OFFSET(%r15)
|
||||
std %f9,FP_OFFSET+8(%r15)
|
||||
std %f10,FP_OFFSET+16(%r15)
|
||||
std %f11,FP_OFFSET+24(%r15)
|
||||
std %f12,FP_OFFSET+32(%r15)
|
||||
std %f13,FP_OFFSET+40(%r15)
|
||||
std %f14,FP_OFFSET+48(%r15)
|
||||
std %f15,FP_OFFSET+56(%r15)
|
||||
|
||||
/* Save the return address as current pc. */
|
||||
stg %r14,PC_OFFSET(%r15)
|
||||
|
||||
/* Save the floating point control register. */
|
||||
stfpc FPC_OFFSET(%r15)
|
||||
|
||||
/* Backup the stack pointer pointing to the old context-data into r1. */
|
||||
lgr %r1,%r15
|
||||
|
||||
/* Load the new context pointer as stack pointer. */
|
||||
lgr %r15,%r3
|
||||
|
||||
/* Restore the call-saved GPRs from the new context. */
|
||||
lmg %r6,%r14,GR_OFFSET+8(%r15)
|
||||
|
||||
/* Restore call-saved floating point registers. */
|
||||
ld %f8,FP_OFFSET(%r15)
|
||||
ld %f9,FP_OFFSET+8(%r15)
|
||||
ld %f10,FP_OFFSET+16(%r15)
|
||||
ld %f11,FP_OFFSET+24(%r15)
|
||||
ld %f12,FP_OFFSET+32(%r15)
|
||||
ld %f13,FP_OFFSET+40(%r15)
|
||||
ld %f14,FP_OFFSET+48(%r15)
|
||||
ld %f15,FP_OFFSET+56(%r15)
|
||||
|
||||
/* Load the floating point control register. */
|
||||
lfpc FPC_OFFSET(%r15)
|
||||
|
||||
/* Restore PC - the location where we will jump to at the end. */
|
||||
lg %r5,PC_OFFSET(%r15)
|
||||
|
||||
ltg %r2,GR_OFFSET(%r15)
|
||||
jnz use_return_slot
|
||||
|
||||
/* We restore a make_fcontext context. Use the function
|
||||
argument slot in the context we just saved and allocate the
|
||||
register save area for the target function. */
|
||||
la %r2,ARG_OFFSET(%r1)
|
||||
aghi %r15,-REG_SAVE_AREA_SIZE
|
||||
|
||||
use_return_slot:
|
||||
/* Save the two fields in transfer_t. When calling a
|
||||
make_fcontext function this becomes the function argument of
|
||||
the target function, otherwise it will be the return value of
|
||||
jump_fcontext. */
|
||||
stg %r1,0(%r2)
|
||||
stg %r4,8(%r2)
|
||||
|
||||
/* Free the restored context. */
|
||||
aghi %r15,CONTEXT_SIZE
|
||||
|
||||
/* Jump to the PC loaded from the new context. */
|
||||
br %r5
|
||||
|
||||
|
||||
.size jump_fcontext,.-jump_fcontext
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,205 +0,0 @@
|
||||
|
||||
; Copyright Oliver Kowalke 2009.
|
||||
; Distributed under the Boost Software License, Version 1.0.
|
||||
; (See accompanying file LICENSE_1_0.txt or copy at
|
||||
; http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xe40 | 0x44 | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x60 | 0x64 | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 32 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x80 | 0x84 | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xa0 | 0xa4 | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | fc_mxcsr|fc_x87_cw| <alignment> | fbr_strg | fc_dealloc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xc0 | 0xc4 | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | limit | base | R12 | R13 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xe0 | 0xe4 | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | R14 | R15 | RDI | RSI |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x100 | 0x104 | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | RBX | RBP | hidden | RIP |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x120 | 0x124 | 0x128 | 0x12c | 0x130 | 0x134 | 0x138 | 0x13c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | parameter area |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x140 | 0x144 | 0x148 | 0x14c | 0x150 | 0x154 | 0x158 | 0x15c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | FCTX | DATA | |
|
||||
; ----------------------------------------------------------------------------------
|
||||
|
||||
.code
|
||||
|
||||
jump_fcontext PROC BOOST_CONTEXT_EXPORT FRAME
|
||||
.endprolog
|
||||
|
||||
; prepare stack
|
||||
lea rsp, [rsp-0118h]
|
||||
|
||||
IFNDEF BOOST_USE_TSX
|
||||
; save XMM storage
|
||||
movaps [rsp], xmm6
|
||||
movaps [rsp+010h], xmm7
|
||||
movaps [rsp+020h], xmm8
|
||||
movaps [rsp+030h], xmm9
|
||||
movaps [rsp+040h], xmm10
|
||||
movaps [rsp+050h], xmm11
|
||||
movaps [rsp+060h], xmm12
|
||||
movaps [rsp+070h], xmm13
|
||||
movaps [rsp+080h], xmm14
|
||||
movaps [rsp+090h], xmm15
|
||||
; save MMX control- and status-word
|
||||
stmxcsr [rsp+0a0h]
|
||||
; save x87 control-word
|
||||
fnstcw [rsp+0a4h]
|
||||
ENDIF
|
||||
|
||||
; load NT_TIB
|
||||
mov r10, gs:[030h]
|
||||
; save fiber local storage
|
||||
mov rax, [r10+020h]
|
||||
mov [rsp+0b0h], rax
|
||||
; save current deallocation stack
|
||||
mov rax, [r10+01478h]
|
||||
mov [rsp+0b8h], rax
|
||||
; save current stack limit
|
||||
mov rax, [r10+010h]
|
||||
mov [rsp+0c0h], rax
|
||||
; save current stack base
|
||||
mov rax, [r10+08h]
|
||||
mov [rsp+0c8h], rax
|
||||
|
||||
mov [rsp+0d0h], r12 ; save R12
|
||||
mov [rsp+0d8h], r13 ; save R13
|
||||
mov [rsp+0e0h], r14 ; save R14
|
||||
mov [rsp+0e8h], r15 ; save R15
|
||||
mov [rsp+0f0h], rdi ; save RDI
|
||||
mov [rsp+0f8h], rsi ; save RSI
|
||||
mov [rsp+0100h], rbx ; save RBX
|
||||
mov [rsp+0108h], rbp ; save RBP
|
||||
|
||||
mov [rsp+0110h], rcx ; save hidden address of transport_t
|
||||
|
||||
; preserve RSP (pointing to context-data) in R9
|
||||
mov r9, rsp
|
||||
|
||||
; restore RSP (pointing to context-data) from RDX
|
||||
mov rsp, rdx
|
||||
|
||||
IFNDEF BOOST_USE_TSX
|
||||
; restore XMM storage
|
||||
movaps xmm6, [rsp]
|
||||
movaps xmm7, [rsp+010h]
|
||||
movaps xmm8, [rsp+020h]
|
||||
movaps xmm9, [rsp+030h]
|
||||
movaps xmm10, [rsp+040h]
|
||||
movaps xmm11, [rsp+050h]
|
||||
movaps xmm12, [rsp+060h]
|
||||
movaps xmm13, [rsp+070h]
|
||||
movaps xmm14, [rsp+080h]
|
||||
movaps xmm15, [rsp+090h]
|
||||
; restore MMX control- and status-word
|
||||
ldmxcsr [rsp+0a0h]
|
||||
; save x87 control-word
|
||||
fldcw [rsp+0a4h]
|
||||
ENDIF
|
||||
|
||||
; load NT_TIB
|
||||
mov r10, gs:[030h]
|
||||
; restore fiber local storage
|
||||
mov rax, [rsp+0b0h]
|
||||
mov [r10+020h], rax
|
||||
; restore current deallocation stack
|
||||
mov rax, [rsp+0b8h]
|
||||
mov [r10+01478h], rax
|
||||
; restore current stack limit
|
||||
mov rax, [rsp+0c0h]
|
||||
mov [r10+010h], rax
|
||||
; restore current stack base
|
||||
mov rax, [rsp+0c8h]
|
||||
mov [r10+08h], rax
|
||||
|
||||
mov r12, [rsp+0d0h] ; restore R12
|
||||
mov r13, [rsp+0d8h] ; restore R13
|
||||
mov r14, [rsp+0e0h] ; restore R14
|
||||
mov r15, [rsp+0e8h] ; restore R15
|
||||
mov rdi, [rsp+0f0h] ; restore RDI
|
||||
mov rsi, [rsp+0f8h] ; restore RSI
|
||||
mov rbx, [rsp+0100h] ; restore RBX
|
||||
mov rbp, [rsp+0108h] ; restore RBP
|
||||
|
||||
mov rax, [rsp+0110h] ; restore hidden address of transport_t
|
||||
|
||||
; prepare stack
|
||||
lea rsp, [rsp+0118h]
|
||||
|
||||
; load return-address
|
||||
pop r10
|
||||
|
||||
; transport_t returned in RAX
|
||||
; return parent fcontext_t
|
||||
mov [rax], r9
|
||||
; return data
|
||||
mov [rax+08h], r8
|
||||
|
||||
; transport_t as 1.arg of context-function
|
||||
mov rcx, rax
|
||||
|
||||
; indirect jump to context
|
||||
jmp r10
|
||||
jump_fcontext ENDP
|
||||
END
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | R15 | RBX | RBP | RIP | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.file "jump_x86_64_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl jump_fcontext
|
||||
.type jump_fcontext,@function
|
||||
.align 16
|
||||
jump_fcontext:
|
||||
leaq -0x38(%rsp), %rsp /* prepare stack */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
stmxcsr (%rsp) /* save MMX control- and status-word */
|
||||
fnstcw 0x4(%rsp) /* save x87 control-word */
|
||||
#endif
|
||||
|
||||
movq %r12, 0x8(%rsp) /* save R12 */
|
||||
movq %r13, 0x10(%rsp) /* save R13 */
|
||||
movq %r14, 0x18(%rsp) /* save R14 */
|
||||
movq %r15, 0x20(%rsp) /* save R15 */
|
||||
movq %rbx, 0x28(%rsp) /* save RBX */
|
||||
movq %rbp, 0x30(%rsp) /* save RBP */
|
||||
|
||||
/* store RSP (pointing to context-data) in RAX */
|
||||
movq %rsp, %rax
|
||||
|
||||
/* restore RSP (pointing to context-data) from RDI */
|
||||
movq %rdi, %rsp
|
||||
|
||||
movq 0x38(%rsp), %r8 /* restore return-address */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
ldmxcsr (%rsp) /* restore MMX control- and status-word */
|
||||
fldcw 0x4(%rsp) /* restore x87 control-word */
|
||||
#endif
|
||||
|
||||
movq 0x8(%rsp), %r12 /* restore R12 */
|
||||
movq 0x10(%rsp), %r13 /* restore R13 */
|
||||
movq 0x18(%rsp), %r14 /* restore R14 */
|
||||
movq 0x20(%rsp), %r15 /* restore R15 */
|
||||
movq 0x28(%rsp), %rbx /* restore RBX */
|
||||
movq 0x30(%rsp), %rbp /* restore RBP */
|
||||
|
||||
leaq 0x40(%rsp), %rsp /* prepare stack */
|
||||
|
||||
/* return transfer_t from jump */
|
||||
#if !defined(_ILP32)
|
||||
/* RAX == fctx, RDX == data */
|
||||
movq %rsi, %rdx
|
||||
#else
|
||||
/* RAX == data:fctx */
|
||||
salq $32, %rsi
|
||||
orq %rsi, %rax
|
||||
#endif
|
||||
/* pass transfer_t as first arg in context function */
|
||||
#if !defined(_ILP32)
|
||||
/* RDI == fctx, RSI == data */
|
||||
#else
|
||||
/* RDI == data:fctx */
|
||||
#endif
|
||||
movq %rax, %rdi
|
||||
|
||||
/* indirect jump to context */
|
||||
jmp *%r8
|
||||
.size jump_fcontext,.-jump_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | R15 | RBX | RBP | RIP | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.text
|
||||
.globl _jump_fcontext
|
||||
.align 8
|
||||
_jump_fcontext:
|
||||
leaq -0x38(%rsp), %rsp /* prepare stack */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
stmxcsr (%rsp) /* save MMX control- and status-word */
|
||||
fnstcw 0x4(%rsp) /* save x87 control-word */
|
||||
#endif
|
||||
|
||||
movq %r12, 0x8(%rsp) /* save R12 */
|
||||
movq %r13, 0x10(%rsp) /* save R13 */
|
||||
movq %r14, 0x18(%rsp) /* save R14 */
|
||||
movq %r15, 0x20(%rsp) /* save R15 */
|
||||
movq %rbx, 0x28(%rsp) /* save RBX */
|
||||
movq %rbp, 0x30(%rsp) /* save RBP */
|
||||
|
||||
/* store RSP (pointing to context-data) in RAX */
|
||||
movq %rsp, %rax
|
||||
|
||||
/* restore RSP (pointing to context-data) from RDI */
|
||||
movq %rdi, %rsp
|
||||
|
||||
movq 0x38(%rsp), %r8 /* restore return-address */
|
||||
|
||||
#if !defined(BOOST_USE_TSX)
|
||||
ldmxcsr (%rsp) /* restore MMX control- and status-word */
|
||||
fldcw 0x4(%rsp) /* restore x87 control-word */
|
||||
#endif
|
||||
|
||||
movq 0x8(%rsp), %r12 /* restore R12 */
|
||||
movq 0x10(%rsp), %r13 /* restore R13 */
|
||||
movq 0x18(%rsp), %r14 /* restore R14 */
|
||||
movq 0x20(%rsp), %r15 /* restore R15 */
|
||||
movq 0x28(%rsp), %rbx /* restore RBX */
|
||||
movq 0x30(%rsp), %rbp /* restore RBP */
|
||||
|
||||
leaq 0x40(%rsp), %rsp /* prepare stack */
|
||||
|
||||
/* return transfer_t from jump */
|
||||
/* RAX == fctx, RDX == data */
|
||||
movq %rsi, %rdx
|
||||
/* pass transfer_t as first arg in context function */
|
||||
/* RDI == fctx, RSI == data */
|
||||
movq %rax, %rdi
|
||||
|
||||
/* indirect jump to context */
|
||||
jmp *%r8
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
Copyright Edward Nevill + Oliver Kowalke 2015
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d8 | d9 | d10 | d11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d12 | d13 | d14 | d15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x19 | x20 | x21 | x22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x23 | x24 | x25 | x26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x27 | x28 | FP | LR | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0xa0| 0xa4| 0xa8| 0xac| | | *
|
||||
* ------------------------------------------------- *
|
||||
* | PC | align | | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "make_arm64_aapcs_elf_gas.S"
|
||||
.text
|
||||
.align 2
|
||||
.global make_fcontext
|
||||
.type make_fcontext, %function
|
||||
make_fcontext:
|
||||
# shift address in x0 (allocated stack) to lower 16 byte boundary
|
||||
and x0, x0, ~0xF
|
||||
|
||||
# reserve space for context-data on context-stack
|
||||
sub x0, x0, #0xb0
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
# store address as a PC to jump in
|
||||
str x2, [x0, #0xa0]
|
||||
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns (LR register)
|
||||
adr x1, finish
|
||||
str x1, [x0, #0x98]
|
||||
|
||||
ret x30 // return pointer to context-data (x0)
|
||||
|
||||
finish:
|
||||
# exit code is zero
|
||||
mov x0, #0
|
||||
# exit application
|
||||
bl _exit
|
||||
|
||||
.size make_fcontext,.-make_fcontext
|
||||
# Mark that we don't need executable stack.
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Copyright Edward Nevill + Oliver Kowalke 2015
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d8 | d9 | d10 | d11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | d12 | d13 | d14 | d15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x19 | x20 | x21 | x22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x23 | x24 | x25 | x26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x80| 0x84| 0x88| 0x8c| 0x90| 0x94| 0x98| 0x9c| *
|
||||
* ------------------------------------------------- *
|
||||
* | x27 | x28 | FP | LR | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0xa0| 0xa4| 0xa8| 0xac| | | *
|
||||
* ------------------------------------------------- *
|
||||
* | PC | align | | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
.balign 16
|
||||
|
||||
_make_fcontext:
|
||||
; shift address in x0 (allocated stack) to lower 16 byte boundary
|
||||
and x0, x0, ~0xF
|
||||
|
||||
; reserve space for context-data on context-stack
|
||||
sub x0, x0, #0xb0
|
||||
|
||||
; third arg of make_fcontext() == address of context-function
|
||||
; store address as a PC to jump in
|
||||
str x2, [x0, #0xa0]
|
||||
|
||||
adr x1, finish
|
||||
|
||||
; save address of finish as return-address for context-function
|
||||
; will be entered after context-function returns (LR register)
|
||||
str x1, [x0, #0x98]
|
||||
|
||||
ret lr ; return pointer to context-data (x0)
|
||||
|
||||
finish:
|
||||
; exit code is zero
|
||||
mov x0, #0
|
||||
; exit application
|
||||
bl __exit
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x40| 0x44| 0x48| 0x4c| 0x50| 0x54| 0x58| 0x5c| *
|
||||
* ------------------------------------------------- *
|
||||
* |hiddn| v1 | v2 | v3 | v4 | v5 | v6 | v7 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x60| 0x64| 0x68| 0x6c| 0x70| 0x74| 0x78| 0x7c| *
|
||||
* ------------------------------------------------- *
|
||||
* | v8 | lr | pc | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "make_arm_aapcs_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.align 2
|
||||
.type make_fcontext,%function
|
||||
.syntax unified
|
||||
make_fcontext:
|
||||
@ shift address in A1 to lower 16 byte boundary
|
||||
bic a1, a1, #15
|
||||
|
||||
@ reserve space for context-data on context-stack
|
||||
sub a1, a1, #124
|
||||
|
||||
@ third arg of make_fcontext() == address of context-function
|
||||
str a3, [a1, #104]
|
||||
|
||||
@ compute address of returned transfer_t
|
||||
add a2, a1, #108
|
||||
mov a3, a2
|
||||
str a3, [a1, #64]
|
||||
|
||||
@ compute abs address of label finish
|
||||
adr a2, finish
|
||||
@ save address of finish as return-address for context-function
|
||||
@ will be entered after context-function returns
|
||||
str a2, [a1, #100]
|
||||
|
||||
#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
|
||||
#endif
|
||||
|
||||
bx lr @ return pointer to context-data
|
||||
|
||||
finish:
|
||||
@ exit code is zero
|
||||
mov a1, #0
|
||||
@ exit application
|
||||
bl _exit@PLT
|
||||
.size make_fcontext,.-make_fcontext
|
||||
|
||||
@ Mark that we don't need executable stack.
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s16 | s17 | s18 | s19 | s20 | s21 | s22 | s23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | s24 | s25 | s26 | s27 | s28 | s29 | s30 | s31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10| 0x14| 0x18| 0x1c| *
|
||||
* ------------------------------------------------- *
|
||||
* | sjlj|hiddn| v1 | v2 | v3 | v4 | v5 | v6 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0x20| 0x24| 0x28| 0x2c| 0x30| 0x34| 0x38| 0x3c| *
|
||||
* ------------------------------------------------- *
|
||||
* | v7 | v8 | lr | pc | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
.align 2
|
||||
_make_fcontext:
|
||||
@ shift address in A1 to lower 16 byte boundary
|
||||
bic a1, a1, #15
|
||||
|
||||
@ reserve space for context-data on context-stack
|
||||
sub a1, a1, #124
|
||||
|
||||
@ third arg of make_fcontext() == address of context-function
|
||||
str a3, [a1, #108]
|
||||
|
||||
@ compute address of returned transfer_t
|
||||
add a2, a1, #112
|
||||
mov a3, a2
|
||||
str a3, [a1, #68]
|
||||
|
||||
@ compute abs address of label finish
|
||||
adr a2, finish
|
||||
@ save address of finish as return-address for context-function
|
||||
@ will be entered after context-function returns
|
||||
str a2, [a1, #104]
|
||||
|
||||
bx lr @ return pointer to context-data
|
||||
|
||||
finish:
|
||||
@ exit code is zero
|
||||
mov a1, #0
|
||||
@ exit application
|
||||
bl __exit
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
Copyright Sergue E. Leontiev 2013.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
// Stub file for universal binary
|
||||
|
||||
#if defined(__i386__)
|
||||
#include "make_i386_sysv_macho_gas.S"
|
||||
#elif defined(__x86_64__)
|
||||
#include "make_x86_64_sysv_macho_gas.S"
|
||||
#elif defined(__ppc__)
|
||||
#include "make_ppc32_sysv_macho_gas.S"
|
||||
#elif defined(__ppc64__)
|
||||
#include "make_ppc64_sysv_macho_gas.S"
|
||||
#elif defined(__arm__)
|
||||
#include "make_arm_aapcs_macho_gas.S"
|
||||
#elif defined(__arm64__)
|
||||
#include "make_arm64_aapcs_macho_gas.S"
|
||||
#else
|
||||
#error "No arch's"
|
||||
#endif
|
||||
@@ -1,140 +0,0 @@
|
||||
|
||||
; Copyright Oliver Kowalke 2009.
|
||||
; Distributed under the Boost Software License, Version 1.0.
|
||||
; (See accompanying file LICENSE_1_0.txt or copy at
|
||||
; http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 0h | 04h | 08h | 0ch | 010h | 014h | 018h | 01ch |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | fc_mxcsr|fc_x87_cw| fc_strg |fc_deallo| limit | base | fc_seh | EDI |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | 020h | 024h | 028h | 02ch | 030h | 034h | 038h | 03ch |
|
||||
; ---------------------------------------------------------------------------------
|
||||
; | ESI | EBX | EBP | EIP | to | data | EH NXT |SEH HNDLR|
|
||||
; ---------------------------------------------------------------------------------
|
||||
|
||||
.386
|
||||
.XMM
|
||||
.model flat, c
|
||||
; standard C library function
|
||||
_exit PROTO, value:SDWORD
|
||||
.code
|
||||
|
||||
make_fcontext PROC BOOST_CONTEXT_EXPORT
|
||||
; first arg of make_fcontext() == top of context-stack
|
||||
mov eax, [esp+04h]
|
||||
|
||||
; reserve space for first argument of context-function
|
||||
; EAX might already point to a 16byte border
|
||||
lea eax, [eax-08h]
|
||||
|
||||
; shift address in EAX to lower 16 byte boundary
|
||||
and eax, -16
|
||||
|
||||
; reserve space for context-data on context-stack
|
||||
; on context-function entry: (ESP -0x4) % 8 == 0
|
||||
; additional space is required for SEH
|
||||
lea eax, [eax-040h]
|
||||
|
||||
; save MMX control- and status-word
|
||||
stmxcsr [eax]
|
||||
; save x87 control-word
|
||||
fnstcw [eax+04h]
|
||||
|
||||
; first arg of make_fcontext() == top of context-stack
|
||||
mov ecx, [esp+04h]
|
||||
; save top address of context stack as 'base'
|
||||
mov [eax+014h], ecx
|
||||
; second arg of make_fcontext() == size of context-stack
|
||||
mov edx, [esp+08h]
|
||||
; negate stack size for LEA instruction (== substraction)
|
||||
neg edx
|
||||
; compute bottom address of context stack (limit)
|
||||
lea ecx, [ecx+edx]
|
||||
; save bottom address of context-stack as 'limit'
|
||||
mov [eax+010h], ecx
|
||||
; save bottom address of context-stack as 'dealloction stack'
|
||||
mov [eax+0ch], ecx
|
||||
; set fiber-storage to zero
|
||||
xor ecx, ecx
|
||||
mov [eax+08h], ecx
|
||||
|
||||
; third arg of make_fcontext() == address of context-function
|
||||
; stored in EBX
|
||||
mov ecx, [esp+0ch]
|
||||
mov [eax+024h], ecx
|
||||
|
||||
; compute abs address of label trampoline
|
||||
mov ecx, trampoline
|
||||
; save address of trampoline as return-address for context-function
|
||||
; will be entered after calling jump_fcontext() first time
|
||||
mov [eax+02ch], ecx
|
||||
|
||||
; compute abs address of label finish
|
||||
mov ecx, finish
|
||||
; save address of finish as return-address for context-function in EBP
|
||||
; will be entered after context-function returns
|
||||
mov [eax+028h], ecx
|
||||
|
||||
; traverse current seh chain to get the last exception handler installed by Windows
|
||||
; note that on Windows Server 2008 and 2008 R2, SEHOP is activated by default
|
||||
; the exception handler chain is tested for the presence of ntdll.dll!FinalExceptionHandler
|
||||
; at its end by RaiseException all seh-handlers are disregarded if not present and the
|
||||
; program is aborted
|
||||
assume fs:nothing
|
||||
; load NT_TIB into ECX
|
||||
mov ecx, fs:[0h]
|
||||
assume fs:error
|
||||
|
||||
walk:
|
||||
; load 'next' member of current SEH into EDX
|
||||
mov edx, [ecx]
|
||||
; test if 'next' of current SEH is last (== 0xffffffff)
|
||||
inc edx
|
||||
jz found
|
||||
dec edx
|
||||
; exchange content; ECX contains address of next SEH
|
||||
xchg edx, ecx
|
||||
; inspect next SEH
|
||||
jmp walk
|
||||
|
||||
found:
|
||||
; load 'handler' member of SEH == address of last SEH handler installed by Windows
|
||||
mov ecx, [ecx+04h]
|
||||
; save address in ECX as SEH handler for context
|
||||
mov [eax+03ch], ecx
|
||||
; set ECX to -1
|
||||
mov ecx, 0ffffffffh
|
||||
; save ECX as next SEH item
|
||||
mov [eax+038h], ecx
|
||||
; load address of next SEH item
|
||||
lea ecx, [eax+038h]
|
||||
; save next SEH
|
||||
mov [eax+018h], ecx
|
||||
|
||||
ret ; return pointer to context-data
|
||||
|
||||
trampoline:
|
||||
; move transport_t for entering context-function
|
||||
; FCTX == EAX, DATA == EDX
|
||||
mov [esp], eax
|
||||
mov [esp+04h], edx
|
||||
push ebp
|
||||
; jump to context-function
|
||||
jmp ebx
|
||||
|
||||
finish:
|
||||
; exit code is zero
|
||||
xor eax, eax
|
||||
mov [esp], eax
|
||||
; exit application
|
||||
call _exit
|
||||
hlt
|
||||
make_fcontext ENDP
|
||||
END
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | hidden | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | to | data | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.file "make_i386_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.align 2
|
||||
.type make_fcontext,@function
|
||||
make_fcontext:
|
||||
/* first arg of make_fcontext() == top of context-stack */
|
||||
movl 0x4(%esp), %eax
|
||||
|
||||
/* reserve space for first argument of context-function
|
||||
eax might already point to a 16byte border */
|
||||
leal -0x8(%eax), %eax
|
||||
|
||||
/* shift address in EAX to lower 16 byte boundary */
|
||||
andl $-16, %eax
|
||||
|
||||
/* reserve space for context-data on context-stack */
|
||||
leal -0x28(%eax), %eax
|
||||
|
||||
/* third arg of make_fcontext() == address of context-function */
|
||||
/* stored in EBX */
|
||||
movl 0xc(%esp), %ecx
|
||||
movl %ecx, 0x10(%eax)
|
||||
|
||||
/* save MMX control- and status-word */
|
||||
stmxcsr (%eax)
|
||||
/* save x87 control-word */
|
||||
fnstcw 0x4(%eax)
|
||||
|
||||
/* return transport_t */
|
||||
/* FCTX == EDI, DATA == ESI */
|
||||
leal 0x8(%eax), %ecx
|
||||
movl %ecx, 0x1c(%eax)
|
||||
|
||||
/* compute abs address of label trampoline */
|
||||
call 1f
|
||||
/* address of trampoline 1 */
|
||||
1: popl %ecx
|
||||
/* compute abs address of label trampoline */
|
||||
addl $trampoline-1b, %ecx
|
||||
/* save address of trampoline as return address */
|
||||
/* will be entered after calling jump_fcontext() first time */
|
||||
movl %ecx, 0x18(%eax)
|
||||
|
||||
/* compute abs address of label finish */
|
||||
call 2f
|
||||
/* address of label 2 */
|
||||
2: popl %ecx
|
||||
/* compute abs address of label finish */
|
||||
addl $finish-2b, %ecx
|
||||
/* save address of finish as return-address for context-function */
|
||||
/* will be entered after context-function returns */
|
||||
movl %ecx, 0x14(%eax)
|
||||
|
||||
ret /* return pointer to context-data */
|
||||
|
||||
trampoline:
|
||||
/* move transport_t for entering context-function */
|
||||
movl %edi, (%esp)
|
||||
movl %esi, 0x4(%esp)
|
||||
pushl %ebp
|
||||
/* jump to context-function */
|
||||
jmp *%ebx
|
||||
|
||||
finish:
|
||||
call 3f
|
||||
/* address of label 3 */
|
||||
3: popl %ebx
|
||||
/* compute address of GOT and store it in EBX */
|
||||
addl $_GLOBAL_OFFSET_TABLE_+[.-3b], %ebx
|
||||
|
||||
/* exit code is zero */
|
||||
xorl %eax, %eax
|
||||
movl %eax, (%esp)
|
||||
/* exit application */
|
||||
call _exit@PLT
|
||||
hlt
|
||||
.size make_fcontext,.-make_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| EDI | ESI | EBX | EBP | EIP | to | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | data | | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
.align 2
|
||||
_make_fcontext:
|
||||
/* first arg of make_fcontext() == top of context-stack */
|
||||
movl 0x4(%esp), %eax
|
||||
|
||||
/* reserve space for first argument of context-function
|
||||
eax might already point to a 16byte border */
|
||||
leal -0x8(%eax), %eax
|
||||
|
||||
/* shift address in EAX to lower 16 byte boundary */
|
||||
andl $-16, %eax
|
||||
|
||||
/* reserve space for context-data on context-stack */
|
||||
leal -0x2c(%eax), %eax
|
||||
|
||||
/* third arg of make_fcontext() == address of context-function */
|
||||
/* stored in EBX */
|
||||
movl 0xc(%esp), %ecx
|
||||
movl %ecx, 0x10(%eax)
|
||||
|
||||
/* save MMX control- and status-word */
|
||||
stmxcsr (%eax)
|
||||
/* save x87 control-word */
|
||||
fnstcw 0x4(%eax)
|
||||
|
||||
/* compute abs address of label trampoline */
|
||||
call 1f
|
||||
/* address of trampoline 1 */
|
||||
1: popl %ecx
|
||||
/* compute abs address of label trampoline */
|
||||
addl $trampoline-1b, %ecx
|
||||
/* save address of trampoline as return address */
|
||||
/* will be entered after calling jump_fcontext() first time */
|
||||
movl %ecx, 0x18(%eax)
|
||||
|
||||
/* compute abs address of label finish */
|
||||
call 2f
|
||||
/* address of label 2 */
|
||||
2: popl %ecx
|
||||
/* compute abs address of label finish */
|
||||
addl $finish-2b, %ecx
|
||||
/* save address of finish as return-address for context-function */
|
||||
/* will be entered after context-function returns */
|
||||
movl %ecx, 0x14(%eax)
|
||||
|
||||
ret /* return pointer to context-data */
|
||||
|
||||
trampoline:
|
||||
/* move transport_t for entering context-function */
|
||||
movl %eax, (%esp)
|
||||
movl %edx, 0x4(%esp)
|
||||
pushl %ebp
|
||||
/* jump to context-function */
|
||||
jmp *%ebx
|
||||
|
||||
finish:
|
||||
/* exit code is zero */
|
||||
xorl %eax, %eax
|
||||
movl %eax, (%esp)
|
||||
/* exit application */
|
||||
call __exit
|
||||
hlt
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F20 | F22 | F24 | F26 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F28 | F30 | S0 | S1 | S2 | S3 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S4 | S5 | S6 | S7 | FP |hiddn| RA | PC | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | ABI ARGS | GP | FCTX| DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
* *****************************************************/
|
||||
|
||||
.file "make_mips32_o32_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.align 2
|
||||
.type make_fcontext,@function
|
||||
.ent make_fcontext
|
||||
make_fcontext:
|
||||
#ifdef __PIC__
|
||||
.set noreorder
|
||||
.cpload $t9
|
||||
.set reorder
|
||||
#endif
|
||||
# shift address in A0 to lower 16 byte boundary
|
||||
li $v1, -16 # 0xfffffffffffffff0
|
||||
and $v0, $v1, $a0
|
||||
|
||||
# reserve space for context-data on context-stack
|
||||
# includes an extra 32 bytes for:
|
||||
# - 16-byte incoming argument area required by mips ABI used when
|
||||
# jump_context calls the initial function
|
||||
# - 4 bytes to save our GP register used in finish
|
||||
# - 8 bytes to as space for transfer_t returned to finish
|
||||
# - 4 bytes for alignment
|
||||
addiu $v0, $v0, -128
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
sw $a2, 92($v0)
|
||||
# save global pointer in context-data
|
||||
sw $gp, 112($v0)
|
||||
|
||||
# compute address of returned transfer_t
|
||||
addiu $t0, $v0, 116
|
||||
sw $t0, 84($v0)
|
||||
|
||||
# compute abs address of label finish
|
||||
la $t9, finish
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns
|
||||
sw $t9, 88($v0)
|
||||
|
||||
jr $ra # return pointer to context-data
|
||||
|
||||
finish:
|
||||
# reload our gp register (needed for la)
|
||||
lw $gp, 16($sp)
|
||||
|
||||
# call _exit(0)
|
||||
# the previous function should have left the 16 bytes incoming argument
|
||||
# area on the stack which we reuse for calling _exit
|
||||
la $t9, _exit
|
||||
move $a0, $zero
|
||||
jr $t9
|
||||
.end make_fcontext
|
||||
.size make_fcontext, .-make_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright Jiaxun Yang 2018.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 8 | 16 | 24 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F24 | F25 | F26 | F27 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 40 | 48 | 56 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F28 | F29 | F30 | F31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 72 | 80 | 88 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S0 | S1 | S2 | S3 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | S4 | S5 | S6 | S7 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | FP | GP | RA | PC | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
* *****************************************************/
|
||||
|
||||
.file "make_mips64_n64_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.align 3
|
||||
.type make_fcontext,@function
|
||||
.ent make_fcontext
|
||||
make_fcontext:
|
||||
#ifdef __PIC__
|
||||
.set noreorder
|
||||
.cpload $t9
|
||||
.set reorder
|
||||
#endif
|
||||
# shift address in A0 to lower 16 byte boundary
|
||||
li $v1, 0xfffffffffffffff0
|
||||
and $v0, $v1, $a0
|
||||
|
||||
# reserve space for context-data on context-stack
|
||||
daddiu $v0, $v0, -160
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
sd $a2, 152($v0)
|
||||
# save global pointer in context-data
|
||||
sd $gp, 136($v0)
|
||||
|
||||
# psudo instruction compute abs address of label finish based on GP
|
||||
dla $t9, finish
|
||||
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns
|
||||
sd $t9, 144($v0)
|
||||
|
||||
jr $ra # return pointer to context-data
|
||||
|
||||
finish:
|
||||
# reload our gp register (needed for la)
|
||||
daddiu $t0, $sp, -160
|
||||
ld $gp, 136($t0)
|
||||
|
||||
# call _exit(0)
|
||||
# the previous function should have left the 16 bytes incoming argument
|
||||
# area on the stack which we reuse for calling _exit
|
||||
dla $t9, _exit
|
||||
move $a0, $zero
|
||||
jr $t9
|
||||
.end make_fcontext
|
||||
.size make_fcontext, .-make_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* |bchai|hiddn| fpscr | PC | CR | R14 | R15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R16 | R17 | R18 | R19 | R20 | R21 | R22 | R23 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R24 | R25 | R26 | R27 | R28 | R29 | R30 | R31 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F14 | F15 | F16 | F17 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F18 | F19 | F20 | F21 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F22 | F23 | F24 | F25 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F26 | F27 | F28 | F29 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------|------------ *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | *
|
||||
* ------------------------|------------ *
|
||||
* | F30 | F31 |bchai| LR | *
|
||||
* ------------------------|------------ *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "make_ppc32_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.align 2
|
||||
.type make_fcontext,@function
|
||||
make_fcontext:
|
||||
# save return address into R6
|
||||
mflr %r6
|
||||
|
||||
# first arg of make_fcontext() == top address of context-function
|
||||
# shift address in R3 to lower 16 byte boundary
|
||||
clrrwi %r3, %r3, 4
|
||||
|
||||
# reserve space on context-stack, including 16 bytes of linkage
|
||||
# and parameter area + 240 bytes of context-data (R1 % 16 == 0)
|
||||
subi %r3, %r3, 16 + 240
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
#ifdef __linux__
|
||||
# save context-function as PC
|
||||
stw %r5, 16(%r3)
|
||||
#else
|
||||
# save context-function for trampoline
|
||||
stw %r5, 248(%r3)
|
||||
#endif
|
||||
|
||||
# set back-chain to zero
|
||||
li %r0, 0
|
||||
stw %r0, 240(%r3)
|
||||
|
||||
# copy FPSCR to new context
|
||||
mffs %f0
|
||||
stfd %f0, 8(%r3)
|
||||
|
||||
#ifdef __linux__
|
||||
# set hidden pointer for returning transfer_t
|
||||
la %r0, 248(%r3)
|
||||
stw %r0, 4(%r3)
|
||||
#endif
|
||||
|
||||
# load address of label 1 into R4
|
||||
bl 1f
|
||||
1: mflr %r4
|
||||
#ifndef __linux__
|
||||
# compute abs address of trampoline, use as PC
|
||||
addi %r7, %r4, trampoline - 1b
|
||||
stw %r7, 16(%r3)
|
||||
#endif
|
||||
# compute abs address of label finish
|
||||
addi %r4, %r4, finish - 1b
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns
|
||||
stw %r4, 244(%r3)
|
||||
|
||||
# restore return address from R6
|
||||
mtlr %r6
|
||||
|
||||
blr # return pointer to context-data
|
||||
|
||||
#ifndef __linux__
|
||||
trampoline:
|
||||
# On systems other than Linux, jump_fcontext is returning the
|
||||
# transfer_t in R3:R4, but we need to pass transfer_t * R3 to
|
||||
# our context-function.
|
||||
lwz %r0, 8(%r1) # address of context-function
|
||||
mtctr %r0
|
||||
stw %r3, 8(%r1)
|
||||
stw %r4, 12(%r1)
|
||||
la %r3, 8(%r1) # address of transfer_t
|
||||
bctr
|
||||
#endif
|
||||
|
||||
finish:
|
||||
# Use the secure PLT for _exit(0). If we use the insecure BSS PLT
|
||||
# here, then the linker may use the insecure BSS PLT even if the
|
||||
# C++ compiler wanted the secure PLT.
|
||||
|
||||
# set R30 for secure PLT, large model
|
||||
bl 2f
|
||||
2: mflr %r30
|
||||
addis %r30, %r30, .Ltoc - 2b@ha
|
||||
addi %r30, %r30, .Ltoc - 2b@l
|
||||
|
||||
# call _exit(0) with special addend 0x8000 for large model
|
||||
li %r3, 0
|
||||
bl _exit + 0x8000@plt
|
||||
.size make_fcontext, .-make_fcontext
|
||||
|
||||
/* Provide the GOT pointer for secure PLT, large model. */
|
||||
.section .got2,"aw"
|
||||
.Ltoc = . + 0x8000
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F14 | F15 | F16 | F17 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F18 | F19 | F20 | F21 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F22 | F23 | F24 | F25 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F26 | F27 | F28 | F29 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | F30 | F31 | fpscr | R13 | R14 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R15 | R16 | R17 | R18 | R19 | R20 | R21 | R22 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R23 | R24 | R25 | R26 | R27 | R28 | R29 | R30 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R31 |hiddn| CR | LR | PC |bchai|linkr| FCTX| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | 256 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | DATA| | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
.align 2
|
||||
_make_fcontext:
|
||||
# save return address into R6
|
||||
mflr r6
|
||||
|
||||
# first arg of make_fcontext() == top address of context-function
|
||||
# shift address in R3 to lower 16 byte boundary
|
||||
clrrwi r3, r3, 4
|
||||
|
||||
# reserve space for context-data on context-stack
|
||||
# including 64 byte of linkage + parameter area (R1 16 == 0)
|
||||
subi r3, r3, 336
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
stw r5, 240(r3)
|
||||
|
||||
# set back-chain to zero
|
||||
li r0, 0
|
||||
stw r0, 244(r3)
|
||||
|
||||
mffs f0 # load FPSCR
|
||||
stfd f0, 144(r3) # save FPSCR
|
||||
|
||||
# compute address of returned transfer_t
|
||||
addi r0, r3, 252
|
||||
mr r4, r0
|
||||
stw r4, 228(r3)
|
||||
|
||||
# load LR
|
||||
mflr r0
|
||||
# jump to label 1
|
||||
bl 1f
|
||||
1:
|
||||
# load LR into R4
|
||||
mflr r4
|
||||
# compute abs address of label finish
|
||||
addi r4, r4, finish - 1b
|
||||
# restore LR
|
||||
mtlr r0
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns
|
||||
stw r4, 236(r3)
|
||||
|
||||
# restore return address from R6
|
||||
mtlr r6
|
||||
|
||||
blr # return pointer to context-data
|
||||
|
||||
finish:
|
||||
# save return address into R0
|
||||
mflr r0
|
||||
# save return address on stack, set up stack frame
|
||||
stw r0, 4(r1)
|
||||
# allocate stack space, R1 16 == 0
|
||||
stwu r1, -16(r1)
|
||||
|
||||
# exit code is zero
|
||||
li r3, 0
|
||||
# exit application
|
||||
bl _exit@plt
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC | R14 | R15 | R16 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R17 | R18 | R19 | R20 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R21 | R22 | R23 | R24 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R25 | R26 | R27 | R28 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R29 | R30 | R31 | hidden | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | CR | LR | PC | back-chain| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | cr saved | lr saved | compiler | linker | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC saved | FCTX | DATA | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
*******************************************************/
|
||||
|
||||
.file "make_ppc64_sysv_elf_gas.S"
|
||||
.globl make_fcontext
|
||||
#if _CALL_ELF == 2
|
||||
.text
|
||||
.align 2
|
||||
make_fcontext:
|
||||
addis %r2, %r12, .TOC.-make_fcontext@ha
|
||||
addi %r2, %r2, .TOC.-make_fcontext@l
|
||||
.localentry make_fcontext, . - make_fcontext
|
||||
#else
|
||||
.section ".opd","aw"
|
||||
.align 3
|
||||
make_fcontext:
|
||||
# ifdef _CALL_LINUX
|
||||
.quad .L.make_fcontext,.TOC.@tocbase,0
|
||||
.type make_fcontext,@function
|
||||
.text
|
||||
.align 2
|
||||
.L.make_fcontext:
|
||||
# else
|
||||
.hidden .make_fcontext
|
||||
.globl .make_fcontext
|
||||
.quad .make_fcontext,.TOC.@tocbase,0
|
||||
.size make_fcontext,24
|
||||
.type .make_fcontext,@function
|
||||
.text
|
||||
.align 2
|
||||
.make_fcontext:
|
||||
# endif
|
||||
#endif
|
||||
# save return address into R6
|
||||
mflr %r6
|
||||
|
||||
# first arg of make_fcontext() == top address of context-stack
|
||||
# shift address in R3 to lower 16 byte boundary
|
||||
clrrdi %r3, %r3, 4
|
||||
|
||||
# reserve space for context-data on context-stack
|
||||
# including 64 byte of linkage + parameter area (R1 % 16 == 0)
|
||||
subi %r3, %r3, 248
|
||||
|
||||
# third arg of make_fcontext() == address of context-function
|
||||
# entry point (ELFv2) or descriptor (ELFv1)
|
||||
#if _CALL_ELF == 2
|
||||
# save address of context-function entry point
|
||||
std %r5, 176(%r3)
|
||||
#else
|
||||
# save address of context-function entry point
|
||||
ld %r4, 0(%r5)
|
||||
std %r4, 176(%r3)
|
||||
# save TOC of context-function
|
||||
ld %r4, 8(%r5)
|
||||
std %r4, 0(%r3)
|
||||
#endif
|
||||
|
||||
# set back-chain to zero
|
||||
li %r0, 0
|
||||
std %r0, 184(%r3)
|
||||
|
||||
#if _CALL_ELF != 2
|
||||
# zero in r3 indicates first jump to context-function
|
||||
std %r0, 152(%r3)
|
||||
#endif
|
||||
|
||||
# load LR
|
||||
mflr %r0
|
||||
# jump to label 1
|
||||
bl 1f
|
||||
1:
|
||||
# load LR into R4
|
||||
mflr %r4
|
||||
# compute abs address of label finish
|
||||
addi %r4, %r4, finish - 1b
|
||||
# restore LR
|
||||
mtlr %r0
|
||||
# save address of finish as return-address for context-function
|
||||
# will be entered after context-function returns
|
||||
std %r4, 168(%r3)
|
||||
|
||||
# restore return address from R6
|
||||
mtlr %r6
|
||||
|
||||
blr # return pointer to context-data
|
||||
|
||||
finish:
|
||||
# save return address into R0
|
||||
mflr %r0
|
||||
# save return address on stack, set up stack frame
|
||||
std %r0, 8(%r1)
|
||||
# allocate stack space, R1 % 16 == 0
|
||||
stdu %r1, -32(%r1)
|
||||
|
||||
# exit code is zero
|
||||
li %r3, 0
|
||||
# exit application
|
||||
bl _exit
|
||||
nop
|
||||
#if _CALL_ELF == 2
|
||||
.size make_fcontext, .-make_fcontext
|
||||
#else
|
||||
# ifdef _CALL_LINUX
|
||||
.size .make_fcontext, .-.L.make_fcontext
|
||||
# else
|
||||
.size .make_fcontext, .-.make_fcontext
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/*******************************************************
|
||||
* *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 4 | 8 | 12 | 16 | 20 | 24 | 28 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC | R14 | R15 | R16 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 36 | 40 | 44 | 48 | 52 | 56 | 60 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R17 | R18 | R19 | R20 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 68 | 72 | 76 | 80 | 84 | 88 | 92 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R21 | R22 | R23 | R24 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R25 | R26 | R27 | R28 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 132 | 136 | 140 | 144 | 148 | 152 | 156 | *
|
||||
* ------------------------------------------------- *
|
||||
* | R29 | R30 | R31 | hidden | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | *
|
||||
* ------------------------------------------------- *
|
||||
* | CR | LR | PC | back-chain| *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 192 | 196 | 200 | 204 | 208 | 212 | 216 | 220 | *
|
||||
* ------------------------------------------------- *
|
||||
* | cr saved | lr saved | compiler | linker | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 224 | 228 | 232 | 236 | 240 | 244 | 248 | 252 | *
|
||||
* ------------------------------------------------- *
|
||||
* | TOC saved | FCTX | DATA | | *
|
||||
* ------------------------------------------------- *
|
||||
* *
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
_make_fcontext:
|
||||
; save return address into R6
|
||||
mflr r6
|
||||
|
||||
; first arg of make_fcontext() == top address of context-function
|
||||
; shift address in R3 to lower 16 byte boundary
|
||||
clrrwi r3, r3, 4
|
||||
|
||||
; reserve space for context-data on context-stack
|
||||
; including 64 byte of linkage + parameter area (R1 16 == 0)
|
||||
subi r3, r3, 248
|
||||
|
||||
; third arg of make_fcontext() == address of context-function
|
||||
stw r5, 176(r3)
|
||||
|
||||
; set back-chain to zero
|
||||
li %r0, 0
|
||||
std %r0, 184(%r3)
|
||||
|
||||
; compute address of returned transfer_t
|
||||
addi %r0, %r3, 232
|
||||
mr %r4, %r0
|
||||
std %r4, 152(%r3)
|
||||
|
||||
; load LR
|
||||
mflr r0
|
||||
; jump to label 1
|
||||
bl l1
|
||||
l1:
|
||||
; load LR into R4
|
||||
mflr r4
|
||||
; compute abs address of label finish
|
||||
addi r4, r4, lo16((finish - .) + 4)
|
||||
; restore LR
|
||||
mtlr r0
|
||||
; save address of finish as return-address for context-function
|
||||
; will be entered after context-function returns
|
||||
std r4, 168(r3)
|
||||
|
||||
; restore return address from R6
|
||||
mtlr r6
|
||||
|
||||
blr ; return pointer to context-data
|
||||
|
||||
finish:
|
||||
; save return address into R0
|
||||
mflr r0
|
||||
; save return address on stack, set up stack frame
|
||||
stw r0, 8(r1)
|
||||
; allocate stack space, R1 16 == 0
|
||||
stwu r1, -32(r1)
|
||||
|
||||
; set return value to zero
|
||||
li r3, 0
|
||||
; exit application
|
||||
bl __exit
|
||||
nop
|
||||
@@ -1,108 +0,0 @@
|
||||
/*******************************************************
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 0 | 8 | 16 | 24 | *
|
||||
* ------------------------------------------------- *
|
||||
* | t.fctx | t.data | r2 | r6 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 40 | 48 | 56 | *
|
||||
* ------------------------------------------------- *
|
||||
* | r7 | r8 | r9 | r10 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 64 | 72 | 80 | 88 | *
|
||||
* ------------------------------------------------- *
|
||||
* | r11 | r12 | r13 | r14 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 96 | 104 | 112 | 120 | *
|
||||
* ------------------------------------------------- *
|
||||
* | f8 | f9 | f10 | f11 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 128 | 136 | 144 | 152 | *
|
||||
* ------------------------------------------------- *
|
||||
* | f12 | f13 | f14 | f15 | *
|
||||
* ------------------------------------------------- *
|
||||
* ------------------------------------------------- *
|
||||
* | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | *
|
||||
* ------------------------------------------------- *
|
||||
* | 160 | 168 | 176 | | *
|
||||
* ------------------------------------------------- *
|
||||
* | fpc | pc | | | *
|
||||
* ------------------------------------------------- *
|
||||
*******************************************************/
|
||||
|
||||
.text
|
||||
.align 8
|
||||
.global make_fcontext
|
||||
.type make_fcontext, @function
|
||||
|
||||
#define ARG_OFFSET 0
|
||||
#define GR_OFFSET 16
|
||||
#define R14_OFFSET 88
|
||||
#define FP_OFFSET 96
|
||||
#define FPC_OFFSET 160
|
||||
#define PC_OFFSET 168
|
||||
#define CONTEXT_SIZE 176
|
||||
|
||||
/*
|
||||
|
||||
fcontext_t make_fcontext( void * sp, std::size_t size, void (* fn)( transfer_t) );
|
||||
|
||||
Create and return a context below SP to call FN.
|
||||
|
||||
Incoming args
|
||||
r2 - The stack location where to create the context
|
||||
r3 - The size of the context
|
||||
r4 - The address of the context function
|
||||
|
||||
*/
|
||||
|
||||
make_fcontext:
|
||||
.machine "z10"
|
||||
/* Align the stack to an 8 byte boundary. */
|
||||
nill %r2,0xfff0
|
||||
|
||||
/* Allocate stack space for the context. */
|
||||
aghi %r2,-CONTEXT_SIZE
|
||||
|
||||
/* Set the r2 save slot to zero. This indicates jump_fcontext
|
||||
that this is a special context. */
|
||||
mvghi GR_OFFSET(%r2),0
|
||||
|
||||
/* Save the floating point control register. */
|
||||
stfpc FPC_OFFSET(%r2)
|
||||
|
||||
/* Store the address of the target function as new pc. */
|
||||
stg %r4,PC_OFFSET(%r2)
|
||||
|
||||
/* Store a pointer to the finish routine as r14. If a function
|
||||
called via context routines just returns that value will be
|
||||
loaded and used as return address. Hence the program will
|
||||
just exit. */
|
||||
larl %r1,finish
|
||||
stg %r1,R14_OFFSET(%r2)
|
||||
|
||||
/* Return as usual with the new context returned in r2. */
|
||||
br %r14
|
||||
|
||||
finish:
|
||||
/* In finish tasks, you load the exit code and exit the
|
||||
make_fcontext This is called when the context-function is
|
||||
entirely executed. */
|
||||
lghi %r2,0
|
||||
brasl %r14,_exit@PLT
|
||||
|
||||
.size make_fcontext,.-make_fcontext
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,163 +0,0 @@
|
||||
|
||||
; Copyright Oliver Kowalke 2009.
|
||||
; Distributed under the Boost Software License, Version 1.0.
|
||||
; (See accompanying file LICENSE_1_0.txt or copy at
|
||||
; http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xe40 | 0x44 | 0x48 | 0x4c | 0x50 | 0x54 | 0x58 | 0x5c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x60 | 0x64 | 0x68 | 0x6c | 0x70 | 0x74 | 0x78 | 0x7c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 32 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x80 | 0x84 | 0x88 | 0x8c | 0x90 | 0x94 | 0x98 | 0x9c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | SEE registers (XMM6-XMM15) |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xa0 | 0xa4 | 0xa8 | 0xac | 0xb0 | 0xb4 | 0xb8 | 0xbc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | fc_mxcsr|fc_x87_cw| <alignment> | fbr_strg | fc_dealloc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xc0 | 0xc4 | 0xc8 | 0xcc | 0xd0 | 0xd4 | 0xd8 | 0xdc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | limit | base | R12 | R13 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0xe0 | 0xe4 | 0xe8 | 0xec | 0xf0 | 0xf4 | 0xf8 | 0xfc |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | R14 | R15 | RDI | RSI |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x100 | 0x104 | 0x108 | 0x10c | 0x110 | 0x114 | 0x118 | 0x11c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | RBX | RBP | hidden | RIP |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x120 | 0x124 | 0x128 | 0x12c | 0x130 | 0x134 | 0x138 | 0x13c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | parameter area |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | 0x140 | 0x144 | 0x148 | 0x14c | 0x150 | 0x154 | 0x158 | 0x15c |
|
||||
; ----------------------------------------------------------------------------------
|
||||
; | FCTX | DATA | |
|
||||
; ----------------------------------------------------------------------------------
|
||||
|
||||
; standard C library function
|
||||
EXTERN _exit:PROC
|
||||
.code
|
||||
|
||||
; generate function table entry in .pdata and unwind information in
|
||||
make_fcontext PROC BOOST_CONTEXT_EXPORT FRAME
|
||||
; .xdata for a function's structured exception handling unwind behavior
|
||||
.endprolog
|
||||
|
||||
; first arg of make_fcontext() == top of context-stack
|
||||
mov rax, rcx
|
||||
|
||||
; shift address in RAX to lower 16 byte boundary
|
||||
; == pointer to fcontext_t and address of context stack
|
||||
and rax, -16
|
||||
|
||||
; reserve space for context-data on context-stack
|
||||
; on context-function entry: (RSP -0x8) % 16 == 0
|
||||
sub rax, 0150h
|
||||
|
||||
; third arg of make_fcontext() == address of context-function
|
||||
; stored in RBX
|
||||
mov [rax+0100h], r8
|
||||
|
||||
; first arg of make_fcontext() == top of context-stack
|
||||
; save top address of context stack as 'base'
|
||||
mov [rax+0c8h], rcx
|
||||
; second arg of make_fcontext() == size of context-stack
|
||||
; negate stack size for LEA instruction (== substraction)
|
||||
neg rdx
|
||||
; compute bottom address of context stack (limit)
|
||||
lea rcx, [rcx+rdx]
|
||||
; save bottom address of context stack as 'limit'
|
||||
mov [rax+0c0h], rcx
|
||||
; save address of context stack limit as 'dealloction stack'
|
||||
mov [rax+0b8h], rcx
|
||||
; set fiber-storage to zero
|
||||
xor rcx, rcx
|
||||
mov [rax+0b0h], rcx
|
||||
|
||||
; save MMX control- and status-word
|
||||
stmxcsr [rax+0a0h]
|
||||
; save x87 control-word
|
||||
fnstcw [rax+0a4h]
|
||||
|
||||
; compute address of transport_t
|
||||
lea rcx, [rax+0140h]
|
||||
; store address of transport_t in hidden field
|
||||
mov [rax+0110h], rcx
|
||||
|
||||
; compute abs address of label trampoline
|
||||
lea rcx, trampoline
|
||||
; save address of trampoline as return-address for context-function
|
||||
; will be entered after calling jump_fcontext() first time
|
||||
mov [rax+0118h], rcx
|
||||
|
||||
; compute abs address of label finish
|
||||
lea rcx, finish
|
||||
; save address of finish as return-address for context-function in RBP
|
||||
; will be entered after context-function returns
|
||||
mov [rax+0108h], rcx
|
||||
|
||||
ret ; return pointer to context-data
|
||||
|
||||
trampoline:
|
||||
; store return address on stack
|
||||
; fix stack alignment
|
||||
push rbp
|
||||
; jump to context-function
|
||||
jmp rbx
|
||||
|
||||
finish:
|
||||
; exit code is zero
|
||||
xor rcx, rcx
|
||||
; exit application
|
||||
call _exit
|
||||
hlt
|
||||
make_fcontext ENDP
|
||||
END
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | R15 | RBX | RBP | RIP | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.file "make_x86_64_sysv_elf_gas.S"
|
||||
.text
|
||||
.globl make_fcontext
|
||||
.type make_fcontext,@function
|
||||
.align 16
|
||||
make_fcontext:
|
||||
/* first arg of make_fcontext() == top of context-stack */
|
||||
movq %rdi, %rax
|
||||
|
||||
/* shift address in RAX to lower 16 byte boundary */
|
||||
andq $-16, %rax
|
||||
|
||||
/* reserve space for context-data on context-stack */
|
||||
/* on context-function entry: (RSP -0x8) % 16 == 0 */
|
||||
leaq -0x40(%rax), %rax
|
||||
|
||||
/* third arg of make_fcontext() == address of context-function */
|
||||
/* stored in RBX */
|
||||
movq %rdx, 0x28(%rax)
|
||||
|
||||
/* save MMX control- and status-word */
|
||||
stmxcsr (%rax)
|
||||
/* save x87 control-word */
|
||||
fnstcw 0x4(%rax)
|
||||
|
||||
/* compute abs address of label trampoline */
|
||||
leaq trampoline(%rip), %rcx
|
||||
/* save address of trampoline as return-address for context-function */
|
||||
/* will be entered after calling jump_fcontext() first time */
|
||||
movq %rcx, 0x38(%rax)
|
||||
|
||||
/* compute abs address of label finish */
|
||||
leaq finish(%rip), %rcx
|
||||
/* save address of finish as return-address for context-function */
|
||||
/* will be entered after context-function returns */
|
||||
movq %rcx, 0x30(%rax)
|
||||
|
||||
ret /* return pointer to context-data */
|
||||
|
||||
trampoline:
|
||||
/* store return address on stack */
|
||||
/* fix stack alignment */
|
||||
push %rbp
|
||||
/* jump to context-function */
|
||||
jmp *%rbx
|
||||
|
||||
finish:
|
||||
/* exit code is zero */
|
||||
xorq %rdi, %rdi
|
||||
/* exit application */
|
||||
call _exit@PLT
|
||||
hlt
|
||||
.size make_fcontext,.-make_fcontext
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright Oliver Kowalke 2009.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* | R15 | RBX | RBP | RIP | *
|
||||
* ---------------------------------------------------------------------------------- *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
.text
|
||||
.globl _make_fcontext
|
||||
.align 8
|
||||
_make_fcontext:
|
||||
/* first arg of make_fcontext() == top of context-stack */
|
||||
movq %rdi, %rax
|
||||
|
||||
/* shift address in RAX to lower 16 byte boundary */
|
||||
andq $-16, %rax
|
||||
|
||||
/* reserve space for context-data on context-stack */
|
||||
/* on context-function entry: (RSP -0x8) % 16 == 0 */
|
||||
leaq -0x40(%rax), %rax
|
||||
|
||||
/* third arg of make_fcontext() == address of context-function */
|
||||
/* stored in RBX */
|
||||
movq %rdx, 0x28(%rax)
|
||||
|
||||
/* save MMX control- and status-word */
|
||||
stmxcsr (%rax)
|
||||
/* save x87 control-word */
|
||||
fnstcw 0x4(%rax)
|
||||
|
||||
/* compute abs address of label trampoline */
|
||||
leaq trampoline(%rip), %rcx
|
||||
/* save address of trampoline as return-address for context-function */
|
||||
/* will be entered after calling jump_fcontext() first time */
|
||||
movq %rcx, 0x38(%rax)
|
||||
|
||||
/* compute abs address of label finish */
|
||||
leaq finish(%rip), %rcx
|
||||
/* save address of finish as return-address for context-function */
|
||||
/* will be entered after context-function returns */
|
||||
movq %rcx, 0x30(%rax)
|
||||
|
||||
ret /* return pointer to context-data */
|
||||
|
||||
trampoline:
|
||||
/* store return address on stack */
|
||||
/* fix stack alignment */
|
||||
push %rbp
|
||||
/* jump to context-function */
|
||||
jmp *%rbx
|
||||
|
||||
finish:
|
||||
/* exit code is zero */
|
||||
xorq %rdi, %rdi
|
||||
/* exit application */
|
||||
call __exit
|
||||
hlt
|
||||
-422
@@ -1,422 +0,0 @@
|
||||
<?php
|
||||
if (function_exists("date_default_timezone_set")) {
|
||||
date_default_timezone_set("UTC");
|
||||
}
|
||||
|
||||
function simple() {
|
||||
$a = 0;
|
||||
for ($i = 0; $i < 1000000; $i++)
|
||||
$a++;
|
||||
|
||||
$thisisanotherlongname = 0;
|
||||
for ($thisisalongname = 0; $thisisalongname < 1000000; $thisisalongname++)
|
||||
$thisisanotherlongname++;
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function simplecall() {
|
||||
for ($i = 0; $i < 1000000; $i++)
|
||||
strlen("hallo");
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function hallo($a) {
|
||||
}
|
||||
|
||||
function simpleucall() {
|
||||
for ($i = 0; $i < 1000000; $i++)
|
||||
hallo("hallo");
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function simpleudcall() {
|
||||
for ($i = 0; $i < 1000000; $i++)
|
||||
hallo2("hallo");
|
||||
}
|
||||
|
||||
function hallo2($a) {
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function mandel() {
|
||||
$w1=50;
|
||||
$h1=150;
|
||||
$recen=-.45;
|
||||
$imcen=0.0;
|
||||
$r=0.7;
|
||||
$s=0; $rec=0; $imc=0; $re=0; $im=0; $re2=0; $im2=0;
|
||||
$x=0; $y=0; $w2=0; $h2=0; $color=0;
|
||||
$s=2*$r/$w1;
|
||||
$w2=40;
|
||||
$h2=12;
|
||||
for ($y=0 ; $y<=$w1; $y=$y+1) {
|
||||
$imc=$s*($y-$h2)+$imcen;
|
||||
for ($x=0 ; $x<=$h1; $x=$x+1) {
|
||||
$rec=$s*($x-$w2)+$recen;
|
||||
$re=$rec;
|
||||
$im=$imc;
|
||||
$color=1000;
|
||||
$re2=$re*$re;
|
||||
$im2=$im*$im;
|
||||
while( ((($re2+$im2)<1000000) && $color>0)) {
|
||||
$im=$re*$im*2+$imc;
|
||||
$re=$re2-$im2+$rec;
|
||||
$re2=$re*$re;
|
||||
$im2=$im*$im;
|
||||
$color=$color-1;
|
||||
}
|
||||
if ( $color==0 ) {
|
||||
print "_";
|
||||
} else {
|
||||
print "#";
|
||||
}
|
||||
}
|
||||
print "<br>";
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function mandel2() {
|
||||
$b = " .:,;!/>)|&IH%*#";
|
||||
//float r, i, z, Z, t, c, C;
|
||||
for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){
|
||||
for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ < 75;){
|
||||
for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k<5000; $k++)
|
||||
if ($z*$z + $Z*$Z > 500000) break;
|
||||
echo $b[$k%16];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function Ack($m, $n){
|
||||
if($m == 0) return $n+1;
|
||||
if($n == 0) return Ack($m-1, 1);
|
||||
return Ack($m - 1, Ack($m, ($n - 1)));
|
||||
}
|
||||
|
||||
function ackermann($n) {
|
||||
$r = Ack(3,$n);
|
||||
print "Ack(3,$n): $r\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function ary($n) {
|
||||
for ($i=0; $i<$n; $i++) {
|
||||
$X[$i] = $i;
|
||||
}
|
||||
for ($i=$n-1; $i>=0; $i--) {
|
||||
$Y[$i] = $X[$i];
|
||||
}
|
||||
$last = $n-1;
|
||||
print "$Y[$last]\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function ary2($n) {
|
||||
for ($i=0; $i<$n;) {
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
$X[$i] = $i; ++$i;
|
||||
}
|
||||
for ($i=$n-1; $i>=0;) {
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
$Y[$i] = $X[$i]; --$i;
|
||||
}
|
||||
$last = $n-1;
|
||||
print "$Y[$last]\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function ary3($n) {
|
||||
for ($i=0; $i<$n; $i++) {
|
||||
$X[$i] = $i + 1;
|
||||
$Y[$i] = 0;
|
||||
}
|
||||
for ($k=0; $k<1000; $k++) {
|
||||
for ($i=$n-1; $i>=0; $i--) {
|
||||
$Y[$i] += $X[$i];
|
||||
}
|
||||
}
|
||||
$last = $n-1;
|
||||
print "$Y[0] $Y[$last]\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function fibo_r($n){
|
||||
return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1));
|
||||
}
|
||||
|
||||
function fibo($n) {
|
||||
$r = fibo_r($n);
|
||||
print "$r\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function hash1($n) {
|
||||
for ($i = 1; $i <= $n; $i++) {
|
||||
$X[dechex($i)] = $i;
|
||||
}
|
||||
$c = 0;
|
||||
for ($i = $n; $i > 0; $i--) {
|
||||
if ($X[dechex($i)]) { $c++; }
|
||||
}
|
||||
print "$c\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function hash2($n) {
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$hash1["foo_$i"] = $i;
|
||||
$hash2["foo_$i"] = 0;
|
||||
}
|
||||
for ($i = $n; $i > 0; $i--) {
|
||||
foreach($hash1 as $key => $value) $hash2[$key] += $value;
|
||||
}
|
||||
$first = "foo_0";
|
||||
$last = "foo_".($n-1);
|
||||
print "$hash1[$first] $hash1[$last] $hash2[$first] $hash2[$last]\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function gen_random ($n) {
|
||||
global $LAST;
|
||||
return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM );
|
||||
}
|
||||
|
||||
function heapsort_r($n, &$ra) {
|
||||
$l = ($n >> 1) + 1;
|
||||
$ir = $n;
|
||||
|
||||
while (1) {
|
||||
if ($l > 1) {
|
||||
$rra = $ra[--$l];
|
||||
} else {
|
||||
$rra = $ra[$ir];
|
||||
$ra[$ir] = $ra[1];
|
||||
if (--$ir == 1) {
|
||||
$ra[1] = $rra;
|
||||
return;
|
||||
}
|
||||
}
|
||||
$i = $l;
|
||||
$j = $l << 1;
|
||||
while ($j <= $ir) {
|
||||
if (($j < $ir) && ($ra[$j] < $ra[$j+1])) {
|
||||
$j++;
|
||||
}
|
||||
if ($rra < $ra[$j]) {
|
||||
$ra[$i] = $ra[$j];
|
||||
$j += ($i = $j);
|
||||
} else {
|
||||
$j = $ir + 1;
|
||||
}
|
||||
}
|
||||
$ra[$i] = $rra;
|
||||
}
|
||||
}
|
||||
|
||||
function heapsort($N) {
|
||||
global $LAST;
|
||||
|
||||
define("IM", 139968);
|
||||
define("IA", 3877);
|
||||
define("IC", 29573);
|
||||
|
||||
$LAST = 42;
|
||||
for ($i=1; $i<=$N; $i++) {
|
||||
$ary[$i] = gen_random(1);
|
||||
}
|
||||
heapsort_r($N, $ary);
|
||||
printf("%.10f\n", $ary[$N]);
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function mkmatrix ($rows, $cols) {
|
||||
$count = 1;
|
||||
$mx = array();
|
||||
for ($i=0; $i<$rows; $i++) {
|
||||
for ($j=0; $j<$cols; $j++) {
|
||||
$mx[$i][$j] = $count++;
|
||||
}
|
||||
}
|
||||
return($mx);
|
||||
}
|
||||
|
||||
function mmult ($rows, $cols, $m1, $m2) {
|
||||
$m3 = array();
|
||||
for ($i=0; $i<$rows; $i++) {
|
||||
for ($j=0; $j<$cols; $j++) {
|
||||
$x = 0;
|
||||
for ($k=0; $k<$cols; $k++) {
|
||||
$x += $m1[$i][$k] * $m2[$k][$j];
|
||||
}
|
||||
$m3[$i][$j] = $x;
|
||||
}
|
||||
}
|
||||
return($m3);
|
||||
}
|
||||
|
||||
function matrix($n) {
|
||||
$SIZE = 30;
|
||||
$m1 = mkmatrix($SIZE, $SIZE);
|
||||
$m2 = mkmatrix($SIZE, $SIZE);
|
||||
while ($n--) {
|
||||
$mm = mmult($SIZE, $SIZE, $m1, $m2);
|
||||
}
|
||||
print "{$mm[0][0]} {$mm[2][3]} {$mm[3][2]} {$mm[4][4]}\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function nestedloop($n) {
|
||||
$x = 0;
|
||||
for ($a=0; $a<$n; $a++)
|
||||
for ($b=0; $b<$n; $b++)
|
||||
for ($c=0; $c<$n; $c++)
|
||||
for ($d=0; $d<$n; $d++)
|
||||
for ($e=0; $e<$n; $e++)
|
||||
for ($f=0; $f<$n; $f++)
|
||||
$x++;
|
||||
print "$x\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function sieve($n) {
|
||||
$count = 0;
|
||||
while ($n-- > 0) {
|
||||
$count = 0;
|
||||
$flags = range (0,8192);
|
||||
for ($i=2; $i<8193; $i++) {
|
||||
if ($flags[$i] > 0) {
|
||||
for ($k=$i+$i; $k <= 8192; $k+=$i) {
|
||||
$flags[$k] = 0;
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
print "Count: $count\n";
|
||||
}
|
||||
|
||||
/****/
|
||||
|
||||
function strcat($n) {
|
||||
$str = "";
|
||||
while ($n-- > 0) {
|
||||
$str .= "hello\n";
|
||||
}
|
||||
$len = strlen($str);
|
||||
print "$len\n";
|
||||
}
|
||||
|
||||
/*****/
|
||||
|
||||
function gethrtime()
|
||||
{
|
||||
$hrtime = hrtime();
|
||||
return (($hrtime[0]*1000000000 + $hrtime[1]) / 1000000000);
|
||||
}
|
||||
|
||||
function start_test()
|
||||
{
|
||||
ob_start();
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
function end_test($start, $name)
|
||||
{
|
||||
global $total;
|
||||
$end = gethrtime();
|
||||
ob_end_clean();
|
||||
$total += $end-$start;
|
||||
$num = number_format($end-$start,3);
|
||||
$pad = str_repeat(" ", 24-strlen($name)-strlen($num));
|
||||
|
||||
echo $name.$pad.$num."\n";
|
||||
ob_start();
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
function total()
|
||||
{
|
||||
global $total;
|
||||
$pad = str_repeat("-", 24);
|
||||
echo $pad."\n";
|
||||
$num = number_format($total,3);
|
||||
$pad = str_repeat(" ", 24-strlen("Total")-strlen($num));
|
||||
echo "Total".$pad.$num."\n";
|
||||
}
|
||||
|
||||
$t0 = $t = start_test();
|
||||
simple();
|
||||
$t = end_test($t, "simple");
|
||||
simplecall();
|
||||
$t = end_test($t, "simplecall");
|
||||
simpleucall();
|
||||
$t = end_test($t, "simpleucall");
|
||||
simpleudcall();
|
||||
$t = end_test($t, "simpleudcall");
|
||||
mandel();
|
||||
$t = end_test($t, "mandel");
|
||||
mandel2();
|
||||
$t = end_test($t, "mandel2");
|
||||
ackermann(7);
|
||||
$t = end_test($t, "ackermann(7)");
|
||||
ary(50000);
|
||||
$t = end_test($t, "ary(50000)");
|
||||
ary2(50000);
|
||||
$t = end_test($t, "ary2(50000)");
|
||||
ary3(2000);
|
||||
$t = end_test($t, "ary3(2000)");
|
||||
fibo(30);
|
||||
$t = end_test($t, "fibo(30)");
|
||||
hash1(50000);
|
||||
$t = end_test($t, "hash1(50000)");
|
||||
hash2(500);
|
||||
$t = end_test($t, "hash2(500)");
|
||||
heapsort(20000);
|
||||
$t = end_test($t, "heapsort(20000)");
|
||||
matrix(20);
|
||||
$t = end_test($t, "matrix(20)");
|
||||
nestedloop(12);
|
||||
$t = end_test($t, "nestedloop(12)");
|
||||
sieve(30);
|
||||
$t = end_test($t, "sieve(30)");
|
||||
strcat(200000);
|
||||
$t = end_test($t, "strcat(200000)");
|
||||
total();
|
||||
?>
|
||||
@@ -1,358 +0,0 @@
|
||||
<?php
|
||||
|
||||
function hallo() {
|
||||
}
|
||||
|
||||
function simpleucall($n) {
|
||||
for ($i = 0; $i < $n; $i++)
|
||||
hallo();
|
||||
}
|
||||
|
||||
function simpleudcall($n) {
|
||||
for ($i = 0; $i < $n; $i++)
|
||||
hallo2();
|
||||
}
|
||||
|
||||
function hallo2() {
|
||||
}
|
||||
|
||||
function simpleicall($n) {
|
||||
for ($i = 0; $i < $n; $i++)
|
||||
func_num_args();
|
||||
}
|
||||
|
||||
class Foo {
|
||||
static $a = 0;
|
||||
public $b = 0;
|
||||
const TEST = 0;
|
||||
|
||||
static function read_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = self::$a;
|
||||
}
|
||||
}
|
||||
|
||||
static function write_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
self::$a = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static function isset_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = isset(self::$a);
|
||||
}
|
||||
}
|
||||
|
||||
static function empty_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = empty(self::$a);
|
||||
}
|
||||
}
|
||||
|
||||
static function f() {
|
||||
}
|
||||
|
||||
static function call_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
self::f();
|
||||
}
|
||||
}
|
||||
|
||||
function read_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $this->b;
|
||||
}
|
||||
}
|
||||
|
||||
function write_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$this->b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function assign_add_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$this->b += 2;
|
||||
}
|
||||
}
|
||||
|
||||
function pre_inc_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
++$this->b;
|
||||
}
|
||||
}
|
||||
|
||||
function pre_dec_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
--$this->b;
|
||||
}
|
||||
}
|
||||
|
||||
function post_inc_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$this->b++;
|
||||
}
|
||||
}
|
||||
|
||||
function post_dec_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$this->b--;
|
||||
}
|
||||
}
|
||||
|
||||
function isset_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = isset($this->b);
|
||||
}
|
||||
}
|
||||
|
||||
function empty_prop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = empty($this->b);
|
||||
}
|
||||
}
|
||||
|
||||
function g() {
|
||||
}
|
||||
|
||||
function call($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$this->g();
|
||||
}
|
||||
}
|
||||
|
||||
function read_const($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $this::TEST;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function read_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = Foo::$a;
|
||||
}
|
||||
}
|
||||
|
||||
function write_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
Foo::$a = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function isset_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = isset(Foo::$a);
|
||||
}
|
||||
}
|
||||
|
||||
function empty_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = empty(Foo::$a);
|
||||
}
|
||||
}
|
||||
|
||||
function call_static($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
Foo::f();
|
||||
}
|
||||
}
|
||||
|
||||
function create_object($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = new Foo();
|
||||
}
|
||||
}
|
||||
|
||||
define('TEST', null);
|
||||
|
||||
function read_const($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = TEST;
|
||||
}
|
||||
}
|
||||
|
||||
function read_auto_global($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $_GET;
|
||||
}
|
||||
}
|
||||
|
||||
$g_var = 0;
|
||||
|
||||
function read_global_var($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $GLOBALS['g_var'];
|
||||
}
|
||||
}
|
||||
|
||||
function read_hash($n) {
|
||||
$hash = array('test' => 0);
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $hash['test'];
|
||||
}
|
||||
}
|
||||
|
||||
function read_str_offset($n) {
|
||||
$str = "test";
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $str[1];
|
||||
}
|
||||
}
|
||||
|
||||
function issetor($n) {
|
||||
$val = array(0,1,2,3,4,5,6,7,8,9);
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $val ?: null;
|
||||
}
|
||||
}
|
||||
|
||||
function issetor2($n) {
|
||||
$f = false; $j = 0;
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $f ?: $j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
function ternary($n) {
|
||||
$val = array(0,1,2,3,4,5,6,7,8,9);
|
||||
$f = false;
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $f ? null : $val;
|
||||
}
|
||||
}
|
||||
|
||||
function ternary2($n) {
|
||||
$f = false; $j = 0;
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$x = $f ? $f : $j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*****/
|
||||
|
||||
function empty_loop($n) {
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
}
|
||||
}
|
||||
|
||||
function gethrtime()
|
||||
{
|
||||
$hrtime = hrtime();
|
||||
return (($hrtime[0]*1000000000 + $hrtime[1]) / 1000000000);
|
||||
}
|
||||
|
||||
function start_test()
|
||||
{
|
||||
ob_start();
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
function end_test($start, $name, $overhead = null)
|
||||
{
|
||||
global $total;
|
||||
global $last_time;
|
||||
$end = gethrtime();
|
||||
ob_end_clean();
|
||||
$last_time = $end-$start;
|
||||
$total += $last_time;
|
||||
$num = number_format($last_time,3);
|
||||
$pad = str_repeat(" ", 24-strlen($name)-strlen($num));
|
||||
if (is_null($overhead)) {
|
||||
echo $name.$pad.$num."\n";
|
||||
} else {
|
||||
$num2 = number_format($last_time - $overhead,3);
|
||||
echo $name.$pad.$num." ".$num2."\n";
|
||||
}
|
||||
ob_start();
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
function total()
|
||||
{
|
||||
global $total;
|
||||
$pad = str_repeat("-", 24);
|
||||
echo $pad."\n";
|
||||
$num = number_format($total,3);
|
||||
$pad = str_repeat(" ", 24-strlen("Total")-strlen($num));
|
||||
echo "Total".$pad.$num."\n";
|
||||
}
|
||||
|
||||
const N = 5000000;
|
||||
|
||||
$t0 = $t = start_test();
|
||||
empty_loop(N);
|
||||
$t = end_test($t, 'empty_loop');
|
||||
$overhead = $last_time;
|
||||
simpleucall(N);
|
||||
$t = end_test($t, 'func()', $overhead);
|
||||
simpleudcall(N);
|
||||
$t = end_test($t, 'undef_func()', $overhead);
|
||||
simpleicall(N);
|
||||
$t = end_test($t, 'int_func()', $overhead);
|
||||
Foo::read_static(N);
|
||||
$t = end_test($t, '$x = self::$x', $overhead);
|
||||
Foo::write_static(N);
|
||||
$t = end_test($t, 'self::$x = 0', $overhead);
|
||||
Foo::isset_static(N);
|
||||
$t = end_test($t, 'isset(self::$x)', $overhead);
|
||||
Foo::empty_static(N);
|
||||
$t = end_test($t, 'empty(self::$x)', $overhead);
|
||||
read_static(N);
|
||||
$t = end_test($t, '$x = Foo::$x', $overhead);
|
||||
write_static(N);
|
||||
$t = end_test($t, 'Foo::$x = 0', $overhead);
|
||||
isset_static(N);
|
||||
$t = end_test($t, 'isset(Foo::$x)', $overhead);
|
||||
empty_static(N);
|
||||
$t = end_test($t, 'empty(Foo::$x)', $overhead);
|
||||
Foo::call_static(N);
|
||||
$t = end_test($t, 'self::f()', $overhead);
|
||||
call_static(N);
|
||||
$t = end_test($t, 'Foo::f()', $overhead);
|
||||
$x = new Foo();
|
||||
$x->read_prop(N);
|
||||
$t = end_test($t, '$x = $this->x', $overhead);
|
||||
$x->write_prop(N);
|
||||
$t = end_test($t, '$this->x = 0', $overhead);
|
||||
$x->assign_add_prop(N);
|
||||
$t = end_test($t, '$this->x += 2', $overhead);
|
||||
$x->pre_inc_prop(N);
|
||||
$t = end_test($t, '++$this->x', $overhead);
|
||||
$x->pre_dec_prop(N);
|
||||
$t = end_test($t, '--$this->x', $overhead);
|
||||
$x->post_inc_prop(N);
|
||||
$t = end_test($t, '$this->x++', $overhead);
|
||||
$x->post_dec_prop(N);
|
||||
$t = end_test($t, '$this->x--', $overhead);
|
||||
$x->isset_prop(N);
|
||||
$t = end_test($t, 'isset($this->x)', $overhead);
|
||||
$x->empty_prop(N);
|
||||
$t = end_test($t, 'empty($this->x)', $overhead);
|
||||
$x->call(N);
|
||||
$t = end_test($t, '$this->f()', $overhead);
|
||||
$x->read_const(N);
|
||||
$t = end_test($t, '$x = Foo::TEST', $overhead);
|
||||
create_object(N);
|
||||
$t = end_test($t, 'new Foo()', $overhead);
|
||||
read_const(N);
|
||||
$t = end_test($t, '$x = TEST', $overhead);
|
||||
read_auto_global(N);
|
||||
$t = end_test($t, '$x = $_GET', $overhead);
|
||||
read_global_var(N);
|
||||
$t = end_test($t, '$x = $GLOBALS[\'v\']', $overhead);
|
||||
read_hash(N);
|
||||
$t = end_test($t, '$x = $hash[\'v\']', $overhead);
|
||||
read_str_offset(N);
|
||||
$t = end_test($t, '$x = $str[0]', $overhead);
|
||||
issetor(N);
|
||||
$t = end_test($t, '$x = $a ?: null', $overhead);
|
||||
issetor2(N);
|
||||
$t = end_test($t, '$x = $f ?: tmp', $overhead);
|
||||
ternary(N);
|
||||
$t = end_test($t, '$x = $f ? $f : $a', $overhead);
|
||||
ternary2(N);
|
||||
$t = end_test($t, '$x = $f ? $f : tmp', $overhead);
|
||||
total($t0, "Total");
|
||||
Loaded 100 of 20359 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user