| /* |
| * 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 "WHLSLAST.h" |
| #include "WHLSLProgram.h" |
| #include "WHLSLReplaceWith.h" |
| #include "WHLSLVisitor.h" |
| |
| namespace WebCore { |
| |
| namespace WHLSL { |
| |
| class PropertyResolver : public Visitor { |
| void handleLeftHandSideBase(UniqueRef<AST::Expression> base, UniqueRef<AST::Expression>& slot, Vector<UniqueRef<AST::Expression>>& expressions) |
| { |
| if (!base->mayBeEffectful()) { |
| slot = WTFMove(base); |
| return; |
| } |
| |
| auto leftAddressSpace = base->typeAnnotation().leftAddressSpace(); |
| RELEASE_ASSERT(leftAddressSpace); |
| CodeLocation codeLocation = base->codeLocation(); |
| Ref<AST::UnnamedType> baseType = base->resolvedType(); |
| Ref<AST::PointerType> pointerType = AST::PointerType::create(codeLocation, *leftAddressSpace, baseType.copyRef()); |
| |
| UniqueRef<AST::VariableDeclaration> pointerVariable = makeUniqueRef<AST::VariableDeclaration>(codeLocation, AST::Qualifiers { }, pointerType.ptr(), String(), nullptr, nullptr); |
| |
| auto makeVariableReference = [&] { |
| auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(pointerVariable)); |
| variableReference->setType(pointerType.copyRef()); |
| variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); |
| return variableReference; |
| }; |
| |
| { |
| auto pointerOfBase = makeUniqueRef<AST::MakePointerExpression>(codeLocation, WTFMove(base), AST::AddressEscapeMode::DoesNotEscape); |
| pointerOfBase->setType(pointerType.copyRef()); |
| pointerOfBase->setTypeAnnotation(AST::RightValue()); |
| |
| auto assignment = makeUniqueRef<AST::AssignmentExpression>(codeLocation, makeVariableReference(), WTFMove(pointerOfBase)); |
| assignment->setType(pointerType.copyRef()); |
| assignment->setTypeAnnotation(AST::RightValue()); |
| |
| expressions.append(WTFMove(assignment)); |
| } |
| |
| { |
| auto dereference = makeUniqueRef<AST::DereferenceExpression>(codeLocation, makeVariableReference()); |
| dereference->setType(baseType.copyRef()); |
| dereference->setTypeAnnotation(AST::LeftValue { *leftAddressSpace }); |
| |
| slot = WTFMove(dereference); |
| } |
| |
| m_variables.append(WTFMove(pointerVariable)); |
| } |
| |
| void handlePropertyAccess(AST::PropertyAccessExpression& propertyAccess, Vector<UniqueRef<AST::Expression>>& expressions) |
| { |
| AST::PropertyAccessExpression* currentPtr = &propertyAccess; |
| // a.b[c].d will go into this array as [.d, [c], .b] |
| Vector<std::reference_wrapper<AST::PropertyAccessExpression>> chain; |
| |
| while (true) { |
| AST::PropertyAccessExpression& current = *currentPtr; |
| chain.append(current); |
| if (is<AST::IndexExpression>(current)) |
| checkErrorAndVisit(downcast<AST::IndexExpression>(current).indexExpression()); |
| if (!is<AST::PropertyAccessExpression>(current.base())) |
| break; |
| currentPtr = &downcast<AST::PropertyAccessExpression>(current.base()); |
| } |
| |
| AST::PropertyAccessExpression& current = *currentPtr; |
| |
| checkErrorAndVisit(current.base()); |
| |
| CodeLocation baseCodeLocation = current.base().codeLocation(); |
| |
| if (current.base().typeAnnotation().isRightValue()) { |
| UniqueRef<AST::VariableDeclaration> copy = makeUniqueRef<AST::VariableDeclaration>(baseCodeLocation, AST::Qualifiers { }, ¤t.base().resolvedType(), String(), nullptr, nullptr); |
| Ref<AST::UnnamedType> baseType = current.base().resolvedType(); |
| |
| auto makeVariableReference = [&] { |
| auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(copy)); |
| variableReference->setType(baseType.copyRef()); |
| variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); |
| return variableReference; |
| }; |
| |
| auto assignment = makeUniqueRef<AST::AssignmentExpression>(baseCodeLocation, makeVariableReference(), current.takeBase()); |
| assignment->setType(baseType.copyRef()); |
| assignment->setTypeAnnotation(AST::RightValue()); |
| |
| expressions.append(WTFMove(assignment)); |
| |
| current.baseReference() = makeVariableReference(); |
| |
| m_variables.append(WTFMove(copy)); |
| } else |
| handleLeftHandSideBase(current.takeBase(), current.baseReference(), expressions); |
| |
| for (size_t i = chain.size(); i--; ) { |
| auto& access = chain[i].get(); |
| if (is<AST::IndexExpression>(access) && downcast<AST::IndexExpression>(access).indexExpression().mayBeEffectful()) { |
| auto& indexExpression = downcast<AST::IndexExpression>(access); |
| |
| Ref<AST::UnnamedType> indexType = indexExpression.indexExpression().resolvedType(); |
| |
| UniqueRef<AST::VariableDeclaration> indexVariable = makeUniqueRef<AST::VariableDeclaration>(access.codeLocation(), AST::Qualifiers { }, indexType.ptr(), String(), nullptr, nullptr); |
| |
| auto makeVariableReference = [&] { |
| auto variableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(indexVariable)); |
| variableReference->setType(indexType.copyRef()); |
| variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); |
| return variableReference; |
| }; |
| |
| { |
| auto assignment = makeUniqueRef<AST::AssignmentExpression>(baseCodeLocation, makeVariableReference(), indexExpression.takeIndex()); |
| assignment->setType(indexType.copyRef()); |
| assignment->setTypeAnnotation(AST::RightValue()); |
| |
| expressions.append(WTFMove(assignment)); |
| } |
| |
| indexExpression.indexReference() = makeVariableReference(); |
| |
| m_variables.append(WTFMove(indexVariable)); |
| } |
| } |
| } |
| |
| void handlePropertyAccess(AST::PropertyAccessExpression& propertyAccess) |
| { |
| Vector<UniqueRef<AST::Expression>> expressions; |
| |
| handlePropertyAccess(propertyAccess, expressions); |
| |
| Ref<AST::UnnamedType> accessType = propertyAccess.resolvedType(); |
| |
| AST::CommaExpression* comma; |
| CodeLocation codeLocation = propertyAccess.codeLocation(); |
| if (is<AST::IndexExpression>(propertyAccess)) { |
| auto& indexExpression = downcast<AST::IndexExpression>(propertyAccess); |
| |
| auto newIndexExpression = makeUniqueRef<AST::IndexExpression>(codeLocation, indexExpression.takeBase(), indexExpression.takeIndex()); |
| newIndexExpression->setType(indexExpression.resolvedType()); |
| newIndexExpression->setTypeAnnotation(AST::TypeAnnotation(indexExpression.typeAnnotation())); |
| |
| expressions.append(WTFMove(newIndexExpression)); |
| |
| comma = AST::replaceWith<AST::CommaExpression>(indexExpression, codeLocation, WTFMove(expressions)); |
| } else { |
| RELEASE_ASSERT(is<AST::DotExpression>(propertyAccess)); |
| auto& dotExpression = downcast<AST::DotExpression>(propertyAccess); |
| |
| auto newDotExpression = makeUniqueRef<AST::DotExpression>(codeLocation, dotExpression.takeBase(), String(dotExpression.fieldName())); |
| newDotExpression->setType(dotExpression.resolvedType()); |
| newDotExpression->setTypeAnnotation(AST::TypeAnnotation(dotExpression.typeAnnotation())); |
| |
| expressions.append(WTFMove(newDotExpression)); |
| |
| comma = AST::replaceWith<AST::CommaExpression>(dotExpression, codeLocation, WTFMove(expressions)); |
| } |
| |
| comma->setType(WTFMove(accessType)); |
| comma->setTypeAnnotation(AST::RightValue()); |
| } |
| |
| public: |
| void visit(AST::DotExpression& dotExpression) override |
| { |
| handlePropertyAccess(dotExpression); |
| } |
| |
| void visit(AST::IndexExpression& indexExpression) override |
| { |
| handlePropertyAccess(indexExpression); |
| } |
| |
| void visit(AST::ReadModifyWriteExpression& readModifyWrite) override |
| { |
| checkErrorAndVisit(readModifyWrite.newValueExpression()); |
| checkErrorAndVisit(readModifyWrite.resultExpression()); |
| |
| Vector<UniqueRef<AST::Expression>> expressions; |
| |
| CodeLocation codeLocation = readModifyWrite.codeLocation(); |
| |
| Ref<AST::UnnamedType> type = readModifyWrite.resolvedType(); |
| |
| if (is<AST::PropertyAccessExpression>(readModifyWrite.leftValue())) |
| handlePropertyAccess(downcast<AST::PropertyAccessExpression>(readModifyWrite.leftValue()), expressions); |
| else |
| handleLeftHandSideBase(readModifyWrite.takeLeftValue(), readModifyWrite.leftValueReference(), expressions); |
| |
| { |
| UniqueRef<AST::ReadModifyWriteExpression> newReadModifyWrite = makeUniqueRef<AST::ReadModifyWriteExpression>( |
| readModifyWrite.codeLocation(), readModifyWrite.takeLeftValue(), readModifyWrite.takeOldValue(), readModifyWrite.takeNewValue()); |
| newReadModifyWrite->setNewValueExpression(readModifyWrite.takeNewValueExpression()); |
| newReadModifyWrite->setResultExpression(readModifyWrite.takeResultExpression()); |
| newReadModifyWrite->setType(type.copyRef()); |
| newReadModifyWrite->setTypeAnnotation(AST::TypeAnnotation(readModifyWrite.typeAnnotation())); |
| |
| expressions.append(WTFMove(newReadModifyWrite)); |
| } |
| |
| auto* comma = AST::replaceWith<AST::CommaExpression>(readModifyWrite, codeLocation, WTFMove(expressions)); |
| comma->setType(WTFMove(type)); |
| comma->setTypeAnnotation(AST::RightValue()); |
| } |
| |
| void visit(AST::FunctionDefinition& functionDefinition) override |
| { |
| RELEASE_ASSERT(m_variables.isEmpty()); |
| |
| checkErrorAndVisit(static_cast<AST::FunctionDeclaration&>(functionDefinition)); |
| checkErrorAndVisit(functionDefinition.block()); |
| |
| if (!m_variables.isEmpty()) { |
| functionDefinition.block().statements().insert(0, |
| makeUniqueRef<AST::VariableDeclarationsStatement>(functionDefinition.block().codeLocation(), WTFMove(m_variables))); |
| } |
| } |
| |
| private: |
| AST::VariableDeclarations m_variables; |
| }; |
| |
| void resolveProperties(Program& program) |
| { |
| // The goal of this phase is two allow two things: |
| // 1. For property access expressions, metal codegen should be allowed to evaluate |
| // the base, and if it's an index expression, the index, as many times as needed. |
| // So this patch ensures that if Metal evaluates such things, effects aren't performed |
| // more than once. |
| // |
| // 2. For ReadModifyWrite expressions, metal codegen should be able to evaluate the |
| // leftValueExpression as many times as it'd like without performing the effects of |
| // leftValueExpression more than once. |
| // |
| // We do these things because it's convenient for metal codegen to be able to rely on |
| // this with the way it structures the generated code. |
| PropertyResolver resolver; |
| for (auto& function : program.functionDefinitions()) |
| resolver.visit(function); |
| } |
| |
| } // namespace WHLSL |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(WEBGPU) |