浏览PHP源码的时候,在很多*.stub.php中,发现了这样的注释,@refcount1.通过查看build/gen_stub.php的源码,发现解析*.stub.php文件时返回信息的代码。phpDocType??$this->类型;$isScalarType=$type!==null&&$type->isScalar();如果($refcount===null){$this->refcount=$isScalarType?自我::REFCOUNT_0:自我::REFCOUNT_N;返回;}if(!in_array($refcount,ReturnInfo::REFCOUNTS,true)){thrownewException("@refcount必须具有以下值之一:\"0\",\"1\",\"N\",$refcount给出");}if($isScalarType&&$refcount!==self::REFCOUNT_0){thrownewException('A标量返回类型"'.$类型->__toString()。'"必须有"'的引用计数。自我::REFCOUNT_0。'"');}if(!$isScalarType&&$refcount===self::REFCOUNT_0){thrownewException('Anon-scalarreturntypeof"'.$type->__toString().'"cannothavearefcountof"'.self::REFCOUNT_0.'"');}$this->refcount=$refcount;}很明显,如果返回值类型是标量,即标量(基本数据类型,整型,浮点型,string等),则refcount指定为0,否则为N。如果设置了annotation,则annotation优先级最高。以函数ob_list_handlers为例:/***@returnarray*@refcount1*/functionob_list_handlers():array{}返回值是数组,所以默认的refcount应该是N,但是由于注解@refcount1,所以返回值的引用计数被替换为1.这些逻辑我都能看懂,但是设置返回值的引用计数有什么用呢?生成func_info的时候计数会不一样,如果返回值引用计数是1或者N,对应的g宏将用于初始化func_info结构。如果为0,则不进入初始化列表。上面的代码逻辑还是可以在gen_stub.php中找到的,第1393行,getOptimizerInfo。publicfunctiongetOptimizerInfo():?string{if($this->isMethod()){returnnull;}if($this->alias!==null){返回null;}if($this->return->refcount!==ReturnInfo::REFCOUNT_1&&$this->return->phpDocType===null){returnnull;$type=$this->return->phpDocType??$this->return->type;如果($type===null){返回null;}返回"\tF"。$this->return->refcount。'("'.$this->name->__toString().'",'.$type->toOptimizerTypeMask()."),\n";}获取函数原型的refcount,生成F1()或FN()等代码,生成的头文件位于Zend/Optimizer/zend_func_infos.h。staticconstfunc_info_tfunc_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_"STRAITS_ARITING",MAY_BE_ARRAY_OF_"STRITES_ARITING",MAY_BE_ARRAY_OF_ARYRAY|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),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_BE_FONG_FONG),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的宏义typedefstruct_func_info_t{constchar*name;未签名的name_len;uint32_t信息;info_func_tinfo_func;}func_info_t;#defineF0(名称,信息)\{名称,sizeof(名称)-1,(信息),NULL}#defineF1(名称,信息)\{名称,sizeof(名称)-1,(MAY_BE_RC1|(信息)),NULL}#defineFN(name,info)\{name,sizeof(name)-1,(MAY_BE_RC1|MAY_BE_RCN|(info)),NULL}#defineFC(name,callback)\{name,sizeof(name))-1,0,callback}只是设置不同的类型掩码,F1设置MAY_BE_RC1,FN设置MAY_BE_RCN|MAY_BE_RC1。还是一头雾水,不过通过目录名,可以隐约猜到跟性能优化和JIT有关系。我决定追踪它并查看这些初始化结构的使用位置。我们很清楚,使用|设置位信息,然后用&判断是否设置,全局搜索&MAY_BE_RCN,然后查看有哪些代码是优化相关的,找到如下代码,在zend_jit.c的530行:#ifdefZEND_JIT_USE_RC_INFERENCE/*Refcount可以通过RETURN操作码增加*/if((info&MAY_BE_RC1)&&!(info&MAY_BE_RCN)){for(j=0;jcfg.blocks_count;j++){if((ssa->cfg.blocks[j].flags&ZEND_BB_REACHABLE)&&ssa->cfg.blocks[j].len>0){constzend_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)){信息|=MAY_BE_RCN;休息;}}}}}#endif如果返回值的引用计数为1而不是N,并且开启了返回值引用计数的推导功能,运行这段代码即可。这段代码还涉及到所谓的SSA,静态单赋值的编译器设计方式。在编译器设计中,静态单赋值形式(通常缩写为SSA形式或简称SSA)是中间表示(IR)的一种属性,它要求每个变量只被赋值一次,并且每个变量在使用前定义.原始IR中的现有变量被拆分为多个版本,而在教科书中,新变量通常以原始名称作为下标,以便每个定义都有自己的版本。在SSA形式中,use-def链是显式的,每个链包含一个元素。所以上面的代码就是判断SSA的cfg(controlflowgraph)的block是否可达,如果可达,则执行condition中的代码。它仍然不是很透明。虽然可以推断出设置refcount与优化和静态单赋值有关,但是在写扩展的时候什么时候使用@refcount1还是不清楚。希望对源码有深入研究的朋友可以提一下我,谢谢。