Move unique stubs to unique-stubs-resumable.cpp
[hiphop-php.git] / hphp / runtime / vm / jit / vasm-fusion.cpp
blob79e3e38aa0ae963e9f62741b942ab0cd5ba25c78
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/vasm.h"
19 #include "hphp/runtime/vm/jit/vasm-instr.h"
20 #include "hphp/runtime/vm/jit/vasm-print.h"
21 #include "hphp/runtime/vm/jit/vasm-reg.h"
22 #include "hphp/runtime/vm/jit/vasm-unit.h"
23 #include "hphp/runtime/vm/jit/vasm-visit.h"
25 TRACE_SET_MOD(vasm);
27 namespace HPHP { namespace jit {
29 namespace {
31 // if inst is testb{r,r,d}, return true,d
32 bool match_testb(Vinstr& inst, Vreg r) {
33 return inst.op == Vinstr::testb &&
34 inst.testb_.s0 == r &&
35 inst.testb_.s1 == r;
38 bool match_jcc(Vinstr& inst, Vreg flags) {
39 return inst.op == Vinstr::jcc && inst.jcc_.sf == flags &&
40 (inst.jcc_.cc == CC_E || inst.jcc_.cc == CC_NE);
43 #define CMOV_MATCH(type) \
44 bool match_##type(Vinstr& inst, Vreg flags) { \
45 return inst.op == Vinstr::type && inst.type##_.sf == flags && \
46 (inst.type##_.cc == CC_E || inst.type##_.cc == CC_NE); \
48 CMOV_MATCH(cmovb)
49 CMOV_MATCH(cmovw)
50 CMOV_MATCH(cmovl)
51 CMOV_MATCH(cmovq)
52 #undef CMOV_MATCH
54 bool sets_flags(const Vunit& unit, const Vinstr& inst) {
55 // Some special cases that also clobber flags:
56 switch (inst.op) {
57 case Vinstr::vcall:
58 case Vinstr::vinvoke:
59 case Vinstr::call:
60 case Vinstr::callm:
61 case Vinstr::callr:
62 case Vinstr::calls:
63 case Vinstr::callstub:
64 case Vinstr::callfaststub:
65 case Vinstr::callphp:
66 case Vinstr::callarray:
67 case Vinstr::contenter:
68 return true;
69 default:
70 break;
73 Vreg flags;
74 visitDefs(unit, inst, [&] (Vreg r, Width w) {
75 if (w == Width::Flags) flags = r;
76 });
77 return flags.isValid();
82 * Branch fusion:
83 * Analyze blocks one at a time, looking for the sequence:
85 * setcc cc, f1 => b
86 * ...
87 * testb b, b => f2
88 * ...
89 * jcc E|NE, f2 (OR cmov E|NE, f, t, d)
91 * If found, and f2 is only used by the jcc, then change the code to:
93 * setcc cc, f1 => b
94 * ...
95 * nop
96 * ...
97 * jcc !cc|cc, f1 (OR cmov !cc|cc, f, t, d)
99 * Later, vasm-dead will clean up the nop, and the setcc if b became dead.
101 * During the search, any other instruction that has a status flag result
102 * will reset the pattern matcher. No instruction can "kill" flags,
103 * since flags are SSA variables. However the transformation we want to
104 * make extends the setcc flags lifetime, and we don't want it to overlap
105 * another flag's lifetime.
107 void fuseBranches(Vunit& unit) {
108 auto blocks = sortBlocks(unit);
109 jit::vector<unsigned> uses(unit.next_vr);
110 for (auto b : blocks) {
111 for (auto& inst : unit.blocks[b].code) {
112 visitUses(unit, inst, [&] (Vreg r) { uses[r]++; });
115 bool should_print = false;
116 for (auto b : blocks) {
117 auto& code = unit.blocks[b].code;
118 ConditionCode cc;
119 Vreg setcc_flags, setcc_dest, testb_flags;
120 unsigned testb_index;
121 for (unsigned i = 0, n = code.size(); i < n; ++i) {
122 if (code[i].op == Vinstr::setcc) {
123 cc = code[i].setcc_.cc;
124 setcc_flags = code[i].setcc_.sf;
125 setcc_dest = code[i].setcc_.d;
126 continue;
128 if (setcc_flags.isValid() &&
129 match_testb(code[i], setcc_dest) &&
130 uses[code[i].testb_.sf] == 1) {
131 testb_flags = code[i].testb_.sf;
132 testb_index = i;
133 continue;
135 if (match_jcc(code[i], testb_flags)) {
136 code[testb_index] = nop{}; // erase the testb
137 auto& jcc = code[i].jcc_;
138 jcc.cc = jcc.cc == CC_NE ? cc : ccNegate(cc);
139 jcc.sf = setcc_flags;
140 should_print = true;
141 continue;
144 // Check for different cmov flavors
145 #define CMOV_IMPL(type) \
146 if (match_##type(code[i], testb_flags)) { \
147 code[testb_index] = nop{}; \
148 auto& cmov = code[i].type##_; \
149 cmov.cc = cmov.cc == CC_NE ? cc : ccNegate(cc); \
150 cmov.sf = setcc_flags; \
151 should_print = true; \
152 continue; \
154 CMOV_IMPL(cmovb);
155 CMOV_IMPL(cmovw);
156 CMOV_IMPL(cmovl);
157 CMOV_IMPL(cmovq);
158 #undef CMOV_IMPL
160 if (setcc_flags.isValid() && sets_flags(unit, code[i])) {
161 setcc_flags = testb_flags = Vreg{};
165 if (should_print) {
166 printUnit(kVasmFusionLevel, "after vasm-fusion", unit);