WebKit Bugzilla
Attachment 370786 Details for
Bug 195794
: [WHLSL] Enforce variable lifetimes
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
WIP
b-backup.diff (text/plain), 42.79 KB, created by
Saam Barati
on 2019-05-28 14:52:17 PDT
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Saam Barati
Created:
2019-05-28 14:52:17 PDT
Size:
42.79 KB
patch
obsolete
>Index: Source/WTF/wtf/PrintStream.h >=================================================================== >--- Source/WTF/wtf/PrintStream.h (revision 245692) >+++ Source/WTF/wtf/PrintStream.h (working copy) >@@ -132,7 +132,7 @@ void printInternal(PrintStream& out, con > #define MAKE_PRINT_ADAPTOR(Name, Type, function) \ > class Name { \ > public: \ >- Name(const Type& value) \ >+ Name(Type value) \ > : m_value(value) \ > { \ > } \ >Index: Source/WebCore/ChangeLog >=================================================================== >--- Source/WebCore/ChangeLog (revision 245728) >+++ Source/WebCore/ChangeLog (working copy) >@@ -1,3 +1,26 @@ >+2019-05-23 Saam barati <sbarati@apple.com> >+ >+ [WHLSL] ReadModifyWriteExpression always has a result and new value expression >+ https://bugs.webkit.org/show_bug.cgi?id=198079 >+ >+ Reviewed by Myles Maxfield. >+ >+ Let's not pretend it might not. >+ >+ * Modules/webgpu/WHLSL/AST/WHLSLReadModifyWriteExpression.h: >+ (WebCore::WHLSL::AST::ReadModifyWriteExpression::newValueExpression): >+ (WebCore::WHLSL::AST::ReadModifyWriteExpression::resultExpression): >+ (WebCore::WHLSL::AST::ReadModifyWriteExpression::takeNewValueExpression): >+ (WebCore::WHLSL::AST::ReadModifyWriteExpression::takeResultExpression): >+ * Modules/webgpu/WHLSL/WHLSLASTDumper.cpp: >+ (WebCore::WHLSL::ASTDumper::visit): >+ * Modules/webgpu/WHLSL/WHLSLChecker.cpp: >+ (WebCore::WHLSL::Checker::visit): >+ * Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp: >+ (WebCore::WHLSL::PropertyResolver::visit): >+ * Modules/webgpu/WHLSL/WHLSLVisitor.cpp: >+ (WebCore::WHLSL::Visitor::visit): >+ > 2019-05-23 Devin Rousso <drousso@apple.com> > > Web Inspector: Overlay: rulers should switch sides if they intersect the highlighted node(s) so they don't obstruct any content >Index: Source/WebCore/Sources.txt >=================================================================== >--- Source/WebCore/Sources.txt (revision 245692) >+++ Source/WebCore/Sources.txt (working copy) >@@ -322,6 +322,7 @@ Modules/webgpu/WHLSL/WHLSLIntrinsics.cpp > Modules/webgpu/WHLSL/WHLSLStatementBehaviorChecker.cpp > Modules/webgpu/WHLSL/WHLSLNameContext.cpp > Modules/webgpu/WHLSL/WHLSLNameResolver.cpp >+Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp > Modules/webgpu/WHLSL/WHLSLResolveOverloadImpl.cpp > Modules/webgpu/WHLSL/WHLSLRecursionChecker.cpp > Modules/webgpu/WHLSL/WHLSLVisitor.cpp >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.cpp (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.cpp (working copy) >@@ -441,6 +441,12 @@ void ASTDumper::visit(AST::DotExpression > m_out.print(".", dotExpression.fieldName()); > } > >+void ASTDumper::visit(AST::InternalStructFieldExpression& internalStructFieldExpression) >+{ >+ visit(internalStructFieldExpression.base()); >+ m_out.print("=>", internalStructFieldExpression.fieldName()); >+} >+ > void ASTDumper::visit(AST::IndexExpression& indexExpression) > { > visit(static_cast<AST::PropertyAccessExpression&>(indexExpression)); >@@ -529,7 +535,12 @@ void ASTDumper::visit(AST::VariableDecla > visit(*variableDeclaration.type()); > m_out.print(" "); > } >- m_out.print(variableDeclaration.name()); >+ >+ if (variableDeclaration.name().isEmpty()) >+ m_out.print("$", RawPointer(&variableDeclaration)); >+ else >+ m_out.print(variableDeclaration.name()); >+ > if (variableDeclaration.semantic()) > visit(*variableDeclaration.semantic()); > if (variableDeclaration.initializer()) { >@@ -547,6 +558,7 @@ void ASTDumper::visit(AST::AssignmentExp > > void ASTDumper::visit(AST::CallExpression& callExpression) > { >+ dataLogLn("CallExpression.name() = ", callExpression.name()); > m_out.print(callExpression.name(), "("); > bool once = false; > for (auto& argument : callExpression.arguments()) { >@@ -625,7 +637,7 @@ void ASTDumper::visit(AST::ReadModifyWri > > visit(newVariable.get()); > m_out.print(" = "); >- visit(*readModifyWriteExpression.newValueExpression()); >+ visit(readModifyWriteExpression.newValueExpression()); > m_out.print(", "); > > visit(readModifyWriteExpression.leftValue()); >@@ -633,7 +645,7 @@ void ASTDumper::visit(AST::ReadModifyWri > visit(newVariable.get()); > m_out.print(", "); > >- visit(*readModifyWriteExpression.resultExpression()); >+ visit(readModifyWriteExpression.resultExpression()); > m_out.print(")"); > } > >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.h (working copy) >@@ -44,7 +44,6 @@ public: > > String toString() { return m_out.toString(); } > >-private: > void visit(AST::UnnamedType&) override; > void visit(AST::NamedType&) override; > void visit(AST::TypeDefinition&) override; >@@ -88,6 +87,7 @@ private: > void visit(AST::DoWhileLoop&) override; > void visit(AST::Expression&) override; > void visit(AST::DotExpression&) override; >+ void visit(AST::InternalStructFieldExpression&) override; > void visit(AST::IndexExpression&) override; > void visit(AST::PropertyAccessExpression&) override; > void visit(AST::EffectfulExpressionStatement&) override; >@@ -113,6 +113,7 @@ private: > void visit(AST::TernaryExpression&) override; > void visit(AST::VariableReference&) override; > >+private: > struct Indent { > Indent(ASTDumper& dumper) > : m_scope(dumper.m_indent, dumper.m_indent + " ") >@@ -128,16 +129,23 @@ private: > String m_indent; > }; > >-static ALWAYS_INLINE String toString(Program& program) >+template <typename T> >+ALWAYS_INLINE void dumpASTNode(PrintStream& out, T& value) > { > ASTDumper dumper; >- dumper.visit(program); >- return dumper.toString(); >+ dumper.visit(value); >+ out.print(dumper.toString()); > } >+MAKE_PRINT_ADAPTOR(ExpressionDumper, AST::Expression&, dumpASTNode); >+MAKE_PRINT_ADAPTOR(StatementDumper, AST::Statement&, dumpASTNode); >+MAKE_PRINT_ADAPTOR(ProgramDumper, Program&, dumpASTNode); >+MAKE_PRINT_ADAPTOR(StructureDefinitionDumper, AST::StructureDefinition&, dumpASTNode); >+MAKE_PRINT_ADAPTOR(FunctionDefinitionDumper, AST::FunctionDefinition&, dumpASTNode); >+ > > static ALWAYS_INLINE void dumpAST(Program& program) > { >- dataLogLn(toString(program)); >+ dataLogLn(ProgramDumper(program)); > } > > } // namespace WHLSL >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp (working copy) >@@ -847,7 +847,7 @@ void Checker::visit(AST::ReadModifyWrite > > // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198166 Figure out what to do with the ReadModifyWriteExpression's AnonymousVariables. > >- auto newValueInfo = recurseAndGetInfo(*readModifyWriteExpression.newValueExpression()); >+ auto newValueInfo = recurseAndGetInfo(readModifyWriteExpression.newValueExpression()); > if (!newValueInfo) > return; > >@@ -856,7 +856,7 @@ void Checker::visit(AST::ReadModifyWrite > return; > } > >- auto resultInfo = recurseAndGetInfo(*readModifyWriteExpression.resultExpression()); >+ auto resultInfo = recurseAndGetInfo(readModifyWriteExpression.resultExpression()); > if (!resultInfo) > return; > >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLPrepare.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLPrepare.cpp (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLPrepare.cpp (working copy) >@@ -37,6 +37,7 @@ > #include "WHLSLMetalCodeGenerator.h" > #include "WHLSLNameResolver.h" > #include "WHLSLParser.h" >+#include "WHLSLPreserveVariableLifetimes.h" > #include "WHLSLProgram.h" > #include "WHLSLPropertyResolver.h" > #include "WHLSLRecursionChecker.h" >@@ -124,6 +125,8 @@ static Optional<Program> prepareShared(S > RUN_PASS(checkRecursion, program); > RUN_PASS(checkFunctionStages, program); > >+ preserveVariableLifetimes(program); >+ > dumpASTAtEndIfNeeded(program); > > return program; >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp (nonexistent) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp (working copy) >@@ -0,0 +1,268 @@ >+/* >+ * 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 "WHLSLPreserveVariableLifetimes.h" >+ >+#if ENABLE(WEBGPU) >+ >+#include "WHLSLAST.h" >+#include "WHLSLVisitor.h" >+#include "WHLSLASTDumper.h" >+ >+namespace WebCore { >+ >+namespace WHLSL { >+ >+/* >+Thoughts: >+- Let's do this before metal codegen. So let's have it be a modifying pass over AST. >+- Make all entry points declare a struct that has fields for each variable. >+- Conservatively, each variable/argument would work. >+- Conservatively, but more optimized, every variable that has its address taken should also work. >+- Make each non-entrypoint function take a "struct*" as its first or last parameter >+- Make the first statements of each entry point to declare and then take the address of this struct. >+- Make the first, or in the case of an entry point, after struct declaration, statement of any function where the parameter ends up in the struct store into the struct as the first thing it does. Alternatively, each callsite could have a unique entry in the struct to represent such an argument. Calls would then store into the struct prior to call, and functions would remove said arguments from their argument list, and call sites would be modified to not pass them in. The latter sounds harder than the former. >+- After variable declarations for things that end up in struct, make them store into the struct. >+ >+*/ >+ >+class EscapedVariableCollector : public Visitor { >+ using Base = Visitor; >+public: >+ void visit(AST::MakePointerExpression& makePointerExpression) override >+ { >+ // OOPS: figure out "(&(*foo))" >+ // OOPS: If we solve &*foo, I think this can be an assert? Because we should >+ // have transformed &foo.bar to ander calls already. But maybe I'm missing something >+ // obvious. >+ if (!is<AST::VariableReference>(makePointerExpression.leftValue())) { >+ Base::visit(makePointerExpression.leftValue()); >+ return; >+ } >+ >+ auto& variableReference = downcast<AST::VariableReference>(makePointerExpression.leftValue()); >+ auto* variable = variableReference.variable(); >+ // If variable is unnamed, it means it's internal. We don't allow internal variables to escape. >+ // OOPS: verify this is correct. >+ if (variable->name().isEmpty()) >+ return; >+ m_escapedVariables.add(variable, makeString("_", variable->name(), "_", m_count++)); >+ } >+ >+ HashMap<AST::VariableDeclaration*, String> takeEscapedVariables() { return WTFMove(m_escapedVariables); } >+ >+private: >+ size_t m_count { 1 }; >+ HashMap<AST::VariableDeclaration*, String> m_escapedVariables; >+}; >+ >+static ALWAYS_INLINE Lexer::Token makeToken(Lexer::Token::Type type) >+{ >+ // OOPS: Almost certainly wrong >+ return Lexer::Token { { }, 0, type }; >+} >+ >+class ModifyCallArgumentsAndVariableReferences : public Visitor { >+ using Base = Visitor; >+public: >+ ModifyCallArgumentsAndVariableReferences(UniqueRef<AST::TypeReference>& structType, const HashMap<AST::VariableDeclaration*, String>& variableMapping) >+ : m_structType(structType) >+ , m_pointerToStructType(makeUniqueRef<AST::PointerType>(makeToken(Lexer::Token::Type::Identifier), AST::AddressSpace::Thread, m_structType->clone())) >+ , m_variableMapping(variableMapping) >+ { } >+ >+ void visit(AST::FunctionDefinition& functionDefinition) override >+ { >+ bool isEntryPoint = !!functionDefinition.entryPointType(); >+ if (isEntryPoint) { >+ // Entry points will declare the struct as the first thing they do. >+ >+ auto structVariableDeclaration = makeUniqueRef<AST::VariableDeclaration>(makeToken(Lexer::Token::Type::Identifier), AST::Qualifiers(), >+ m_structType->clone(), String(), WTF::nullopt, WTF::nullopt); >+ auto* structVariable = &structVariableDeclaration; >+ AST::VariableDeclarations structVariableDeclarations; >+ structVariableDeclarations.append(WTFMove(structVariableDeclaration)); >+ auto structDeclarationStatement = makeUniqueRef<AST::VariableDeclarationsStatement>(makeToken(Lexer::Token::Type::Identifier), WTFMove(structVariableDeclarations)); >+ >+ // OOPS: set type/address space stuff here! >+ auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*structVariable)); >+ auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(makeToken(Lexer::Token::Type::Identifier), WTFMove(structVariableReference)); >+ >+ auto pointerDeclaration = makeUniqueRef<AST::VariableDeclaration>(makeToken(Lexer::Token::Type::Identifier), AST::Qualifiers(), >+ m_pointerToStructType->clone(), String(), WTF::nullopt, Optional<UniqueRef<AST::Expression>>(WTFMove(makePointerExpression))); >+ m_structVariable = &pointerDeclaration; >+ AST::VariableDeclarations pointerVariableDeclarations; >+ pointerVariableDeclarations.append(WTFMove(pointerDeclaration)); >+ auto pointerDeclarationStatement = makeUniqueRef<AST::VariableDeclarationsStatement>(makeToken(Lexer::Token::Type::Identifier), WTFMove(pointerVariableDeclarations)); >+ >+ functionDefinition.block().statements().insert(0, WTFMove(structDeclarationStatement)); >+ functionDefinition.block().statements().insert(1, WTFMove(pointerDeclarationStatement)); >+ } else { >+ auto pointerDeclaration = makeUniqueRef<AST::VariableDeclaration>(makeToken(Lexer::Token::Type::Identifier), AST::Qualifiers(), >+ m_pointerToStructType->clone(), String(), WTF::nullopt, WTF::nullopt); >+ m_structVariable = &pointerDeclaration; >+ // Non entry points will take the stuct as their final parameter. >+ functionDefinition.parameters().append(WTFMove(pointerDeclaration)); >+ } >+ >+ Base::visit(functionDefinition); >+ >+ for (auto& parameter : functionDefinition.parameters()) { >+ auto iter = m_variableMapping.find(¶meter); >+ if (iter == m_variableMapping.end()) >+ continue; >+ >+ // OOPS: types! >+ auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*m_structVariable)); >+ auto lhs = makeUniqueRef<AST::InternalStructFieldExpression>(Lexer::Token(parameter->origin()), WTFMove(structVariableReference), String(iter->value)); >+ auto rhs = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(parameter.get())); >+ auto assignment = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(parameter->origin()), WTFMove(lhs), WTFMove(rhs)); >+ functionDefinition.block().statements().insert(isEntryPoint ? 2 : 0, >+ makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(assignment))); >+ } >+ >+ m_structVariable = nullptr; // OOPS: can we define inner functions? Don't think so... >+ } >+ >+ void visit(AST::CallExpression& callExpression) override >+ { >+ RELEASE_ASSERT(m_structVariable); >+ >+ // This works because it's illegal to call an entrypoint. Therefore, we can only >+ // call functions where we've already appended this struct as its final parameter. >+ // OOPS: set type/address space here! >+ auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*m_structVariable)); >+ callExpression.arguments().append(WTFMove(structVariableReference)); >+ >+ Base::visit(callExpression); >+ } >+ >+ void visit(AST::VariableReference& variableReference) override >+ { >+ RELEASE_ASSERT(m_structVariable); >+ >+ auto iter = m_variableMapping.find(variableReference.variable()); >+ if (iter == m_variableMapping.end()) >+ return; >+ >+ auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*m_structVariable)); >+ >+ auto* internalField = AST::replaceWith<AST::InternalStructFieldExpression>(variableReference, Lexer::Token(variableReference.origin()), WTFMove(structVariableReference), String(iter->value)); >+ // OOPS: set type/address space here. >+ UNUSED_PARAM(internalField); >+ } >+ >+ void visit(AST::VariableDeclarationsStatement& variableDeclarationsStatement) override >+ { >+ RELEASE_ASSERT(m_structVariable); >+ >+ Base::visit(variableDeclarationsStatement); >+ >+ Vector<std::pair<AST::VariableDeclaration*, Optional<UniqueRef<AST::Expression>>>> variables; >+ for (auto& variableDeclaration : variableDeclarationsStatement.variableDeclarations()) >+ variables.append({ &variableDeclaration, variableDeclaration->takeInitializer() }); >+ >+ auto origin = Lexer::Token(variableDeclarationsStatement.origin()); >+ Vector<UniqueRef<AST::Statement>> blockStatements; >+ blockStatements.append(makeUniqueRef<AST::VariableDeclarationsStatement>(Lexer::Token(origin), WTFMove(variableDeclarationsStatement.variableDeclarations()))); >+ >+ for (auto& pair : variables) { >+ if (pair.second) { >+ auto lhs = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*pair.first)); >+ auto rhs = WTFMove(pair.second.value()); >+ auto assignment = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(rhs->origin()), WTFMove(lhs), WTFMove(rhs)); >+ blockStatements.append(makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(assignment))); >+ } >+ >+ if (m_variableMapping.contains(pair.first)) { >+ auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*m_structVariable)); >+ auto lhs = makeUniqueRef<AST::InternalStructFieldExpression>(Lexer::Token(origin), WTFMove(structVariableReference), String(m_variableMapping.get(pair.first))); >+ auto rhs = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*pair.first)); >+ auto assignment = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(origin), WTFMove(lhs), WTFMove(rhs)); >+ blockStatements.append(makeUniqueRef<AST::EffectfulExpressionStatement>(WTFMove(assignment))); >+ } >+ } >+ >+ AST::replaceWith<AST::Block>(variableDeclarationsStatement, Lexer::Token(origin), WTFMove(blockStatements)); >+ } >+ >+private: >+ // OOPS: ok assumption to make that this isn't a stack? >+ // We can have just a single pointer since we only traverse over a single function >+ // at a time. We don't allow function nesting. >+ AST::VariableDeclaration* m_structVariable { nullptr }; >+ >+ UniqueRef<AST::TypeReference>& m_structType; >+ UniqueRef<AST::PointerType> m_pointerToStructType; >+ const HashMap<AST::VariableDeclaration*, String>& m_variableMapping; >+}; >+ >+void preserveVariableLifetimes(Program& program) >+{ >+ dataLogLn("before preserve variable lifetimes: "); >+ dataLogLn(ProgramDumper(program)); >+ dataLogLn("--------------"); >+ >+ HashMap<AST::VariableDeclaration*, String> escapedVariables; >+ { >+ EscapedVariableCollector collector; >+ collector.Visitor::visit(program); >+ escapedVariables = collector.takeEscapedVariables(); >+ } >+ >+ AST::StructureElements elements; >+ for (auto& pair : escapedVariables) { >+ auto* variable = pair.key; >+ String name = pair.value; >+ // OOPS: qualifiers? >+ // OOPS: Do I need to clone the type? Does ptr value itself matter? >+ elements.append(AST::StructureElement { makeToken(Lexer::Token::Type::Identifier), { }, variable->type()->clone(), WTFMove(name), WTF::nullopt }); >+ } >+ >+ String structName = "OOPS_make_this_a_real_unique_name"; >+ >+ auto wrapperStruct = makeUniqueRef<AST::StructureDefinition>(makeToken(Lexer::Token::Type::Struct), String(structName), WTFMove(elements)); >+ dataLogLn(StructureDefinitionDumper(wrapperStruct.get())); >+ >+ auto structType = makeUniqueRef<AST::TypeReference>(makeToken(Lexer::Token::Type::Identifier), String(structName), AST::TypeArguments()); >+ structType->setResolvedType(wrapperStruct.get()); >+ >+ { >+ ModifyCallArgumentsAndVariableReferences modifyCallArgumentsAndVariableReferences(structType, escapedVariables); >+ modifyCallArgumentsAndVariableReferences.Visitor::visit(program); >+ } >+ >+ dataLogLn("--------------"); >+ dataLogLn("after preserve variable lifetimes: "); >+ dataLogLn(ProgramDumper(program)); >+ dataLogLn("--------------"); >+} >+ >+} // namespace WHLSL >+ >+} // namespace WebCore >+ >+#endif // ENABLE(WEBGPU) >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h (nonexistent) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h (working copy) >@@ -0,0 +1,42 @@ >+/* >+ * 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. >+ */ >+ >+#pragma once >+ >+#if ENABLE(WEBGPU) >+ >+namespace WebCore { >+ >+namespace WHLSL { >+ >+class Program; >+ >+void preserveVariableLifetimes(Program&); >+ >+} >+ >+} >+ >+#endif >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLPropertyResolver.cpp (working copy) >@@ -474,8 +474,7 @@ void PropertyResolver::visit(AST::ReadMo > variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right? > > auto newValueExpression = readModifyWriteExpression.takeNewValueExpression(); >- ASSERT(newValueExpression); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198170 Relax this constraint. >- auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference), WTFMove(*newValueExpression)); >+ auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.origin()), WTFMove(variableReference), WTFMove(newValueExpression)); > assignmentExpression->setType(baseType->clone()); > assignmentExpression->setTypeAnnotation(AST::RightValue()); > >@@ -503,9 +502,8 @@ void PropertyResolver::visit(AST::ReadMo > } > > auto resultExpression = readModifyWriteExpression.takeResultExpression(); >- ASSERT(resultExpression); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198170 Be resilient to this being null. >- auto type = (*resultExpression)->resolvedType().clone(); >- expressions.append(WTFMove(*resultExpression)); >+ auto type = resultExpression->resolvedType().clone(); >+ expressions.append(WTFMove(resultExpression)); > > UniqueRef<AST::VariableDeclaration> oldVariableDeclaration = readModifyWriteExpression.takeOldValue(); > UniqueRef<AST::VariableDeclaration> newVariableDeclaration = readModifyWriteExpression.takeNewValue(); >@@ -549,8 +547,7 @@ void PropertyResolver::visit(AST::ReadMo > variableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread }); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198169 Is this right? > > auto newValueExpression = readModifyWriteExpression.takeNewValueExpression(); >- ASSERT(newValueExpression); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198170 Relax this constraint >- auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), WTFMove(variableReference), WTFMove(*newValueExpression)); >+ auto assignmentExpression = makeUniqueRef<AST::AssignmentExpression>(Lexer::Token(readModifyWriteExpression.leftValue().origin()), WTFMove(variableReference), WTFMove(newValueExpression)); > assignmentExpression->setType(readModifyWriteExpression.leftValue().resolvedType().clone()); > assignmentExpression->setTypeAnnotation(AST::RightValue()); > >@@ -567,9 +564,8 @@ void PropertyResolver::visit(AST::ReadMo > simplifyLeftValue(modifyResult->innerLeftValue); > > auto resultExpression = readModifyWriteExpression.takeResultExpression(); >- ASSERT(resultExpression); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198170 Be resilient to this being null. >- auto type = (*resultExpression)->resolvedType().clone(); >- modifyResult->expressions.append(WTFMove(*resultExpression)); >+ auto type = resultExpression->resolvedType().clone(); >+ modifyResult->expressions.append(WTFMove(resultExpression)); > > UniqueRef<AST::VariableDeclaration> oldVariableDeclaration = readModifyWriteExpression.takeOldValue(); > UniqueRef<AST::VariableDeclaration> newVariableDeclaration = readModifyWriteExpression.takeNewValue(); >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.cpp >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.cpp (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.cpp (working copy) >@@ -376,6 +376,8 @@ void Visitor::visit(AST::Expression& exp > checkErrorAndVisit(downcast<AST::NullLiteral>(expression)); > else if (is<AST::DotExpression>(expression)) > checkErrorAndVisit(downcast<AST::DotExpression>(expression)); >+ else if (is<AST::InternalStructFieldExpression>(expression)) >+ checkErrorAndVisit(downcast<AST::InternalStructFieldExpression>(expression)); > else if (is<AST::IndexExpression>(expression)) > checkErrorAndVisit(downcast<AST::IndexExpression>(expression)); > else if (is<AST::ReadModifyWriteExpression>(expression)) >@@ -397,6 +399,11 @@ void Visitor::visit(AST::DotExpression& > checkErrorAndVisit(static_cast<AST::PropertyAccessExpression&>(dotExpression)); > } > >+void Visitor::visit(AST::InternalStructFieldExpression& internalStructFieldExpression) >+{ >+ checkErrorAndVisit(internalStructFieldExpression.base()); >+} >+ > void Visitor::visit(AST::IndexExpression& indexExpression) > { > checkErrorAndVisit(indexExpression.indexExpression()); >@@ -536,10 +543,8 @@ void Visitor::visit(AST::ReadModifyWrite > checkErrorAndVisit(readModifyWriteExpression.leftValue()); > checkErrorAndVisit(readModifyWriteExpression.oldValue()); > checkErrorAndVisit(readModifyWriteExpression.newValue()); >- if (readModifyWriteExpression.newValueExpression()) >- checkErrorAndVisit(*readModifyWriteExpression.newValueExpression()); >- if (readModifyWriteExpression.resultExpression()) >- checkErrorAndVisit(*readModifyWriteExpression.resultExpression()); >+ checkErrorAndVisit(readModifyWriteExpression.newValueExpression()); >+ checkErrorAndVisit(readModifyWriteExpression.resultExpression()); > } > > void Visitor::visit(AST::TernaryExpression& ternaryExpression) >Index: Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.h (working copy) >@@ -72,6 +72,7 @@ class Continue; > class DoWhileLoop; > class Expression; > class DotExpression; >+class InternalStructFieldExpression; > class IndexExpression; > class PropertyAccessExpression; > class EffectfulExpressionStatement; >@@ -151,6 +152,7 @@ public: > virtual void visit(AST::DoWhileLoop&); > virtual void visit(AST::Expression&); > virtual void visit(AST::DotExpression&); >+ virtual void visit(AST::InternalStructFieldExpression&); > virtual void visit(AST::IndexExpression&); > virtual void visit(AST::PropertyAccessExpression&); > virtual void visit(AST::EffectfulExpressionStatement&); >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLDotExpression.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLDotExpression.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLDotExpression.h (working copy) >@@ -74,6 +74,27 @@ private: > String m_fieldName; > }; > >+// OOPS: make own file! >+class InternalStructFieldExpression : public Expression { >+public: >+ InternalStructFieldExpression(Lexer::Token&& origin, UniqueRef<Expression>&& base, String&& fieldName) >+ : Expression(WTFMove(origin)) >+ , m_base(WTFMove(base)) >+ , m_fieldName(WTFMove(fieldName)) >+ { >+ } >+ >+ virtual ~InternalStructFieldExpression() = default; >+ bool isInternalStructFieldExpression() const override { return true; } >+ const String& fieldName() { return m_fieldName; } >+ >+ Expression& base() { return m_base.get(); } >+ >+private: >+ UniqueRef<Expression> m_base; >+ String m_fieldName; >+}; >+ > } // namespace AST > > } >@@ -81,5 +102,6 @@ private: > } > > SPECIALIZE_TYPE_TRAITS_WHLSL_EXPRESSION(DotExpression, isDotExpression()) >+SPECIALIZE_TYPE_TRAITS_WHLSL_EXPRESSION(InternalStructFieldExpression, isInternalStructFieldExpression()) > > #endif >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLExpression.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLExpression.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLExpression.h (working copy) >@@ -41,9 +41,10 @@ namespace WHLSL { > namespace AST { > > class Expression : public Value { >+ using Base = Value; > public: > Expression(Lexer::Token&& origin) >- : m_origin(WTFMove(origin)) >+ : Base(WTFMove(origin)) > { > } > >@@ -55,8 +56,6 @@ public: > Expression& operator=(const Expression&) = delete; > Expression& operator=(Expression&&) = default; > >- const Lexer::Token& origin() const { return m_origin; } >- > UnnamedType* maybeResolvedType() { return m_type ? &*m_type : nullptr; } > > UnnamedType& resolvedType() >@@ -91,6 +90,7 @@ public: > virtual bool isCommaExpression() const { return false; } > virtual bool isDereferenceExpression() const { return false; } > virtual bool isDotExpression() const { return false; } >+ virtual bool isInternalStructFieldExpression() const { return false; } > virtual bool isFloatLiteral() const { return false; } > virtual bool isIndexExpression() const { return false; } > virtual bool isIntegerLiteral() const { return false; } >@@ -107,7 +107,6 @@ public: > virtual bool isEnumerationMemberLiteral() const { return false; } > > private: >- Lexer::Token m_origin; > Optional<UniqueRef<UnnamedType>> m_type; > Optional<TypeAnnotation> m_typeAnnotation; > }; >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLReadModifyWriteExpression.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLReadModifyWriteExpression.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLReadModifyWriteExpression.h (working copy) >@@ -86,13 +86,31 @@ public: > Expression& leftValue() { return m_leftValue; } > VariableDeclaration& oldValue() { return m_oldValue; } > VariableDeclaration& newValue() { return m_newValue; } >- Expression* newValueExpression() { return m_newValueExpression ? &*m_newValueExpression : nullptr; } >- Expression* resultExpression() { return m_resultExpression ? &*m_resultExpression : nullptr; } >+ Expression& newValueExpression() >+ { >+ ASSERT(m_newValueExpression); >+ return *m_newValueExpression; >+ } >+ Expression& resultExpression() >+ { >+ ASSERT(m_resultExpression); >+ return *m_resultExpression; >+ } > UniqueRef<Expression> takeLeftValue() { return WTFMove(m_leftValue); } > UniqueRef<VariableDeclaration> takeOldValue() { return WTFMove(m_oldValue); } > UniqueRef<VariableDeclaration> takeNewValue() { return WTFMove(m_newValue); } >- Optional<UniqueRef<Expression>> takeNewValueExpression() { return WTFMove(m_newValueExpression); } >- Optional<UniqueRef<Expression>> takeResultExpression() { return WTFMove(m_resultExpression); } >+ UniqueRef<Expression> takeNewValueExpression() >+ { >+ auto result = WTFMove(m_newValueExpression.value()); >+ m_newValueExpression.reset(); >+ return result; >+ } >+ UniqueRef<Expression> takeResultExpression() >+ { >+ auto result = WTFMove(m_resultExpression.value()); >+ m_resultExpression.reset(); >+ return result; >+ } > > private: > template<class U, class... Args> friend UniqueRef<U> WTF::makeUniqueRef(Args&&...); >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatement.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatement.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatement.h (working copy) >@@ -38,9 +38,10 @@ namespace WHLSL { > namespace AST { > > class Statement : public Value { >+ using Base = Value; > public: > Statement(Lexer::Token&& origin) >- : m_origin(WTFMove(origin)) >+ : Base(WTFMove(origin)) > { > } > >@@ -63,9 +64,6 @@ public: > virtual bool isTrap() const { return false; } > virtual bool isVariableDeclarationsStatement() const { return false; } > virtual bool isWhileLoop() const { return false; } >- >-private: >- Lexer::Token m_origin; > }; > > using Statements = Vector<UniqueRef<Statement>>; >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLValue.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLValue.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLValue.h (working copy) >@@ -37,7 +37,8 @@ namespace AST { > > class Value : public Node { > public: >- Value() >+ Value(Lexer::Token&& origin) >+ : m_origin(WTFMove(origin)) > { > } > >@@ -49,7 +50,10 @@ public: > Value& operator=(const Value&) = default; > Value& operator=(Value&&) = default; > >-private: >+ const Lexer::Token& origin() const { return m_origin; } >+ >+protected: >+ Lexer::Token m_origin; > }; > > } // namespace AST >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h (working copy) >@@ -45,9 +45,10 @@ namespace WHLSL { > namespace AST { > > class VariableDeclaration : public Value { >+ using Base = Value; > public: > VariableDeclaration(Lexer::Token&& origin, Qualifiers&& qualifiers, Optional<UniqueRef<UnnamedType>>&& type, String&& name, Optional<Semantic>&& semantic, Optional<UniqueRef<Expression>>&& initializer) >- : m_origin(WTFMove(origin)) >+ : Base(WTFMove(origin)) > , m_qualifiers(WTFMove(qualifiers)) > , m_type(WTFMove(type)) > , m_name(WTFMove(name)) >@@ -61,7 +62,6 @@ public: > VariableDeclaration(const VariableDeclaration&) = delete; > VariableDeclaration(VariableDeclaration&&) = default; > >- const Lexer::Token& origin() const { return m_origin; } > String& name() { return m_name; } > > const Optional<UniqueRef<UnnamedType>>& type() const { return m_type; } // Anonymous variables inside ReadModifyWriteExpressions have their type set by the type checker. >@@ -69,9 +69,9 @@ public: > Optional<Semantic>& semantic() { return m_semantic; } > Expression* initializer() { return m_initializer ? &*m_initializer : nullptr; } > bool isAnonymous() const { return m_name.isNull(); } >+ Optional<UniqueRef<Expression>> takeInitializer() { return WTFMove(m_initializer); } > > private: >- Lexer::Token m_origin; > Qualifiers m_qualifiers; > Optional<UniqueRef<UnnamedType>> m_type; > String m_name; >Index: Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h >=================================================================== >--- Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h (revision 245727) >+++ Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h (working copy) >@@ -55,6 +55,7 @@ public: > { > VariableReference result(Lexer::Token(variableDeclaration.origin())); > result.m_variable = &variableDeclaration; >+ result.m_name = variableDeclaration.name(); > return result; > } > >Index: Source/WebCore/WebCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/WebCore/WebCore.xcodeproj/project.pbxproj (revision 245692) >+++ Source/WebCore/WebCore.xcodeproj/project.pbxproj (working copy) >@@ -8323,6 +8323,8 @@ > 51FB67DA1AE6B5E400D06C5A /* ContentExtensionStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionStyleSheet.h; sourceTree = "<group>"; }; > 52131E5A1C4F15610033F802 /* VideoFullscreenInterfaceMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VideoFullscreenInterfaceMac.mm; sourceTree = "<group>"; }; > 5215862C229377B7005925EF /* WHLSLAST.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLAST.h; sourceTree = "<group>"; }; >+ 522E1A172297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WHLSLPreserveVariableLifetimes.cpp; sourceTree = "<group>"; }; >+ 522E1A192297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLPreserveVariableLifetimes.h; sourceTree = "<group>"; }; > 526724F11CB2FDF60075974D /* TextTrackRepresentationCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextTrackRepresentationCocoa.mm; sourceTree = "<group>"; }; > 526724F21CB2FDF60075974D /* TextTrackRepresentationCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTrackRepresentationCocoa.h; sourceTree = "<group>"; }; > 52B0D4BD1C57FD1E0077CE53 /* PlatformView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformView.h; sourceTree = "<group>"; }; >@@ -25428,6 +25430,8 @@ > C24A57AF21FAD53F004C6DD1 /* WHLSLPipelineDescriptor.h */, > C24A57BA21FEAFEA004C6DD1 /* WHLSLPrepare.cpp */, > C24A57BB21FEAFEA004C6DD1 /* WHLSLPrepare.h */, >+ 522E1A172297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.cpp */, >+ 522E1A192297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.h */, > C21BF73A21CD8D7000227979 /* WHLSLProgram.h */, > 1CAA82F62242AE0500E84BBB /* WHLSLPropertyResolver.cpp */, > 1CAA82F72242AE0500E84BBB /* WHLSLPropertyResolver.h */, >Index: LayoutTests/webgpu/whlsl.html >=================================================================== >--- LayoutTests/webgpu/whlsl.html (revision 245728) >+++ LayoutTests/webgpu/whlsl.html (working copy) >@@ -6,13 +6,34 @@ > <canvas id="canvas" width="400" height="400"></canvas> > <script> > const shaderSource = ` >-vertex float4 vertexShader(float4 position : attribute(0), float i : attribute(1)) : SV_Position { >- return position; >+ >+int test(int y) { >+ int x = 42; >+ int foo = x; >+ thread int* ptr = &x; >+ thread int* ptr2 = &y; >+ int z = y; >+ return 42; >+} >+ >+int baz(int x, int y) >+{ >+ return x; > } > >-fragment float4 fragmentShader(float4 position : SV_Position) : SV_Target 0 { >+void foo(int x) >+{ >+ int fooY = baz(x, x); >+ thread int* ptr = &fooY; >+} >+ >+vertex float4 vertexShader(float4 position : attribute(0), float i : attribute(1)) : SV_Position { >+ int x = 44; >+ thread int* ptr = &x; >+ int resultBaz = baz(x, 45); > return position; > } >+ > `; > async function start() { > const adapter = await navigator.gpu.requestAdapter();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 195794
:
370539
|
370620
|
370786
|
370795
|
370802
|
370821
|
370827
|
370915
|
370916
|
370959
|
370999
|
371004
|
371027