| /* |
| * Copyright (C) 2015-2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "AirValidate.h" |
| |
| #if ENABLE(B3_JIT) |
| |
| #include "AirCode.h" |
| #include "AirInstInlines.h" |
| #include "B3Procedure.h" |
| |
| namespace JSC { namespace B3 { namespace Air { |
| |
| namespace { |
| |
| class Validater { |
| public: |
| Validater(Code& code, const char* dumpBefore) |
| : m_code(code) |
| , m_dumpBefore(dumpBefore) |
| { |
| } |
| |
| #define VALIDATE(condition, message) do { \ |
| if (condition) \ |
| break; \ |
| fail(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #condition, toCString message); \ |
| } while (false) |
| |
| void run() |
| { |
| HashSet<StackSlot*> validSlots; |
| HashSet<BasicBlock*> validBlocks; |
| HashSet<Special*> validSpecials; |
| |
| for (BasicBlock* block : m_code) |
| validBlocks.add(block); |
| for (StackSlot* slot : m_code.stackSlots()) |
| validSlots.add(slot); |
| for (Special* special : m_code.specials()) |
| validSpecials.add(special); |
| |
| for (BasicBlock* block : m_code) { |
| // Blocks that are entrypoints must not have predecessors. |
| if (m_code.isEntrypoint(block)) |
| VALIDATE(!block->numPredecessors(), ("At entrypoint ", *block)); |
| |
| for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) { |
| Inst& inst = block->at(instIndex); |
| for (Arg& arg : inst.args) { |
| switch (arg.kind()) { |
| case Arg::Stack: |
| VALIDATE(validSlots.contains(arg.stackSlot()), ("At ", inst, " in ", *block)); |
| break; |
| case Arg::Special: |
| VALIDATE(validSpecials.contains(arg.special()), ("At ", inst, " in ", *block)); |
| break; |
| default: |
| break; |
| } |
| } |
| VALIDATE(inst.isValidForm(), ("At ", inst, " in ", *block)); |
| if (instIndex == block->size() - 1) |
| VALIDATE(inst.isTerminal(), ("At ", inst, " in ", *block)); |
| else |
| VALIDATE(!inst.isTerminal(), ("At ", inst, " in ", *block)); |
| |
| // forEachArg must return Arg&'s that point into the args array. |
| inst.forEachArg( |
| [&] (Arg& arg, Arg::Role, Bank, Width) { |
| VALIDATE(&arg >= &inst.args[0], ("At ", arg, " in ", inst, " in ", *block)); |
| VALIDATE(&arg <= &inst.args.last(), ("At ", arg, " in ", inst, " in ", *block)); |
| }); |
| |
| switch (inst.kind.opcode) { |
| case EntrySwitch: |
| VALIDATE(block->numSuccessors() == m_code.proc().numEntrypoints(), ("At ", inst, " in ", *block)); |
| break; |
| case Shuffle: |
| // We can't handle trapping shuffles because of how we lower them. That could |
| // be fixed though. Ditto for shuffles that would do fences, which is the other |
| // use of this bit. |
| VALIDATE(!inst.kind.effects, ("At ", inst, " in ", *block)); |
| break; |
| default: |
| break; |
| } |
| } |
| for (BasicBlock* successor : block->successorBlocks()) |
| VALIDATE(validBlocks.contains(successor), ("In ", *block)); |
| } |
| |
| for (BasicBlock* block : m_code) { |
| // We expect the predecessor list to be de-duplicated. |
| HashSet<BasicBlock*> predecessors; |
| for (BasicBlock* predecessor : block->predecessors()) |
| predecessors.add(predecessor); |
| VALIDATE(block->numPredecessors() == predecessors.size(), ("At ", *block)); |
| } |
| } |
| |
| private: |
| NO_RETURN_DUE_TO_CRASH void fail( |
| const char* filename, int lineNumber, const char* function, const char* condition, |
| CString message) |
| { |
| CString failureMessage; |
| { |
| StringPrintStream out; |
| out.print("AIR VALIDATION FAILURE\n"); |
| out.print(" ", condition, " (", filename, ":", lineNumber, ")\n"); |
| out.print(" ", message, "\n"); |
| out.print(" After ", m_code.lastPhaseName(), "\n"); |
| failureMessage = out.toCString(); |
| } |
| |
| dataLog(failureMessage); |
| if (m_dumpBefore) { |
| dataLog("Before ", m_code.lastPhaseName(), ":\n"); |
| dataLog(m_dumpBefore); |
| } |
| dataLog("At time of failure:\n"); |
| dataLog(m_code); |
| |
| dataLog(failureMessage); |
| WTFReportAssertionFailure(filename, lineNumber, function, condition); |
| CRASH(); |
| } |
| |
| Code& m_code; |
| const char* m_dumpBefore; |
| }; |
| |
| } // anonymous namespace |
| |
| void validate(Code& code, const char* dumpBefore) |
| { |
| Validater validater(code, dumpBefore); |
| validater.run(); |
| } |
| |
| } } } // namespace JSC::B3::Air |
| |
| #endif // ENABLE(B3_JIT) |
| |