2 +----------------------------------------------------------------------+
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"
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
&&
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
) {
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
];
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); \
72 bool sets_flags(const Vunit
& unit
, const Vinstr
& inst
) {
73 // Some special cases that also clobber flags:
81 case Vinstr::callstub
:
82 case Vinstr::callfaststub
:
84 case Vinstr::callphpfe
:
85 case Vinstr::callphpr
:
86 case Vinstr::callphps
:
87 case Vinstr::contenter
:
94 visitDefs(unit
, inst
, [&] (Vreg r
, Width w
) {
95 if (w
== Width::Flags
) flags
= r
;
97 return flags
.isValid();
103 * Analyze blocks one at a time, looking for the sequence:
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:
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
;
139 Vreg setcc_flags
, testb_flags
;
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
;
147 setcc_dest
.add(code
[i
].setcc_
.d
);
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
;
158 if (auto const d
= match_copy(unit
, code
[i
], setcc_dest
);
159 d
.isValid() && d
.isVirt()) {
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
;
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; \
190 if (setcc_flags
.isValid() && sets_flags(unit
, code
[i
])) {
191 setcc_flags
= testb_flags
= Vreg
{};
197 printUnit(kVasmFusionLevel
, "after vasm-fusion", unit
);