blob: a93e0372566c83052294d0261a166fa6151ab658 [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 "WHLSLPropertyResolver.h"
#if ENABLE(WEBGPU)
#include "WHLSLAssignmentExpression.h"
#include "WHLSLCallExpression.h"
#include "WHLSLCommaExpression.h"
#include "WHLSLDereferenceExpression.h"
#include "WHLSLDotExpression.h"
#include "WHLSLFunctionDeclaration.h"
#include "WHLSLFunctionDefinition.h"
#include "WHLSLMakeArrayReferenceExpression.h"
#include "WHLSLMakePointerExpression.h"
#include "WHLSLPointerType.h"
#include "WHLSLReadModifyWriteExpression.h"
#include "WHLSLVariableDeclaration.h"
#include "WHLSLVariableReference.h"
#include "WHLSLVisitor.h"
namespace WebCore {
namespace WHLSL {
class PropertyResolver : public Visitor {
public:
private:
void visit(AST::FunctionDefinition&) override;
void visit(AST::DotExpression&) override;
void visit(AST::IndexExpression&) override;
void visit(AST::AssignmentExpression&) override;
void visit(AST::ReadModifyWriteExpression&) override;
void simplifyRightValue(AST::PropertyAccessExpression&);
bool simplifyAbstractLeftValue(AST::AssignmentExpression&, AST::DotExpression&, UniqueRef<AST::Expression>&& right);
void simplifyLeftValue(AST::Expression&);
AST::VariableDeclarations m_variableDeclarations;
};
void PropertyResolver::visit(AST::DotExpression& dotExpression)
{
// Unless we're inside an AssignmentExpression or a ReadModifyWriteExpression, we're a right value.
simplifyRightValue(dotExpression);
}
void PropertyResolver::visit(AST::IndexExpression& indexExpression)
{
checkErrorAndVisit(indexExpression.indexExpression());
// Unless we're inside an AssignmentExpression or a ReadModifyWriteExpression, we're a right value.
simplifyRightValue(indexExpression);
}
void PropertyResolver::visit(AST::FunctionDefinition& functionDefinition)
{
Visitor::visit(functionDefinition);
if (!m_variableDeclarations.isEmpty())
functionDefinition.block().statements().insert(0, makeUniqueRef<AST::VariableDeclarationsStatement>(Lexer::Token(m_variableDeclarations[0]->origin()), WTFMove(m_variableDeclarations)));
}
enum class WhichAnder {
ThreadAnder,
Ander
};
struct AnderCallArgumentResult {
UniqueRef<AST::Expression> expression;
Optional<UniqueRef<AST::VariableDeclaration>> variableDeclaration;
WhichAnder whichAnder;
};
template <typename ExpressionConstructor, typename TypeConstructor>
static Optional<AnderCallArgumentResult> wrapAnderCallArgument(UniqueRef<AST::Expression>& expression, bool anderFunction, bool threadAnderFunction)
{
if (auto addressSpace = expression->typeAnnotation().leftAddressSpace()) {
if (!anderFunction)
return WTF::nullopt;
auto origin = expression->origin();
auto baseType = expression->resolvedType().clone();
auto makeArrayReference = makeUniqueRef<ExpressionConstructor>(Lexer::Token(origin), WTFMove(expression));
makeArrayReference->setType(makeUniqueRef<TypeConstructor>(WTFMove(origin), *addressSpace, WTFMove(baseType)));
makeArrayReference->setTypeAnnotation(AST::RightValue());
return {{ WTFMove(makeArrayReference), WTF::nullopt, WhichAnder::Ander }};
}
if (threadAnderFunction) {
auto origin = expression->origin();
auto baseType = expression->resolvedType().clone();
auto variableDeclaration = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(origin), AST::Qualifiers(), baseType->clone(), String(), WTF::nullopt, WTF::nullopt);
auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
variableReference1->setType(baseType->clone());
variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(variableReference1), WTFMove(expression));
assignmentExpression->setType(baseType->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
auto variableReference2 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
variableReference2->setType(baseType->clone());
variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
auto expression = makeUniqueRef<ExpressionConstructor>(Lexer::Token(origin), WTFMove(variableReference2));
auto resultType = makeUniqueRef<TypeConstructor>(Lexer::Token(origin), AST::AddressSpace::Thread, WTFMove(baseType));
expression->setType(resultType->clone());
expression->setTypeAnnotation(AST::RightValue());
Vector<UniqueRef<AST::Expression>> expressions;
expressions.append(WTFMove(assignmentExpression));
expressions.append(WTFMove(expression));
auto commaExpression = makeUniqueRef<AST::CommaExpression>(WTFMove(origin), WTFMove(expressions));
commaExpression->setType(WTFMove(resultType));
commaExpression->setTypeAnnotation(AST::RightValue());
return {{ WTFMove(commaExpression), { WTFMove(variableDeclaration) }, WhichAnder::ThreadAnder}};
}
return WTF::nullopt;
}
static Optional<AnderCallArgumentResult> anderCallArgument(UniqueRef<AST::Expression>& expression, bool anderFunction, bool threadAnderFunction)
{
auto& unifyNode = expression->resolvedType().unifyNode();
if (is<AST::UnnamedType>(unifyNode)) {
auto& unnamedType = downcast<AST::UnnamedType>(unifyNode);
ASSERT(!is<AST::PointerType>(unnamedType));
if (is<AST::ArrayReferenceType>(unnamedType))
return {{ WTFMove(expression), WTF::nullopt, WhichAnder::Ander }};
if (is<AST::ArrayType>(unnamedType))
return wrapAnderCallArgument<AST::MakeArrayReferenceExpression, AST::ArrayReferenceType>(expression, anderFunction, threadAnderFunction);
}
return wrapAnderCallArgument<AST::MakePointerExpression, AST::PointerType>(expression, anderFunction, threadAnderFunction);
}
static Optional<UniqueRef<AST::Expression>> setterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, UniqueRef<AST::Expression>&& newValue, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, AST::VariableDeclaration* indexVariable)
{
auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
if (!indexVariable)
return;
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
ASSERT(indexVariable->type());
variableReference->setType(indexVariable->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
arguments.append(WTFMove(variableReference));
};
if (relevantAnder) {
// *operator&.foo(&v) = newValue
auto leftValue = leftValueFactory();
auto argument = anderCallArgument(leftValue, true, true);
ASSERT(argument);
ASSERT(!argument->variableDeclaration);
ASSERT(argument->whichAnder == WhichAnder::Ander);
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(WTFMove(argument->expression));
maybeAddIndexArgument(arguments);
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
callExpression->setType(relevantAnder->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*relevantAnder);
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(callExpression));
dereferenceExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(dereferenceExpression), WTFMove(newValue));
assignmentExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
return UniqueRef<AST::Expression>(WTFMove(assignmentExpression));
}
// v = operator.foo=(v, newValue)
ASSERT(propertyAccessExpression.setterFunction());
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(leftValueFactory());
maybeAddIndexArgument(arguments);
arguments.append(WTFMove(newValue));
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(propertyAccessExpression.setterFunction()->name()), WTFMove(arguments));
callExpression->setType(propertyAccessExpression.setterFunction()->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*propertyAccessExpression.setterFunction());
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), leftValueFactory(), WTFMove(callExpression));
assignmentExpression->setType(propertyAccessExpression.setterFunction()->type().clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
return UniqueRef<AST::Expression>(WTFMove(assignmentExpression));
}
static Optional<UniqueRef<AST::Expression>> getterCall(AST::PropertyAccessExpression& propertyAccessExpression, AST::FunctionDeclaration* relevantAnder, const std::function<UniqueRef<AST::Expression>()>& leftValueFactory, AST::VariableDeclaration* indexVariable)
{
auto maybeAddIndexArgument = [&](Vector<UniqueRef<AST::Expression>>& arguments) {
if (!indexVariable)
return;
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
ASSERT(indexVariable->type());
variableReference->setType(indexVariable->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
arguments.append(WTFMove(variableReference));
};
if (relevantAnder) {
// *operator&.foo(&v)
auto leftValue = leftValueFactory();
auto argument = anderCallArgument(leftValue, true, true);
ASSERT(argument);
ASSERT(!argument->variableDeclaration);
ASSERT(argument->whichAnder == WhichAnder::Ander);
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(WTFMove(argument->expression));
maybeAddIndexArgument(arguments);
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(relevantAnder->name()), WTFMove(arguments));
callExpression->setType(relevantAnder->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*relevantAnder);
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(callExpression));
dereferenceExpression->setType(downcast<AST::PointerType>(relevantAnder->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
return UniqueRef<AST::Expression>(WTFMove(dereferenceExpression));
}
// operator.foo(v)
ASSERT(propertyAccessExpression.getterFunction());
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(leftValueFactory());
maybeAddIndexArgument(arguments);
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(propertyAccessExpression.origin()), String(propertyAccessExpression.getterFunction()->name()), WTFMove(arguments));
callExpression->setType(propertyAccessExpression.getterFunction()->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*propertyAccessExpression.getterFunction());
return UniqueRef<AST::Expression>(WTFMove(callExpression));
}
struct ModifyResult {
AST::Expression& innerLeftValue;
Vector<UniqueRef<AST::Expression>> expressions;
Vector<UniqueRef<AST::VariableDeclaration>> variableDeclarations;
};
struct ModificationResult {
Vector<UniqueRef<AST::Expression>> expressions;
UniqueRef<AST::Expression> result;
};
static Optional<ModifyResult> modify(AST::PropertyAccessExpression& propertyAccessExpression, std::function<Optional<ModificationResult>(Optional<UniqueRef<AST::Expression>>&&)> modification)
{
// Consider a.b.c.d++;
// This would get transformed into:
//
// Step 1:
// p = &a;
//
// Step 2:
// q = operator.b(*p);
// r = operator.c(q);
//
// Step 3:
// oldValue = operator.d(r);
// newValue = ...;
//
// Step 4:
// r = operator.d=(r, newValue);
// q = operator.c=(q, r);
//
// Step 5:
// *p = operator.b=(*p, q);
// If the expression is a.b.c.d = e, Step 3 disappears and "newValue" in step 4 becomes "e".
// Find the ".b" ".c" and ".d" expressions. They end up in the order [".d", ".c", ".b"].
Vector<std::reference_wrapper<AST::PropertyAccessExpression>> chain;
AST::PropertyAccessExpression* iterator = &propertyAccessExpression;
while (true) {
chain.append(*iterator);
if (iterator->base().typeAnnotation().leftAddressSpace())
break;
ASSERT(!iterator->base().typeAnnotation().isRightValue());
ASSERT(is<AST::PropertyAccessExpression>(iterator->base()));
iterator = &downcast<AST::PropertyAccessExpression>(iterator->base());
}
auto leftExpression = iterator->takeBase();
AST::Expression& innerLeftExpression = leftExpression;
// Create "p" variable.
auto pointerVariable = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(leftExpression->origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(makeUniqueRef<AST::PointerType>(Lexer::Token(leftExpression->origin()), *leftExpression->typeAnnotation().leftAddressSpace(), leftExpression->resolvedType().clone())), String(), WTF::nullopt, WTF::nullopt);
// Create "q" and "r" variables.
Vector<UniqueRef<AST::VariableDeclaration>> intermediateVariables;
intermediateVariables.reserveInitialCapacity(chain.size() - 1);
for (size_t i = 1; i < chain.size(); ++i) {
auto& propertyAccessExpression = static_cast<AST::PropertyAccessExpression&>(chain[i]);
intermediateVariables.uncheckedAppend(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(propertyAccessExpression.origin()), AST::Qualifiers(), propertyAccessExpression.resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt));
}
// Consider a[foo()][b] = c;
// Naively, This would get expanded to:
//
// temp = operator[](a, foo());
// temp = operator[]=(temp, b, c);
// a = operator[]=(a, foo(), temp);
//
// However, if we did this, we would have to run foo() twice, which would be incorrect.
// Instead, we need to save foo() and b into more temporary variables.
// These temporary variables are parallel to "chain" above, with nullopt referring to a DotExpression (which doesn't have an index value to save to a variable).
//
// Instead, this gets expanded to:
//
// p = &a;
// temp = foo();
// q = operator[](*p, temp);
// temp2 = b;
// q = operator[]=(q, temp2, c);
// *p = operator[]=(*p, temp, q);
Vector<Optional<UniqueRef<AST::VariableDeclaration>>> indexVariables;
indexVariables.reserveInitialCapacity(chain.size());
for (AST::PropertyAccessExpression& propertyAccessExpression : chain) {
if (!is<AST::IndexExpression>(propertyAccessExpression)) {
indexVariables.append(WTF::nullopt);
continue;
}
auto& indexExpression = downcast<AST::IndexExpression>(propertyAccessExpression);
indexVariables.uncheckedAppend(makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(propertyAccessExpression.origin()), AST::Qualifiers(), indexExpression.indexExpression().resolvedType().clone(), String(), WTF::nullopt, WTF::nullopt));
}
Vector<UniqueRef<AST::Expression>> expressions;
// Step 1:
{
auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(innerLeftExpression.origin()), WTFMove(leftExpression));
makePointerExpression->setType(makeUniqueRef<AST::PointerType>(Lexer::Token(innerLeftExpression.origin()), *innerLeftExpression.typeAnnotation().leftAddressSpace(), innerLeftExpression.resolvedType().clone()));
makePointerExpression->setTypeAnnotation(AST::RightValue());
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
ASSERT(pointerVariable->type());
variableReference->setType(pointerVariable->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(innerLeftExpression.origin()), WTFMove(variableReference), WTFMove(makePointerExpression));
assignmentExpression->setType(pointerVariable->type()->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
// Step 2:
AST::VariableDeclaration* previous = nullptr;
auto previousLeftValue = [&]() -> UniqueRef<AST::Expression> {
if (previous) {
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*previous));
ASSERT(previous->type());
variableReference->setType(previous->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
return variableReference;
}
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
ASSERT(pointerVariable->type());
variableReference->setType(pointerVariable->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
ASSERT(pointerVariable->type());
dereferenceExpression->setType(downcast<AST::PointerType>(*pointerVariable->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
return dereferenceExpression;
};
auto appendIndexAssignment = [&](AST::PropertyAccessExpression& propertyAccessExpression, Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable) {
if (!indexVariable)
return;
auto& indexExpression = downcast<AST::IndexExpression>(propertyAccessExpression);
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*indexVariable));
ASSERT(indexVariable->get().type());
variableReference->setType(indexVariable->get().type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference), indexExpression.takeIndex());
assignmentExpression->setType(indexVariable->get().type()->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
};
for (size_t i = chain.size(); --i; ) {
AST::PropertyAccessExpression& propertyAccessExpression = chain[i];
AST::VariableDeclaration& variableDeclaration = intermediateVariables[i - 1];
Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable = indexVariables[i];
appendIndexAssignment(propertyAccessExpression, indexVariable);
AST::FunctionDeclaration* relevantAnder = i == chain.size() - 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
auto callExpression = getterCall(propertyAccessExpression, relevantAnder, previousLeftValue, indexVariable ? &*indexVariable : nullptr);
if (!callExpression)
return WTF::nullopt;
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
ASSERT(variableDeclaration.type());
variableReference->setType(variableDeclaration.type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference), WTFMove(*callExpression));
assignmentExpression->setType(variableDeclaration.type()->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
previous = &variableDeclaration;
}
appendIndexAssignment(chain[0], indexVariables[0]);
AST::FunctionDeclaration* relevantAnder = chain.size() == 1 ? propertyAccessExpression.anderFunction() : propertyAccessExpression.threadAnderFunction();
auto lastGetterCallExpression = getterCall(chain[0], relevantAnder, previousLeftValue, indexVariables[0] ? &*(indexVariables[0]) : nullptr);
// Step 3:
auto modificationResult = modification(WTFMove(lastGetterCallExpression));
if (!modificationResult)
return WTF::nullopt;
for (size_t i = 0; i < modificationResult->expressions.size(); ++i)
expressions.append(WTFMove(modificationResult->expressions[i]));
// Step 4:
UniqueRef<AST::Expression> rightValue = WTFMove(modificationResult->result);
auto expressionType = rightValue->resolvedType().clone();
for (size_t i = 0; i < chain.size() - 1; ++i) {
AST::PropertyAccessExpression& propertyAccessExpression = chain[i];
AST::VariableDeclaration& variableDeclaration = intermediateVariables[i];
Optional<UniqueRef<AST::VariableDeclaration>>& indexVariable = indexVariables[i];
auto assignmentExpression = setterCall(propertyAccessExpression, propertyAccessExpression.threadAnderFunction(), WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
ASSERT(variableDeclaration.type());
variableReference->setType(variableDeclaration.type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
return variableReference;
}, indexVariable ? &*indexVariable : nullptr);
if (!assignmentExpression)
return WTF::nullopt;
expressions.append(WTFMove(*assignmentExpression));
rightValue = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variableDeclaration));
ASSERT(variableDeclaration.type());
rightValue->setType(variableDeclaration.type()->clone());
rightValue->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
}
// Step 5:
{
AST::PropertyAccessExpression& propertyAccessExpression = chain[chain.size() - 1];
auto assignmentExpression = setterCall(propertyAccessExpression, propertyAccessExpression.anderFunction(), WTFMove(rightValue), [&]() -> UniqueRef<AST::Expression> {
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
ASSERT(pointerVariable->type());
variableReference->setType(pointerVariable->type()->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(propertyAccessExpression.origin()), WTFMove(variableReference));
ASSERT(pointerVariable->type());
dereferenceExpression->setType(downcast<AST::PointerType>(*pointerVariable->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(*pointerVariable->type()).addressSpace() });
return dereferenceExpression;
}, indexVariables[indexVariables.size() - 1] ? &*(indexVariables[indexVariables.size() - 1]) : nullptr);
if (!assignmentExpression)
return WTF::nullopt;
expressions.append(WTFMove(*assignmentExpression));
}
Vector<UniqueRef<AST::VariableDeclaration>> variableDeclarations;
variableDeclarations.append(WTFMove(pointerVariable));
for (auto& intermediateVariable : intermediateVariables)
variableDeclarations.append(WTFMove(intermediateVariable));
for (auto& indexVariable : indexVariables) {
if (indexVariable)
variableDeclarations.append(WTFMove(*indexVariable));
}
return {{ innerLeftExpression, WTFMove(expressions), WTFMove(variableDeclarations) }};
}
void PropertyResolver::visit(AST::AssignmentExpression& assignmentExpression)
{
if (assignmentExpression.left().typeAnnotation().leftAddressSpace()) {
simplifyLeftValue(assignmentExpression.left());
checkErrorAndVisit(assignmentExpression.right());
return;
}
ASSERT(!assignmentExpression.left().typeAnnotation().isRightValue());
ASSERT(is<AST::PropertyAccessExpression>(assignmentExpression.left()));
auto type = assignmentExpression.right().resolvedType().clone();
checkErrorAndVisit(assignmentExpression.right());
auto modifyResult = modify(downcast<AST::PropertyAccessExpression>(assignmentExpression.left()), [&](Optional<UniqueRef<AST::Expression>>&&) -> Optional<ModificationResult> {
return {{ Vector<UniqueRef<AST::Expression>>(), assignmentExpression.takeRight() }};
});
if (!modifyResult) {
setError();
return;
}
simplifyLeftValue(modifyResult->innerLeftValue);
Lexer::Token origin = assignmentExpression.origin();
auto* commaExpression = AST::replaceWith<AST::CommaExpression>(assignmentExpression, WTFMove(origin), WTFMove(modifyResult->expressions));
commaExpression->setType(WTFMove(type));
commaExpression->setTypeAnnotation(AST::RightValue());
for (auto& variableDeclaration : modifyResult->variableDeclarations)
m_variableDeclarations.append(WTFMove(variableDeclaration));
}
void PropertyResolver::visit(AST::ReadModifyWriteExpression& readModifyWriteExpression)
{
if (readModifyWriteExpression.leftValue().typeAnnotation().leftAddressSpace()) {
// Consider a++;
// This would get transformed into:
//
// p = &a;
// oldValue = *p;
// newValue = ...;
// *p = newValue;
simplifyLeftValue(readModifyWriteExpression.leftValue());
auto baseType = readModifyWriteExpression.leftValue().resolvedType().clone();
auto pointerType = makeUniqueRef<AST::PointerType>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), *readModifyWriteExpression.leftValue().typeAnnotation().leftAddressSpace(), baseType->clone());
auto pointerVariable = makeUniqueRef<AST::VariableDeclaration>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), AST::Qualifiers(), UniqueRef<AST::UnnamedType>(pointerType->clone()), String(), WTF::nullopt, WTF::nullopt);
Vector<UniqueRef<AST::Expression>> expressions;
{
auto origin = Lexer::Token(readModifyWriteExpression.leftValue().origin());
auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(Lexer::Token(origin), readModifyWriteExpression.takeLeftValue());
makePointerExpression->setType(pointerType->clone());
makePointerExpression->setTypeAnnotation(AST::RightValue());
auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
variableReference->setType(pointerType->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(WTFMove(origin), WTFMove(variableReference), WTFMove(makePointerExpression));
assignmentExpression->setType(pointerType->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
{
auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
variableReference1->setType(pointerType->clone());
variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference1));
dereferenceExpression->setType(baseType->clone());
dereferenceExpression->setTypeAnnotation(AST::RightValue());
auto variableReference2 = readModifyWriteExpression.oldVariableReference();
variableReference2->setType(baseType->clone());
variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference2), WTFMove(dereferenceExpression));
assignmentExpression->setType(baseType->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
{
auto variableReference = readModifyWriteExpression.newVariableReference();
variableReference->setType(baseType->clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto newValueExpression = readModifyWriteExpression.takeNewValueExpression();
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference), WTFMove(newValueExpression));
assignmentExpression->setType(baseType->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
{
auto variableReference1 = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable));
variableReference1->setType(pointerType->clone());
variableReference1->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto dereferenceExpression = makeUniqueRef<AST::DereferenceExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference1));
dereferenceExpression->setType(baseType->clone());
dereferenceExpression->setTypeAnnotation(AST::RightValue());
auto variableReference2 = readModifyWriteExpression.newVariableReference();
variableReference2->setType(baseType->clone());
variableReference2->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(dereferenceExpression), WTFMove(variableReference2));
assignmentExpression->setType(baseType->clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
auto resultExpression = readModifyWriteExpression.takeResultExpression();
auto type = resultExpression->resolvedType().clone();
expressions.append(WTFMove(resultExpression));
UniqueRef<AST::VariableDeclaration> oldVariableDeclaration = readModifyWriteExpression.takeOldValue();
UniqueRef<AST::VariableDeclaration> newVariableDeclaration = readModifyWriteExpression.takeNewValue();
Lexer::Token origin = readModifyWriteExpression.origin();
auto* commaExpression = AST::replaceWith<AST::CommaExpression>(readModifyWriteExpression, WTFMove(origin), WTFMove(expressions));
commaExpression->setType(WTFMove(type));
commaExpression->setTypeAnnotation(AST::RightValue());
m_variableDeclarations.append(WTFMove(pointerVariable));
m_variableDeclarations.append(WTFMove(oldVariableDeclaration));
m_variableDeclarations.append(WTFMove(newVariableDeclaration));
return;
}
ASSERT(!readModifyWriteExpression.leftValue().typeAnnotation().isRightValue());
if (!is<AST::PropertyAccessExpression>(readModifyWriteExpression.leftValue())) {
setError();
return;
}
auto modifyResult = modify(downcast<AST::PropertyAccessExpression>(readModifyWriteExpression.leftValue()), [&](Optional<UniqueRef<AST::Expression>>&& lastGetterCallExpression) -> Optional<ModificationResult> {
Vector<UniqueRef<AST::Expression>> expressions;
if (!lastGetterCallExpression)
return WTF::nullopt;
{
auto variableReference = readModifyWriteExpression.oldVariableReference();
variableReference->setType(readModifyWriteExpression.leftValue().resolvedType().clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), WTFMove(variableReference), WTFMove(*lastGetterCallExpression));
assignmentExpression->setType(readModifyWriteExpression.leftValue().resolvedType().clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
{
auto variableReference = readModifyWriteExpression.newVariableReference();
variableReference->setType(readModifyWriteExpression.leftValue().resolvedType().clone());
variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right?
auto newValueExpression = readModifyWriteExpression.takeNewValueExpression();
auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), WTFMove(variableReference), WTFMove(newValueExpression));
assignmentExpression->setType(readModifyWriteExpression.leftValue().resolvedType().clone());
assignmentExpression->setTypeAnnotation(AST::RightValue());
expressions.append(WTFMove(assignmentExpression));
}
return {{ WTFMove(expressions), readModifyWriteExpression.newVariableReference() }};
});
if (!modifyResult) {
setError();
return;
}
simplifyLeftValue(modifyResult->innerLeftValue);
auto resultExpression = readModifyWriteExpression.takeResultExpression();
auto type = resultExpression->resolvedType().clone();
modifyResult->expressions.append(WTFMove(resultExpression));
UniqueRef<AST::VariableDeclaration> oldVariableDeclaration = readModifyWriteExpression.takeOldValue();
UniqueRef<AST::VariableDeclaration> newVariableDeclaration = readModifyWriteExpression.takeNewValue();
Lexer::Token origin = readModifyWriteExpression.origin();
auto* commaExpression = AST::replaceWith<AST::CommaExpression>(readModifyWriteExpression, WTFMove(origin), WTFMove(modifyResult->expressions));
commaExpression->setType(WTFMove(type));
commaExpression->setTypeAnnotation(AST::RightValue());
for (auto& variableDeclaration : modifyResult->variableDeclarations)
m_variableDeclarations.append(WTFMove(variableDeclaration));
m_variableDeclarations.append(WTFMove(oldVariableDeclaration));
m_variableDeclarations.append(WTFMove(newVariableDeclaration));
}
static Optional<AnderCallArgumentResult> anderCallArgument(AST::PropertyAccessExpression& propertyAccessExpression)
{
return anderCallArgument(propertyAccessExpression.baseReference(), propertyAccessExpression.anderFunction(), propertyAccessExpression.threadAnderFunction());
}
void PropertyResolver::simplifyRightValue(AST::PropertyAccessExpression& propertyAccessExpression)
{
Lexer::Token origin = propertyAccessExpression.origin();
checkErrorAndVisit(propertyAccessExpression.base());
if (auto argument = anderCallArgument(propertyAccessExpression)) {
auto* anderFunction = argument->whichAnder == WhichAnder::ThreadAnder ? propertyAccessExpression.threadAnderFunction() : propertyAccessExpression.anderFunction();
ASSERT(anderFunction);
auto origin = propertyAccessExpression.origin();
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(WTFMove(argument->expression));
if (is<AST::IndexExpression>(propertyAccessExpression))
arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
callExpression->setType(anderFunction->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*anderFunction);
auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
if (auto& variableDeclaration = argument->variableDeclaration)
m_variableDeclarations.append(WTFMove(*variableDeclaration));
return;
}
ASSERT(propertyAccessExpression.getterFunction());
auto& getterFunction = *propertyAccessExpression.getterFunction();
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(propertyAccessExpression.takeBase());
if (is<AST::IndexExpression>(propertyAccessExpression))
arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
auto* callExpression = AST::replaceWith<AST::CallExpression>(propertyAccessExpression, WTFMove(origin), String(getterFunction.name()), WTFMove(arguments));
callExpression->setFunction(getterFunction);
callExpression->setType(getterFunction.type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
}
class LeftValueSimplifier : public Visitor {
private:
void visit(AST::DotExpression&) override;
void visit(AST::IndexExpression&) override;
void visit(AST::DereferenceExpression&) override;
void finishVisiting(AST::PropertyAccessExpression&);
};
void LeftValueSimplifier::finishVisiting(AST::PropertyAccessExpression& propertyAccessExpression)
{
ASSERT(propertyAccessExpression.base().typeAnnotation().leftAddressSpace());
ASSERT(propertyAccessExpression.anderFunction());
Lexer::Token origin = propertyAccessExpression.origin();
auto* anderFunction = propertyAccessExpression.anderFunction();
auto argument = anderCallArgument(propertyAccessExpression);
ASSERT(argument);
ASSERT(!argument->variableDeclaration);
ASSERT(argument->whichAnder == WhichAnder::Ander);
Vector<UniqueRef<AST::Expression>> arguments;
arguments.append(WTFMove(argument->expression));
if (is<AST::IndexExpression>(propertyAccessExpression))
arguments.append(downcast<AST::IndexExpression>(propertyAccessExpression).takeIndex());
auto callExpression = makeUniqueRef<AST::CallExpression>(Lexer::Token(origin), String(anderFunction->name()), WTFMove(arguments));
callExpression->setType(anderFunction->type().clone());
callExpression->setTypeAnnotation(AST::RightValue());
callExpression->setFunction(*anderFunction);
auto* dereferenceExpression = AST::replaceWith<AST::DereferenceExpression>(propertyAccessExpression, WTFMove(origin), WTFMove(callExpression));
dereferenceExpression->setType(downcast<AST::PointerType>(anderFunction->type()).elementType().clone());
dereferenceExpression->setTypeAnnotation(AST::LeftValue { downcast<AST::PointerType>(anderFunction->type()).addressSpace() });
}
void LeftValueSimplifier::visit(AST::DotExpression& dotExpression)
{
Visitor::visit(dotExpression);
finishVisiting(dotExpression);
}
void LeftValueSimplifier::visit(AST::IndexExpression& indexExpression)
{
Visitor::visit(indexExpression);
PropertyResolver().Visitor::visit(indexExpression.indexExpression());
finishVisiting(indexExpression);
}
void LeftValueSimplifier::visit(AST::DereferenceExpression& dereferenceExpression)
{
// Dereference expressions are the only expressions where the children might be more-right than we are.
// For example, a dereference expression may be a left value but its child may be a call expression which is a right value.
// LeftValueSimplifier doesn't handle right values, so we instead need to use PropertyResolver.
PropertyResolver().Visitor::visit(dereferenceExpression);
}
void PropertyResolver::simplifyLeftValue(AST::Expression& expression)
{
LeftValueSimplifier().Visitor::visit(expression);
}
void resolveProperties(Program& program)
{
PropertyResolver().Visitor::visit(program);
}
} // namespace WHLSL
} // namespace WebCore
#endif // ENABLE(WEBGPU)