Changeset 248339 in webkit
- Timestamp:
- Aug 6, 2019, 9:17:29 PM (7 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 2 edited
-
ChangeLog (modified) (1 diff)
-
Modules/webgpu/WHLSL/WHLSLChecker.cpp (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r248335 r248339 1 2019-08-06 Saam Barati <sbarati@apple.com> 2 3 [WHLSL] Make resolveFunction in Checker faster 4 https://bugs.webkit.org/show_bug.cgi?id=200287 5 6 Reviewed by Robin Morisset. 7 8 This patch makes compute_boids faster by making function overload 9 resolution faster inside the Checker. It's a ~6ms speedup in the 10 checker. The main idea is to limit the number of overloads we need 11 to look for by using a hash table that describes a function's type 12 instead of just using a hash table keyed by a function's name. 13 14 The interesting implementation detail here is we must construct entries 15 in the hash table such that they still allow constants to be resolved to 16 various types. This means that the key in the hash table must normalize 17 the vector of types it uses to express a function's identity. The normalization 18 rules are: 19 - int => float 20 - uint => float 21 - T* => float* 22 - T[] => float* 23 24 The first two rules are because int constants can be matched against 25 the float and uint types. The latter two rules are because the null 26 literal can be matched against any pointer or any array reference 27 (we pick float* arbitrarily). Even though it seems like these 28 normalization rules would drastically broaden the efficacy of the hash 29 table, we still see a 100x reduction in the number of overloads we must 30 resolve inside compute_boids. We go from having to resolve 400,000 31 overloads to just resolving 4,000. 32 33 * Modules/webgpu/WHLSL/WHLSLChecker.cpp: 34 (WebCore::WHLSL::FunctionKey::FunctionKey): 35 (WebCore::WHLSL::FunctionKey::isEmptyValue const): 36 (WebCore::WHLSL::FunctionKey::isHashTableDeletedValue const): 37 (WebCore::WHLSL::FunctionKey::hash const): 38 (WebCore::WHLSL::FunctionKey::operator== const): 39 (WebCore::WHLSL::FunctionKey::Hash::hash): 40 (WebCore::WHLSL::FunctionKey::Hash::equal): 41 (WebCore::WHLSL::FunctionKey::Traits::isEmptyValue): 42 (WebCore::WHLSL::Checker::Checker): 43 (WebCore::WHLSL::Checker::wrappedFloatType): 44 (WebCore::WHLSL::Checker::genericPointerType): 45 (WebCore::WHLSL::Checker::normalizedTypeForFunctionKey): 46 (WebCore::WHLSL::Checker::resolveFunction): 47 (WebCore::WHLSL::Checker::finishVisiting): 48 (WebCore::WHLSL::Checker::visit): 49 (WebCore::WHLSL::resolveFunction): Deleted. 50 1 51 2019-08-06 Loïc Yhuel <loic.yhuel@softathome.com> 2 52 -
trunk/Source/WebCore/Modules/webgpu/WHLSL/WHLSLChecker.cpp
r248303 r248339 117 117 checkErrorAndVisit(typeReference.resolvedType()); 118 118 } 119 }; 120 121 class FunctionKey { 122 public: 123 FunctionKey() = default; 124 FunctionKey(WTF::HashTableDeletedValueType) 125 { 126 m_castReturnType = bitwise_cast<AST::NamedType*>(static_cast<uintptr_t>(1)); 127 } 128 129 FunctionKey(String name, Vector<std::reference_wrapper<AST::UnnamedType>> types, AST::NamedType* castReturnType = nullptr) 130 : m_name(WTFMove(name)) 131 , m_types(WTFMove(types)) 132 , m_castReturnType(castReturnType) 133 { } 134 135 bool isEmptyValue() const { return m_name.isNull(); } 136 bool isHashTableDeletedValue() const { return m_castReturnType == bitwise_cast<AST::NamedType*>(static_cast<uintptr_t>(1)); } 137 138 unsigned hash() const 139 { 140 unsigned hash = IntHash<size_t>::hash(m_types.size()); 141 hash ^= m_name.hash(); 142 for (size_t i = 0; i < m_types.size(); ++i) 143 hash ^= m_types[i].get().hash() + i; 144 145 if (m_castReturnType) 146 hash ^= WTF::PtrHash<AST::Type*>::hash(&m_castReturnType->unifyNode()); 147 148 return hash; 149 } 150 151 bool operator==(const FunctionKey& other) const 152 { 153 if (m_types.size() != other.m_types.size()) 154 return false; 155 156 if (m_name != other.m_name) 157 return false; 158 159 for (size_t i = 0; i < m_types.size(); ++i) { 160 if (!matches(m_types[i].get(), other.m_types[i].get())) 161 return false; 162 } 163 164 if (!!m_castReturnType != !!other.m_castReturnType) 165 return false; 166 167 if (!m_castReturnType) 168 return true; 169 170 if (&m_castReturnType->unifyNode() == &other.m_castReturnType->unifyNode()) 171 return true; 172 173 return false; 174 } 175 176 struct Hash { 177 static unsigned hash(const FunctionKey& key) 178 { 179 return key.hash(); 180 } 181 182 static bool equal(const FunctionKey& a, const FunctionKey& b) 183 { 184 return a == b; 185 } 186 187 static const bool safeToCompareToEmptyOrDeleted = false; 188 static const bool emptyValueIsZero = false; 189 }; 190 191 struct Traits : public WTF::SimpleClassHashTraits<FunctionKey> { 192 static const bool hasIsEmptyValueFunction = true; 193 static bool isEmptyValue(const FunctionKey& key) { return key.isEmptyValue(); } 194 }; 195 196 private: 197 String m_name; 198 Vector<std::reference_wrapper<AST::UnnamedType>> m_types; 199 AST::NamedType* m_castReturnType; 119 200 }; 120 201 … … 218 299 } 219 300 220 static AST::FunctionDeclaration* resolveFunction(Program& program, Vector<std::reference_wrapper<AST::FunctionDeclaration>, 1>* possibleOverloads, Vector<std::reference_wrapper<ResolvingType>>& types, const String& name, CodeLocation location, const Intrinsics& intrinsics, AST::NamedType* castReturnType = nullptr)221 {222 if (possibleOverloads) {223 if (AST::FunctionDeclaration* function = resolveFunctionOverload(*possibleOverloads, types, castReturnType))224 return function;225 }226 227 if (auto newFunction = resolveByInstantiation(name, location, types, intrinsics)) {228 program.append(WTFMove(*newFunction));229 return &program.nativeFunctionDeclarations().last();230 }231 232 return nullptr;233 }234 235 301 static bool checkSemantics(Vector<EntryPointItem>& inputItems, Vector<EntryPointItem>& outputItems, const Optional<AST::EntryPointType>& entryPointType, const Intrinsics& intrinsics) 236 302 { … … 455 521 , m_program(program) 456 522 { 523 auto addFunction = [&] (AST::FunctionDeclaration& function) { 524 AST::NamedType* castReturnType = nullptr; 525 if (function.isCast() && is<AST::NamedType>(function.type().unifyNode())) 526 castReturnType = &downcast<AST::NamedType>(function.type().unifyNode()); 527 528 Vector<std::reference_wrapper<AST::UnnamedType>> types; 529 types.reserveInitialCapacity(function.parameters().size()); 530 531 for (auto& param : function.parameters()) 532 types.uncheckedAppend(normalizedTypeForFunctionKey(*param->type())); 533 534 auto addResult = m_functions.add(FunctionKey { function.name(), WTFMove(types), castReturnType }, Vector<std::reference_wrapper<AST::FunctionDeclaration>, 1>()); 535 addResult.iterator->value.append(function); 536 }; 537 538 for (auto& function : m_program.functionDefinitions()) 539 addFunction(function.get()); 540 for (auto& function : m_program.nativeFunctionDeclarations()) 541 addFunction(function.get()); 457 542 } 458 543 … … 512 597 void finishVisiting(AST::PropertyAccessExpression&, ResolvingType* additionalArgumentType = nullptr); 513 598 599 AST::FunctionDeclaration* resolveFunction(Vector<std::reference_wrapper<ResolvingType>>& types, const String& name, CodeLocation, AST::NamedType* castReturnType = nullptr); 600 601 AST::UnnamedType& wrappedFloatType() 602 { 603 if (!m_wrappedFloatType) 604 m_wrappedFloatType = AST::TypeReference::wrap({ }, m_intrinsics.floatType()); 605 return *m_wrappedFloatType; 606 } 607 608 AST::UnnamedType& genericPointerType() 609 { 610 if (!m_genericPointerType) 611 m_genericPointerType = AST::PointerType::create({ }, AST::AddressSpace::Thread, AST::TypeReference::wrap({ }, m_intrinsics.floatType())); 612 return *m_genericPointerType; 613 } 614 615 AST::UnnamedType& normalizedTypeForFunctionKey(AST::UnnamedType& type) 616 { 617 auto* unifyNode = &type.unifyNode(); 618 if (unifyNode == &m_intrinsics.uintType() || unifyNode == &m_intrinsics.intType()) 619 return wrappedFloatType(); 620 621 if (is<AST::ReferenceType>(type)) 622 return genericPointerType(); 623 624 return type; 625 } 626 627 RefPtr<AST::TypeReference> m_wrappedFloatType; 628 RefPtr<AST::UnnamedType> m_genericPointerType; 514 629 HashMap<AST::Expression*, std::unique_ptr<ResolvingType>> m_typeMap; 515 630 HashSet<String> m_vertexEntryPoints; … … 519 634 Program& m_program; 520 635 AST::FunctionDefinition* m_currentFunction { nullptr }; 636 HashMap<FunctionKey, Vector<std::reference_wrapper<AST::FunctionDeclaration>, 1>, FunctionKey::Hash, FunctionKey::Traits> m_functions; 521 637 }; 522 638 … … 637 753 return &resolvableTypeReference->resolvableType().resolvedType(); 638 754 })); 755 } 756 757 AST::FunctionDeclaration* Checker::resolveFunction(Vector<std::reference_wrapper<ResolvingType>>& types, const String& name, CodeLocation location, AST::NamedType* castReturnType) 758 { 759 Vector<std::reference_wrapper<AST::UnnamedType>> unnamedTypes; 760 unnamedTypes.reserveInitialCapacity(types.size()); 761 762 for (auto resolvingType : types) { 763 AST::UnnamedType* type = resolvingType.get().visit(WTF::makeVisitor([&](Ref<AST::UnnamedType>& unnamedType) -> AST::UnnamedType* { 764 return unnamedType.ptr(); 765 }, [&](RefPtr<ResolvableTypeReference>& resolvableTypeReference) -> AST::UnnamedType* { 766 if (resolvableTypeReference->resolvableType().maybeResolvedType()) 767 return &resolvableTypeReference->resolvableType().resolvedType(); 768 769 if (resolvableTypeReference->resolvableType().isFloatLiteralType() 770 || resolvableTypeReference->resolvableType().isIntegerLiteralType() 771 || resolvableTypeReference->resolvableType().isUnsignedIntegerLiteralType()) 772 return &wrappedFloatType(); 773 774 if (resolvableTypeReference->resolvableType().isNullLiteralType()) 775 return &genericPointerType(); 776 777 return commit(resolvableTypeReference->resolvableType()).get(); 778 })); 779 780 if (!type) { 781 setError(Error("Could not resolve the type of a constant.")); 782 return nullptr; 783 } 784 785 unnamedTypes.uncheckedAppend(normalizedTypeForFunctionKey(*type)); 786 } 787 788 { 789 auto iter = m_functions.find(FunctionKey { name, WTFMove(unnamedTypes), castReturnType }); 790 if (iter != m_functions.end()) { 791 if (AST::FunctionDeclaration* function = resolveFunctionOverload(iter->value, types, castReturnType)) 792 return function; 793 } 794 } 795 796 if (auto newFunction = resolveByInstantiation(name, location, types, m_intrinsics)) { 797 m_program.append(WTFMove(*newFunction)); 798 return &m_program.nativeFunctionDeclarations().last(); 799 } 800 801 return nullptr; 639 802 } 640 803 … … 962 1125 getterArgumentTypes.append(*additionalArgumentType); 963 1126 auto getterName = propertyAccessExpression.getterFunctionName(); 964 auto* getterFunctions = m_program.nameContext().getFunctions(getterName); 965 getterFunction = resolveFunction(m_program, getterFunctions, getterArgumentTypes, getterName, propertyAccessExpression.codeLocation(), m_intrinsics); 1127 getterFunction = resolveFunction(getterArgumentTypes, getterName, propertyAccessExpression.codeLocation()); 1128 if (hasError()) 1129 return; 966 1130 if (getterFunction) 967 1131 getterReturnType = &getterFunction->type(); … … 978 1142 anderArgumentTypes.append(*additionalArgumentType); 979 1143 auto anderName = propertyAccessExpression.anderFunctionName(); 980 auto* anderFunctions = m_program.nameContext().getFunctions(anderName); 981 anderFunction = resolveFunction(m_program, anderFunctions, anderArgumentTypes, anderName, propertyAccessExpression.codeLocation(), m_intrinsics); 1144 anderFunction = resolveFunction(anderArgumentTypes, anderName, propertyAccessExpression.codeLocation()); 1145 if (hasError()) 1146 return; 982 1147 if (anderFunction) 983 1148 anderReturnType = &downcast<AST::PointerType>(anderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer … … 993 1158 threadAnderArgumentTypes.append(*additionalArgumentType); 994 1159 auto anderName = propertyAccessExpression.anderFunctionName(); 995 auto* anderFunctions = m_program.nameContext().getFunctions(anderName); 996 threadAnderFunction = resolveFunction(m_program, anderFunctions, threadAnderArgumentTypes, anderName, propertyAccessExpression.codeLocation(), m_intrinsics); 1160 threadAnderFunction = resolveFunction(threadAnderArgumentTypes, anderName, propertyAccessExpression.codeLocation()); 1161 if (hasError()) 1162 return; 997 1163 if (threadAnderFunction) 998 1164 threadAnderReturnType = &downcast<AST::PointerType>(threadAnderFunction->type()).elementType(); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198164 Enforce the return of anders will always be a pointer … … 1040 1206 setterArgumentTypes.append(fieldResolvingType); 1041 1207 auto setterName = propertyAccessExpression.setterFunctionName(); 1042 auto* setterFunctions = m_program.nameContext().getFunctions(setterName); 1043 setterFunction = resolveFunction(m_program, setterFunctions, setterArgumentTypes, setterName, propertyAccessExpression.codeLocation(), m_intrinsics); 1208 setterFunction = resolveFunction(setterArgumentTypes, setterName, propertyAccessExpression.codeLocation()); 1209 if (hasError()) 1210 return; 1044 1211 if (setterFunction) 1045 1212 setterReturnType = &setterFunction->type(); … … 1421 1588 // We don't want to recurse to the same node twice. 1422 1589 1423 NameContext& nameContext = m_program.nameContext(); 1424 auto* functions = nameContext.getFunctions(callExpression.name()); 1425 if (!functions) { 1426 if (auto* types = nameContext.getTypes(callExpression.name())) { 1427 if (types->size() == 1) { 1428 if ((functions = nameContext.getFunctions("operator cast"_str))) 1429 callExpression.setCastData((*types)[0].get()); 1590 auto* function = resolveFunction(types, callExpression.name(), callExpression.codeLocation()); 1591 if (hasError()) 1592 return; 1593 1594 if (!function) { 1595 NameContext& nameContext = m_program.nameContext(); 1596 if (auto* castTypes = nameContext.getTypes(callExpression.name())) { 1597 if (castTypes->size() == 1) { 1598 AST::NamedType& castType = (*castTypes)[0].get(); 1599 function = resolveFunction(types, "operator cast"_str, callExpression.codeLocation(), &castType); 1600 if (hasError()) 1601 return; 1602 if (function) 1603 callExpression.setCastData(castType); 1430 1604 } 1431 1605 } 1432 1606 } 1433 if (!functions) { 1434 setError(Error("Could not find any functions with appropriate name.", callExpression.codeLocation())); 1435 return; 1436 } 1437 1438 auto* function = resolveFunction(m_program, functions, types, callExpression.name(), callExpression.codeLocation(), m_intrinsics, callExpression.castReturnType()); 1607 1439 1608 if (!function) { 1440 1609 // FIXME: Add better error messages for why we can't resolve to one of the overrides.
Note:
See TracChangeset
for help on using the changeset viewer.