php里refcount遇到问题该怎样处理
在浏览PHP源码的时候,在众多的*.stub.php中,发现了这样的注释,@refcount 1。
通过翻看build/gen_stub.php源码,发现了在解析*.stub.php文件时,关于返回信息的代码。
?php
class ReturnInfo {
const REFCOUNT_0 = "0";
const REFCOUNT_1 = "1";
const REFCOUNT_N = "N";
const REFCOUNTS = [
self::REFCOUNT_0,
self::REFCOUNT_1,
self::REFCOUNT_N,
];
//...
private function setRefcount(?string $refcount): void
{
$type = $this->
phpDocType ?? $this->
type;
$isScalarType = $type !== null &
&
$type->
isScalar();
if ($refcount === null) {
$this->
refcount = $isScalarType ? self::REFCOUNT_0 : self::REFCOUNT_N;
return;
}
if (!in_array($refcount, ReturnInfo::REFCOUNTS, true)) {
throw new Exception("@refcount must have one of the following values: \"0\", \"1\", \"N\", $refcount given");
}
if ($isScalarType &
&
$refcount !== self::REFCOUNT_0) {
throw new Exception('A scalar return type of "' . $type->
__toString() . '" must have a refcount of "' . self::REFCOUNT_0 . '"');
}
if (!$isScalarType &
&
$refcount === self::REFCOUNT_0) {
throw new Exception('A non-scalar return type of "' . $type->
__toString() . '" cannot have a refcount of "' . self::REFCOUNT_0 . '"');
}
$this->
refcount = $refcount;
}
明显,如果返回值类型是scalar,也就是标量(基本数据类型,整型、浮点型、字符串等),那么refcount指定为0,否则为N。如果设置了注释,那么以注释为最高优先级。
以函数ob_list_handlers为例:
/**
* @return arrayint, string>
* @refcount 1
*/
function ob_list_handlers(): array {
}
返回值是array,所以默认的refcount应该是N,但由于设置了注释@refcount 1,所以返回值的引用计数被替换成1。
我接着往下排查,发现通过返回值的引用计数,在生成func_info的时候,会有些不同。如果返回值引用计数为1或N,则会用对应的宏去初始化func_info结构体。如果是0,则不进入初始化列表。
以上的代码逻辑依然可以在gen_stub.php中找到,1393行,getOptimizerInfo。
public function getOptimizerInfo(): ?string {
if ($this->
isMethod()) {
return null;
}
if ($this->
alias !== null) {
return null;
}
if ($this->
return->
refcount !== ReturnInfo::REFCOUNT_1 &
&
$this->
return->
phpDocType === null) {
return null;
}
$type = $this->
return->
phpDocType ?? $this->
return->
type;
if ($type === null) {
return null;
}
return "\tF" . $this->
return->
refcount . '("' . $this->
name->
__toString() . '", ' . $type->
toOptimizerTypeMask() . "),\n";
}
获取函数原型的refcount,生成诸如F1()或FN()的代码,生成的头文件位置在Zend/Optimizer/zend_func_infos.h。
static const func_info_t func_infos[] = {
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_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_OBJECT|MAY_BE_NULL),
FN("set_exception_handler", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_OBJECT|MAY_BE_NULL),
F1("get_declared_classes", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
F1("get_declared_traits", 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_loaded_extensions", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
F1("get_defined_constants", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY),
F1("debug_backtrace", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ARRAY),
F1("get_extension_funcs", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE),
F1("gc_status", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_FALSE|MAY_BE_ARRAY_OF_TRUE),
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),
FN("bzopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
F1("bzerror", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING),
F1("cal_from_jd", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_NULL),
F1("cal_info", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY),
F1("curl_copy_handle", MAY_BE_OBJECT|MAY_BE_FALSE),
//...
}
;
再去看看F1和FN的宏定义。
typedef struct _func_info_t {
const char *name;
unsigned 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}
仅仅是设置了不同的type mask,F1设置了MAY_BE_RC1,FN设置了MAY_BE_RCN | MAY_BE_RC1。
我们很清楚,设置位信息用|,那判断有没有设置肯定用&
,全局搜索&
MAY_BE_RCN,再看看哪些代码跟优化有关,定位到了如下代码,在zend_jit.c的530行:
#ifdef ZEND_JIT_USE_RC_INFERENCE
/* Refcount may be increased by RETURN opcode */
if ((info &
MAY_BE_RC1) &
&
!(info &
MAY_BE_RCN)) {
for (j = 0;
j ssa->
cfg.blocks_count;
j++) {
if ((ssa->
cfg.blocks[j].flags &
ZEND_BB_REACHABLE) &
&
ssa->
cfg.blocks[j].len >
0) {
const zend_op *opline = op_array->
opcodes + ssa->
cfg.blocks[j].start + ssa->
cfg.blocks[j].len - 1;
if (opline->
opcode == ZEND_RETURN) {
if (opline->
op1_type == IS_CV &
&
opline->
op1.var == EX_NUM_TO_VAR(var)) {
info |= MAY_BE_RCN;
break;
}
}
}
}
}
#endif
如果返回值的引用计数是1,而不是N的时候,并且开启了返回值引用计数推导功能,就走这段代码。这段代码又涉及到所谓SSA,静态单赋值的编译器设计方式。
所以上面的代码就是判断SSA的cfg(control flow graph控制流图)的块是不是可达的,如果可达,执行条件中的代码。
还是不太通透,虽然能推断出设置refcount跟优化有关,跟静态单一赋值有关,但在写扩展的时候,什么时候该用@refcount 1,还是不太清楚。到此这篇关于“php里refcount遇到问题该怎样处理”的文章就介绍到这了,感谢各位的阅读,更多相关php里refcount遇到问题该怎样处理内容,欢迎关注网络资讯频道,小编将为大家输出更多高质量的实用文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: php里refcount遇到问题该怎样处理
本文地址: https://pptw.com/jishu/650202.html
