Convert resume and retransopt to vasm
[hiphop-php.git] / hphp / runtime / vm / jit / vasm-check.cpp
blob4e62ddc4ce614410ca02bf7fc4b6b6dd1e06c211
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-x64.h"
18 #include "hphp/runtime/vm/jit/vasm-print.h"
19 #include "hphp/util/assertions.h"
20 #include <boost/dynamic_bitset.hpp>
22 TRACE_SET_MOD(vasm);
24 namespace HPHP { namespace jit {
26 namespace {
28 typedef boost::dynamic_bitset<> Bits;
29 bool checkSSA(Vunit& unit, jit::vector<Vlabel>& blocks) DEBUG_ONLY;
30 bool checkSSA(Vunit& unit, jit::vector<Vlabel>& blocks) {
31 using namespace reg;
32 jit::vector<Bits> block_defs(unit.blocks.size()); // index by [Vlabel]
33 Bits global_defs(unit.next_vr);
34 Bits consts(unit.next_vr);
35 for (auto& c : unit.cpool) {
36 global_defs.set(c.second);
37 consts.set(c.second);
39 for (auto b : blocks) {
40 Bits local_defs;
41 if (block_defs[b].empty()) {
42 local_defs.resize(unit.next_vr);
43 for (auto& c : unit.cpool) {
44 local_defs.set(c.second);
46 } else {
47 local_defs = block_defs[b];
49 for (auto& inst : unit.blocks[b].code) {
50 visitUses(unit, inst, [&](Vreg v) {
51 assert_flog(v.isValid(), "invalid vreg used in B{}\n{}",
52 size_t(b), show(unit));
53 assert_flog(!v.isVirt() || local_defs[v],
54 "%{} used before def in B{}\n{}",
55 size_t(v), size_t(b), show(unit));
56 });
57 visitDefs(unit, inst, [&](Vreg v) {
58 assert_flog(v.isValid(), "invalid vreg defined in B{}\n{}",
59 size_t(b), show(unit));
60 assert_flog(!v.isVirt() || !consts.test(v),
61 "%{} const defined in B{}\n{}",
62 size_t(v), size_t(b), show(unit));
63 assert_flog(!v.isVirt() || !local_defs[v],
64 "%{} locally redefined in B{}\n{}",
65 size_t(v), size_t(b), show(unit));
66 assert_flog(!v.isVirt() || !global_defs[v],
67 "%{} redefined in B{}\n{}",
68 size_t(v), size_t(b), show(unit));
69 local_defs.set(v);
70 global_defs.set(v);
71 });
73 auto& block = unit.blocks[b];
74 auto lastOp = block.code.back().op;
75 if (lastOp == Vinstr::phijmp || lastOp == Vinstr::phijcc) {
76 for (DEBUG_ONLY auto s : succs(block)) {
77 assert_flog(!unit.blocks[s].code.empty()
78 && unit.blocks[s].code.front().op == Vinstr::phidef,
79 "B{} ends in {} but successor B{} doesn't begin with phidef\n",
80 size_t(b), vinst_names[lastOp], size_t(s));
83 for (auto s : succs(block)) {
84 if (block_defs[s].empty()) {
85 block_defs[s] = local_defs;
86 } else {
87 block_defs[s] &= local_defs;
91 return true;
94 // make sure syncpoint{}, nothrow{}, or unwind{} only appear immediately
95 // after a call.
96 bool checkCalls(Vunit& unit, jit::vector<Vlabel>& blocks) DEBUG_ONLY;
97 bool checkCalls(Vunit& unit, jit::vector<Vlabel>& blocks) {
98 for (auto b: blocks) {
99 bool unwind_valid = false;
100 bool nothrow_valid = false;
101 bool sync_valid = false;
102 bool hcunwind_valid = false;
103 bool hcsync_valid = false;
104 bool hcnocatch_valid = false;
105 for (auto& inst : unit.blocks[b].code) {
106 switch (inst.op) {
107 case Vinstr::call:
108 case Vinstr::callm:
109 case Vinstr::callr:
110 case Vinstr::mccall:
111 case Vinstr::vcall:
112 sync_valid = unwind_valid = nothrow_valid = true;
113 break;
114 case Vinstr::syncpoint:
115 assert(sync_valid);
116 sync_valid = false;
117 break;
118 case Vinstr::unwind:
119 assert(unwind_valid);
120 unwind_valid = nothrow_valid = false;
121 break;
122 case Vinstr::nothrow:
123 assert(nothrow_valid);
124 unwind_valid = nothrow_valid = false;
125 break;
126 case Vinstr::hostcall:
127 hcsync_valid = hcunwind_valid = hcnocatch_valid = true;
128 break;
129 case Vinstr::hcsync:
130 assert(hcsync_valid);
131 hcsync_valid = false;
132 break;
133 case Vinstr::hcunwind:
134 assert(hcunwind_valid);
135 hcunwind_valid = hcnocatch_valid = false;
136 break;
137 case Vinstr::hcnocatch:
138 assert(hcnocatch_valid);
139 hcunwind_valid = hcnocatch_valid = false;
140 break;
141 default:
142 unwind_valid = nothrow_valid = sync_valid = false;
143 hcunwind_valid = hcnocatch_valid = hcsync_valid = false;
144 break;
148 return true;
153 bool isBlockEnd(Vinstr& inst) {
154 switch (inst.op) {
155 // service request-y things
156 case Vinstr::bindjcc1st:
157 case Vinstr::bindjmp:
158 case Vinstr::fallback:
159 case Vinstr::svcreq:
160 // control flow
161 case Vinstr::jcc:
162 case Vinstr::jmp:
163 case Vinstr::jmpr:
164 case Vinstr::jmpm:
165 case Vinstr::phijmp:
166 case Vinstr::phijcc:
167 // terminal
168 case Vinstr::ud2:
169 case Vinstr::unwind:
170 case Vinstr::vinvoke:
171 case Vinstr::ret:
172 case Vinstr::end:
173 // arm specific
174 case Vinstr::hcunwind:
175 case Vinstr::cbcc:
176 case Vinstr::tbcc:
177 case Vinstr::brk:
178 return true;
179 default:
180 return false;
184 // check that each block has exactly one terminal instruction at the end.
185 bool checkBlockEnd(Vunit& unit, Vlabel b) {
186 assert(!unit.blocks[b].code.empty());
187 auto& block = unit.blocks[b];
188 auto n = block.code.size();
189 for (size_t i = 0; i < n - 1; ++i) {
190 assert(!isBlockEnd(block.code[i]));
192 assert(isBlockEnd(block.code[n - 1]));
193 return true;
196 bool check(Vunit& unit) {
197 auto blocks = sortBlocks(unit);
198 assert(checkSSA(unit, blocks));
199 assert(checkCalls(unit, blocks));
200 return true;