mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-23 04:09:40 +00:00
698 lines
33 KiB
C++
698 lines
33 KiB
C++
/*
|
|
* 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 "B3Validate.h"
|
|
|
|
#if ENABLE(B3_JIT)
|
|
|
|
#include "AirCode.h"
|
|
#include "B3ArgumentRegValue.h"
|
|
#include "B3AtomicValue.h"
|
|
#include "B3Dominators.h"
|
|
#include "B3MemoryValue.h"
|
|
#include "B3Procedure.h"
|
|
#include "B3ProcedureInlines.h"
|
|
#include "B3SlotBaseValue.h"
|
|
#include "B3SwitchValue.h"
|
|
#include "B3UpsilonValue.h"
|
|
#include "B3ValueInlines.h"
|
|
#include "B3Variable.h"
|
|
#include "B3VariableValue.h"
|
|
#include "B3WasmBoundsCheckValue.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;
|
|
HashMap<Value*, BasicBlock*> valueOwner;
|
|
HashMap<Value*, unsigned> valueIndex;
|
|
HashMap<Value*, Vector<Optional<Type>>> extractions;
|
|
|
|
for (unsigned tuple = 0; tuple < m_procedure.tuples().size(); ++tuple) {
|
|
VALIDATE(m_procedure.tuples()[tuple].size(), ("In tuple ", tuple));
|
|
for (unsigned i = 0; i < m_procedure.tuples()[tuple].size(); ++i)
|
|
VALIDATE(m_procedure.tuples()[tuple][i].isNumeric(), ("In tuple ", tuple, " at index", i));
|
|
}
|
|
|
|
for (BasicBlock* block : m_procedure) {
|
|
blocks.add(block);
|
|
for (unsigned i = 0; i < block->size(); ++i) {
|
|
Value* value = block->at(i);
|
|
valueInBlock.add(value, 0).iterator->value++;
|
|
valueOwner.add(value, block);
|
|
valueIndex.add(value, i);
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
// Compute dominators ourselves to avoid perturbing Procedure.
|
|
Dominators dominators(m_procedure);
|
|
|
|
for (Value* value : valueInProc) {
|
|
for (Value* child : value->children()) {
|
|
VALIDATE(child, ("At ", *value));
|
|
VALIDATE(valueInProc.contains(child), ("At ", *value, "->", pointerDump(child)));
|
|
if (valueOwner.get(child) == valueOwner.get(value))
|
|
VALIDATE(valueIndex.get(value) > valueIndex.get(child), ("At ", *value, "->", pointerDump(child)));
|
|
else
|
|
VALIDATE(dominators.dominates(valueOwner.get(child), valueOwner.get(value)), ("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(!block->at(i)->effects().terminal, ("At ", *block->at(i)));
|
|
VALIDATE(block->last()->effects().terminal, ("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 Fence:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
break;
|
|
case Identity:
|
|
case Opaque:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
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->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Int32, ("At ", *value));
|
|
break;
|
|
case Const64:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Int64, ("At ", *value));
|
|
break;
|
|
case ConstDouble:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Double, ("At ", *value));
|
|
break;
|
|
case ConstFloat:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Float, ("At ", *value));
|
|
break;
|
|
case BottomTuple:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type().isTuple(), ("At ", *value));
|
|
break;
|
|
case Set:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == value->as<VariableValue>()->variable()->type(), ("At ", *value));
|
|
break;
|
|
case Get:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == value->as<VariableValue>()->variable()->type(), ("At ", *value));
|
|
break;
|
|
case SlotBase:
|
|
case FramePointer:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == pointerType(), ("At ", *value));
|
|
break;
|
|
case ArgumentReg:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
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:
|
|
case UDiv:
|
|
case Mod:
|
|
case UMod:
|
|
case BitAnd:
|
|
case BitOr:
|
|
case BitXor:
|
|
VALIDATE(!value->kind().traps(), ("At ", *value));
|
|
switch (value->opcode()) {
|
|
case Div:
|
|
case Mod:
|
|
if (value->isChill()) {
|
|
VALIDATE(value->opcode() == Div || value->opcode() == Mod, ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
}
|
|
break;
|
|
default:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
break;
|
|
}
|
|
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().isNumeric(), ("At ", *value));
|
|
break;
|
|
case Neg:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
|
|
VALIDATE(value->type().isNumeric(), ("At ", *value));
|
|
break;
|
|
case Shl:
|
|
case SShr:
|
|
case ZShr:
|
|
case RotR:
|
|
case RotL:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 2, ("At ", *value));
|
|
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
|
|
VALIDATE(value->child(1)->type() == Int32, ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
break;
|
|
case BitwiseCast:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
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)
|
|
|| (value->type() == Float && value->child(0)->type() == Int32)
|
|
|| (value->type() == Int32 && value->child(0)->type() == Float),
|
|
("At ", *value));
|
|
break;
|
|
case SExt8:
|
|
case SExt16:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
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->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
|
|
VALIDATE(value->type() == Int64, ("At ", *value));
|
|
break;
|
|
case Clz:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
break;
|
|
case Trunc:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(
|
|
(value->type() == Int32 && value->child(0)->type() == Int64)
|
|
|| (value->type() == Float && value->child(0)->type() == Double),
|
|
("At ", *value));
|
|
break;
|
|
case Abs:
|
|
case Ceil:
|
|
case Floor:
|
|
case Sqrt:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isFloat(), ("At ", *value));
|
|
VALIDATE(value->type().isFloat(), ("At ", *value));
|
|
break;
|
|
case IToD:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type() == Double, ("At ", *value));
|
|
break;
|
|
case IToF:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type() == Float, ("At ", *value));
|
|
break;
|
|
case FloatToDouble:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == Float, ("At ", *value));
|
|
VALIDATE(value->type() == Double, ("At ", *value));
|
|
break;
|
|
case DoubleToFloat:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == Double, ("At ", *value));
|
|
VALIDATE(value->type() == Float, ("At ", *value));
|
|
break;
|
|
case Equal:
|
|
case NotEqual:
|
|
case LessThan:
|
|
case GreaterThan:
|
|
case LessEqual:
|
|
case GreaterEqual:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
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->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 2, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == value->child(1)->type(), ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type() == Int32, ("At ", *value));
|
|
break;
|
|
case EqualOrUnordered:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 2, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == value->child(1)->type(), ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isFloat(), ("At ", *value));
|
|
VALIDATE(value->type() == Int32, ("At ", *value));
|
|
break;
|
|
case Select:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 3, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("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->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
|
|
VALIDATE(value->type() == Int32, ("At ", *value));
|
|
validateFence(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case Load:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
|
|
VALIDATE(value->type().isNumeric(), ("At ", *value));
|
|
validateFence(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case Store8:
|
|
case Store16:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
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));
|
|
validateFence(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case Store:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 2, ("At ", *value));
|
|
VALIDATE(value->child(1)->type() == pointerType(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
validateFence(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case AtomicWeakCAS:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 3, ("At ", *value));
|
|
VALIDATE(value->type() == Int32, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == value->child(1)->type(), ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->child(2)->type() == pointerType(), ("At ", *value));
|
|
validateAtomic(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case AtomicStrongCAS:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 3, ("At ", *value));
|
|
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
|
|
VALIDATE(value->type() == value->child(1)->type(), ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->child(2)->type() == pointerType(), ("At ", *value));
|
|
validateAtomic(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case AtomicXchgAdd:
|
|
case AtomicXchgAnd:
|
|
case AtomicXchgOr:
|
|
case AtomicXchgSub:
|
|
case AtomicXchgXor:
|
|
case AtomicXchg:
|
|
VALIDATE(!value->kind().isChill(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 2, ("At ", *value));
|
|
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->child(1)->type() == pointerType(), ("At ", *value));
|
|
validateAtomic(value);
|
|
validateStackAccess(value);
|
|
break;
|
|
case Depend:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->type() == value->child(0)->type(), ("At ", *value));
|
|
VALIDATE(value->type().isInt(), ("At ", *value));
|
|
break;
|
|
case WasmAddress:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
|
|
VALIDATE(value->type() == pointerType(), ("At ", *value));
|
|
break;
|
|
case CCall:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() >= 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == pointerType(), ("At ", *value));
|
|
break;
|
|
case Patchpoint:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
if (value->type() == Void) {
|
|
VALIDATE(value->as<PatchpointValue>()->resultConstraints.size() == 1, ("At ", *value));
|
|
VALIDATE(value->as<PatchpointValue>()->resultConstraints[0] == ValueRep::WarmAny, ("At ", *value));
|
|
} else {
|
|
if (value->type().isNumeric()) {
|
|
VALIDATE(value->as<PatchpointValue>()->resultConstraints.size() == 1, ("At ", *value));
|
|
validateStackmapConstraint(value, ConstrainedValue(value, value->as<PatchpointValue>()->resultConstraints[0]), ConstraintRole::Def);
|
|
} else {
|
|
VALIDATE(m_procedure.isValidTuple(value->type()), ("At ", *value));
|
|
VALIDATE(value->as<PatchpointValue>()->resultConstraints.size() == m_procedure.tupleForType(value->type()).size(), ("At ", *value));
|
|
for (unsigned i = 0; i < value->as<PatchpointValue>()->resultConstraints.size(); ++i)
|
|
validateStackmapConstraint(value, ConstrainedValue(value, value->as<PatchpointValue>()->resultConstraints[i]), ConstraintRole::Def, i);
|
|
}
|
|
}
|
|
validateStackmap(value);
|
|
break;
|
|
case Extract: {
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == Tuple, ("At ", *value));
|
|
VALIDATE(value->type().isNumeric(), ("At ", *value));
|
|
break;
|
|
}
|
|
case CheckAdd:
|
|
case CheckSub:
|
|
case CheckMul:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() >= 2, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->child(1)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->as<StackmapValue>()->constrainedChild(0).rep() == ValueRep::WarmAny, ("At ", *value));
|
|
VALIDATE(value->as<StackmapValue>()->constrainedChild(1).rep() == ValueRep::WarmAny, ("At ", *value));
|
|
validateStackmap(value);
|
|
break;
|
|
case Check:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() >= 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->as<StackmapValue>()->constrainedChild(0).rep() == ValueRep::WarmAny, ("At ", *value));
|
|
validateStackmap(value);
|
|
break;
|
|
case WasmBoundsCheck:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() == Int32, ("At ", *value));
|
|
switch (value->as<WasmBoundsCheckValue>()->boundsType()) {
|
|
case WasmBoundsCheckValue::Type::Pinned:
|
|
VALIDATE(m_procedure.code().isPinned(value->as<WasmBoundsCheckValue>()->bounds().pinnedSize), ("At ", *value));
|
|
break;
|
|
case WasmBoundsCheckValue::Type::Maximum:
|
|
break;
|
|
}
|
|
VALIDATE(m_procedure.code().wasmBoundsCheckGenerator(), ("At ", *value));
|
|
break;
|
|
case Upsilon:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->as<UpsilonValue>()->phi(), ("At ", *value));
|
|
VALIDATE(value->as<UpsilonValue>()->phi()->opcode() == Phi, ("At ", *value));
|
|
VALIDATE(value->child(0)->type() != Void, ("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->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() != Void, ("At ", *value));
|
|
break;
|
|
case Jump:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(valueOwner.get(value)->numSuccessors() == 1, ("At ", *value));
|
|
break;
|
|
case Oops:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(!valueOwner.get(value)->numSuccessors(), ("At ", *value));
|
|
break;
|
|
case Return:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() <= 1, ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(!valueOwner.get(value)->numSuccessors(), ("At ", *value));
|
|
break;
|
|
case Branch:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(valueOwner.get(value)->numSuccessors() == 2, ("At ", *value));
|
|
break;
|
|
case Switch: {
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(value->numChildren() == 1, ("At ", *value));
|
|
VALIDATE(value->child(0)->type().isInt(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(value->as<SwitchValue>()->hasFallThrough(valueOwner.get(value)), ("At ", *value));
|
|
// This validates the same thing as hasFallThrough, but more explicitly. We want to
|
|
// make sure that if anyone tries to change the definition of hasFallThrough, they
|
|
// will feel some pain here, since this is fundamental.
|
|
VALIDATE(valueOwner.get(value)->numSuccessors() == value->as<SwitchValue>()->numCaseValues() + 1, ("At ", *value));
|
|
|
|
// Check that there are no duplicate cases.
|
|
Vector<int64_t> caseValues = value->as<SwitchValue>()->caseValues();
|
|
std::sort(caseValues.begin(), caseValues.end());
|
|
for (unsigned i = 1; i < caseValues.size(); ++i)
|
|
VALIDATE(caseValues[i - 1] != caseValues[i], ("At ", *value, ", caseValue = ", caseValues[i]));
|
|
break;
|
|
}
|
|
case EntrySwitch:
|
|
VALIDATE(!value->kind().hasExtraBits(), ("At ", *value));
|
|
VALIDATE(!value->numChildren(), ("At ", *value));
|
|
VALIDATE(value->type() == Void, ("At ", *value));
|
|
VALIDATE(valueOwner.get(value)->numSuccessors() == m_procedure.numEntrypoints(), ("At ", *value));
|
|
break;
|
|
}
|
|
|
|
VALIDATE(!(value->effects().writes && value->key()), ("At ", *value));
|
|
}
|
|
|
|
for (Variable* variable : m_procedure.variables())
|
|
VALIDATE(variable->type() != Void, ("At ", *variable));
|
|
|
|
for (BasicBlock* block : m_procedure) {
|
|
// 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:
|
|
void validateStackmap(Value* value)
|
|
{
|
|
StackmapValue* stackmap = value->as<StackmapValue>();
|
|
VALIDATE(stackmap, ("At ", *value));
|
|
VALIDATE(stackmap->numChildren() >= stackmap->reps().size(), ("At ", *stackmap));
|
|
for (ConstrainedValue child : stackmap->constrainedChildren())
|
|
validateStackmapConstraint(stackmap, child);
|
|
}
|
|
|
|
enum class ConstraintRole {
|
|
Use,
|
|
Def
|
|
};
|
|
void validateStackmapConstraint(Value* context, const ConstrainedValue& value, ConstraintRole role = ConstraintRole::Use, unsigned tupleIndex = 0)
|
|
{
|
|
switch (value.rep().kind()) {
|
|
case ValueRep::WarmAny:
|
|
case ValueRep::SomeRegister:
|
|
case ValueRep::StackArgument:
|
|
break;
|
|
case ValueRep::LateColdAny:
|
|
case ValueRep::ColdAny:
|
|
VALIDATE(role == ConstraintRole::Use, ("At ", *context, ": ", value));
|
|
break;
|
|
case ValueRep::SomeRegisterWithClobber:
|
|
VALIDATE(role == ConstraintRole::Use, ("At ", *context, ": ", value));
|
|
VALIDATE(context->as<PatchpointValue>(), ("At ", *context));
|
|
break;
|
|
case ValueRep::SomeEarlyRegister:
|
|
VALIDATE(role == ConstraintRole::Def, ("At ", *context, ": ", value));
|
|
break;
|
|
case ValueRep::Register:
|
|
case ValueRep::LateRegister:
|
|
case ValueRep::SomeLateRegister:
|
|
if (value.rep().kind() == ValueRep::LateRegister)
|
|
VALIDATE(role == ConstraintRole::Use, ("At ", *context, ": ", value));
|
|
if (value.rep().reg().isGPR()) {
|
|
if (value.value()->type().isTuple())
|
|
VALIDATE(m_procedure.extractFromTuple(value.value()->type(), tupleIndex).isInt(), ("At ", *context, ": ", value));
|
|
else
|
|
VALIDATE(value.value()->type().isInt(), ("At ", *context, ": ", value));
|
|
} else {
|
|
if (value.value()->type().isTuple())
|
|
VALIDATE(m_procedure.extractFromTuple(value.value()->type(), tupleIndex).isFloat(), ("At ", *context, ": ", value));
|
|
else
|
|
VALIDATE(value.value()->type().isFloat(), ("At ", *context, ": ", value));
|
|
}
|
|
break;
|
|
default:
|
|
VALIDATE(false, ("At ", *context, ": ", value));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void validateFence(Value* value)
|
|
{
|
|
MemoryValue* memory = value->as<MemoryValue>();
|
|
if (memory->hasFence())
|
|
VALIDATE(memory->accessBank() == GP, ("Fence at ", *memory));
|
|
}
|
|
|
|
void validateAtomic(Value* value)
|
|
{
|
|
AtomicValue* atomic = value->as<AtomicValue>();
|
|
|
|
VALIDATE(bestType(GP, atomic->accessWidth()) == atomic->accessType(), ("At ", *value));
|
|
}
|
|
|
|
void validateStackAccess(Value* value)
|
|
{
|
|
MemoryValue* memory = value->as<MemoryValue>();
|
|
SlotBaseValue* slotBase = value->lastChild()->as<SlotBaseValue>();
|
|
if (!slotBase)
|
|
return;
|
|
|
|
VALIDATE(memory->offset() >= 0, ("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)
|