blob: 474fc8038c219775acf7f7f44379558d896156e5 [file] [log] [blame]
/*
* Copyright (C) 2019 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. AND ITS CONTRIBUTORS ``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 ITS 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 "WHLSLStatementBehaviorChecker.h"
#if ENABLE(WEBGPU)
#include "WHLSLBlock.h"
#include "WHLSLBreak.h"
#include "WHLSLContinue.h"
#include "WHLSLDoWhileLoop.h"
#include "WHLSLEffectfulExpressionStatement.h"
#include "WHLSLFallthrough.h"
#include "WHLSLForLoop.h"
#include "WHLSLIfStatement.h"
#include "WHLSLInferTypes.h"
#include "WHLSLProgram.h"
#include "WHLSLReturn.h"
#include "WHLSLSwitchCase.h"
#include "WHLSLSwitchStatement.h"
#include "WHLSLTrap.h"
#include "WHLSLVariableDeclarationsStatement.h"
#include "WHLSLVisitor.h"
#include <cstdint>
#include <wtf/OptionSet.h>
#include <wtf/Vector.h>
namespace WebCore {
namespace WHLSL {
class StatementBehaviorChecker : public Visitor {
public:
enum class Behavior : uint8_t {
Return = 1 << 0,
Break = 1 << 1,
Continue = 1 << 2,
Fallthrough = 1 << 3,
Nothing = 1 << 4
};
OptionSet<Behavior> takeFunctionBehavior()
{
ASSERT(m_stack.size() == 1);
return m_stack.takeLast();
}
private:
void visit(AST::Break&) override
{
m_stack.append({ Behavior::Break });
}
void visit(AST::Fallthrough&) override
{
m_stack.append({ Behavior::Fallthrough });
}
void visit(AST::Continue&) override
{
m_stack.append({ Behavior::Continue });
}
void visit(AST::Return&) override
{
m_stack.append({ Behavior::Return });
}
void visit(AST::Trap&) override
{
m_stack.append({ Behavior::Return });
}
void visit(AST::DoWhileLoop& doWhileLoop) override
{
checkErrorAndVisit(doWhileLoop.body());
if (error())
return;
auto b = m_stack.takeLast();
b.remove(Behavior::Break);
b.remove(Behavior::Continue);
b.add(Behavior::Nothing);
m_stack.append(b);
}
void visit(AST::ForLoop& forLoop) override
{
checkErrorAndVisit(forLoop.body());
if (error())
return;
auto b = m_stack.takeLast();
b.remove(Behavior::Break);
b.remove(Behavior::Continue);
b.add(Behavior::Nothing);
m_stack.append(b);
}
void visit(AST::SwitchCase& switchCase) override
{
Visitor::visit(switchCase);
}
void visit(AST::SwitchStatement& switchStatement) override
{
if (switchStatement.switchCases().isEmpty()) {
m_stack.append({ Behavior::Nothing });
return;
}
OptionSet<Behavior> reduction = { };
for (auto& switchCase : switchStatement.switchCases()) {
checkErrorAndVisit(switchCase);
if (error())
return;
auto b = m_stack.takeLast();
reduction = reduction | b;
}
if (reduction.contains(Behavior::Nothing)) {
setError();
return;
}
reduction.remove(Behavior::Break);
reduction.remove(Behavior::Fallthrough);
reduction.add(Behavior::Nothing);
m_stack.append(reduction);
}
void visit(AST::IfStatement& ifStatement) override
{
checkErrorAndVisit(ifStatement.body());
if (error())
return;
auto b = m_stack.takeLast();
OptionSet<Behavior> bPrime;
if (ifStatement.elseBody()) {
checkErrorAndVisit(*ifStatement.elseBody());
if (error())
return;
bPrime = m_stack.takeLast();
} else
bPrime = { Behavior::Nothing };
m_stack.append(b | bPrime);
}
void visit(AST::EffectfulExpressionStatement&) override
{
m_stack.append({ Behavior::Nothing });
}
void visit(AST::Block& block) override
{
if (block.statements().isEmpty()) {
m_stack.append({ Behavior::Nothing });
return;
}
OptionSet<Behavior> reduction = { };
for (size_t i = 0; i < block.statements().size() - 1; ++i) {
checkErrorAndVisit(block.statements()[i]);
if (error())
return;
auto b = m_stack.takeLast();
if (!b.contains(Behavior::Nothing)) {
setError();
return;
}
b.remove(Behavior::Nothing);
if (b.contains(Behavior::Fallthrough)) {
setError();
return;
}
reduction = reduction | b;
}
checkErrorAndVisit(block.statements()[block.statements().size() - 1]);
if (error())
return;
auto b = m_stack.takeLast();
m_stack.append(reduction | b);
}
void visit(AST::VariableDeclarationsStatement&) override
{
m_stack.append({ Behavior::Nothing });
}
Vector<OptionSet<Behavior>> m_stack;
};
bool checkStatementBehavior(Program& program)
{
StatementBehaviorChecker statementBehaviorChecker;
for (auto& functionDefinition : program.functionDefinitions()) {
statementBehaviorChecker.Visitor::visit(functionDefinition);
if (statementBehaviorChecker.error())
return false;
auto behavior = statementBehaviorChecker.takeFunctionBehavior();
if (matches(functionDefinition->type(), program.intrinsics().voidType())) {
behavior.remove(StatementBehaviorChecker::Behavior::Return);
behavior.remove(StatementBehaviorChecker::Behavior::Nothing);
if (behavior != OptionSet<StatementBehaviorChecker::Behavior>())
return false;
} else {
if (behavior != StatementBehaviorChecker::Behavior::Return)
return false;
}
}
return true;
}
} // namespace WHLSL
} // namespace WebCore
#endif // ENABLE(WEBGPU)