]> granicus.if.org Git - php/commitdiff
Added ability to avoid code generation for useless opcode handlers.
authorDmitry Stogov <dmitry@zend.com>
Fri, 11 Mar 2016 13:51:06 +0000 (16:51 +0300)
committerDmitry Stogov <dmitry@zend.com>
Fri, 11 Mar 2016 13:51:06 +0000 (16:51 +0300)
- SPEC(NO_CONST_CONST) may prevent codegeneration for handlers like ADD_SPEC_CONST_CONST. Compiler and optimizaer should care about constants folding.
- SPEC(COMMUTATIVE) generate only single handler instead of two eqivalents like ADD_SPEC_CONST_CV and ADD_SPEC_CV_CONST. Compiler and optimizer should care avout operands swapping.

Zend/zend_vm_gen.php
Zend/zend_vm_opcodes.h

index 4385708ff62528431915e3b1472775f4c26672d9..6d7027698b74e11056044dbe2f1654941ebe082c 100644 (file)
@@ -74,7 +74,7 @@ $vm_op_flags = array(
        "ZEND_VM_EXT_ARG_NUM"     => 1<<18,
        "ZEND_VM_EXT_ARRAY_INIT"  => 1<<19,
        "ZEND_VM_EXT_REF"         => 1<<20,
-       "ZEND_VM_EXT_MASK"        => 0xff000000,
+       "ZEND_VM_EXT_MASK"        => 0x0f000000,
        "ZEND_VM_EXT_NUM"         => 0x01000000,
     // unused 0x2000000
        "ZEND_VM_EXT_JMP_ADDR"    => 0x03000000,
@@ -87,6 +87,8 @@ $vm_op_flags = array(
        "ZEND_VM_EXT_FAST_RET"    => 0x0a000000,
        "ZEND_VM_EXT_SRC"         => 0x0b000000,
        "ZEND_VM_EXT_SEND"        => 0x0c000000,
+       "ZEND_VM_NO_CONST_CONST"  => 0x40000000,
+       "ZEND_VM_COMMUTATIVE"     => 0x80000000,
 );
 
 foreach ($vm_op_flags as $name => $val) {
@@ -179,6 +181,17 @@ $typecode = array(
        "TMPVARCV" => 0,
 );
 
+$commutative_order = array(
+       "ANY"      => 0,
+       "TMP"      => 1,
+       "VAR"      => 2,
+       "CONST"    => 0,
+       "UNUSED"   => 0,
+       "CV"       => 4,
+       "TMPVAR"   => 2,
+       "TMPVARCV" => 4,
+);
+
 $op1_type = array(
        "ANY"      => "opline->op1_type",
        "TMP"      => "IS_TMP_VAR",
@@ -908,7 +921,21 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name, $extra_sp
 
 // Generates opcode handler
 function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, $extra_spec = null, &$switch_labels = array()) {
-       global $definition_file, $prefix, $typecode, $opnames;
+       global $definition_file, $prefix, $typecode, $opnames, $commutative_order;
+
+       if ($spec &&
+           isset($extra_spec["NO_CONST_CONST"]) &&
+           $op1 == "CONST" && $op2 == "CONST") {
+           // Skip useless constant handlers
+               return;
+       }
+
+       if ($spec &&
+           isset($extra_spec["COMMUTATIVE"]) &&
+           $commutative_order[$op1] > $commutative_order[$op2]) {
+           // Skip duplicate commutative handlers
+               return;
+       }
 
        if (ZEND_VM_LINES) {
                out($f, "#line $lineno \"$definition_file\"\n");
@@ -982,6 +1009,21 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno,
        gen_code($f, $spec, $kind, 0, $code, $op1, $op2, $name);
 }
 
+
+function gen_null_label($f, $kind, $prolog) {
+       switch ($kind) {
+               case ZEND_VM_KIND_CALL:
+                       out($f,$prolog."ZEND_NULL_HANDLER,\n");
+                       break;
+               case ZEND_VM_KIND_SWITCH:
+                       out($f,$prolog."(void*)(uintptr_t)-1,\n");
+                       break;
+               case ZEND_VM_KIND_GOTO:
+                       out($f,$prolog."(void*)&&ZEND_NULL_HANDLER,\n");
+                       break;
+       }
+}
+
 // Generates array of opcode handlers (specialized or unspecialized)
 function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()) {
        global $opcodes, $op_types, $prefix;
@@ -1099,13 +1141,28 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
                                };
                        };
                        $generate = function ($op1, $op2, $extra_spec = array()) use ($f, $kind, $dsc, $prefix, $prolog, $num, $switch_labels, &$label) {
-                               global $typecode;
+                               global $typecode, $commutative_order;
 
                                // Check if specialized handler is defined
                                /* TODO: figure out better way to signal "specialized and not defined" than an extra lookup */
                                if (isset($dsc["op1"][$op1]) &&
                                    isset($dsc["op2"][$op2]) &&
                                    (!isset($extra_spec["OP_DATA"]) || isset($dsc["spec"]["OP_DATA"][$extra_spec["OP_DATA"]]))) {
+
+                                       if (isset($extra_spec["NO_CONST_CONST"]) &&
+                                           $op1 == "CONST" && $op2 == "CONST") {
+                                           // Skip useless constant handlers
+                                               gen_null_label($f, $kind, $prolog);
+                                               $label++;
+                                               return;
+                                       } else if (isset($extra_spec["COMMUTATIVE"]) &&
+                                           $commutative_order[$op1] > $commutative_order[$op2]) {
+                                           // Skip duplicate commutative handlers
+                                               gen_null_label($f, $kind, $prolog);
+                                               $label++;
+                                               return;
+                                       }
+                                       
                                        // Emit pointer to specialized handler
                                        $spec_name = $dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec);
                                        switch ($kind) {
@@ -1124,20 +1181,8 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
                                        }
                                } else {
                                        // Emit pointer to handler of undefined opcode
-                                       switch ($kind) {
-                                               case ZEND_VM_KIND_CALL:
-                                                       out($f,$prolog."ZEND_NULL_HANDLER,\n");
-                                                       $label++;
-                                                       break;
-                                               case ZEND_VM_KIND_SWITCH:
-                                                       out($f,$prolog."(void*)(uintptr_t)-1,\n");
-                                                       $label++;
-                                                       break;
-                                               case ZEND_VM_KIND_GOTO:
-                                                       out($f,$prolog."(void*)&&ZEND_NULL_HANDLER,\n");
-                                                       $label++;
-                                                       break;
-                                       }
+                                       gen_null_label($f, $kind, $prolog);
+                                       $label++;
                                }
                        };
 
@@ -1805,6 +1850,12 @@ function parse_spec_rules($def, $lineno, $str) {
                                case "SMART_BRANCH":
                                        $ret["SMART_BRANCH"] = array(0, 1, 2);
                                        break;
+                               case "NO_CONST_CONST":
+                                       $ret["NO_CONST_CONST"] = array(1);
+                                       break;
+                               case "COMMUTATIVE":
+                                       $ret["COMMUTATIVE"] = array(1);
+                                       break;
                                default:
                                        die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
                        }
@@ -1877,6 +1928,12 @@ function gen_vm($def, $skel) {
                        $opcodes[$code] = array("op"=>$op,"op1"=>$op1,"op2"=>$op2,"code"=>"","flags"=>$flags);
                        if (isset($m[8])) {
                                $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[8]);
+                               if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) {
+                                       $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"];
+                               }
+                               if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) {
+                                       $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"];
+                               }
                        }
                        $opnames[$op] = $code;
                        $handler = $code;
index da38f6c339abcdd4a81f286ddb4f0576c223922d..90f2ea4e51a27bd933aa9969b56136b313ac87d7 100644 (file)
@@ -46,7 +46,7 @@
 #define ZEND_VM_EXT_ARG_NUM      0x00040000
 #define ZEND_VM_EXT_ARRAY_INIT   0x00080000
 #define ZEND_VM_EXT_REF          0x00100000
-#define ZEND_VM_EXT_MASK         0xff000000
+#define ZEND_VM_EXT_MASK         0x0f000000
 #define ZEND_VM_EXT_NUM          0x01000000
 #define ZEND_VM_EXT_JMP_ADDR     0x03000000
 #define ZEND_VM_EXT_DIM_OBJ      0x04000000
@@ -58,6 +58,8 @@
 #define ZEND_VM_EXT_FAST_RET     0x0a000000
 #define ZEND_VM_EXT_SRC          0x0b000000
 #define ZEND_VM_EXT_SEND         0x0c000000
+#define ZEND_VM_NO_CONST_CONST   0x40000000
+#define ZEND_VM_COMMUTATIVE      0x80000000
 #define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)
 #define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)