1/*
2 * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AirValidate.h"
28
29#if ENABLE(B3_JIT)
30
31#include "AirCode.h"
32#include "AirInstInlines.h"
33#include "B3Procedure.h"
34
35namespace JSC { namespace B3 { namespace Air {
36
37namespace {
38
39class Validater {
40public:
41 Validater(Code& code, const char* dumpBefore)
42 : m_code(code)
43 , m_dumpBefore(dumpBefore)
44 {
45 }
46
47#define VALIDATE(condition, message) do { \
48 if (condition) \
49 break; \
50 fail(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #condition, toCString message); \
51 } while (false)
52
53 void run()
54 {
55 HashSet<StackSlot*> validSlots;
56 HashSet<BasicBlock*> validBlocks;
57 HashSet<Special*> validSpecials;
58
59 for (BasicBlock* block : m_code)
60 validBlocks.add(block);
61 for (StackSlot* slot : m_code.stackSlots())
62 validSlots.add(slot);
63 for (Special* special : m_code.specials())
64 validSpecials.add(special);
65
66 for (BasicBlock* block : m_code) {
67 // Blocks that are entrypoints must not have predecessors.
68 if (m_code.isEntrypoint(block))
69 VALIDATE(!block->numPredecessors(), ("At entrypoint ", *block));
70
71 for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
72 Inst& inst = block->at(instIndex);
73 for (Arg& arg : inst.args) {
74 switch (arg.kind()) {
75 case Arg::Stack:
76 VALIDATE(validSlots.contains(arg.stackSlot()), ("At ", inst, " in ", *block));
77 break;
78 case Arg::Special:
79 VALIDATE(validSpecials.contains(arg.special()), ("At ", inst, " in ", *block));
80 break;
81 default:
82 break;
83 }
84 }
85 VALIDATE(inst.isValidForm(), ("At ", inst, " in ", *block));
86 if (instIndex == block->size() - 1)
87 VALIDATE(inst.isTerminal(), ("At ", inst, " in ", *block));
88 else
89 VALIDATE(!inst.isTerminal(), ("At ", inst, " in ", *block));
90
91 // forEachArg must return Arg&'s that point into the args array.
92 inst.forEachArg(
93 [&] (Arg& arg, Arg::Role, Bank, Width) {
94 VALIDATE(&arg >= &inst.args[0], ("At ", arg, " in ", inst, " in ", *block));
95 VALIDATE(&arg <= &inst.args.last(), ("At ", arg, " in ", inst, " in ", *block));
96 });
97
98 switch (inst.kind.opcode) {
99 case EntrySwitch:
100 VALIDATE(block->numSuccessors() == m_code.proc().numEntrypoints(), ("At ", inst, " in ", *block));
101 break;
102 case Shuffle:
103 // We can't handle trapping shuffles because of how we lower them. That could
104 // be fixed though. Ditto for shuffles that would do fences, which is the other
105 // use of this bit.
106 VALIDATE(!inst.kind.effects, ("At ", inst, " in ", *block));
107 break;
108 default:
109 break;
110 }
111 }
112 for (BasicBlock* successor : block->successorBlocks())
113 VALIDATE(validBlocks.contains(successor), ("In ", *block));
114 }
115
116 for (BasicBlock* block : m_code) {
117 // We expect the predecessor list to be de-duplicated.
118 HashSet<BasicBlock*> predecessors;
119 for (BasicBlock* predecessor : block->predecessors())
120 predecessors.add(predecessor);
121 VALIDATE(block->numPredecessors() == predecessors.size(), ("At ", *block));
122 }
123 }
124
125private:
126 NO_RETURN_DUE_TO_CRASH void fail(
127 const char* filename, int lineNumber, const char* function, const char* condition,
128 CString message)
129 {
130 CString failureMessage;
131 {
132 StringPrintStream out;
133 out.print("AIR VALIDATION FAILURE\n");
134 out.print(" ", condition, " (", filename, ":", lineNumber, ")\n");
135 out.print(" ", message, "\n");
136 out.print(" After ", m_code.lastPhaseName(), "\n");
137 failureMessage = out.toCString();
138 }
139
140 dataLog(failureMessage);
141 if (m_dumpBefore) {
142 dataLog("Before ", m_code.lastPhaseName(), ":\n");
143 dataLog(m_dumpBefore);
144 }
145 dataLog("At time of failure:\n");
146 dataLog(m_code);
147
148 dataLog(failureMessage);
149 WTFReportAssertionFailure(filename, lineNumber, function, condition);
150 CRASH();
151 }
152
153 Code& m_code;
154 const char* m_dumpBefore;
155};
156
157} // anonymous namespace
158
159void validate(Code& code, const char* dumpBefore)
160{
161 Validater validater(code, dumpBefore);
162 validater.run();
163}
164
165} } } // namespace JSC::B3::Air
166
167#endif // ENABLE(B3_JIT)
168
169