xref: /aosp_15_r20/external/angle/src/compiler/translator/gen_builtin_symbols.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/env vpython3
2#
3# [VPYTHON:BEGIN]
4# wheel: <
5#   name: "infra/python/wheels/perfect-hash-py2_py3"
6#   version: "version:0.2.1"
7# >
8# [VPYTHON:END]
9#
10# Copyright 2018 The ANGLE Project Authors. All rights reserved.
11# Use of this source code is governed by a BSD-style license that can be
12# found in the LICENSE file.
13#
14# gen_builtin_symbols.py:
15#  Code generation for the built-in symbol tables.
16import sys
17
18# Conditional import enables getting inputs/outputs with python3 instead of vpython3
19if len(sys.argv) < 2:
20    from perfect_hash import generate_hash, Hash2
21
22from collections import OrderedDict
23import argparse
24import copy
25import hashlib
26import json
27import re
28import os
29import random
30
31template_immutablestring_cpp = """// GENERATED FILE - DO NOT EDIT.
32// Generated by {script_name} using data from {variable_data_source_name} and
33// {function_data_source_name}.
34//
35// Copyright 2020 The ANGLE Project Authors. All rights reserved.
36// Use of this source code is governed by a BSD-style license that can be
37// found in the LICENSE file.
38//
39// ImmutableString_autogen.cpp: Wrapper for static or pool allocated char arrays, that are guaranteed to be
40// valid and unchanged for the duration of the compilation.
41// Implements mangledNameHash using perfect hash function from gen_builtin_symbols.py
42
43#include "compiler/translator/ImmutableString.h"
44
45namespace sh {{
46
47std::ostream &operator<<(std::ostream &os, const ImmutableString &str)
48{{
49    return os.write(str.data(), str.length());
50}}
51
52}}
53
54#if defined(_MSC_VER)
55#    pragma warning(disable : 4309)  // truncation of constant value
56#endif
57
58
59namespace
60{{
61
62constexpr int mangledkT1[] = {{{mangled_S1}}};
63constexpr int mangledkT2[] = {{{mangled_S2}}};
64constexpr int mangledkG[] = {{{mangled_G}}};
65
66int MangledHashG(const char *key, const int *T)
67{{
68    int sum = 0;
69
70    for (int i = 0; key[i] != '\\0'; i++)
71    {{
72        sum += T[i] * key[i];
73        sum %= {mangled_NG};
74    }}
75    return mangledkG[sum];
76}}
77
78int MangledPerfectHash(const char *key)
79{{
80    if (strlen(key) > {mangled_NS})
81        return 0;
82
83    return (MangledHashG(key, mangledkT1) + MangledHashG(key, mangledkT2)) % {mangled_NG};
84}}
85
86constexpr int unmangledkT1[] = {{{unmangled_S1}}};
87constexpr int unmangledkT2[] = {{{unmangled_S2}}};
88constexpr int unmangledkG[] = {{{unmangled_G}}};
89
90int UnmangledHashG(const char *key, const int *T)
91{{
92    int sum = 0;
93
94    for (int i = 0; key[i] != '\\0'; i++)
95    {{
96        sum += T[i] * key[i];
97        sum %= {unmangled_NG};
98    }}
99    return unmangledkG[sum];
100}}
101
102int UnmangledPerfectHash(const char *key)
103{{
104    if (strlen(key) > {unmangled_NS})
105        return 0;
106
107    return (UnmangledHashG(key, unmangledkT1) + UnmangledHashG(key, unmangledkT2)) % {unmangled_NG};
108}}
109
110}}
111
112namespace sh
113{{
114
115template <>
116const size_t ImmutableString::FowlerNollVoHash<4>::kFnvPrime = 16777619u;
117
118template <>
119const size_t ImmutableString::FowlerNollVoHash<4>::kFnvOffsetBasis = 0x811c9dc5u;
120
121template <>
122const size_t ImmutableString::FowlerNollVoHash<8>::kFnvPrime =
123    static_cast<size_t>(1099511628211ull);
124
125template <>
126const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis =
127    static_cast<size_t>(0xcbf29ce484222325ull);
128
129uint32_t ImmutableString::mangledNameHash() const
130{{
131    return MangledPerfectHash(data());
132}}
133
134uint32_t ImmutableString::unmangledNameHash() const
135{{
136    return UnmangledPerfectHash(data());
137}}
138
139}}  // namespace sh
140"""
141
142template_immutablestringtest_cpp = """// GENERATED FILE - DO NOT EDIT.
143// Generated by {script_name} using data from {function_data_source_name}.
144//
145// Copyright 2020 The ANGLE Project Authors. All rights reserved.
146// Use of this source code is governed by a BSD-style license that can be
147// found in the LICENSE file.
148//
149// ImmutableString_test_autogen.cpp:
150//   Tests for matching script-generated hashes with runtime computed hashes.
151
152#include "compiler/translator/ImmutableString.h"
153#include "gtest/gtest.h"
154
155namespace sh
156{{
157
158TEST(ImmutableStringTest, ScriptGeneratedHashesMatch)
159{{
160{script_generated_hash_tests}
161{unmangled_script_generated_hash_tests}
162}}
163
164}}  // namespace sh
165"""
166
167# The header file has a "get" function for each variable. They are used in traversers.
168# It also declares id values of built-ins with human readable names, so they can be used to identify built-ins.
169template_builtin_header = """// GENERATED FILE - DO NOT EDIT.
170// Generated by {script_name} using data from {variable_data_source_name} and
171// {function_data_source_name}.
172//
173// Copyright 2020 The ANGLE Project Authors. All rights reserved.
174// Use of this source code is governed by a BSD-style license that can be
175// found in the LICENSE file.
176//
177// BuiltIn_autogen.h:
178//   Compile-time initialized built-ins.
179
180#ifndef COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
181#define COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
182
183#include "compiler/translator/SymbolUniqueId.h"
184
185namespace sh
186{{
187
188class TVariable;
189
190class BuiltInId
191{{
192public:
193
194{builtin_id_declarations}
195
196}};  // class BuiltInId
197
198namespace BuiltInVariable
199{{
200
201{get_variable_declarations}
202
203}}  // namespace BuiltInVariable
204
205}}  // namespace sh
206
207#endif  // COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
208"""
209
210template_symboltable_header = """// GENERATED FILE - DO NOT EDIT.
211// Generated by {script_name} using data from {variable_data_source_name} and
212// {function_data_source_name}.
213//
214// Copyright 2020 The ANGLE Project Authors. All rights reserved.
215// Use of this source code is governed by a BSD-style license that can be
216// found in the LICENSE file.
217//
218// SymbolTable_autogen.h:
219//   Autogenerated member variables of TSymbolTable.
220
221#ifndef COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
222#define COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
223
224namespace sh
225{{
226
227class TSymbolTableBase
228{{
229  public:
230    TSymbolTableBase() = default;
231{declare_member_variables}
232}};
233
234}}  // namespace sh
235
236#endif  // COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
237"""
238
239# By having the variables defined in a cpp file we ensure that there's just one instance of each of the declared variables.
240template_symboltable_cpp = """// GENERATED FILE - DO NOT EDIT.
241// Generated by {script_name} using data from {variable_data_source_name} and
242// {function_data_source_name}.
243//
244// Copyright 2020 The ANGLE Project Authors. All rights reserved.
245// Use of this source code is governed by a BSD-style license that can be
246// found in the LICENSE file.
247//
248// SymbolTable_autogen.cpp:
249//   Compile-time initialized built-ins.
250
251#include "compiler/translator/SymbolTable.h"
252
253#include "angle_gl.h"
254#include "compiler/translator/tree_util/BuiltIn.h"
255#include "compiler/translator/ImmutableString.h"
256#include "compiler/translator/StaticType.h"
257#include "compiler/translator/Symbol.h"
258#include "compiler/translator/SymbolTable.h"
259
260namespace sh
261{{
262using Resources = ShBuiltInResources;
263using TableBase = TSymbolTableBase;
264
265struct SymbolIdChecker
266{{
267    static_assert(TSymbolTable::kFirstUserDefinedSymbolId > {last_builtin_id});
268}};
269
270namespace BuiltInName
271{{
272
273constexpr const ImmutableString _empty("");
274{name_declarations}
275
276}}  // namespace BuiltInName
277
278// TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
279// this from TVariable. Now symbol constructors taking an id have to be public even though they're
280// not supposed to be accessible from outside of here. http://anglebug.com/42261100
281namespace BuiltInVariable
282{{
283
284{type_array_sizes_declarations}
285
286{variable_declarations}
287
288{get_variable_definitions}
289
290}}  // namespace BuiltInVariable
291
292namespace BuiltInParameters
293{{
294
295{parameter_declarations}
296
297}}  // namespace BuiltInParameters
298
299// TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
300// this from TFunction. Now symbol constructors taking an id have to be public even though they're
301// not supposed to be accessible from outside of here. http://anglebug.com/42261100
302namespace Func
303{{
304
305{function_declarations}
306
307}}  // namespace Func
308
309namespace BuiltInArray
310{{
311using namespace Func;
312using Rule = SymbolRule;
313
314// Rules used to initialize the mangled name array.
315constexpr SymbolRule kRules[] = {{
316{mangled_rules}
317}};
318
319// Flat array of all mangled names.
320constexpr const char *kMangledNames[] = {{
321{mangled_names_array}
322}};
323
324// Flat array of offsets from a symbol into the rules table.
325constexpr uint16_t kMangledOffsets[] = {{
326{mangled_offsets_array}
327}};
328
329using Ext = TExtension;
330
331// Flat array of all unmangled name identifiers.
332constexpr UnmangledEntry unmangled[] = {{
333{unmangled_array}
334}};
335
336}}
337
338void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType,
339                                              ShShaderSpec spec,
340                                              const ShBuiltInResources &resources)
341{{
342    const TSourceLoc zeroSourceLoc = {{0, 0, 0, 0}};
343{init_member_variables}
344}}
345
346namespace
347{{
348uint16_t GetNextRuleIndex(uint32_t nameHash)
349{{
350    if (nameHash == {num_mangled_names} - 1)
351        return ArraySize(BuiltInArray::kRules);
352    return BuiltInArray::kMangledOffsets[nameHash + 1];
353}}
354}}  // namespace
355
356const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name,
357                                         int shaderVersion) const
358{{
359    if (name.length() > {max_mangled_name_length})
360        return nullptr;
361
362    uint32_t nameHash = name.mangledNameHash();
363    if (nameHash >= {num_mangled_names})
364        return nullptr;
365
366    const char *actualName = BuiltInArray::kMangledNames[nameHash];
367    if (name != actualName)
368        return nullptr;
369
370    uint16_t startIndex = BuiltInArray::kMangledOffsets[nameHash];
371    uint16_t nextIndex = GetNextRuleIndex(nameHash);
372
373    return FindMangledBuiltIn(mShaderSpec, shaderVersion, mShaderType, mResources, *this, BuiltInArray::kRules, startIndex, nextIndex);
374}}
375
376bool TSymbolTable::isUnmangledBuiltInName(const ImmutableString &name,
377                                          int shaderVersion,
378                                          const TExtensionBehavior &extensions) const
379{{
380    if (name.length() > {max_unmangled_name_length})
381        return false;
382
383    uint32_t nameHash = name.unmangledNameHash();
384    if (nameHash >= {num_unmangled_names})
385        return false;
386
387    return BuiltInArray::unmangled[nameHash].matches(name, mShaderSpec, shaderVersion, mShaderType, extensions);
388}}
389
390}}  // namespace sh
391"""
392
393template_operator_header = """// GENERATED FILE - DO NOT EDIT.
394// Generated by {script_name} using data from {function_data_source_name}.
395//
396// Copyright 2021 The ANGLE Project Authors. All rights reserved.
397// Use of this source code is governed by a BSD-style license that can be
398// found in the LICENSE file.
399//
400// Operator_autogen.h:
401//   Operators used by the high-level (parse tree) representation.
402
403#ifndef COMPILER_TRANSLATOR_OPERATOR_AUTOGEN_H_
404#define COMPILER_TRANSLATOR_OPERATOR_AUTOGEN_H_
405
406#include <stdint.h>
407
408namespace sh
409{{
410
411enum TOperator : uint16_t
412{{
413    EOpNull,  // if in a node, should only mean a node is still being built
414
415    // Call a function defined in the AST. This might be a user-defined function or a function
416    // inserted by an AST transformation.
417    EOpCallFunctionInAST,
418
419    // Call an internal helper function with a raw implementation - the implementation can't be
420    // subject to AST transformations. Raw functions have a few constraints to keep them compatible
421    // with AST traversers:
422    // * They should not return arrays.
423    // * They should not have out parameters.
424    //
425    // DEPRECATED; DO NOT USE.  TODO: remove this.  http://anglebug.com/42264589
426    //
427    EOpCallInternalRawFunction,
428
429    //
430    // Branch (TIntermBranch)
431    //
432
433    EOpKill,  // Fragment only
434    EOpReturn,
435    EOpBreak,
436    EOpContinue,
437
438    //
439    // Constructor (TIntermAggregate)
440    //
441
442    EOpConstruct,
443
444    //
445    // Unary operators with special GLSL syntax (TIntermUnary).
446    //
447
448    EOpNegative,
449    EOpPositive,
450    EOpLogicalNot,
451    EOpBitwiseNot,
452
453    EOpPostIncrement,
454    EOpPostDecrement,
455    EOpPreIncrement,
456    EOpPreDecrement,
457
458    EOpArrayLength,
459
460    //
461    // Binary operators with special GLSL syntax (TIntermBinary).
462    //
463
464    EOpAdd,
465    EOpSub,
466    EOpMul,
467    EOpDiv,
468    EOpIMod,
469
470    EOpEqual,
471    EOpNotEqual,
472    EOpLessThan,
473    EOpGreaterThan,
474    EOpLessThanEqual,
475    EOpGreaterThanEqual,
476
477    EOpComma,
478
479    EOpVectorTimesScalar,
480    EOpVectorTimesMatrix,
481    EOpMatrixTimesVector,
482    EOpMatrixTimesScalar,
483    EOpMatrixTimesMatrix,
484
485    EOpLogicalOr,
486    EOpLogicalXor,
487    EOpLogicalAnd,
488
489    EOpBitShiftLeft,
490    EOpBitShiftRight,
491
492    EOpBitwiseAnd,
493    EOpBitwiseXor,
494    EOpBitwiseOr,
495
496    EOpIndexDirect,
497    EOpIndexIndirect,
498    EOpIndexDirectStruct,
499    EOpIndexDirectInterfaceBlock,
500
501    //
502    // Moves (TIntermBinary)
503    //
504
505    EOpAssign,
506    EOpInitialize,
507    EOpAddAssign,
508    EOpSubAssign,
509
510    EOpMulAssign,
511    EOpVectorTimesMatrixAssign,
512    EOpVectorTimesScalarAssign,
513    EOpMatrixTimesScalarAssign,
514    EOpMatrixTimesMatrixAssign,
515
516    EOpDivAssign,
517    EOpIModAssign,
518    EOpBitShiftLeftAssign,
519    EOpBitShiftRightAssign,
520    EOpBitwiseAndAssign,
521    EOpBitwiseXorAssign,
522    EOpBitwiseOrAssign,
523
524    // Not an op, but a marker for the start of built-in ops.
525    EOpLastNonBuiltIn = EOpBitwiseOrAssign,
526
527    //
528    // Built-in functions mapped to operators (either unary (TIntermUnary) or with multiple
529    // parameters (TIntermAggregate))
530    //
531    {operator_enum_declarations}
532}};
533
534// Returns the string corresponding to the operator in GLSL.  For built-in functions use the
535// function name directly.
536const char *GetOperatorString(TOperator op);
537
538// Say whether or not a binary or unary operation changes the value of a variable.
539bool IsAssignment(TOperator op);
540
541namespace BuiltInGroup
542{{
543static inline bool IsBuiltIn(TOperator op)
544{{
545    return op > EOpLastNonBuiltIn;
546}}
547{is_in_group_definitions}
548}}  // namespace BuiltInGroup
549
550}}  // namespace sh
551
552#endif  // COMPILER_TRANSLATOR_OPERATOR_AUTOGEN_H_
553
554"""
555
556template_rule = """Rule::Get<{version}, {shaders}, {extension}>({symbol_or_var})"""
557
558basic_types_enumeration = [
559    'Void',
560    'Float',
561    'Int',
562    'UInt',
563    'Bool',
564    'AtomicCounter',
565    'YuvCscStandardEXT',
566    'Sampler2D',
567    'Sampler3D',
568    'SamplerCube',
569    'Sampler2DArray',
570    'SamplerExternalOES',
571    'SamplerExternal2DY2YEXT',
572    'Sampler2DRect',
573    'Sampler2DMS',
574    'Sampler2DMSArray',
575    'ISampler2D',
576    'ISampler3D',
577    'ISamplerCube',
578    'ISampler2DArray',
579    'ISampler2DMS',
580    'ISampler2DMSArray',
581    'USampler2D',
582    'USampler3D',
583    'USamplerCube',
584    'USampler2DArray',
585    'USampler2DMS',
586    'USampler2DMSArray',
587    'Sampler2DShadow',
588    'SamplerCubeShadow',
589    'Sampler2DArrayShadow',
590    'SamplerBuffer',
591    'SamplerCubeArray',
592    'SamplerCubeArrayShadow',
593    'Sampler2DRectShadow',
594    'ISampler2DRect',
595    'ISamplerBuffer',
596    'ISamplerCubeArray',
597    'USampler2DRect',
598    'USamplerBuffer',
599    'USamplerCubeArray',
600    'SamplerVideoWEBGL',
601    'Image2D',
602    'Image3D',
603    'Image2DArray',
604    'ImageCube',
605    'Image2DMS',
606    'Image2DMSArray',
607    'ImageCubeArray',
608    'ImageRect',
609    'ImageBuffer',
610    'IImage2D',
611    'IImage3D',
612    'IImage2DArray',
613    'IImageCube',
614    'IImage2DMS',
615    'IImage2DMSArray',
616    'IImageCubeArray',
617    'IImageRect',
618    'IImageBuffer',
619    'UImage2D',
620    'UImage3D',
621    'UImage2DArray',
622    'UImageCube',
623    'UImage2DMS',
624    'UImage2DMSArray',
625    'UImageCubeArray',
626    'UImageRect',
627    'UImageBuffer',
628    'PixelLocalANGLE',
629    'IPixelLocalANGLE',
630    'UPixelLocalANGLE',
631    'SubpassInput',
632    'ISubpassInput',
633    'USubpassInput',
634]
635
636id_counter = 0
637
638
639def set_working_dir():
640    script_dir = os.path.dirname(os.path.abspath(__file__))
641    os.chdir(script_dir)
642
643
644def get_basic_mangled_name(basic):
645    index = basic_types_enumeration.index(basic)
646    if index < 26:
647        return '0' + chr(ord('A') + index)
648    if index < 52:
649        return '0' + chr(ord('a') + index - 26)
650    if index < 78:
651        return '1' + chr(ord('A') + index - 52)
652    return '1' + chr(ord('a') + index - 78)
653
654
655essl_levels = [
656    'ESSL3_2_BUILTINS', 'ESSL3_1_BUILTINS', 'ESSL3_BUILTINS', 'ESSL1_BUILTINS', 'COMMON_BUILTINS',
657    'ESSL_INTERNAL_BACKEND_BUILTINS'
658]
659
660
661def generate_suffix_from_level(level):
662    assert (level[:4] == 'ESSL')
663    assert (level[-9:] == '_BUILTINS')
664
665    # Turn XYSLN_M_BUILTINS to XYN_M
666    return level[:2] + level[4:-9]
667
668
669def get_essl_shader_version_for_level(level):
670    if level == None:
671        return '-1'
672    elif level == 'ESSL_INTERNAL_BACKEND_BUILTINS':
673        return 'kESSLInternalBackendBuiltIns'
674    elif level == 'ESSL3_2_BUILTINS':
675        return '320'
676    elif level == 'ESSL3_1_BUILTINS':
677        return '310'
678    elif level == 'ESSL3_BUILTINS':
679        return '300'
680    elif level == 'ESSL1_BUILTINS':
681        return '100'
682    elif level == 'COMMON_BUILTINS':
683        return '0'
684    else:
685        raise Exception('Unsupported symbol table level')
686
687
688def get_shader_version_for_level(level):
689    return get_essl_shader_version_for_level(level)
690
691
692def get_extension_list(extensions):
693    extension_list = [ext.strip() for ext in extensions.split(',')]
694    extension_string = ', '.join(['TExtension::' + ext for ext in extension_list])
695    return 'std::array<TExtension, ' + str(len(extension_list)) + 'u>{{' + extension_string + '}}'
696
697
698class GroupedList:
699    """"Class for storing a list of objects grouped by symbol table level and condition."""
700
701    def __init__(self, hashfn, num_names):
702        self.objs = OrderedDict()
703        self.max_name_length = 0
704        self.hashfn = hashfn
705        self.num_names = num_names
706        self.rule_offset = 0
707
708    def add_entry(self, essl_level, shader_type, name, symbol, essl_extension,
709                  script_generated_hash_tests):
710        if essl_level not in essl_levels:
711            raise Exception('Unexpected essl level: ' + str(essl_level))
712        if len(name) > self.max_name_length:
713            self.max_name_length = len(name)
714
715        name_hash = mangledNameHash(name, self.hashfn, script_generated_hash_tests, False)
716        if name_hash not in self.objs:
717            self.objs[name_hash] = OrderedDict()
718
719        self.objs[name_hash]['name'] = name
720
721        if essl_extension == 'UNDEFINED':
722            if 'symbol' in self.objs[name_hash] and self.objs[name_hash]['symbol'] != symbol:
723                # Adding a variable that is part of two ESSL extensions that have become core
724                if 'symbol2' not in self.objs[name_hash]:
725                    self.objs[name_hash]['essl_level2'] = essl_level
726                    self.objs[name_hash]['symbol2'] = symbol
727                    self.objs[name_hash]['shader_type2'] = shader_type
728                elif 'symbol3' not in self.objs[name_hash]:
729                    self.objs[name_hash]['essl_level3'] = essl_level
730                    self.objs[name_hash]['symbol3'] = symbol
731                    self.objs[name_hash]['shader_type3'] = shader_type
732                elif 'symbol4' not in self.objs[name_hash]:
733                    self.objs[name_hash]['essl_level4'] = essl_level
734                    self.objs[name_hash]['symbol4'] = symbol
735                    self.objs[name_hash]['shader_type4'] = shader_type
736                else:
737                    assert (False)
738            else:
739                self.objs[name_hash]['essl_level'] = essl_level
740                self.objs[name_hash]['symbol'] = symbol
741                self.objs[name_hash]['shader_type'] = shader_type
742
743        if essl_extension != 'UNDEFINED':
744            if ('essl_ext_symbol' in self.objs[name_hash] and
745                    self.objs[name_hash]['essl_ext_symbol'] != symbol):
746                # Adding a variable that is part of two ESSL extensions
747                if 'essl_ext_symbol2' not in self.objs[name_hash]:
748                    self.objs[name_hash]['essl_extension2'] = essl_extension
749                    self.objs[name_hash]['essl_ext_level2'] = essl_level
750                    self.objs[name_hash]['essl_ext_symbol2'] = symbol
751                    self.objs[name_hash]['essl_ext_shader_type2'] = shader_type
752                elif 'essl_ext_symbol3' not in self.objs[name_hash]:
753                    self.objs[name_hash]['essl_extension3'] = essl_extension
754                    self.objs[name_hash]['essl_ext_level3'] = essl_level
755                    self.objs[name_hash]['essl_ext_symbol3'] = symbol
756                    self.objs[name_hash]['essl_ext_shader_type3'] = shader_type
757                elif 'essl_ext_symbol4' not in self.objs[name_hash]:
758                    self.objs[name_hash]['essl_extension4'] = essl_extension
759                    self.objs[name_hash]['essl_ext_level4'] = essl_level
760                    self.objs[name_hash]['essl_ext_symbol4'] = symbol
761                    self.objs[name_hash]['essl_ext_shader_type4'] = shader_type
762                else:
763                    assert (False)
764            else:
765                self.objs[name_hash]['essl_extension'] = essl_extension
766                self.objs[name_hash]['essl_ext_level'] = essl_level
767                self.objs[name_hash]['essl_ext_symbol'] = symbol
768                self.objs[name_hash]['essl_ext_shader_type'] = shader_type
769
770    def get_max_name_length(self):
771        return self.max_name_length
772
773    def format_rule(self, rule):
774        return template_rule.format(**rule)
775
776    def format_rules(self, rules):
777        return ", ".join([self.format_rule(rule) for rule in rules])
778
779    def get_rules(self):
780        return self.rules
781
782    def get_names(self):
783        return self.names
784
785    def get_offsets(self):
786        return self.offsets
787
788    def update_arrays(self):
789
790        def add_rule(rules, level, shaders, extension, symbol):
791            var = ("&TableBase::%s" % symbol) if symbol.startswith("m_gl") else None
792
793            extension_list = []
794            versionField = get_shader_version_for_level(level)
795            shadersField = "Shader::%s" % ("ALL" if shaders == "NONE" else shaders)
796            symbolOrVarField = symbol.replace("Func::", "") if var is None else var
797            if extension != None:
798                extension_list = [ext.strip() for ext in extension.split(',')]
799                for ext in extension_list:
800                    rules.append({
801                        "version": versionField,
802                        "shaders": shadersField,
803                        "extension": "0" if ext == None else "EXT_INDEX(%s)" % ext,
804                        "symbol_or_var": symbolOrVarField
805                    })
806            else:
807                rules.append({
808                    "version": versionField,
809                    "shaders": shadersField,
810                    "extension": "0",
811                    "symbol_or_var": symbolOrVarField
812                })
813
814        self.names = []
815        self.offsets = []
816        self.rules = []
817        for hash_val in range(0, self.num_names):
818            if hash_val in self.objs:
819                data = self.objs[hash_val]
820
821                rules = []
822
823                if "symbol" in data and "essl_level" in data:
824                    add_rule(rules, data['essl_level'], data['shader_type'], None, data["symbol"])
825
826                if "symbol2" in data and "essl_level2" in data:
827                    add_rule(rules, data['essl_level2'], data['shader_type2'], None,
828                             data["symbol2"])
829
830                if "symbol3" in data and "essl_level3" in data:
831                    add_rule(rules, data['essl_level3'], data['shader_type3'], None,
832                             data["symbol3"])
833
834                if "symbol4" in data and "essl_level4" in data:
835                    add_rule(rules, data['essl_level4'], data['shader_type4'], None,
836                             data["symbol4"])
837
838                if "essl_ext_symbol" in data:
839                    add_rule(rules, data["essl_ext_level"], data["essl_ext_shader_type"],
840                             data["essl_extension"], data["essl_ext_symbol"])
841
842                if "essl_ext_symbol2" in data:
843                    add_rule(rules, data["essl_ext_level2"], data["essl_ext_shader_type2"],
844                             data["essl_extension2"], data["essl_ext_symbol2"])
845
846                if "essl_ext_symbol3" in data:
847                    add_rule(rules, data["essl_ext_level3"], data["essl_ext_shader_type3"],
848                             data["essl_extension3"], data["essl_ext_symbol3"])
849
850                if "essl_ext_symbol4" in data:
851                    add_rule(rules, data["essl_ext_level4"], data["essl_ext_shader_type4"],
852                             data["essl_extension4"], data["essl_ext_symbol4"])
853
854                name = data['name']
855                name_underscore = name.replace("(", "_")
856
857                self.names.append('"%s"' % name)
858                self.offsets.append("%d, // %s" % (self.rule_offset, name_underscore))
859                self.rules.append("%s" % self.format_rules(rules))
860
861                self.rule_offset += len(rules)
862
863            else:
864                self.names.append('""')
865                self.offsets.append('%d, // Empty' % self.rule_offset)
866
867
868class UnmangledGroupedList:
869    """"Class for storing a list of unmangled objects grouped by symbol table level and condition."""
870
871    def __init__(self, hashfn, num_names):
872        self.objs = OrderedDict()
873        self.max_name_length = 0
874        self.hashfn = hashfn
875        self.num_names = num_names
876
877    def add_entry(self, essl_level, shader_type, name, essl_ext, essl_extension,
878                  unmangled_script_generated_hash_tests):
879        if essl_level not in essl_levels:
880            raise Exception('Unexpected essl level: ' + str(essl_level))
881        if len(name) > self.max_name_length:
882            self.max_name_length = len(name)
883
884        name_hash = mangledNameHash(name, self.hashfn, unmangled_script_generated_hash_tests, True)
885        self.objs[name_hash] = OrderedDict()
886        self.objs[name_hash]['name'] = name
887        self.objs[name_hash]['essl_level'] = essl_level
888        self.objs[name_hash]['shader_type'] = shader_type
889        self.objs[name_hash]['essl_ext'] = essl_ext
890        self.objs[name_hash]['essl_extension'] = essl_extension
891
892    def has_key(self, essl_level, shader_type, name):
893        name_hash = mangledNameHash(name, self.hashfn, None, True, False)
894        if name_hash not in self.objs:
895            return False
896        entry = self.objs[name_hash]
897        if entry['essl_level'] != essl_level:
898            return False
899        if entry['shader_type'] != shader_type:
900            return False
901        return True
902
903    def get(self, essl_level, shader_type, name):
904        if self.has_key(essl_level, shader_type, name):
905            name_hash = mangledNameHash(name, self.hashfn, None, True, False)
906            return self.objs[name_hash]
907        return None
908
909    def get_max_name_length(self):
910        return self.max_name_length
911
912    def get_array(self):
913        code = []
914        for hash_val in range(0, self.num_names):
915            obj = self.objs[hash_val]
916            essl_level = obj['essl_level']
917            shader_type = 'Shader::' + obj['shader_type'] if obj[
918                'shader_type'] != 'NONE' else 'Shader::ALL'
919            data = []
920            data.append('"{name}"'.format(name=obj['name']))
921            essl_extensions = [ext.strip() for ext in obj['essl_extension'].split(',')]
922            template_extensions = 'std::array<TExtension, {count}>{{{{{extensions}}}}}'
923            data.append(
924                template_extensions.format(
925                    count=len(essl_extensions),
926                    extensions=','.join(['Ext::' + ext for ext in essl_extensions])))
927            data.append(get_essl_shader_version_for_level(essl_level))
928            data.append(shader_type)
929
930            code.append('{%s}' % ', '.join(data))
931        return code
932
933
934class TType:
935
936    def __init__(self, glsl_header_type):
937        if isinstance(glsl_header_type, str):
938            self.data = self.parse_type(glsl_header_type)
939        else:
940            self.data = glsl_header_type
941        self.normalize()
942
943    def normalize(self):
944        # Note that this will set primarySize and secondarySize also on genTypes. In that case they
945        # are overridden when the specific types are generated.
946        if 'primarySize' not in self.data:
947            if ('secondarySize' in self.data):
948                raise Exception(
949                    'Unexpected secondarySize on type that does not have primarySize set')
950            self.data['primarySize'] = 1
951        if 'secondarySize' not in self.data:
952            self.data['secondarySize'] = 1
953        if 'precision' not in self.data:
954            self.data['precision'] = 'Undefined'
955        if 'qualifier' not in self.data:
956            self.data['qualifier'] = 'Global'
957
958    def has_array_size(self):
959        return 'arraySize' in self.data
960
961    def get_statictype_string(self):
962        template_type = 'StaticType::Get<Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize}>()'
963        if self.has_array_size():
964            template_type = 'StaticType::GetArray<Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize}, kArraySize{arraySize}, 1>()'
965        return template_type.format(**self.data)
966
967    def get_dynamic_type_string(self):
968        template_type = 'new TType(Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize}'
969        if self.has_array_size():
970            template_type += ', TVector<unsigned int>{{{arraySize}}}'
971        template_type += ')'
972        return template_type.format(**self.data)
973
974    def get_mangled_name(self):
975        mangled_name = ''
976
977        size_key = (self.data['secondarySize'] - 1) * 4 + self.data['primarySize'] - 1
978        if size_key < 10:
979            mangled_name += chr(ord('0') + size_key)
980        else:
981            mangled_name += chr(ord('A') + size_key - 10)
982        mangled_name += get_basic_mangled_name(self.data['basic'])
983        if self.has_array_size():
984            mangled_name += 'x' + str(self.data['arraySize'])
985        return mangled_name
986
987    def get_human_readable_name(self):
988        name = self.data['basic']
989        if self.has_array_size():
990            name = str(self.data['arraySize']) + 'x' + name
991        name += str(self.data['primarySize'])
992        if self.data['secondarySize'] > 1:
993            name += 'x' + str(self.data['secondarySize'])
994        return name
995
996    def is_vector(self):
997        return self.data['primarySize'] > 1 and self.data['secondarySize'] == 1
998
999    def is_matrix(self):
1000        return self.data['secondarySize'] > 1
1001
1002    def get_object_size(self):
1003        return self.data['primarySize'] * self.data['secondarySize']
1004
1005    def specific_sampler_or_image_or_subpass_type(self, basic_type_prefix):
1006        if 'genType' in self.data and self.data['genType'] == 'sampler_or_image_or_subpass':
1007            type = {}
1008            if 'basic' not in self.data:
1009                type['basic'] = {'': 'Float', 'I': 'Int', 'U': 'UInt'}[basic_type_prefix]
1010                type['primarySize'] = self.data['primarySize']
1011            else:
1012                type['basic'] = basic_type_prefix + self.data['basic']
1013                type['primarySize'] = 1
1014            type['precision'] = 'Undefined'
1015            return TType(type)
1016        return self
1017
1018    def specific_type(self, vec_size):
1019        type = {}
1020        if 'genType' in self.data:
1021            type['basic'] = self.data['basic']
1022            type['precision'] = self.data['precision']
1023            type['qualifier'] = self.data['qualifier']
1024            type['primarySize'] = vec_size
1025            type['secondarySize'] = 1
1026            return TType(type)
1027        return self
1028
1029    def parse_type(self, glsl_header_type):
1030        # TODO(http://anglebug.com/42262477): handle readonly, writeonly qualifiers
1031        if glsl_header_type.startswith('readonly writeonly '):
1032            type_obj = self.parse_type(glsl_header_type[19:])
1033            type_obj['qualifier'] = 'Readonly Writeonly'
1034            return type_obj
1035        if glsl_header_type.startswith('readonly '):
1036            type_obj = self.parse_type(glsl_header_type[9:])
1037            type_obj['qualifier'] = 'Readonly'
1038            return type_obj
1039        if glsl_header_type.startswith('writeonly '):
1040            type_obj = self.parse_type(glsl_header_type[10:])
1041            type_obj['qualifier'] = 'Writeonly'
1042            return type_obj
1043        if glsl_header_type.startswith('out '):
1044            type_obj = self.parse_type(glsl_header_type[4:])
1045            type_obj['qualifier'] = 'ParamOut'
1046            return type_obj
1047        if glsl_header_type.startswith('inout '):
1048            type_obj = self.parse_type(glsl_header_type[6:])
1049            type_obj['qualifier'] = 'ParamInOut'
1050            return type_obj
1051
1052        basic_type_map = {
1053            'float': 'Float',
1054            'int': 'Int',
1055            'uint': 'UInt',
1056            'bool': 'Bool',
1057            'void': 'Void',
1058            'atomic_uint': 'AtomicCounter',
1059            'yuvCscStandardEXT': 'YuvCscStandardEXT'
1060        }
1061
1062        if glsl_header_type in basic_type_map:
1063            return {'basic': basic_type_map[glsl_header_type]}
1064
1065        type_obj = {}
1066
1067        basic_type_prefix_map = {'': 'Float', 'i': 'Int', 'u': 'UInt', 'b': 'Bool', 'v': 'Void'}
1068
1069        vec_re = re.compile(r'^([iudb]?)vec([234]?)((\[[234]\])?)$')
1070        vec_match = vec_re.match(glsl_header_type)
1071        if vec_match:
1072            type_obj['basic'] = basic_type_prefix_map[vec_match.group(1)]
1073            if vec_match.group(2) == '':
1074                # Type like "ivec" that represents either ivec2, ivec3 or ivec4
1075                type_obj['genType'] = 'vec'
1076            else:
1077                # vec with specific size
1078                if vec_match.group(3) != '':
1079                    # vec array
1080                    type_obj['primarySize'] = int(vec_match.group(2))
1081                    type_obj['arraySize'] = int(vec_match.group(3)[1])
1082                else:
1083                    type_obj['primarySize'] = int(vec_match.group(2))
1084            return type_obj
1085
1086        mat_re = re.compile(r'^mat([234])(x([234]))?$')
1087        mat_match = mat_re.match(glsl_header_type)
1088        if mat_match:
1089            type_obj['basic'] = 'Float'
1090            if len(glsl_header_type) == 4:
1091                mat_size = int(mat_match.group(1))
1092                type_obj['primarySize'] = mat_size
1093                type_obj['secondarySize'] = mat_size
1094            else:
1095                type_obj['primarySize'] = int(mat_match.group(1))
1096                type_obj['secondarySize'] = int(mat_match.group(3))
1097            return type_obj
1098
1099        gen_re = re.compile(r'^gen([IUDB]?)Type$')
1100        gen_match = gen_re.match(glsl_header_type)
1101        if gen_match:
1102            type_obj['basic'] = basic_type_prefix_map[gen_match.group(1).lower()]
1103            type_obj['genType'] = 'yes'
1104            return type_obj
1105
1106        if glsl_header_type.startswith('sampler'):
1107            type_obj['basic'] = glsl_header_type[0].upper() + glsl_header_type[1:]
1108            return type_obj
1109
1110        if glsl_header_type.startswith('gsampler') or \
1111           glsl_header_type.startswith('gimage') or \
1112           glsl_header_type.startswith('gpixelLocal') or \
1113           glsl_header_type.startswith('gsubpassInput'):
1114            type_obj['basic'] = glsl_header_type[1].upper() + glsl_header_type[2:]
1115            type_obj['genType'] = 'sampler_or_image_or_subpass'
1116            return type_obj
1117
1118        if glsl_header_type == 'gvec4':
1119            return {'primarySize': 4, 'genType': 'sampler_or_image_or_subpass'}
1120        if glsl_header_type == 'gvec3':
1121            return {'primarySize': 3, 'genType': 'sampler_or_image_or_subpass'}
1122
1123        if glsl_header_type == 'IMAGE_PARAMS':
1124            return {'genType': 'image_params'}
1125
1126        raise Exception('Unrecognized type: ' + str(glsl_header_type))
1127
1128
1129class SymbolsData:
1130
1131    def __init__(self):
1132
1133        # Declarations of symbol unique ids
1134        self.builtin_id_declarations = []
1135
1136        # Declarations of name string variables
1137        self.name_declarations = set()
1138
1139        # Code for testing that script-generated hashes match with runtime computed hashes.
1140        self.script_generated_hash_tests = OrderedDict()
1141        self.unmangled_script_generated_hash_tests = OrderedDict()
1142
1143
1144class VariablesData:
1145
1146    def __init__(self):
1147
1148        # Code for defining TVariables stored as members of TSymbolTable.
1149        self.declare_member_variables = []
1150        self.init_member_variables = []
1151
1152        # Declarations of static array sizes if any builtin TVariable is array.
1153        self.type_array_sizes_declarations = set()
1154
1155        # Declarations of builtin TVariables
1156        self.variable_declarations = []
1157
1158        # Functions for querying the pointer to a specific TVariable.
1159        self.get_variable_declarations = []
1160        self.get_variable_definitions = []
1161
1162
1163class FunctionsData:
1164
1165    def __init__(self):
1166
1167        # Declarations of builtin TFunctions
1168        self.function_declarations = []
1169
1170        # TOperator enum values (and grouping comments) for built-in functions.
1171        self.operator_list = dict()
1172        self.operator_enum_declarations = []
1173
1174        # Functions for testing whether a builtin belongs in group.
1175        self.is_in_group_definitions = []
1176
1177        # Declarations of parameter arrays for builtin TFunctions. Map from C++ variable name to the
1178        # full declaration.
1179        self.parameter_declarations = {}
1180
1181        self.defined_function_variants = set()
1182        self.defined_parameter_names = set()
1183
1184    def find_op(self, search_index, direction, limit_for_assertion):
1185
1186        while True:
1187            # Make sure the group is not empty.  An "opSuffix" must be used to distinguish between
1188            # built-ins with the same name, but in different groups.
1189            assert (search_index != limit_for_assertion)
1190
1191            line = self.operator_enum_declarations[search_index].lstrip()
1192            if line.startswith('EOp'):
1193                return line[:line.index(',')]
1194            search_index += direction
1195
1196
1197class HashFunction:
1198
1199    def __init__(self, f1, f2, G):
1200        self.f1 = f1
1201        self.f2 = f2
1202        self.G = G
1203
1204    def hash(self, key):
1205        return (self.G[self.f1(key)] + self.G[self.f2(key)]) % len(self.G)
1206
1207
1208def get_parsed_functions(functions_txt_filename):
1209
1210    def parse_function_parameters(parameters):
1211        if parameters == '':
1212            return []
1213        parametersOut = []
1214        parameters = parameters.split(', ')
1215        for parameter in parameters:
1216            parametersOut.append(TType(parameter.strip()))
1217        return parametersOut
1218
1219    lines = []
1220    with open(functions_txt_filename) as f:
1221        lines = f.readlines()
1222    lines = [
1223        line.strip() for line in lines if line.strip() != '' and not line.strip().startswith('//')
1224    ]
1225
1226    fun_re = re.compile(r'^(\w+) (\w+)\((.*)\);$')
1227
1228    parsed_functions = OrderedDict()
1229    group_stack = []
1230    default_metadata = {}
1231
1232    for line in lines:
1233        if line.startswith('GROUP BEGIN '):
1234            group_rest = line[12:].strip()
1235            group_parts = group_rest.split(' ', 1)
1236            current_group = {'functions': [], 'name': group_parts[0], 'subgroups': {}}
1237            if len(group_parts) > 1:
1238                group_metadata = json.loads(group_parts[1])
1239                current_group.update(group_metadata)
1240            group_stack.append(current_group)
1241        elif line.startswith('GROUP END '):
1242            group_end_name = line[10:].strip()
1243            current_group = group_stack[-1]
1244            if current_group['name'] != group_end_name:
1245                raise Exception('GROUP END: Unexpected function group name "' + group_end_name +
1246                                '" was expecting "' + current_group['name'] + '"')
1247            group_stack.pop()
1248            is_top_level_group = (len(group_stack) == 0)
1249            if is_top_level_group:
1250                if current_group['name'] in parsed_functions:
1251                    raise Exception('GROUP END: Duplicate group name "%s"' % current_group['name'])
1252                parsed_functions[current_group['name']] = current_group
1253                default_metadata = {}
1254            else:
1255                super_group = group_stack[-1]
1256                super_group['subgroups'][current_group['name']] = current_group
1257        elif line.startswith('DEFAULT METADATA'):
1258            line_rest = line[16:].strip()
1259            default_metadata = json.loads(line_rest)
1260        else:
1261            fun_match = fun_re.match(line)
1262            if fun_match:
1263                return_type = fun_match.group(1)
1264                name = fun_match.group(2)
1265                parameters = fun_match.group(3)
1266                function_props = {
1267                    'name': name,
1268                    'returnType': TType(return_type),
1269                    'parameters': parse_function_parameters(parameters)
1270                }
1271                function_props.update(default_metadata)
1272                if 'essl_level' in function_props:
1273                    group_stack[-1]['functions'].append(function_props)
1274            else:
1275                raise Exception('Unexpected function input line: ' + line)
1276
1277    return parsed_functions
1278
1279
1280def mangledNameHash(str, hashfn, script_generated_hash_tests, unmangled, save_test=True):
1281    hash = hashfn.hash(str)
1282    if save_test:
1283        confidence_check = ''
1284        if unmangled:
1285            confidence_check = '    ASSERT_EQ(0x{hash}u, ImmutableString("{str}").unmangledNameHash());'.format(
1286                hash=('%08x' % hash), str=str)
1287        else:
1288            confidence_check = '    ASSERT_EQ(0x{hash}u, ImmutableString("{str}").mangledNameHash());'.format(
1289                hash=('%08x' % hash), str=str)
1290        script_generated_hash_tests.update({confidence_check: None})
1291    return hash
1292
1293
1294def get_function_names(group, mangled_names, unmangled_names):
1295    if 'functions' in group:
1296        for function_props in group['functions']:
1297            function_name = function_props['name']
1298            unmangled_names.append(function_name)
1299            function_variants = gen_function_variants(function_props)
1300            for function_props in function_variants:
1301                parameters = get_parameters(function_props)
1302                mangled_names.append(get_function_mangled_name(function_name, parameters))
1303    if 'subgroups' in group:
1304        for subgroup_name, subgroup in group['subgroups'].items():
1305            get_function_names(subgroup, mangled_names, unmangled_names)
1306
1307
1308def get_variable_names(group, mangled_names):
1309    if 'variables' in group:
1310        for variable_name, props in group['variables'].items():
1311            mangled_names.append(variable_name)
1312    if 'subgroups' in group:
1313        for subgroup_name, subgroup in group['subgroups'].items():
1314            get_variable_names(subgroup, mangled_names)
1315
1316
1317def get_suffix(props):
1318    if 'suffix' in props:
1319        return props['suffix']
1320    return ''
1321
1322
1323def get_essl_extension(props):
1324    if 'essl_extension' in props:
1325        return props['essl_extension']
1326    return 'UNDEFINED'
1327
1328
1329def get_op(name, function_props, group_op_suffix):
1330    return 'EOp' + name[0].upper() + name[1:] + group_op_suffix + function_props.get(
1331        'opSuffix', '')
1332
1333
1334def get_known_to_not_have_side_effects(function_props):
1335    if 'hasSideEffects' in function_props:
1336        has_side_effects = function_props['hasSideEffects']
1337        if isinstance(has_side_effects, str):
1338            assert has_side_effects in ['true',
1339                                        'false'], 'Bad side effects value: ' + has_side_effects
1340            has_side_effects = has_side_effects == 'true'
1341        assert isinstance(has_side_effects, bool)
1342        return 'false' if has_side_effects else 'true'
1343    else:
1344        for param in get_parameters(function_props):
1345            if 'qualifier' in param.data and (param.data['qualifier'] == 'ParamOut' or
1346                                              param.data['qualifier'] == 'ParamInOut'):
1347                return 'false'
1348        return 'true'
1349
1350
1351def get_parameters(function_props):
1352    if 'parameters' in function_props:
1353        return function_props['parameters']
1354    return []
1355
1356
1357def get_function_mangled_name(function_name, parameters):
1358    mangled_name = function_name + '('
1359    for param in parameters:
1360        mangled_name += param.get_mangled_name()
1361    return mangled_name
1362
1363
1364def get_function_human_readable_name(function_name, parameters):
1365    name = function_name
1366    for param in parameters:
1367        name += '_' + param.get_human_readable_name()
1368    return name
1369
1370
1371def get_unique_identifier_name(function_name, parameters):
1372    unique_name = function_name + '_'
1373    for param in parameters:
1374        unique_name += param.get_mangled_name()
1375    return unique_name
1376
1377
1378def get_variable_name_to_store_parameter(param):
1379    unique_name = 'pt'
1380    if 'qualifier' in param.data:
1381        if param.data['qualifier'] == 'ParamOut':
1382            unique_name += '_o_'
1383        if param.data['qualifier'] == 'ParamInOut':
1384            unique_name += '_io_'
1385    unique_name += param.get_mangled_name()
1386    return unique_name
1387
1388
1389def get_variable_name_to_store_parameters(parameters):
1390    if len(parameters) == 0:
1391        return 'empty'
1392    unique_name = 'p'
1393    for param in parameters:
1394        if 'qualifier' in param.data:
1395            if param.data['qualifier'] == 'ParamOut':
1396                unique_name += '_o_'
1397            if param.data['qualifier'] == 'ParamInOut':
1398                unique_name += '_io_'
1399        unique_name += param.get_mangled_name()
1400    return unique_name
1401
1402
1403def define_constexpr_type_array_sizes(template_args, type_array_sizes_declarations):
1404    template_array_sizes_declaration = 'constexpr const unsigned int kArraySize{arraySize}[1] = {{{arraySize}}};'
1405    type_array_sizes_declarations.add(template_array_sizes_declaration.format(**template_args))
1406
1407
1408def define_constexpr_variable(template_args, variable_declarations):
1409    template_args['extension'] = get_extension_list(template_args['extension'])
1410    template_variable_declaration = 'constexpr const TVariable k{name_with_suffix}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, {extension}, {type});'
1411
1412    variable_declarations.append(template_variable_declaration.format(**template_args))
1413
1414
1415def gen_function_variants(function_props):
1416    function_variants = []
1417    parameters = get_parameters(function_props)
1418    function_is_gen_type = False
1419    gen_type = set()
1420    image_params_index = 0
1421    for param in parameters + [function_props['returnType']]:
1422        if 'genType' in param.data:
1423            if param.data['genType'] not in [
1424                    'sampler_or_image_or_subpass', 'vec', 'yes', 'image_params'
1425            ]:
1426                raise Exception(
1427                    'Unexpected value of genType "' + str(param.data['genType']) +
1428                    '" should be "sampler_or_image_or_subpass", "vec", "yes", or "image_params"')
1429            gen_type.add(param.data['genType'])
1430            if param.data['genType'] == 'image_params':
1431                image_params_index = parameters.index(param)
1432
1433    if len(gen_type) == 0:
1434        function_variants.append(function_props)
1435        return function_variants
1436
1437    # If we have image_params then we're generating variants for 33 separate functions,
1438    # each for a different type of image variable
1439    if 'image_params' in gen_type:
1440        variants = [['gimage2D', 'ivec2'], ['gimage3D', 'ivec3'], ['gimageCube', 'ivec3'],
1441                    ['gimageBuffer', 'int'], ['gimage2DArray', 'ivec3'],
1442                    ['gimageCubeArray', 'ivec3'], ['gimageRect', 'ivec2'],
1443                    ['gimage2DMS', 'ivec2', 'int'], ['gimage2DMSArray', 'ivec3', 'int']]
1444        for variant in variants:
1445            image_variant_parameters = []
1446            for param in parameters:
1447                if parameters.index(param) == image_params_index:
1448                    for variant_param in variant:
1449                        image_variant_parameters.append(TType(variant_param))
1450                else:
1451                    image_variant_parameters.append(param)
1452            types = ['', 'I', 'U']
1453            for type in types:
1454                variant_props = function_props.copy()
1455                variant_parameters = []
1456                for param in image_variant_parameters:
1457                    variant_parameters.append(
1458                        param.specific_sampler_or_image_or_subpass_type(type))
1459                variant_props['parameters'] = variant_parameters
1460                variant_props['returnType'] = function_props[
1461                    'returnType'].specific_sampler_or_image_or_subpass_type(type)
1462                function_variants.append(variant_props)
1463        return function_variants
1464
1465    # If we have a gsampler_or_image_or_subpass then we're generating variants for float, int and uint
1466    # samplers.
1467    if 'sampler_or_image_or_subpass' in gen_type:
1468        types = ['', 'I', 'U']
1469        for type in types:
1470            variant_props = function_props.copy()
1471            variant_parameters = []
1472            for param in parameters:
1473                variant_parameters.append(param.specific_sampler_or_image_or_subpass_type(type))
1474            variant_props['parameters'] = variant_parameters
1475            variant_props['returnType'] = function_props[
1476                'returnType'].specific_sampler_or_image_or_subpass_type(type)
1477            function_variants.append(variant_props)
1478        return function_variants
1479
1480    # If we have a normal gentype then we're generating variants for different sizes of vectors.
1481    sizes = range(1, 5)
1482    if 'vec' in gen_type:
1483        sizes = range(2, 5)
1484    for size in sizes:
1485        variant_props = function_props.copy()
1486        variant_parameters = []
1487        for param in parameters:
1488            variant_parameters.append(param.specific_type(size))
1489        variant_props['parameters'] = variant_parameters
1490        variant_props['returnType'] = function_props['returnType'].specific_type(size)
1491        function_variants.append(variant_props)
1492    return function_variants
1493
1494
1495def process_single_function(shader_type, group_name, function_props, symbols, variables, functions,
1496                            group_op_suffix, unmangled_function_if_statements, mangled_builtins):
1497    global id_counter
1498
1499    function_name = function_props['name']
1500    essl_level = function_props['essl_level'] if 'essl_level' in function_props else None
1501    essl_extension = get_essl_extension(function_props)
1502    op = get_op(function_name, function_props, group_op_suffix)
1503    template_args = {
1504        'name': function_name,
1505        'name_with_suffix': function_name + get_suffix(function_props),
1506        'essl_level': essl_level,
1507        'essl_extension': essl_extension,
1508        'extension': essl_extension,
1509        'op': op,
1510        'known_to_not_have_side_effects': get_known_to_not_have_side_effects(function_props)
1511    }
1512
1513    function_variants = gen_function_variants(function_props)
1514
1515    template_name_declaration = 'constexpr const ImmutableString {name_with_suffix}("{name}");'
1516    name_declaration = template_name_declaration.format(**template_args)
1517    if not name_declaration in symbols.name_declarations:
1518        symbols.name_declarations.add(name_declaration)
1519
1520    essl_ext = '{essl_extension}'.format(**template_args)
1521    unmangled_builtin_no_shader_type = unmangled_function_if_statements.get(
1522        essl_level, 'NONE', function_name)
1523    if unmangled_builtin_no_shader_type != None and unmangled_builtin_no_shader_type[
1524            'essl_extension'] == 'UNDEFINED':
1525        # We already have this unmangled name without a shader type nor extension on the same level.
1526        # No need to add a duplicate with a type.
1527        pass
1528    elif (not unmangled_function_if_statements.has_key(essl_level, shader_type, function_name)
1529         ) or (unmangled_builtin_no_shader_type and
1530               (essl_extension == 'UNDEFINED' and
1531                unmangled_builtin_no_shader_type['essl_extension'] != 'UNDEFINED')):
1532        unmangled_function_if_statements.add_entry(essl_level, shader_type, function_name,
1533                                                   essl_ext, essl_extension,
1534                                                   symbols.unmangled_script_generated_hash_tests)
1535
1536    extension_string = get_extension_list(template_args['extension'])
1537
1538    if op not in functions.operator_list:
1539        functions.operator_list[op] = group_name
1540        is_unary = group_name.startswith('Math') and len(get_parameters(function_variants[0])) == 1
1541        assert (not is_unary or
1542                all([len(get_parameters(props)) == 1 for props in function_variants]))
1543
1544        template_operator_enum = '    {op},{is_unary_comment}'
1545        template_args['is_unary_comment'] = '  // Unary' if is_unary else ''
1546
1547        functions.operator_enum_declarations.append(template_operator_enum.format(**template_args))
1548    else:
1549        # Ensure that built-ins in different groups don't generate the same op.  The Is<Group> query
1550        # functions rely on this.
1551        previous_group_name = functions.operator_list[op]
1552        if group_name != previous_group_name:
1553            print('Op ' + op + ' found in group ' + group_name + ' but was previously in group ' +
1554                  previous_group_name)
1555        assert (group_name == previous_group_name)
1556
1557    for function_props in function_variants:
1558        template_args['id'] = id_counter
1559
1560        parameters = get_parameters(function_props)
1561
1562        template_args['unique_name'] = get_unique_identifier_name(
1563            template_args['name_with_suffix'], parameters)
1564        template_args['param_count'] = len(parameters)
1565        template_args['return_type'] = function_props['returnType'].get_statictype_string()
1566        template_args['mangled_name'] = get_function_mangled_name(function_name, parameters)
1567        template_args['human_readable_name'] = get_function_human_readable_name(
1568            template_args['name_with_suffix'], parameters)
1569        template_args['mangled_name_length'] = len(template_args['mangled_name'])
1570
1571        symbol = '&Func::{unique_name}'.format(**template_args)
1572        mangled_builtins.add_entry(essl_level, shader_type, template_args['mangled_name'], symbol,
1573                                   template_args['essl_extension'],
1574                                   symbols.script_generated_hash_tests)
1575
1576        if template_args['unique_name'] in functions.defined_function_variants:
1577            continue
1578        functions.defined_function_variants.add(template_args['unique_name'])
1579
1580        template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {human_readable_name} = TSymbolUniqueId({id});'
1581        symbols.builtin_id_declarations.append(
1582            template_builtin_id_declaration.format(**template_args))
1583
1584        parameters_list = []
1585        for param in parameters:
1586            unique_param_name = get_variable_name_to_store_parameter(param)
1587            param_template_args = {
1588                'name': '_empty',
1589                'name_with_suffix': unique_param_name,
1590                'type': param.get_statictype_string(),
1591                'extension': 'UNDEFINED'
1592            }
1593            if unique_param_name not in functions.defined_parameter_names:
1594                id_counter += 1
1595                param_template_args['id'] = id_counter
1596                template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
1597                symbols.builtin_id_declarations.append(
1598                    template_builtin_id_declaration.format(**param_template_args))
1599                define_constexpr_variable(param_template_args, variables.variable_declarations)
1600                functions.defined_parameter_names.add(unique_param_name)
1601                if param.has_array_size():
1602                    array_size_template_args = {'arraySize': param.data['arraySize']}
1603                    define_constexpr_type_array_sizes(array_size_template_args,
1604                                                      variables.type_array_sizes_declarations)
1605            parameters_list.append(
1606                '&BuiltInVariable::k{name_with_suffix}'.format(**param_template_args))
1607
1608        template_args['parameters_var_name'] = get_variable_name_to_store_parameters(parameters)
1609        if len(parameters) > 0:
1610            template_args['parameters_list'] = ', '.join(parameters_list)
1611            template_parameter_list_declaration = 'constexpr const TVariable *{parameters_var_name}[{param_count}] = {{ {parameters_list} }};'
1612            functions.parameter_declarations[
1613                template_args['parameters_var_name']] = template_parameter_list_declaration.format(
1614                    **template_args)
1615        else:
1616            template_parameter_list_declaration = 'constexpr const TVariable **{parameters_var_name} = nullptr;'
1617            functions.parameter_declarations[
1618                template_args['parameters_var_name']] = template_parameter_list_declaration.format(
1619                    **template_args)
1620
1621        template_args['extension'] = extension_string
1622        template_function_declaration = 'constexpr const TFunction {unique_name}(BuiltInId::{human_readable_name}, BuiltInName::{name_with_suffix}, {extension}, BuiltInParameters::{parameters_var_name}, {param_count}, {return_type}, {op}, {known_to_not_have_side_effects});'
1623        functions.function_declarations.append(
1624            template_function_declaration.format(**template_args))
1625
1626        id_counter += 1
1627
1628
1629def process_single_function_group(shader_type, group_name, group, symbols, variables, functions,
1630                                  group_op_suffix, unmangled_function_if_statements,
1631                                  mangled_builtins):
1632
1633    if 'functions' not in group:
1634        return
1635
1636    for function_props in group['functions']:
1637        process_single_function(shader_type, group_name, function_props, symbols, variables,
1638                                functions, group_op_suffix, unmangled_function_if_statements,
1639                                mangled_builtins)
1640
1641        if 'essl_extension_becomes_core_in' in function_props:
1642            assert ('essl_extension' in function_props)
1643
1644            core_props = copy.deepcopy(function_props)
1645
1646            # Adjust the props by updating the level, removing extension and adding suffix
1647            core_level = function_props['essl_extension_becomes_core_in']
1648            core_props['essl_level'] = core_level
1649            del core_props['essl_extension']
1650            suffix = core_props['suffix'] if 'suffix' in core_props else ''
1651            suffix += generate_suffix_from_level(core_level)
1652            core_props['suffix'] = suffix
1653
1654            process_single_function(shader_type, group_name, core_props, symbols, variables,
1655                                    functions, group_op_suffix, unmangled_function_if_statements,
1656                                    mangled_builtins)
1657
1658
1659def process_function_group(group_name, group, symbols, variables, functions,
1660                           parent_group_op_suffix, unmangled_function_if_statements,
1661                           mangled_builtins):
1662
1663    functions.operator_enum_declarations.append('')
1664    functions.operator_enum_declarations.append('    // Group ' + group_name)
1665    first_op_index = len(functions.operator_enum_declarations)
1666
1667    shader_type = 'NONE'
1668    if 'shader_type' in group:
1669        shader_type = group['shader_type']
1670
1671    group_op_suffix = parent_group_op_suffix + group.get('opSuffix', '')
1672    process_single_function_group(shader_type, group_name, group, symbols, variables, functions,
1673                                  group_op_suffix, unmangled_function_if_statements,
1674                                  mangled_builtins)
1675
1676    if 'subgroups' in group:
1677        for subgroup_name, subgroup in group['subgroups'].items():
1678            process_function_group(group_name + subgroup_name, subgroup, symbols, variables,
1679                                   functions, group_op_suffix, unmangled_function_if_statements,
1680                                   mangled_builtins)
1681
1682    if 'queryFunction' in group:
1683        last_op_index = len(functions.operator_enum_declarations) - 1
1684
1685        first_op = functions.find_op(first_op_index, +1, last_op_index + 1)
1686        last_op = functions.find_op(last_op_index, -1, first_op_index - 1)
1687
1688        template_args = {'first_op': first_op, 'last_op': last_op, 'group_name': group_name}
1689        template_is_in_group_definition = """static inline bool Is{group_name}(TOperator op)
1690{{
1691    return op >= {first_op} && op <= {last_op};
1692}}"""
1693        functions.is_in_group_definitions.append(
1694            template_is_in_group_definition.format(**template_args))
1695
1696
1697def prune_parameters_arrays(parameter_declarations, function_declarations):
1698    # We can share parameters arrays between functions in case one array is a subarray of another.
1699    parameter_variable_name_replacements = {}
1700    used_param_variable_names = set()
1701    for param_variable_name, param_declaration in sorted(
1702            parameter_declarations.items(), key=lambda item: -len(item[0])):
1703        replaced = False
1704        for used in sorted(used_param_variable_names):
1705            if used.startswith(param_variable_name):
1706                parameter_variable_name_replacements[param_variable_name] = used
1707                replaced = True
1708                break
1709        if not replaced:
1710            used_param_variable_names.add(param_variable_name)
1711
1712    for i in range(len(function_declarations)):
1713        for replaced, replacement in parameter_variable_name_replacements.items():
1714            function_declarations[i] = function_declarations[i].replace(
1715                'BuiltInParameters::' + replaced + ',', 'BuiltInParameters::' + replacement + ',')
1716
1717    return [
1718        value for key, value in parameter_declarations.items() if key in used_param_variable_names
1719    ]
1720
1721
1722def process_single_variable(shader_type, variable_name, props, symbols, variables,
1723                            mangled_builtins):
1724    global id_counter
1725
1726    essl_level = props['essl_level'] if 'essl_level' in props else None
1727    template_args = {
1728        'id': id_counter,
1729        'name': variable_name,
1730        'name_with_suffix': variable_name + get_suffix(props),
1731        'essl_level': essl_level,
1732        'essl_extension': get_essl_extension(props),
1733        'extension': get_essl_extension(props),
1734        'class': 'TVariable'
1735    }
1736
1737    template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
1738    symbols.builtin_id_declarations.append(template_builtin_id_declaration.format(**template_args))
1739
1740    template_name_declaration = 'constexpr const ImmutableString {name}("{name}");'
1741    symbols.name_declarations.add(template_name_declaration.format(**template_args))
1742
1743    is_member = True
1744    template_init_variable = ''
1745
1746    extension_string = get_extension_list(template_args['extension'])
1747
1748    if 'type' in props:
1749        if props['type']['basic'] != 'Bool' and 'precision' not in props['type']:
1750            raise Exception('Missing precision for variable ' + variable_name)
1751        template_args['type'] = TType(props['type']).get_statictype_string()
1752
1753    if 'fields' in props:
1754        # Handle struct and interface block definitions.
1755        template_args['class'] = props['class']
1756        template_args['fields'] = 'fields_{name_with_suffix}'.format(**template_args)
1757        variables.init_member_variables.append(
1758            '    TFieldList *{fields} = new TFieldList();'.format(**template_args))
1759        for field_name, field_type in props['fields'].items():
1760            template_args['field_name'] = field_name
1761            template_args['field_type'] = TType(field_type).get_dynamic_type_string()
1762            template_name_declaration = 'constexpr const ImmutableString {field_name}("{field_name}");'
1763            symbols.name_declarations.add(template_name_declaration.format(**template_args))
1764            template_add_field = '    {fields}->push_back(new TField({field_type}, BuiltInName::{field_name}, zeroSourceLoc, SymbolType::BuiltIn));'
1765            variables.init_member_variables.append(template_add_field.format(**template_args))
1766        template_args['extension'] = extension_string
1767        template_init_temp_variable = '    {class} *{name_with_suffix} = new {class}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, {extension}, {fields});'
1768        variables.init_member_variables.append(template_init_temp_variable.format(**template_args))
1769        if 'private' in props and props['private']:
1770            is_member = False
1771        else:
1772            template_init_variable = '    m_{name_with_suffix} = {name_with_suffix};'
1773
1774    elif 'initDynamicType' in props:
1775        # Handle variables whose type can't be expressed as TStaticType
1776        # (type is a struct or has variable array size for example).
1777        template_args['type_name'] = 'type_{name_with_suffix}'.format(**template_args)
1778        template_args['type'] = template_args['type_name']
1779        template_args['ext_or_core_suffix'] = ''
1780        if 'essl_extension_becomes_core_in' in props and 'essl_extension' not in props:
1781            template_args['ext_or_core_suffix'] = generate_suffix_from_level(props['essl_level'])
1782        template_args['initDynamicType'] = props['initDynamicType'].format(**template_args)
1783        template_args['extension'] = extension_string
1784        template_init_variable = """    {initDynamicType}
1785{type_name}->realize();
1786m_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, {extension}, {type});"""
1787
1788    elif 'value' in props:
1789        # Handle variables with constant value, such as gl_MaxDrawBuffers.
1790        if props['value'] != 'resources':
1791            raise Exception('Unrecognized value source in variable properties: ' +
1792                            str(props['value']))
1793        resources_key = variable_name[3:]
1794        if 'valueKey' in props:
1795            resources_key = props['valueKey']
1796        template_args['value'] = 'resources.' + resources_key
1797        template_args['object_size'] = TType(props['type']).get_object_size()
1798        template_args['extension'] = extension_string
1799        template_init_variable = """    m_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, {extension}, {type});
1800{{
1801    TConstantUnion *unionArray = new TConstantUnion[{object_size}];
1802    unionArray[0].setIConst({value});
1803    static_cast<TVariable *>(m_{name_with_suffix})->shareConstPointer(unionArray);
1804}}"""
1805        if template_args['object_size'] > 1:
1806            template_init_variable = """    m_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, {extension}, {type});
1807{{
1808    TConstantUnion *unionArray = new TConstantUnion[{object_size}];
1809    for (size_t index = 0u; index < {object_size}; ++index)
1810    {{
1811        unionArray[index].setIConst({value}[index]);
1812    }}
1813    static_cast<TVariable *>(m_{name_with_suffix})->shareConstPointer(unionArray);
1814}}"""
1815
1816    else:
1817        # Handle variables that can be stored as constexpr TVariable like
1818        # gl_Position, gl_FragColor etc.
1819        define_constexpr_variable(template_args, variables.variable_declarations)
1820        is_member = False
1821
1822        template_get_variable_declaration = 'const TVariable *{name_with_suffix}();'
1823        variables.get_variable_declarations.append(
1824            template_get_variable_declaration.format(**template_args))
1825
1826        template_get_variable_definition = """const TVariable *{name_with_suffix}()
1827{{
1828return &k{name_with_suffix};
1829}}
1830"""
1831        variables.get_variable_definitions.append(
1832            template_get_variable_definition.format(**template_args))
1833
1834        obj = '&BuiltInVariable::k{name_with_suffix}'.format(**template_args)
1835        mangled_builtins.add_entry(essl_level, shader_type, template_args['name'], obj,
1836                                   template_args['essl_extension'],
1837                                   symbols.script_generated_hash_tests)
1838
1839    if is_member:
1840        variables.init_member_variables.append(template_init_variable.format(**template_args))
1841
1842        template_declare_member_variable = 'TSymbol *m_{name_with_suffix} = nullptr;'
1843        variables.declare_member_variables.append(
1844            template_declare_member_variable.format(**template_args))
1845
1846        obj = 'm_{name_with_suffix}'.format(**template_args)
1847
1848        mangled_builtins.add_entry(essl_level, shader_type, template_args['name'], obj,
1849                                   template_args['essl_extension'],
1850                                   symbols.script_generated_hash_tests)
1851
1852    id_counter += 1
1853
1854
1855def process_single_variable_group(shader_type, group, symbols, variables, mangled_builtins):
1856    global id_counter
1857    if 'variables' not in group:
1858        return
1859    for variable_name, props in group['variables'].items():
1860        process_single_variable(shader_type, variable_name, props, symbols, variables,
1861                                mangled_builtins)
1862
1863        if 'essl_extension_becomes_core_in' in props:
1864            assert ('essl_extension' in props)
1865
1866            core_props = copy.deepcopy(props)
1867
1868            # Adjust the props by updating the level, removing extension and adding suffix
1869            core_level = props['essl_extension_becomes_core_in']
1870            core_props['essl_level'] = core_level
1871            del core_props['essl_extension']
1872            suffix = core_props['suffix'] if 'suffix' in core_props else ''
1873            suffix += generate_suffix_from_level(core_level)
1874            core_props['suffix'] = suffix
1875            process_single_variable(shader_type, variable_name, core_props, symbols, variables,
1876                                    mangled_builtins)
1877
1878
1879def process_variable_group(shader_type, group_name, group, symbols, variables, mangled_builtins):
1880    global id_counter
1881
1882    if 'shader_type' in group:
1883        shader_type = group['shader_type']
1884
1885    process_single_variable_group(shader_type, group, symbols, variables, mangled_builtins)
1886
1887    if 'subgroups' in group:
1888        for subgroup_name, subgroup in group['subgroups'].items():
1889            process_variable_group(shader_type, subgroup_name, subgroup, symbols, variables,
1890                                   mangled_builtins)
1891
1892
1893def generate_files(args, functions_txt_filename, variables_json_filename,
1894                   immutablestring_cpp_filename, immutablestringtest_cpp_filename,
1895                   builtin_header_filename, symboltable_cpp_filename, operator_header_filename,
1896                   symboltable_header_filename):
1897
1898    symbols = SymbolsData()
1899    variables = VariablesData()
1900    functions = FunctionsData()
1901
1902    parsed_functions = get_parsed_functions(functions_txt_filename)
1903
1904    if args.dump_intermediate_json:
1905        with open('builtin_functions_ESSL.json', 'w') as outfile:
1906
1907            def serialize_obj(obj):
1908                if isinstance(obj, TType):
1909                    return obj.data
1910                else:
1911                    raise "Cannot serialize to JSON: " + str(obj)
1912
1913            json.dump(
1914                parsed_functions, outfile, indent=4, separators=(',', ': '), default=serialize_obj)
1915
1916    parsed_variables = None
1917    with open(variables_json_filename) as f:
1918        parsed_variables = json.load(f, object_pairs_hook=OrderedDict)
1919
1920    # This script uses a perfect hash function to avoid dealing with collisions
1921    mangled_names = []
1922    unmangled_names = []
1923    for group_name, group in parsed_functions.items():
1924        get_function_names(group, mangled_names, unmangled_names)
1925    for group_name, group in parsed_variables.items():
1926        get_variable_names(group, mangled_names)
1927
1928    # Hashing mangled names
1929    mangled_names = list(dict.fromkeys(mangled_names))
1930    num_mangled_names = len(mangled_names)
1931    mangled_names_dict = dict(zip(mangled_names, range(0, len(mangled_names))))
1932    # Generate the perfect hash function
1933    f1, f2, mangled_G = generate_hash(mangled_names_dict, Hash2)
1934    mangled_hashfn = HashFunction(f1, f2, mangled_G)
1935    mangled_S1 = f1.salt
1936    mangled_S2 = f2.salt
1937    # Array for querying mangled builtins
1938    mangled_builtins = GroupedList(mangled_hashfn, num_mangled_names)
1939
1940    # Hashing unmangled names
1941    unmangled_names = list(dict.fromkeys(unmangled_names))
1942    num_unmangled_names = len(unmangled_names)
1943    unmangled_names_dict = dict(zip(unmangled_names, range(0, len(unmangled_names))))
1944    # Generate the perfect hash function
1945    f1, f2, unmangled_G = generate_hash(unmangled_names_dict, Hash2)
1946    unmangled_hashfn = HashFunction(f1, f2, unmangled_G)
1947    unmangled_S1 = f1.salt
1948    unmangled_S2 = f2.salt
1949    # Array for querying unmangled builtins
1950    unmangled_function_if_statements = UnmangledGroupedList(unmangled_hashfn, num_unmangled_names)
1951
1952    for group_name, group in parsed_functions.items():
1953        process_function_group(group_name, group, symbols, variables, functions, '',
1954                               unmangled_function_if_statements, mangled_builtins)
1955
1956    functions.parameter_declarations = prune_parameters_arrays(functions.parameter_declarations,
1957                                                               functions.function_declarations)
1958
1959    for group_name, group in parsed_variables.items():
1960        process_variable_group('NONE', group_name, group, symbols, variables, mangled_builtins)
1961
1962    mangled_builtins.update_arrays()
1963
1964    output_strings = {
1965        'script_name':
1966            os.path.basename(__file__),
1967        'builtin_id_declarations':
1968            '\n'.join(symbols.builtin_id_declarations),
1969        'last_builtin_id':
1970            id_counter - 1,
1971        'name_declarations':
1972            '\n'.join(sorted(list(symbols.name_declarations))),
1973        'function_data_source_name':
1974            functions_txt_filename,
1975        'function_declarations':
1976            '\n'.join(functions.function_declarations),
1977        'parameter_declarations':
1978            '\n'.join(sorted(functions.parameter_declarations)),
1979        'operator_enum_declarations':
1980            '\n'.join(functions.operator_enum_declarations),
1981        'is_in_group_definitions':
1982            '\n'.join(functions.is_in_group_definitions),
1983        'variable_data_source_name':
1984            variables_json_filename,
1985        'type_array_sizes_declarations':
1986            '\n'.join(sorted(variables.type_array_sizes_declarations)),
1987        'variable_declarations':
1988            '\n'.join(sorted(variables.variable_declarations)),
1989        'get_variable_declarations':
1990            '\n'.join(sorted(variables.get_variable_declarations)),
1991        'get_variable_definitions':
1992            '\n'.join(sorted(variables.get_variable_definitions)),
1993        'declare_member_variables':
1994            '\n'.join(variables.declare_member_variables),
1995        'init_member_variables':
1996            '\n'.join(variables.init_member_variables),
1997        'mangled_names_array':
1998            ',\n'.join(mangled_builtins.get_names()),
1999        'mangled_offsets_array':
2000            '\n'.join(mangled_builtins.get_offsets()),
2001        'mangled_rules':
2002            ',\n'.join(mangled_builtins.get_rules()),
2003        'unmangled_array':
2004            ', '.join(unmangled_function_if_statements.get_array()),
2005        'max_unmangled_name_length':
2006            unmangled_function_if_statements.get_max_name_length(),
2007        'max_mangled_name_length':
2008            mangled_builtins.get_max_name_length(),
2009        'num_unmangled_names':
2010            num_unmangled_names,
2011        'num_mangled_names':
2012            num_mangled_names,
2013        'script_generated_hash_tests':
2014            '\n'.join(symbols.script_generated_hash_tests.keys()),
2015        'unmangled_script_generated_hash_tests':
2016            '\n'.join(symbols.unmangled_script_generated_hash_tests.keys()),
2017        'mangled_S1':
2018            str(mangled_S1).replace('[', ' ').replace(']', ' '),
2019        'mangled_S2':
2020            str(mangled_S2).replace('[', ' ').replace(']', ' '),
2021        'mangled_G':
2022            str(mangled_G).replace('[', ' ').replace(']', ' '),
2023        'mangled_NG':
2024            len(mangled_G),
2025        'mangled_NS':
2026            len(mangled_S1),
2027        'unmangled_S1':
2028            str(unmangled_S1).replace('[', ' ').replace(']', ' '),
2029        'unmangled_S2':
2030            str(unmangled_S2).replace('[', ' ').replace(']', ' '),
2031        'unmangled_G':
2032            str(unmangled_G).replace('[', ' ').replace(']', ' '),
2033        'unmangled_NG':
2034            len(unmangled_G),
2035        'unmangled_NS':
2036            len(unmangled_S1),
2037    }
2038
2039    with open(immutablestring_cpp_filename, 'wt') as outfile_cpp:
2040        output_cpp = template_immutablestring_cpp.format(**output_strings)
2041        outfile_cpp.write(output_cpp)
2042
2043    with open(immutablestringtest_cpp_filename, 'wt') as outfile_cpp:
2044        output_cpp = template_immutablestringtest_cpp.format(**output_strings)
2045        outfile_cpp.write(output_cpp)
2046
2047    with open(builtin_header_filename, 'wt') as outfile_header:
2048        output_header = template_builtin_header.format(**output_strings)
2049        outfile_header.write(output_header)
2050
2051    with open(symboltable_cpp_filename, 'wt') as outfile_cpp:
2052        output_cpp = template_symboltable_cpp.format(**output_strings)
2053        outfile_cpp.write(output_cpp)
2054
2055    with open(operator_header_filename, 'wt') as outfile_header:
2056        output_header = template_operator_header.format(**output_strings)
2057        outfile_header.write(output_header)
2058
2059    with open(symboltable_header_filename, 'wt') as outfile_h:
2060        output_h = template_symboltable_header.format(**output_strings)
2061        outfile_h.write(output_h)
2062
2063
2064def main():
2065    random.seed(0)
2066    set_working_dir()
2067
2068    parser = argparse.ArgumentParser()
2069    parser.add_argument(
2070        '--dump-intermediate-json',
2071        help='Dump parsed function data as a JSON file builtin_functions.json',
2072        action="store_true")
2073    parser.add_argument('auto_script_command', nargs='?', default='')
2074    args = parser.parse_args()
2075
2076    test_filename = '../../tests/compiler_tests/ImmutableString_test_autogen.cpp'
2077    variables_json_filename = 'builtin_variables.json'
2078    functions_txt_filename = 'builtin_function_declarations.txt'
2079
2080    # auto_script parameters.
2081    if args.auto_script_command != '':
2082        inputs = [
2083            functions_txt_filename,
2084            variables_json_filename,
2085        ]
2086        outputs = [
2087            'Operator_autogen.h',
2088            'SymbolTable_autogen.h',
2089            test_filename,
2090            'ImmutableString_autogen.cpp',
2091            'SymbolTable_autogen.cpp',
2092            'tree_util/BuiltIn_autogen.h',
2093        ]
2094
2095        if args.auto_script_command == 'inputs':
2096            print(','.join(inputs))
2097        elif args.auto_script_command == 'outputs':
2098            print(','.join(outputs))
2099        else:
2100            print('Invalid script parameters')
2101            return 1
2102        return 0
2103
2104    # Generate files based on ESSL symbols
2105    generate_files(args, functions_txt_filename, variables_json_filename,
2106                   'ImmutableString_autogen.cpp', test_filename, 'tree_util/BuiltIn_autogen.h',
2107                   'SymbolTable_autogen.cpp', 'Operator_autogen.h', 'SymbolTable_autogen.h')
2108
2109    return 0
2110
2111
2112if __name__ == '__main__':
2113    sys.exit(main())
2114