Switch-related cleanup
[hiphop-php.git] / hphp / runtime / vm / jit / vasm-fold-imms.cpp
blob5e1400151fdd6e29f07fc830b8a1691f38b90547
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 #include "hphp/vixl/a64/assembler-a64.h"
27 #include <boost/dynamic_bitset.hpp>
29 TRACE_SET_MOD(hhir);
31 namespace HPHP { namespace jit {
33 namespace x64 {
34 struct ImmFolder {
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)) { }
42 // helpers
43 bool match_byte(Vreg r, int& val) {
44 if (!valid.test(r)) return false;
45 auto imm64 = vals[r];
46 if (!deltaFits(imm64, sz::byte)) return false;
47 val = imm64;
48 return true;
50 bool match_int(Vreg r, int& val) {
51 if (!valid.test(r)) return false;
52 auto imm64 = vals[r];
53 if (!deltaFits(imm64, sz::dword)) return false;
54 val = imm64;
55 return true;
57 // folders
58 template<class Inst> void fold(Inst&, Vinstr& out) {}
59 void fold(addq& in, Vinstr& out) {
60 int val;
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};
66 } else {
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};
74 } else {
75 out = addqi{val, in.s0, in.d, in.sf};
79 void fold(andq& in, Vinstr& out) {
80 int val;
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) {
85 int val;
86 if (match_byte(in.s0, val)) { out = cmpbi{val, in.s1, in.sf}; }
88 void fold(cmpq& in, Vinstr& out) {
89 int val;
90 if (match_int(in.s0, val)) { out = cmpqi{val, in.s1, in.sf}; }
92 void fold(cmpqm& in, Vinstr& out) {
93 int val;
94 if (match_int(in.s0, val)) { out = cmpqim{val, in.s1, in.sf}; }
96 void fold(cmplm& in, Vinstr& out) {
97 int val;
98 if (match_int(in.s0, val)) { out = cmplim{val, in.s1, in.sf}; }
100 void fold(orq& in, Vinstr& out) {
101 int val;
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) {
106 int val;
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) {
111 int val;
112 if (match_int(in.s, val)) { out = storeli{val, in.m}; }
114 void fold(store& in, Vinstr& out) {
115 int val;
116 if (match_int(in.s, val)) { out = storeqi{val, in.d}; }
118 void fold(subq& in, Vinstr& out) {
119 int val;
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};
125 } else {
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) {
139 int val;
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};
145 } else {
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};
153 } else {
154 out = xorbi{val, in.s0, in.d, in.sf};
158 void fold(xorq& in, Vinstr& out) {
159 int val;
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};
165 } else {
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};
173 } else {
174 out = xorqi{val, in.s0, in.d, in.sf};
178 void fold(movzbl& in, Vinstr& out) {
179 int val;
180 if (match_byte(in.s, val)) {
181 out = copy{in.s, in.d};
182 valid.set(in.d);
183 vals[in.d] = vals[in.s];
186 void fold(load& in, Vinstr& out) {
187 int val;
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)]
191 in.s.index = Vreg{};
192 in.s.disp += val;
196 } // namespace x64
198 namespace arm {
199 struct ImmFolder {
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);
210 return true;
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);
218 return true;
220 bool zero_imm(Vreg r) {
221 if (!valid.test(r)) return false;
222 return vals[r] == 0;
225 template<typename Inst>
226 void fold(Inst& i, Vinstr& out) {}
228 void fold(addq& in, Vinstr& out) {
229 int val;
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) {
234 int val;
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) {
239 int val;
240 if (arith_imm(in.s0, val)) { out = cmpli{val, in.s1, in.sf}; }
242 void fold(cmpq& in, Vinstr& out) {
243 int val;
244 if (arith_imm(in.s0, val)) { out = cmpqi{val, in.s1, in.sf}; }
246 void fold(orq& in, Vinstr& out) {
247 int val;
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) {
264 int val;
265 if (arith_imm(in.s0, val)) { out = subqi{val, in.s1, in.d, in.sf}; }
267 void fold(xorq& in, Vinstr& out) {
268 int val;
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)
287 // would.
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) {
306 switch (inst.op) {
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;\
312 break;\
314 VASM_OPCODES
315 #undef O
319 printUnit(kVasmImmsLevel, "after foldImms", unit);
322 template void foldImms<x64::ImmFolder>(Vunit& unit);
323 template void foldImms<arm::ImmFolder>(Vunit& unit);