Skip to content

Commit

Permalink
Added possibility for extra specialization for SMART_BRANCH (e.g. IS_…
Browse files Browse the repository at this point in the history
…EQUAL+JMPZ superinstruction).
  • Loading branch information
dstogov committed Mar 11, 2016
1 parent d5cf1a1 commit 827a7a1
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 45 deletions.
24 changes: 24 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -2695,10 +2695,34 @@ void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t
} \
ZEND_VM_CONTINUE(); \
} while (0)
# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) do { \
if ((_check) && UNEXPECTED(EG(exception))) { \
HANDLE_EXCEPTION(); \
} \
if (_result) { \
ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
} else { \
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
} \
ZEND_VM_CONTINUE(); \
} while (0)
# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) do { \
if ((_check) && UNEXPECTED(EG(exception))) { \
HANDLE_EXCEPTION(); \
} \
if (!(_result)) { \
ZEND_VM_SET_NEXT_OPCODE(opline + 2); \
} else { \
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \
} \
ZEND_VM_CONTINUE(); \
} while (0)
#else
# define ZEND_VM_REPEATABLE_OPCODE
# define ZEND_VM_REPEAT_OPCODE(_opcode)
# define ZEND_VM_SMART_BRANCH(_result, _check)
# define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check)
# define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check)
#endif

#ifdef __GNUC__
Expand Down
13 changes: 7 additions & 6 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,13 @@ static zend_uchar zend_user_opcodes[256] = {0,
241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};

#define SPEC_START_MASK 0x0000ffff
#define SPEC_RULE_OP1 0x00010000
#define SPEC_RULE_OP2 0x00020000
#define SPEC_RULE_OP_DATA 0x00040000
#define SPEC_RULE_RETVAL 0x00080000
#define SPEC_RULE_QUICK_ARG 0x00100000
#define SPEC_START_MASK 0x0000ffff
#define SPEC_RULE_OP1 0x00010000
#define SPEC_RULE_OP2 0x00020000
#define SPEC_RULE_OP_DATA 0x00040000
#define SPEC_RULE_RETVAL 0x00080000
#define SPEC_RULE_QUICK_ARG 0x00100000
#define SPEC_RULE_SMART_BRANCH 0x00200000

static const uint32_t *zend_spec_handlers;
static const void **zend_opcode_handlers;
Expand Down
121 changes: 82 additions & 39 deletions Zend/zend_vm_gen.php
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,8 @@
$opnames = array(); // opcode name to code mapping
$line_no = 1;

$used_extra_spec = array();

// Writes $s into resulting executor
function out($f, $s) {
global $line_no;
Expand Down Expand Up @@ -675,6 +677,7 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name, $extra_sp
"/FREE_UNFETCHED_OP_DATA\(\)/",
"/RETURN_VALUE_USED\(opline\)/",
"/arg_num <= MAX_ARG_FLAG_NUM/",
"/ZEND_VM_SMART_BRANCH\(\s*([^,)]*)\s*,\s*([^)]*)\s*\)/",
),
array(
$op1_type[$op1],
Expand Down Expand Up @@ -719,13 +722,19 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name, $extra_sp
"#\\1if 0",
$export?"#\\1if 1\n":"#\\1if 0\n",
$export?"#\\1if 0\n":"#\\1if 1\n",
$op_data_type[isset($extra_spec['op_data']) ? $extra_spec['op_data'] : "ANY"],
$op_data_get_zval_ptr[isset($extra_spec['op_data']) ? $extra_spec['op_data'] : "ANY"],
$op_data_get_zval_ptr_deref[isset($extra_spec['op_data']) ? $extra_spec['op_data'] : "ANY"],
$op_data_free_op[isset($extra_spec['op_data']) ? $extra_spec['op_data'] : "ANY"],
$op_data_free_unfetched[isset($extra_spec['op_data']) ? $extra_spec['op_data'] : "ANY"],
isset($extra_spec['retval']) ? $extra_spec['retval'] : "RETURN_VALUE_USED(opline)",
isset($extra_spec['quick_arg']) ? $extra_spec['quick_arg'] : "arg_num <= MAX_ARG_FLAG_NUM",
$op_data_type[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
$op_data_get_zval_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
$op_data_get_zval_ptr_deref[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
$op_data_free_op[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
$op_data_free_unfetched[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],
isset($extra_spec['RETVAL']) ? $extra_spec['RETVAL'] : "RETURN_VALUE_USED(opline)",
isset($extra_spec['QUICK_ARG']) ? $extra_spec['QUICK_ARG'] : "arg_num <= MAX_ARG_FLAG_NUM",
isset($extra_spec['SMART_BRANCH']) ?
($extra_spec['SMART_BRANCH'] == 1 ?
"ZEND_VM_SMART_BRANCH_JMPZ(\\1, \\2)"
: ($extra_spec['SMART_BRANCH'] == 2 ?
"ZEND_VM_SMART_BRANCH_JMPNZ(\\1, \\2)" : ""))
: "ZEND_VM_SMART_BRANCH(\\1, \\2)",
),
$code);

Expand Down Expand Up @@ -1002,15 +1011,15 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
// For each op_data.op_type except ANY
foreach($op_types as $op_data) {
if ($op_data != "ANY") {
if (!isset($dsc["spec"]["op_data"][$op_data])) {
if (($op_data == "TMP" || $op_data == "VAR") && isset($dsc["spec"]["op_data"]["TMPVAR"])) {
if (!isset($dsc["spec"]["OP_DATA"][$op_data])) {
if (($op_data == "TMP" || $op_data == "VAR") && isset($dsc["spec"]["OP_DATA"]["TMPVAR"])) {
$op_data = "TMPVAR";
} else {
// Try to use unspecialized handler
$op_data = "ANY";
}
}
$do($op1, $op2, array("op_data" => $op_data) + $extra_spec);
$do($op1, $op2, array("OP_DATA" => $op_data) + $extra_spec);
}
}
};
Expand All @@ -1029,7 +1038,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
/* 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"]]))) {
(!isset($extra_spec["OP_DATA"]) || isset($dsc["spec"]["OP_DATA"][$extra_spec["OP_DATA"]]))) {
// Emit pointer to specialized handler
$spec_name = $dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec);
switch ($kind) {
Expand Down Expand Up @@ -1068,7 +1077,7 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
$do = $generate;
if ($spec_extra) {
foreach ($spec_extra as $extra => $devnull) {
if ($extra == "op_data") {
if ($extra == "OP_DATA") {
$do = $foreach_op_data($do);
} else {
$do = $foreach_extra_spec($do, $extra);
Expand Down Expand Up @@ -1188,31 +1197,41 @@ function extra_spec_name($extra_spec) {
global $prefix;

$s = "";
if (isset($extra_spec["op_data"])) {
$s .= "_OP_DATA" . $prefix[$extra_spec["op_data"]];
if (isset($extra_spec["OP_DATA"])) {
$s .= "_OP_DATA" . $prefix[$extra_spec["OP_DATA"]];
}
if (isset($extra_spec["retval"])) {
$s .= "_RETVAL_".($extra_spec["retval"] ? "USED" : "UNUSED");
if (isset($extra_spec["RETVAL"])) {
$s .= "_RETVAL_".($extra_spec["RETVAL"] ? "USED" : "UNUSED");
}
if (isset($extra_spec["quick_arg"])) {
if ($extra_spec["quick_arg"]) {
if (isset($extra_spec["QUICK_ARG"])) {
if ($extra_spec["QUICK_ARG"]) {
$s .= "_QUICK";
}
}
if (isset($extra_spec["SMART_BRANCH"])) {
if ($extra_spec["SMART_BRANCH"] == 1) {
$s .= "_JMPZ";
} else if ($extra_spec["SMART_BRANCH"] == 2) {
$s .= "_JMPNZ";
}
}
return $s;
}

function extra_spec_flags($extra_spec) {
$s = array();
if (isset($extra_spec["op_data"])) {
if (isset($extra_spec["OP_DATA"])) {
$s[] = "SPEC_RULE_OP_DATA";
}
if (isset($extra_spec["retval"])) {
if (isset($extra_spec["RETVAL"])) {
$s[] = "SPEC_RULE_RETVAL";
}
if (isset($extra_spec["quick_arg"])) {
if (isset($extra_spec["QUICK_ARG"])) {
$s[] = "SPEC_RULE_QUICK_ARG";
}
if (isset($extra_spec["SMART_BRANCH"])) {
$s[] = "SPEC_RULE_SMART_BRANCH";
}
return $s;
}

Expand All @@ -1224,12 +1243,12 @@ function extra_spec_handler($dsc) {
}
$specs = $dsc["spec"];

if (isset($specs["op_data"])) {
$op_data_specs = $specs["op_data"];
$specs["op_data"] = array();
if (isset($specs["OP_DATA"])) {
$op_data_specs = $specs["OP_DATA"];
$specs["OP_DATA"] = array();
foreach($op_types_ex as $op_data) {
if (isset($dsc["spec"]["op_data"][$op_data])) {
$specs["op_data"][] = $op_data;
if (isset($dsc["spec"]["OP_DATA"][$op_data])) {
$specs["OP_DATA"][] = $op_data;
}
}
}
Expand Down Expand Up @@ -1353,12 +1372,13 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
if (preg_match("/(.*)[{][%]([A-Z_]*)[%][}](.*)/", $line, $m)) {
switch ($m[2]) {
case "DEFINES":
out($f,"#define SPEC_START_MASK 0x0000ffff\n");
out($f,"#define SPEC_RULE_OP1 0x00010000\n");
out($f,"#define SPEC_RULE_OP2 0x00020000\n");
out($f,"#define SPEC_RULE_OP_DATA 0x00040000\n");
out($f,"#define SPEC_RULE_RETVAL 0x00080000\n");
out($f,"#define SPEC_RULE_QUICK_ARG 0x00100000\n");
out($f,"#define SPEC_START_MASK 0x0000ffff\n");
out($f,"#define SPEC_RULE_OP1 0x00010000\n");
out($f,"#define SPEC_RULE_OP2 0x00020000\n");
out($f,"#define SPEC_RULE_OP_DATA 0x00040000\n");
out($f,"#define SPEC_RULE_RETVAL 0x00080000\n");
out($f,"#define SPEC_RULE_QUICK_ARG 0x00100000\n");
out($f,"#define SPEC_RULE_SMART_BRANCH 0x00200000\n");
out($f,"\n");
out($f,"static const uint32_t *zend_spec_handlers;\n");
out($f,"static const void **zend_opcode_handlers;\n");
Expand Down Expand Up @@ -1690,6 +1710,8 @@ function parse_ext_spec($def, $lineno, $str) {
}

function parse_spec_rules($def, $lineno, $str) {
global $used_extra_spec;

$ret = array();
$a = explode(",", $str);
foreach($a as $rule) {
Expand All @@ -1699,22 +1721,27 @@ function parse_spec_rules($def, $lineno, $str) {
$val = trim(substr($rule, $n+1));
switch ($id) {
case "OP_DATA":
$ret["op_data"] = parse_operand_spec($def, $lineno, $val, $devnull);
$ret["OP_DATA"] = parse_operand_spec($def, $lineno, $val, $devnull);
break;
default:
die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
}
$used_extra_spec[$id] = 1;
} else {
switch ($rule) {
case "RETVAL":
$ret["retval"] = array(0, 1);
$ret["RETVAL"] = array(0, 1);
break;
case "QUICK_ARG":
$ret["quick_arg"] = array(0, 1);
$ret["QUICK_ARG"] = array(0, 1);
break;
case "SMART_BRANCH":
$ret["SMART_BRANCH"] = array(0, 1, 2);
break;
default:
die("ERROR ($def:$lineno): Wrong specialization rules '$str'\n");
}
$used_extra_spec[$rule] = 1;
}
}
return $ret;
Expand All @@ -1723,7 +1750,7 @@ function parse_spec_rules($def, $lineno, $str) {
function gen_vm($def, $skel) {
global $definition_file, $skeleton_file, $executor_file,
$op_types, $list, $opcodes, $helpers, $params, $opnames,
$vm_op_flags;
$vm_op_flags, $used_extra_spec;

// Load definition file
$in = @file($def);
Expand Down Expand Up @@ -2030,9 +2057,25 @@ function gen_vm($def, $skel) {
out($f, "\tuint32_t offset = 0;\n");
out($f, "\tif (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];\n");
out($f, "\tif (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];\n");
out($f, "\tif (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n");
out($f, "\tif (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);\n");
out($f, "\tif (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);\n");
if (isset($used_extra_spec["OP_DATA"])) {
out($f, "\tif (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n");
}
if (isset($used_extra_spec["RETVAL"])) {
out($f, "\tif (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);\n");
}
if (isset($used_extra_spec["QUICK_ARG"])) {
out($f, "\tif (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);\n");
}
if (isset($used_extra_spec["SMART_BRANCH"])) {
out($f, "\tif (spec & SPEC_RULE_SMART_BRANCH) {\n");
out($f, "\t\toffset = offset * 3;\n");
out($f, "\t\tif ((op+1)->opcode == ZEND_JMPZ) {\n");
out($f, "\t\t\toffset += 1;\n");
out($f, "\t\t} else if ((op+1)->opcode == ZEND_JMPNZ) {\n");
out($f, "\t\t\toffset += 2;\n");
out($f, "\t\t}\n");
out($f, "\t}\n");
}
out($f, "\treturn zend_opcode_handlers[(spec & SPEC_START_MASK) + offset];\n");
}
out($f, "}\n\n");
Expand Down

0 comments on commit 827a7a1

Please sign in to comment.