blob: 4aac075c7bffe01db0f67b5979aa862deaa22049 [file] [log] [blame]
/*
* Copyright (C) 2015 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 "B3Validate.h"
#if ENABLE(B3_JIT)
#include "B3ArgumentRegValue.h"
#include "B3BasicBlockInlines.h"
#include "B3MemoryValue.h"
#include "B3Procedure.h"
#include "B3StackSlotValue.h"
#include "B3UpsilonValue.h"
#include "B3ValueInlines.h"
#include <wtf/HashSet.h>
#include <wtf/StringPrintStream.h>
#include <wtf/text/CString.h>
namespace JSC { namespace B3 {
namespace {
class Validater {
public:
Validater(Procedure& procedure, const char* dumpBefore)
: m_procedure(procedure)
, 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<BasicBlock*> blocks;
HashSet<Value*> valueInProc;
HashMap<Value*, unsigned> valueInBlock;
for (BasicBlock* block : m_procedure) {
blocks.add(block);
for (Value* value : *block)
valueInBlock.add(value, 0).iterator->value++;
}
for (Value* value : m_procedure.values())
valueInProc.add(value);
for (Value* value : valueInProc)
VALIDATE(valueInBlock.contains(value), ("At ", *value));
for (auto& entry : valueInBlock) {
VALIDATE(valueInProc.contains(entry.key), ("At ", *entry.key));
VALIDATE(entry.value == 1, ("At ", *entry.key));
}
for (Value* value : valueInProc) {
for (Value* child : value->children()) {
VALIDATE(child, ("At ", *value));
VALIDATE(valueInProc.contains(child), ("At ", *value, "->", pointerDump(child)));
}
}
HashMap<BasicBlock*, HashSet<BasicBlock*>> allPredecessors;
for (BasicBlock* block : blocks) {
VALIDATE(block->size() >= 1, ("At ", *block));
for (unsigned i = 0; i < block->size() - 1; ++i)
VALIDATE(!ControlValue::accepts(block->at(i)->opcode()), ("At ", *block->at(i)));
VALIDATE(ControlValue::accepts(block->last()->opcode()), ("At ", *block->last()));
for (BasicBlock* successor : block->successorBlocks()) {
allPredecessors.add(successor, HashSet<BasicBlock*>()).iterator->value.add(block);
VALIDATE(
blocks.contains(successor), ("At ", *block, "->", pointerDump(successor)));
}
}
// Note that this totally allows dead code.
for (auto& entry : allPredecessors) {
BasicBlock* successor = entry.key;
HashSet<BasicBlock*>& predecessors = entry.value;
VALIDATE(predecessors == successor->predecessors(), ("At ", *successor));
}
for (Value* value : m_procedure.values()) {
for (Value* child : value->children())
VALIDATE(child->type() != Void, ("At ", *value, "->", *child));
switch (value->opcode()) {
case Nop:
case Jump:
case Oops:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
break;
case Identity:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
VALIDATE(value->type() != Void, ("At ", *value));
break;
case Const32:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case Const64:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() == Int64, ("At ", *value));
break;
case ConstDouble:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() == Double, ("At ", *value));
break;
case StackSlot:
case FramePointer:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() == pointerType(), ("At ", *value));
break;
case ArgumentReg:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(
(value->as<ArgumentRegValue>()->argumentReg().isGPR() ? pointerType() : Double)
== value->type(), ("At ", *value));
break;
case Add:
case Sub:
case Mul:
case Div:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
VALIDATE(value->type() == value->child(1)->type(), ("At ", *value));
VALIDATE(value->type() != Void, ("At ", *value));
break;
case ChillDiv:
case Mod:
case BitAnd:
case BitOr:
case BitXor:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
VALIDATE(value->type() == value->child(1)->type(), ("At ", *value));
VALIDATE(isInt(value->type()), ("At ", *value));
break;
case Shl:
case SShr:
case ZShr:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
VALIDATE(value->child(1)->type() == Int32, ("At ", *value));
VALIDATE(isInt(value->type()), ("At ", *value));
break;
case BitwiseCast:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->type() != value->child(0)->type(), ("At ", *value));
VALIDATE(
(value->type() == Int64 && value->child(0)->type() == Double)
|| (value->type() == Double && value->child(0)->type() == Int64),
("At ", *value));
break;
case SExt8:
case SExt16:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case SExt32:
case ZExt32:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
VALIDATE(value->type() == Int64, ("At ", *value));
break;
case Trunc:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == Int64, ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case Sqrt:
case FRound:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == Double, ("At ", *value));
VALIDATE(value->type() == Double, ("At ", *value));
break;
case IToD:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(value->type() == Double, ("At ", *value));
break;
case DToI32:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == Double, ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case Equal:
case NotEqual:
case LessThan:
case GreaterThan:
case LessEqual:
case GreaterEqual:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->child(0)->type() == value->child(1)->type(), ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case Above:
case Below:
case AboveEqual:
case BelowEqual:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->child(0)->type() == value->child(1)->type(), ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
break;
case Select:
VALIDATE(value->numChildren() == 3, ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(value->type() == value->child(1)->type(), ("At ", *value));
VALIDATE(value->type() == value->child(2)->type(), ("At ", *value));
break;
case Load8Z:
case Load8S:
case Load16Z:
case Load16S:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() == Int32, ("At ", *value));
validateStackAccess(value);
break;
case LoadFloat:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() == Double, ("At ", *value));
validateStackAccess(value);
break;
case Load:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() != Void, ("At ", *value));
validateStackAccess(value);
break;
case Store8:
case Store16:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
VALIDATE(value->child(1)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
validateStackAccess(value);
break;
case StoreFloat:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->child(0)->type() == Double, ("At ", *value));
VALIDATE(value->child(1)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
validateStackAccess(value);
break;
case Store:
VALIDATE(value->numChildren() == 2, ("At ", *value));
VALIDATE(value->child(1)->type() == pointerType(), ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
validateStackAccess(value);
break;
case CCall:
// This is a wildcard. You can pass any non-void arguments and you can select any
// return type.
break;
case Patchpoint:
validateStackmap(value);
break;
case CheckAdd:
case CheckSub:
case CheckMul:
VALIDATE(value->numChildren() >= 2, ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(isInt(value->child(1)->type()), ("At ", *value));
VALIDATE(value->as<StackmapValue>()->constrainedChild(0).rep() == ValueRep::Any, ("At ", *value));
VALIDATE(value->as<StackmapValue>()->constrainedChild(1).rep() == ValueRep::Any, ("At ", *value));
validateStackmap(value);
break;
case Check:
VALIDATE(value->numChildren() >= 1, ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(value->as<StackmapValue>()->constrainedChild(0).rep() == ValueRep::Any, ("At ", *value));
validateStackmap(value);
break;
case Upsilon:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->as<UpsilonValue>()->phi(), ("At ", *value));
VALIDATE(value->child(0)->type() == value->as<UpsilonValue>()->phi()->type(), ("At ", *value));
VALIDATE(valueInProc.contains(value->as<UpsilonValue>()->phi()), ("At ", *value));
break;
case Phi:
VALIDATE(!value->numChildren(), ("At ", *value));
VALIDATE(value->type() != Void, ("At ", *value));
break;
case Return:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
break;
case Branch:
case Switch:
VALIDATE(value->numChildren() == 1, ("At ", *value));
VALIDATE(isInt(value->child(0)->type()), ("At ", *value));
VALIDATE(value->type() == Void, ("At ", *value));
break;
}
}
}
private:
void validateStackmap(Value* value)
{
StackmapValue* stackmap = value->as<StackmapValue>();
VALIDATE(stackmap, ("At ", *value));
VALIDATE(stackmap->numChildren() >= stackmap->reps().size(), ("At ", *stackmap));
for (unsigned i = 0; i < stackmap->reps().size(); ++i) {
const ValueRep& rep = stackmap->reps()[i];
if (rep.kind() != ValueRep::Register)
continue;
if (rep.reg().isGPR())
VALIDATE(isInt(stackmap->child(i)->type()), ("At ", *stackmap));
else
VALIDATE(isFloat(stackmap->child(i)->type()), ("At ", *stackmap));
}
}
void validateStackAccess(Value* value)
{
MemoryValue* memory = value->as<MemoryValue>();
StackSlotValue* stack = value->lastChild()->as<StackSlotValue>();
if (!stack)
return;
VALIDATE(memory->offset() >= 0, ("At ", *value));
VALIDATE(memory->offset() + memory->accessByteSize() <= stack->byteSize(), ("At ", *value));
}
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("B3 VALIDATION FAILURE\n");
out.print(" ", condition, " (", filename, ":", lineNumber, ")\n");
out.print(" ", message, "\n");
out.print(" After ", m_procedure.lastPhaseName(), "\n");
failureMessage = out.toCString();
}
dataLog(failureMessage);
if (m_dumpBefore) {
dataLog("Before ", m_procedure.lastPhaseName(), ":\n");
dataLog(m_dumpBefore);
}
dataLog("At time of failure:\n");
dataLog(m_procedure);
dataLog(failureMessage);
WTFReportAssertionFailure(filename, lineNumber, function, condition);
CRASH();
}
Procedure& m_procedure;
const char* m_dumpBefore;
};
} // anonymous namespace
void validate(Procedure& procedure, const char* dumpBefore)
{
Validater validater(procedure, dumpBefore);
validater.run();
}
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)