Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / runtime / vm / jit / vasm-fusion.cpp
blob5fa1ee158a9f6e6dec214950da1149612e6fa6cd
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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::jit {
29 namespace {
31 // if inst is testb{r,r,d}, return true,d
32 bool match_testb(const Vinstr& inst, const VregSet& rs) {
33 return inst.op == Vinstr::testb &&
34 rs[inst.testb_.s0] &&
35 rs[inst.testb_.s1];
38 bool match_jcc(const 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 Vreg match_copy(const Vunit& unit, const Vinstr& inst, const VregSet& rs) {
44 switch (inst.op) {
45 case Vinstr::copy:
46 return rs[inst.copy_.s] ? inst.copy_.d : Vreg{};
47 case Vinstr::copyargs: {
48 auto const& s = unit.tuples[inst.copyargs_.s];
49 auto const& d = unit.tuples[inst.copyargs_.d];
50 assertx(s.size() == d.size());
51 for (size_t i = 0; i < s.size(); ++i) {
52 if (rs[s[i]]) return d[i];
54 return Vreg{};
56 default:
57 return Vreg{};
61 #define CMOV_MATCH(type) \
62 bool match_##type(Vinstr& inst, Vreg flags) { \
63 return inst.op == Vinstr::type && inst.type##_.sf == flags && \
64 (inst.type##_.cc == CC_E || inst.type##_.cc == CC_NE); \
66 CMOV_MATCH(cmovb)
67 CMOV_MATCH(cmovw)
68 CMOV_MATCH(cmovl)
69 CMOV_MATCH(cmovq)
70 #undef CMOV_MATCH
72 bool sets_flags(const Vunit& unit, const Vinstr& inst) {
73 // Some special cases that also clobber flags:
74 switch (inst.op) {
75 case Vinstr::vcall:
76 case Vinstr::vinvoke:
77 case Vinstr::call:
78 case Vinstr::callm:
79 case Vinstr::callr:
80 case Vinstr::calls:
81 case Vinstr::callstub:
82 case Vinstr::callfaststub:
83 case Vinstr::callphp:
84 case Vinstr::callphpfe:
85 case Vinstr::callphpr:
86 case Vinstr::callphps:
87 case Vinstr::contenter:
88 return true;
89 default:
90 break;
93 Vreg flags;
94 visitDefs(unit, inst, [&] (Vreg r, Width w) {
95 if (w == Width::Flags) flags = r;
96 });
97 return flags.isValid();
102 * Branch fusion:
103 * Analyze blocks one at a time, looking for the sequence:
105 * setcc cc, f1 => b
106 * ...
107 * testb b, b => f2
108 * ...
109 * jcc E|NE, f2 (OR cmov E|NE, f, t, d)
111 * If found, and f2 is only used by the jcc, then change the code to:
113 * setcc cc, f1 => b
114 * ...
115 * nop
116 * ...
117 * jcc !cc|cc, f1 (OR cmov !cc|cc, f, t, d)
119 * Later, vasm-dead will clean up the nop, and the setcc if b became dead.
121 * During the search, any other instruction that has a status flag result
122 * will reset the pattern matcher. No instruction can "kill" flags,
123 * since flags are SSA variables. However the transformation we want to
124 * make extends the setcc flags lifetime, and we don't want it to overlap
125 * another flag's lifetime.
127 void fuseBranches(Vunit& unit) {
128 auto blocks = sortBlocks(unit);
129 jit::vector<unsigned> uses(unit.next_vr);
130 for (auto b : blocks) {
131 for (auto& inst : unit.blocks[b].code) {
132 visitUses(unit, inst, [&] (Vreg r) { uses[r]++; });
135 bool should_print = false;
136 for (auto b : blocks) {
137 auto& code = unit.blocks[b].code;
138 ConditionCode cc;
139 Vreg setcc_flags, testb_flags;
140 VregSet setcc_dest;
141 unsigned testb_index;
142 for (unsigned i = 0, n = code.size(); i < n; ++i) {
143 if (code[i].op == Vinstr::setcc && code[i].setcc_.d.isVirt()) {
144 cc = code[i].setcc_.cc;
145 setcc_flags = code[i].setcc_.sf;
146 setcc_dest.reset();
147 setcc_dest.add(code[i].setcc_.d);
148 continue;
150 if (setcc_flags.isValid()) {
151 if (match_testb(code[i], setcc_dest) &&
152 uses[code[i].testb_.sf] == 1) {
153 testb_flags = code[i].testb_.sf;
154 testb_index = i;
155 continue;
158 if (auto const d = match_copy(unit, code[i], setcc_dest);
159 d.isValid() && d.isVirt()) {
160 setcc_dest.add(d);
161 continue;
165 if (match_jcc(code[i], testb_flags)) {
166 code[testb_index] = nop{}; // erase the testb
167 auto& jcc = code[i].jcc_;
168 jcc.cc = jcc.cc == CC_NE ? cc : ccNegate(cc);
169 jcc.sf = setcc_flags;
170 should_print = true;
171 continue;
174 // Check for different cmov flavors
175 #define CMOV_IMPL(type) \
176 if (match_##type(code[i], testb_flags)) { \
177 code[testb_index] = nop{}; \
178 auto& cmov = code[i].type##_; \
179 cmov.cc = cmov.cc == CC_NE ? cc : ccNegate(cc); \
180 cmov.sf = setcc_flags; \
181 should_print = true; \
182 continue; \
184 CMOV_IMPL(cmovb);
185 CMOV_IMPL(cmovw);
186 CMOV_IMPL(cmovl);
187 CMOV_IMPL(cmovq);
188 #undef CMOV_IMPL
190 if (setcc_flags.isValid() && sets_flags(unit, code[i])) {
191 setcc_flags = testb_flags = Vreg{};
192 setcc_dest.reset();
196 if (should_print) {
197 printUnit(kVasmFusionLevel, "after vasm-fusion", unit);