2 +----------------------------------------------------------------------+
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 #include "hphp/vixl/a64/assembler-a64.h"
27 #include <boost/dynamic_bitset.hpp>
31 namespace HPHP
{ namespace jit
{
35 jit::vector
<bool> used
;
36 jit::vector
<uint64_t> vals
;
37 boost::dynamic_bitset
<> valid
;
39 explicit ImmFolder(jit::vector
<bool>&& used_in
)
40 : used(std::move(used_in
)) { }
43 bool match_byte(Vreg r
, int& val
) {
44 if (!valid
.test(r
)) return false;
46 if (!deltaFits(imm64
, sz::byte
)) return false;
50 bool match_int(Vreg r
, int& val
) {
51 if (!valid
.test(r
)) return false;
53 if (!deltaFits(imm64
, sz::dword
)) return false;
58 template<class Inst
> void fold(Inst
&, Vinstr
& out
) {}
59 void fold(addq
& in
, Vinstr
& out
) {
61 if (match_int(in
.s0
, val
)) {
62 if (val
== 0 && !used
[in
.sf
]) { // nop sets no flags.
63 out
= copy
{in
.s1
, in
.d
};
64 } else if (val
== 1 && !used
[in
.sf
]) { // CF not set by inc.
65 out
= incq
{in
.s1
, in
.d
, in
.sf
};
67 out
= addqi
{val
, in
.s1
, in
.d
, in
.sf
};
69 } else if (match_int(in
.s1
, val
)) {
70 if (val
== 0 && !used
[in
.sf
]) { // nop sets no flags.
71 out
= copy
{in
.s0
, in
.d
};
72 } else if (val
== 1 && !used
[in
.sf
]) { // CF not set by inc.
73 out
= incq
{in
.s0
, in
.d
, in
.sf
};
75 out
= addqi
{val
, in
.s0
, in
.d
, in
.sf
};
79 void fold(andq
& in
, Vinstr
& out
) {
81 if (match_int(in
.s0
, val
)) { out
= andqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
82 else if (match_int(in
.s1
, val
)) { out
= andqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
84 void fold(cmpb
& in
, Vinstr
& out
) {
86 if (match_byte(in
.s0
, val
)) { out
= cmpbi
{val
, in
.s1
, in
.sf
}; }
88 void fold(cmpq
& in
, Vinstr
& out
) {
90 if (match_int(in
.s0
, val
)) { out
= cmpqi
{val
, in
.s1
, in
.sf
}; }
92 void fold(cmpqm
& in
, Vinstr
& out
) {
94 if (match_int(in
.s0
, val
)) { out
= cmpqim
{val
, in
.s1
, in
.sf
}; }
96 void fold(cmplm
& in
, Vinstr
& out
) {
98 if (match_int(in
.s0
, val
)) { out
= cmplim
{val
, in
.s1
, in
.sf
}; }
100 void fold(orq
& in
, Vinstr
& out
) {
102 if (match_int(in
.s0
, val
)) { out
= orqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
103 else if (match_int(in
.s1
, val
)) { out
= orqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
105 void fold(storeb
& in
, Vinstr
& out
) {
107 if (out
.origin
&& out
.origin
->marker().sk().prologue()) return;
108 if (match_byte(in
.s
, val
)) { out
= storebi
{val
, in
.m
}; }
110 void fold(storel
& in
, Vinstr
& out
) {
112 if (match_int(in
.s
, val
)) { out
= storeli
{val
, in
.m
}; }
114 void fold(store
& in
, Vinstr
& out
) {
116 if (match_int(in
.s
, val
)) { out
= storeqi
{val
, in
.d
}; }
118 void fold(subq
& in
, Vinstr
& out
) {
120 if (match_int(in
.s0
, val
)) {
121 if (val
== 0 && !used
[in
.sf
]) { // copy sets no flags.
122 out
= copy
{in
.s1
, in
.d
};
123 } else if (val
== 1 && !used
[in
.sf
]) { // CF not set by dec.
124 out
= decq
{in
.s1
, in
.d
, in
.sf
};
126 out
= subqi
{val
, in
.s1
, in
.d
, in
.sf
};
128 } else if (match_int(in
.s1
, val
)) {
129 if (val
== 0) out
= neg
{in
.s0
, in
.d
, in
.sf
};
132 void fold(subqi
& in
, Vinstr
& out
) {
133 if (in
.s0
.l() == 0 && !used
[in
.sf
]) { // copy sets no flags.
134 out
= copy
{in
.s1
, in
.d
};
137 // xor clears CF, OF. ZF, SF, PF set accordingly
138 void fold(xorb
& in
, Vinstr
& out
) {
140 if (match_byte(in
.s0
, val
)) {
141 if (val
== 0 && !used
[in
.sf
]) { // copy doesn't set any flags.
142 out
= copy
{in
.s1
, in
.d
};
143 } else if (val
== -1 && !used
[in
.sf
]) { // not doesn't set any flags.
144 out
= notb
{in
.s1
, in
.d
};
146 out
= xorbi
{val
, in
.s1
, in
.d
, in
.sf
};
148 } else if (match_byte(in
.s1
, val
)) {
149 if (val
== 0 && !used
[in
.sf
]) { // copy doesn't set any flags.
150 out
= copy
{in
.s0
, in
.d
};
151 } else if (val
== -1 && !used
[in
.sf
]) { // not doesn't set any flags.
152 out
= notb
{in
.s0
, in
.d
};
154 out
= xorbi
{val
, in
.s0
, in
.d
, in
.sf
};
158 void fold(xorq
& in
, Vinstr
& out
) {
160 if (match_int(in
.s0
, val
)) {
161 if (val
== 0 && !used
[in
.sf
]) { // copy doesn't set any flags
162 out
= copy
{in
.s1
, in
.d
};
163 } else if (val
== -1 && !used
[in
.sf
]) { // not doesn't set any flags
164 out
= not{in
.s1
, in
.d
};
166 out
= xorqi
{val
, in
.s1
, in
.d
, in
.sf
};
168 } else if (match_int(in
.s1
, val
)) {
169 if (val
== 0 && !used
[in
.sf
]) { // copy doesn't set any flags
170 out
= copy
{in
.s0
, in
.d
};
171 } else if (val
== -1 && !used
[in
.sf
]) { // not doesn't set any flags
172 out
= not{in
.s0
, in
.d
};
174 out
= xorqi
{val
, in
.s0
, in
.d
, in
.sf
};
178 void fold(movzbl
& in
, Vinstr
& out
) {
180 if (match_byte(in
.s
, val
)) {
181 out
= copy
{in
.s
, in
.d
};
183 vals
[in
.d
] = vals
[in
.s
];
186 void fold(load
& in
, Vinstr
& out
) {
188 if (in
.s
.index
.isValid() && in
.s
.scale
== 1 && match_int(in
.s
.index
, val
) &&
189 deltaFits(int64_t(in
.s
.disp
) + val
, sz::dword
)) {
190 // index is const: [base+disp+index*1] => [base+(disp+index)]
200 jit::vector
<uint64_t> vals
;
201 boost::dynamic_bitset
<> valid
;
203 explicit ImmFolder(jit::vector
<bool>&&) {}
205 bool arith_imm(Vreg r
, int32_t& out
) {
206 if (!valid
.test(r
)) return false;
207 auto imm64
= vals
[r
];
208 if (!vixl::Assembler::IsImmArithmetic(imm64
)) return false;
209 out
= safe_cast
<int32_t>(imm64
);
212 bool logical_imm(Vreg r
, int32_t& out
) {
213 if (!valid
.test(r
)) return false;
214 auto imm64
= vals
[r
];
215 if (!vixl::Assembler::IsImmLogical(imm64
, vixl::kXRegSize
)) return false;
216 if (!deltaFits(imm64
, sz::word
)) return false;
217 out
= safe_cast
<int32_t>(imm64
);
220 bool zero_imm(Vreg r
) {
221 if (!valid
.test(r
)) return false;
225 template<typename Inst
>
226 void fold(Inst
& i
, Vinstr
& out
) {}
228 void fold(addq
& in
, Vinstr
& out
) {
230 if (arith_imm(in
.s0
, val
)) { out
= addqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
231 else if (arith_imm(in
.s1
, val
)) { out
= addqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
233 void fold(andq
& in
, Vinstr
& out
) {
235 if (logical_imm(in
.s0
, val
)) { out
= andqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
236 else if (logical_imm(in
.s1
, val
)) { out
= andqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
238 void fold(cmpl
& in
, Vinstr
& out
) {
240 if (arith_imm(in
.s0
, val
)) { out
= cmpli
{val
, in
.s1
, in
.sf
}; }
242 void fold(cmpq
& in
, Vinstr
& out
) {
244 if (arith_imm(in
.s0
, val
)) { out
= cmpqi
{val
, in
.s1
, in
.sf
}; }
246 void fold(orq
& in
, Vinstr
& out
) {
248 if (logical_imm(in
.s0
, val
)) { out
= orqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
249 else if (logical_imm(in
.s1
, val
)) { out
= orqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
251 void fold(store
& in
, Vinstr
& out
) {
252 if (zero_imm(in
.s
)) out
= store
{PhysReg(vixl::xzr
), in
.d
};
254 void fold(storeqi
& in
, Vinstr
& out
) {
255 if (in
.s
.q() == 0) out
= store
{PhysReg(vixl::xzr
), in
.m
};
257 void fold(storel
& in
, Vinstr
& out
) {
258 if (zero_imm(in
.s
)) out
= storel
{PhysReg(vixl::wzr
), in
.m
};
260 void fold(storeli
& in
, Vinstr
& out
) {
261 if (in
.s
.l() == 0) out
= storel
{PhysReg(vixl::wzr
), in
.m
};
263 void fold(subq
& in
, Vinstr
& out
) {
265 if (arith_imm(in
.s0
, val
)) { out
= subqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
267 void fold(xorq
& in
, Vinstr
& out
) {
269 if (logical_imm(in
.s0
, val
)) { out
= xorqi
{val
, in
.s1
, in
.d
, in
.sf
}; }
270 else if (logical_imm(in
.s1
, val
)) { out
= xorqi
{val
, in
.s0
, in
.d
, in
.sf
}; }
275 // Immediate-folding. If an instruction takes a register operand defined
276 // as a constant, and there is valid immediate-form of that instruction,
277 // then change the instruction and embed the immediate.
278 template<typename Folder
>
279 void foldImms(Vunit
& unit
) {
280 assertx(check(unit
)); // especially, SSA
281 // block order doesn't matter, but only visit reachable blocks.
282 auto blocks
= sortBlocks(unit
);
284 // Use flag for each registers. If a SR is used then
285 // certain optimizations will not fire since they do not
286 // set the condition codes as the original instruction(s)
288 jit::vector
<bool> used(unit
.next_vr
);
289 for (auto b
: blocks
) {
290 for (auto& inst
: unit
.blocks
[b
].code
) {
291 visitUses(unit
, inst
, [&](Vreg r
) { used
[r
] = true; });
295 Folder
folder(std::move(used
));
296 folder
.vals
.resize(unit
.next_vr
);
297 folder
.valid
.resize(unit
.next_vr
);
298 // figure out which Vregs are constants and stash their values.
299 for (auto& entry
: unit
.constants
) {
300 folder
.valid
.set(entry
.second
);
301 folder
.vals
[entry
.second
] = entry
.first
.val
;
303 // now mutate instructions
304 for (auto b
: blocks
) {
305 for (auto& inst
: unit
.blocks
[b
].code
) {
307 #define O(name, imms, uses, defs)\
308 case Vinstr::name: {\
309 auto origin = inst.origin;\
310 folder.fold(inst.name##_, inst);\
311 inst.origin = origin;\
319 printUnit(kVasmImmsLevel
, "after foldImms", unit
);
322 template void foldImms
<x64::ImmFolder
>(Vunit
& unit
);
323 template void foldImms
<arm::ImmFolder
>(Vunit
& unit
);