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-x64.h"
18 #include "hphp/runtime/vm/jit/vasm-print.h"
19 #include "hphp/util/assertions.h"
20 #include <boost/dynamic_bitset.hpp>
24 namespace HPHP
{ namespace jit
{
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
) {
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
);
39 for (auto b
: blocks
) {
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
);
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
));
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
));
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
;
87 block_defs
[s
] &= local_defs
;
94 // make sure syncpoint{}, nothrow{}, or unwind{} only appear immediately
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
) {
112 sync_valid
= unwind_valid
= nothrow_valid
= true;
114 case Vinstr::syncpoint
:
119 assert(unwind_valid
);
120 unwind_valid
= nothrow_valid
= false;
122 case Vinstr::nothrow
:
123 assert(nothrow_valid
);
124 unwind_valid
= nothrow_valid
= false;
126 case Vinstr::hostcall
:
127 hcsync_valid
= hcunwind_valid
= hcnocatch_valid
= true;
130 assert(hcsync_valid
);
131 hcsync_valid
= false;
133 case Vinstr::hcunwind
:
134 assert(hcunwind_valid
);
135 hcunwind_valid
= hcnocatch_valid
= false;
137 case Vinstr::hcnocatch
:
138 assert(hcnocatch_valid
);
139 hcunwind_valid
= hcnocatch_valid
= false;
142 unwind_valid
= nothrow_valid
= sync_valid
= false;
143 hcunwind_valid
= hcnocatch_valid
= hcsync_valid
= false;
153 bool isBlockEnd(Vinstr
& inst
) {
155 // service request-y things
156 case Vinstr::bindjcc1st
:
157 case Vinstr::bindjmp
:
158 case Vinstr::fallback
:
170 case Vinstr::vinvoke
:
174 case Vinstr::hcunwind
:
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]));
196 bool check(Vunit
& unit
) {
197 auto blocks
= sortBlocks(unit
);
198 assert(checkSSA(unit
, blocks
));
199 assert(checkCalls(unit
, blocks
));