Commit 3b678745 by Olli Etuaho Committed by Commit Bot

Use a specialized hash function for mangled names

The hash values used for looking up built-ins now encode the string length and the location of parentheses as six-bit values, so that we don't need to check for these if the hash matches. This decreases shader_translator binary size on Windows by around 10 KB. BUG=angleproject:2267 BUG=chromium:823856 TEST=angle_unittests Change-Id: If8c28e1c8851750633509ec6273f556e06e91cd1 Reviewed-on: https://chromium-review.googlesource.com/973243Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 2231b4e0
...@@ -35,17 +35,29 @@ template <> ...@@ -35,17 +35,29 @@ template <>
const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis = const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis =
static_cast<size_t>(0xcbf29ce484222325ull); static_cast<size_t>(0xcbf29ce484222325ull);
uint32_t ImmutableString::hash32() const uint32_t ImmutableString::mangledNameHash() const
{ {
const char *data_ptr = data(); const char *dataPtr = data();
uint32_t hash = static_cast<uint32_t>(FowlerNollVoHash<4>::kFnvOffsetBasis); uint32_t hash = static_cast<uint32_t>(FowlerNollVoHash<4>::kFnvOffsetBasis);
while ((*data_ptr) != '\0') const uint32_t kMaxSixBitValue = (1u << 6) - 1u;
uint32_t parenLocation = kMaxSixBitValue;
uint32_t index = 0;
while (dataPtr[index] != '\0')
{ {
hash = hash ^ (*data_ptr); hash = hash ^ dataPtr[index];
hash = hash * static_cast<uint32_t>(FowlerNollVoHash<4>::kFnvPrime); hash = hash * static_cast<uint32_t>(FowlerNollVoHash<4>::kFnvPrime);
++data_ptr; if (dataPtr[index] == '(')
{
// We should only reach here once, since this function should not be called with invalid
// mangled names.
ASSERT(parenLocation == kMaxSixBitValue);
parenLocation = index;
}
++index;
} }
return hash; // Should not be called with strings longer than 63 characters.
ASSERT(index <= kMaxSixBitValue);
return ((hash >> 12) ^ (hash & 0xfff)) | (parenLocation << 26) | (index << 20);
} }
} // namespace sh } // namespace sh
...@@ -128,7 +128,10 @@ class ImmutableString ...@@ -128,7 +128,10 @@ class ImmutableString
} }
}; };
uint32_t hash32() const; // This hash encodes the opening parentheses location (if any) and name length in addition to a
// 20-bit hash. This way the hash is more useful for lookups. The string passed in should be at
// most 63 characters.
uint32_t mangledNameHash() const;
private: private:
const char *mData; const char *mData;
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -202,13 +202,21 @@ void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType, ...@@ -202,13 +202,21 @@ void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType,
const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name, const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name,
int shaderVersion) const int shaderVersion) const
{{ {{
uint32_t nameHash = name.hash32(); if (name.length() > {max_mangled_name_length})
{{
return nullptr;
}}
uint32_t nameHash = name.mangledNameHash();
{get_builtin} {get_builtin}
}} }}
const UnmangledBuiltIn *TSymbolTable::getUnmangledBuiltInForShaderVersion(const ImmutableString &name, int shaderVersion) const UnmangledBuiltIn *TSymbolTable::getUnmangledBuiltInForShaderVersion(const ImmutableString &name, int shaderVersion)
{{ {{
uint32_t nameHash = name.hash32(); if (name.length() > {max_unmangled_name_length})
{{
return nullptr;
}}
uint32_t nameHash = name.mangledNameHash();
{get_unmangled_builtin} {get_unmangled_builtin}
}} }}
...@@ -317,6 +325,7 @@ class GroupedList: ...@@ -317,6 +325,7 @@ class GroupedList:
""""Class for storing a list of objects grouped by symbol table level and condition.""" """"Class for storing a list of objects grouped by symbol table level and condition."""
def __init__(self): def __init__(self):
self.objs = OrderedDict() self.objs = OrderedDict()
self.max_name_length = 0
# We need to add all the levels here instead of lazily since they must be in a specific order. # We need to add all the levels here instead of lazily since they must be in a specific order.
for l in levels: for l in levels:
self.objs[l] = OrderedDict() self.objs[l] = OrderedDict()
...@@ -327,6 +336,8 @@ class GroupedList: ...@@ -327,6 +336,8 @@ class GroupedList:
if condition not in self.objs[level]: if condition not in self.objs[level]:
self.objs[level][condition] = OrderedDict() self.objs[level][condition] = OrderedDict()
self.objs[level][condition][name] = obj self.objs[level][condition][name] = obj
if len(name) > self.max_name_length:
self.max_name_length = len(name)
def has_key(self, level, condition, name): def has_key(self, level, condition, name):
if (level not in levels): if (level not in levels):
...@@ -340,6 +351,9 @@ class GroupedList: ...@@ -340,6 +351,9 @@ class GroupedList:
return self.objs[level][condition][name] return self.objs[level][condition][name]
return None return None
def get_max_name_length(self):
return self.max_name_length
def get_switch_code(self): def get_switch_code(self):
code = [] code = []
for level in levels: for level in levels:
...@@ -357,7 +371,7 @@ class GroupedList: ...@@ -357,7 +371,7 @@ class GroupedList:
switch = {} switch = {}
for name, obj in objs.iteritems(): for name, obj in objs.iteritems():
name_hash = hash32(name) name_hash = mangledNameHash(name)
if name_hash not in switch: if name_hash not in switch:
switch[name_hash] = [] switch[name_hash] = []
switch[name_hash].append(obj['hash_matched_code']) switch[name_hash].append(obj['hash_matched_code'])
...@@ -685,15 +699,22 @@ variable_name_count = {} ...@@ -685,15 +699,22 @@ variable_name_count = {}
id_counter = 0 id_counter = 0
def hash32(str, save_test = True): def mangledNameHash(str, save_test = True):
fnvOffsetBasis = 0x811c9dc5 fnvOffsetBasis = 0x811c9dc5
fnvPrime = 16777619 fnvPrime = 16777619
hash = fnvOffsetBasis hash = fnvOffsetBasis
index = 0
max_six_bit_value = (1 << 6) - 1
paren_location = max_six_bit_value
for c in str: for c in str:
hash = hash ^ ord(c) hash = hash ^ ord(c)
hash = hash * fnvPrime & 0xffffffff hash = hash * fnvPrime & 0xffffffff
if c == '(':
paren_location = index
index += 1
hash = ((hash >> 12) ^ (hash & 0xfff)) | (paren_location << 26) | (index << 20)
if save_test: if save_test:
sanity_check = ' ASSERT_EQ(0x{hash}u, ImmutableString("{str}").hash32());'.format(hash = ('%08x' % hash), str = str) sanity_check = ' ASSERT_EQ(0x{hash}u, ImmutableString("{str}").mangledNameHash());'.format(hash = ('%08x' % hash), str = str)
script_generated_hash_tests.update({sanity_check: None}) script_generated_hash_tests.update({sanity_check: None})
return hash return hash
...@@ -740,7 +761,7 @@ def mangled_name_hash_can_collide_with_different_parameters(function_variant_pro ...@@ -740,7 +761,7 @@ def mangled_name_hash_can_collide_with_different_parameters(function_variant_pro
# We exhaustively search through all possible lists of parameters and see if any other mangled # We exhaustively search through all possible lists of parameters and see if any other mangled
# name has the same hash. # name has the same hash.
mangled_name = function_variant_props['mangled_name'] mangled_name = function_variant_props['mangled_name']
hash = hash32(mangled_name) hash = mangledNameHash(mangled_name)
mangled_name_prefix = function_variant_props['name'] + '(' mangled_name_prefix = function_variant_props['name'] + '('
parameters_mangled_name_len = len(mangled_name) - len(mangled_name_prefix) parameters_mangled_name_len = len(mangled_name) - len(mangled_name_prefix)
parameters_mangled_name = mangled_name[len(mangled_name_prefix):] parameters_mangled_name = mangled_name[len(mangled_name_prefix):]
...@@ -751,7 +772,7 @@ def mangled_name_hash_can_collide_with_different_parameters(function_variant_pro ...@@ -751,7 +772,7 @@ def mangled_name_hash_can_collide_with_different_parameters(function_variant_pro
# may be possible. # may be possible.
return True return True
for variant in gen_parameters_mangled_name_variants(parameters_mangled_name_len): for variant in gen_parameters_mangled_name_variants(parameters_mangled_name_len):
if parameters_mangled_name != variant and hash32(mangled_name_prefix + variant, False) == hash: if parameters_mangled_name != variant and mangledNameHash(mangled_name_prefix + variant, False) == hash:
return True return True
return False return False
...@@ -880,7 +901,6 @@ def process_single_function_group(condition, group_name, group): ...@@ -880,7 +901,6 @@ def process_single_function_group(condition, group_name, group):
parameters = get_parameters(function_props) parameters = get_parameters(function_props)
template_args['unique_name'] = get_unique_identifier_name(template_args['name_with_suffix'], parameters) template_args['unique_name'] = get_unique_identifier_name(template_args['name_with_suffix'], parameters)
template_args['unique_name_no_parameters'] = get_unique_identifier_name(template_args['name_with_suffix'], [])
if template_args['unique_name'] in defined_function_variants: if template_args['unique_name'] in defined_function_variants:
continue continue
...@@ -936,10 +956,9 @@ def process_single_function_group(condition, group_name, group): ...@@ -936,10 +956,9 @@ def process_single_function_group(condition, group_name, group):
return &BuiltInFunction::kFunction_{unique_name}; return &BuiltInFunction::kFunction_{unique_name};
}}""" }}"""
else: else:
template_mangled_name_declaration = 'constexpr const ImmutableString {unique_name_no_parameters}("{name}(");' template_mangled_if = """if (name.beginsWith(BuiltInName::{name_with_suffix}))
name_declarations.add(template_mangled_name_declaration.format(**template_args))
template_mangled_if = """if (name.length() == {mangled_name_length} && name.beginsWith(BuiltInName::{unique_name_no_parameters}))
{{ {{
ASSERT(name.length() == {mangled_name_length});
return &BuiltInFunction::kFunction_{unique_name}; return &BuiltInFunction::kFunction_{unique_name};
}}""" }}"""
mangled_if = template_mangled_if.format(**template_args) mangled_if = template_mangled_if.format(**template_args)
...@@ -1195,6 +1214,8 @@ output_strings = { ...@@ -1195,6 +1214,8 @@ output_strings = {
'get_unmangled_builtin': unmangled_function_if_statements.get_switch_code(), 'get_unmangled_builtin': unmangled_function_if_statements.get_switch_code(),
'get_builtin': get_builtin_if_statements.get_switch_code(), 'get_builtin': get_builtin_if_statements.get_switch_code(),
'max_unmangled_name_length': unmangled_function_if_statements.get_max_name_length(),
'max_mangled_name_length': get_builtin_if_statements.get_max_name_length(),
'script_generated_hash_tests': '\n'.join(script_generated_hash_tests.iterkeys()) 'script_generated_hash_tests': '\n'.join(script_generated_hash_tests.iterkeys())
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment