1COPYRIGHT = """\
2/*
3 * Copyright 2020 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25"""
26
27import argparse
28import math
29import os
30
31from mako.template import Template
32
33# Mesa-local imports must be declared in meson variable
34# '{file_without_suffix}_depend_files'.
35from vk_entrypoints import get_entrypoints_from_xml
36
37# We generate a static hash table for entry point lookup
38# (vkGetProcAddress). We use a linear congruential generator for our hash
39# function and a power-of-two size table. The prime numbers are determined
40# experimentally.
41
42TEMPLATE_H = Template(COPYRIGHT + """\
43/* This file generated from ${filename}, don't edit directly. */
44
45#ifndef VK_DISPATCH_TABLE_H
46#define VK_DISPATCH_TABLE_H
47
48#include "vulkan/vulkan.h"
49#include "vulkan/vk_android_native_buffer.h"
50
51#include "vk_extensions.h"
52
53/* Windows api conflict */
54#ifdef _WIN32
55#include <windows.h>
56#ifdef CreateSemaphore
57#undef CreateSemaphore
58#endif
59#ifdef CreateEvent
60#undef CreateEvent
61#endif
62#endif
63
64#ifdef __cplusplus
65extern "C" {
66#endif
67
68#ifdef _MSC_VER
69VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void);
70#endif
71
72<%def name="dispatch_table(entrypoints)">
73% for e in entrypoints:
74  % if e.alias:
75    <% continue %>
76  % endif
77  % if e.guard is not None:
78#ifdef ${e.guard}
79  % endif
80  % if e.aliases:
81    union {
82        PFN_vk${e.name} ${e.name};
83      % for a in e.aliases:
84        PFN_vk${a.name} ${a.name};
85      % endfor
86    };
87  % else:
88    PFN_vk${e.name} ${e.name};
89  % endif
90  % if e.guard is not None:
91#else
92    % if e.aliases:
93    union {
94        PFN_vkVoidFunction ${e.name};
95      % for a in e.aliases:
96        PFN_vkVoidFunction ${a.name};
97      % endfor
98    };
99    % else:
100    PFN_vkVoidFunction ${e.name};
101    % endif
102#endif
103  % endif
104% endfor
105</%def>
106
107<%def name="entrypoint_table(type, entrypoints)">
108struct vk_${type}_entrypoint_table {
109% for e in entrypoints:
110  % if e.guard is not None:
111#ifdef ${e.guard}
112  % endif
113    PFN_vk${e.name} ${e.name};
114  % if e.guard is not None:
115#else
116    PFN_vkVoidFunction ${e.name};
117# endif
118  % endif
119% endfor
120};
121</%def>
122
123struct vk_instance_dispatch_table {
124  ${dispatch_table(instance_entrypoints)}
125};
126
127struct vk_physical_device_dispatch_table {
128  ${dispatch_table(physical_device_entrypoints)}
129};
130
131struct vk_device_dispatch_table {
132  ${dispatch_table(device_entrypoints)}
133};
134
135struct vk_dispatch_table {
136    union {
137        struct {
138            struct vk_instance_dispatch_table instance;
139            struct vk_physical_device_dispatch_table physical_device;
140            struct vk_device_dispatch_table device;
141        };
142
143        struct {
144            ${dispatch_table(instance_entrypoints)}
145            ${dispatch_table(physical_device_entrypoints)}
146            ${dispatch_table(device_entrypoints)}
147        };
148    };
149};
150
151${entrypoint_table('instance', instance_entrypoints)}
152${entrypoint_table('physical_device', physical_device_entrypoints)}
153${entrypoint_table('device', device_entrypoints)}
154
155void
156vk_instance_dispatch_table_load(struct vk_instance_dispatch_table *table,
157                                PFN_vkGetInstanceProcAddr gpa,
158                                VkInstance instance);
159void
160vk_physical_device_dispatch_table_load(struct vk_physical_device_dispatch_table *table,
161                                       PFN_vkGetInstanceProcAddr gpa,
162                                       VkInstance instance);
163void
164vk_device_dispatch_table_load(struct vk_device_dispatch_table *table,
165                              PFN_vkGetDeviceProcAddr gpa,
166                              VkDevice device);
167
168void vk_instance_dispatch_table_from_entrypoints(
169    struct vk_instance_dispatch_table *dispatch_table,
170    const struct vk_instance_entrypoint_table *entrypoint_table,
171    bool overwrite);
172
173void vk_physical_device_dispatch_table_from_entrypoints(
174    struct vk_physical_device_dispatch_table *dispatch_table,
175    const struct vk_physical_device_entrypoint_table *entrypoint_table,
176    bool overwrite);
177
178void vk_device_dispatch_table_from_entrypoints(
179    struct vk_device_dispatch_table *dispatch_table,
180    const struct vk_device_entrypoint_table *entrypoint_table,
181    bool overwrite);
182
183PFN_vkVoidFunction
184vk_instance_dispatch_table_get(const struct vk_instance_dispatch_table *table,
185                               const char *name);
186
187PFN_vkVoidFunction
188vk_physical_device_dispatch_table_get(const struct vk_physical_device_dispatch_table *table,
189                                      const char *name);
190
191PFN_vkVoidFunction
192vk_device_dispatch_table_get(const struct vk_device_dispatch_table *table,
193                             const char *name);
194
195PFN_vkVoidFunction
196vk_instance_dispatch_table_get_if_supported(
197    const struct vk_instance_dispatch_table *table,
198    const char *name,
199    uint32_t core_version,
200    const struct vk_instance_extension_table *instance_exts);
201
202PFN_vkVoidFunction
203vk_physical_device_dispatch_table_get_if_supported(
204    const struct vk_physical_device_dispatch_table *table,
205    const char *name,
206    uint32_t core_version,
207    const struct vk_instance_extension_table *instance_exts);
208
209PFN_vkVoidFunction
210vk_device_dispatch_table_get_if_supported(
211    const struct vk_device_dispatch_table *table,
212    const char *name,
213    uint32_t core_version,
214    const struct vk_instance_extension_table *instance_exts,
215    const struct vk_device_extension_table *device_exts);
216
217#ifdef __cplusplus
218}
219#endif
220
221#endif /* VK_DISPATCH_TABLE_H */
222""")
223
224TEMPLATE_C = Template(COPYRIGHT + """\
225/* This file generated from ${filename}, don't edit directly. */
226
227#include "vk_dispatch_table.h"
228
229#include "util/macros.h"
230#include "string.h"
231
232<%def name="load_dispatch_table(type, VkType, ProcAddr, entrypoints)">
233void
234vk_${type}_dispatch_table_load(struct vk_${type}_dispatch_table *table,
235                               PFN_vk${ProcAddr} gpa,
236                               ${VkType} obj)
237{
238% if type != 'physical_device':
239    table->${ProcAddr} = gpa;
240% endif
241% for e in entrypoints:
242  % if e.alias or e.name == '${ProcAddr}':
243    <% continue %>
244  % endif
245  % if e.guard is not None:
246#ifdef ${e.guard}
247  % endif
248    table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${e.name}");
249  % for a in e.aliases:
250    if (table->${e.name} == NULL) {
251        table->${e.name} = (PFN_vk${e.name}) gpa(obj, "vk${a.name}");
252    }
253  % endfor
254  % if e.guard is not None:
255#endif
256  % endif
257% endfor
258}
259</%def>
260
261${load_dispatch_table('instance', 'VkInstance', 'GetInstanceProcAddr',
262                      instance_entrypoints)}
263
264${load_dispatch_table('physical_device', 'VkInstance', 'GetInstanceProcAddr',
265                      physical_device_entrypoints)}
266
267${load_dispatch_table('device', 'VkDevice', 'GetDeviceProcAddr',
268                      device_entrypoints)}
269
270
271struct string_map_entry {
272   uint32_t name;
273   uint32_t hash;
274   uint32_t num;
275};
276
277/* We use a big string constant to avoid lots of reloctions from the entry
278 * point table to lots of little strings. The entries in the entry point table
279 * store the index into this big string.
280 */
281
282<%def name="strmap(strmap, prefix)">
283static const char ${prefix}_strings[] =
284% for s in strmap.sorted_strings:
285    "${s.string}\\0"
286% endfor
287;
288
289static const struct string_map_entry ${prefix}_string_map_entries[] = {
290% for s in strmap.sorted_strings:
291    { ${s.offset}, ${'{:0=#8x}'.format(s.hash)}, ${s.num} }, /* ${s.string} */
292% endfor
293};
294
295/* Hash table stats:
296 * size ${len(strmap.sorted_strings)} entries
297 * collisions entries:
298% for i in range(10):
299 *     ${i}${'+' if i == 9 else ' '}     ${strmap.collisions[i]}
300% endfor
301 */
302
303#define none 0xffff
304static const uint16_t ${prefix}_string_map[${strmap.hash_size}] = {
305% for e in strmap.mapping:
306    ${ '{:0=#6x}'.format(e) if e >= 0 else 'none' },
307% endfor
308};
309
310static int
311${prefix}_string_map_lookup(const char *str)
312{
313    static const uint32_t prime_factor = ${strmap.prime_factor};
314    static const uint32_t prime_step = ${strmap.prime_step};
315    const struct string_map_entry *e;
316    uint32_t hash, h;
317    uint16_t i;
318    const char *p;
319
320    hash = 0;
321    for (p = str; *p; p++)
322        hash = hash * prime_factor + *p;
323
324    h = hash;
325    while (1) {
326        i = ${prefix}_string_map[h & ${strmap.hash_mask}];
327        if (i == none)
328           return -1;
329        e = &${prefix}_string_map_entries[i];
330        if (e->hash == hash && strcmp(str, ${prefix}_strings + e->name) == 0)
331            return e->num;
332        h += prime_step;
333    }
334
335    return -1;
336}
337</%def>
338
339${strmap(instance_strmap, 'instance')}
340${strmap(physical_device_strmap, 'physical_device')}
341${strmap(device_strmap, 'device')}
342
343<% assert len(instance_entrypoints) < 2**8 %>
344static const uint8_t instance_compaction_table[] = {
345% for e in instance_entrypoints:
346    ${e.disp_table_index},
347% endfor
348};
349
350<% assert len(physical_device_entrypoints) < 2**8 %>
351static const uint8_t physical_device_compaction_table[] = {
352% for e in physical_device_entrypoints:
353    ${e.disp_table_index},
354% endfor
355};
356
357<% assert len(device_entrypoints) < 2**16 %>
358static const uint16_t device_compaction_table[] = {
359% for e in device_entrypoints:
360    ${e.disp_table_index},
361% endfor
362};
363
364static bool
365vk_instance_entrypoint_is_enabled(int index, uint32_t core_version,
366                                  const struct vk_instance_extension_table *instance)
367{
368   switch (index) {
369% for e in instance_entrypoints:
370   case ${e.entry_table_index}:
371      /* ${e.name} */
372   % if e.core_version:
373      return ${e.core_version.c_vk_version()} <= core_version;
374   % elif e.extensions:
375     % for ext in e.extensions:
376        % if ext.type == 'instance':
377      if (instance->${ext.name[3:]}) return true;
378        % else:
379      /* All device extensions are considered enabled at the instance level */
380      return true;
381        % endif
382     % endfor
383      return false;
384   % else:
385      return true;
386   % endif
387% endfor
388   default:
389      return false;
390   }
391}
392
393/** Return true if the core version or extension in which the given entrypoint
394 * is defined is enabled.
395 *
396 * If device is NULL, all device extensions are considered enabled.
397 */
398static bool
399vk_physical_device_entrypoint_is_enabled(int index, uint32_t core_version,
400                                         const struct vk_instance_extension_table *instance)
401{
402   switch (index) {
403% for e in physical_device_entrypoints:
404   case ${e.entry_table_index}:
405      /* ${e.name} */
406   % if e.core_version:
407      return ${e.core_version.c_vk_version()} <= core_version;
408   % elif e.extensions:
409     % for ext in e.extensions:
410        % if ext.type == 'instance':
411      if (instance->${ext.name[3:]}) return true;
412        % else:
413      /* All device extensions are considered enabled at the instance level */
414      return true;
415        % endif
416     % endfor
417      return false;
418   % else:
419      return true;
420   % endif
421% endfor
422   default:
423      return false;
424   }
425}
426
427/** Return true if the core version or extension in which the given entrypoint
428 * is defined is enabled.
429 *
430 * If device is NULL, all device extensions are considered enabled.
431 */
432static bool
433vk_device_entrypoint_is_enabled(int index, uint32_t core_version,
434                                const struct vk_instance_extension_table *instance,
435                                const struct vk_device_extension_table *device)
436{
437   switch (index) {
438% for e in device_entrypoints:
439   case ${e.entry_table_index}:
440      /* ${e.name} */
441   % if e.core_version:
442      return ${e.core_version.c_vk_version()} <= core_version;
443   % elif e.extensions:
444     % for ext in e.extensions:
445        % if ext.type == 'instance':
446      if (instance->${ext.name[3:]}) return true;
447        % else:
448      if (!device || device->${ext.name[3:]}) return true;
449        % endif
450     % endfor
451      return false;
452   % else:
453      return true;
454   % endif
455% endfor
456   default:
457      return false;
458   }
459}
460
461#ifdef _MSC_VER
462VKAPI_ATTR void VKAPI_CALL vk_entrypoint_stub(void)
463{
464   unreachable("Entrypoint not implemented");
465}
466
467static const void *get_function_target(const void *func)
468{
469   const uint8_t *address = func;
470#ifdef _M_X64
471   /* Incremental linking may indirect through relative jump */
472   if (*address == 0xE9)
473   {
474      /* Compute JMP target if the first byte is opcode 0xE9 */
475      uint32_t offset;
476      memcpy(&offset, address + 1, 4);
477      address += offset + 5;
478   }
479#else
480   /* Add other platforms here if necessary */
481#endif
482   return address;
483}
484
485static bool vk_function_is_stub(PFN_vkVoidFunction func)
486{
487   return (func == vk_entrypoint_stub) || (get_function_target(func) == get_function_target(vk_entrypoint_stub));
488}
489#endif
490
491<%def name="dispatch_table_from_entrypoints(type)">
492void vk_${type}_dispatch_table_from_entrypoints(
493    struct vk_${type}_dispatch_table *dispatch_table,
494    const struct vk_${type}_entrypoint_table *entrypoint_table,
495    bool overwrite)
496{
497    PFN_vkVoidFunction *disp = (PFN_vkVoidFunction *)dispatch_table;
498    PFN_vkVoidFunction *entry = (PFN_vkVoidFunction *)entrypoint_table;
499
500    if (overwrite) {
501        memset(dispatch_table, 0, sizeof(*dispatch_table));
502        for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) {
503#ifdef _MSC_VER
504            assert(entry[i] != NULL);
505            if (vk_function_is_stub(entry[i]))
506#else
507            if (entry[i] == NULL)
508#endif
509                continue;
510            unsigned disp_index = ${type}_compaction_table[i];
511            assert(disp[disp_index] == NULL);
512            disp[disp_index] = entry[i];
513        }
514    } else {
515        for (unsigned i = 0; i < ARRAY_SIZE(${type}_compaction_table); i++) {
516            unsigned disp_index = ${type}_compaction_table[i];
517#ifdef _MSC_VER
518            assert(entry[i] != NULL);
519            if (disp[disp_index] == NULL && !vk_function_is_stub(entry[i]))
520#else
521            if (disp[disp_index] == NULL)
522#endif
523                disp[disp_index] = entry[i];
524        }
525    }
526}
527</%def>
528
529${dispatch_table_from_entrypoints('instance')}
530${dispatch_table_from_entrypoints('physical_device')}
531${dispatch_table_from_entrypoints('device')}
532
533<%def name="lookup_funcs(type)">
534static PFN_vkVoidFunction
535vk_${type}_dispatch_table_get_for_entry_index(
536    const struct vk_${type}_dispatch_table *table, int entry_index)
537{
538    assert(entry_index < ARRAY_SIZE(${type}_compaction_table));
539    int disp_index = ${type}_compaction_table[entry_index];
540    return ((PFN_vkVoidFunction *)table)[disp_index];
541}
542
543PFN_vkVoidFunction
544vk_${type}_dispatch_table_get(
545    const struct vk_${type}_dispatch_table *table, const char *name)
546{
547    int entry_index = ${type}_string_map_lookup(name);
548    if (entry_index < 0)
549        return NULL;
550
551    return vk_${type}_dispatch_table_get_for_entry_index(table, entry_index);
552}
553</%def>
554
555${lookup_funcs('instance')}
556${lookup_funcs('physical_device')}
557${lookup_funcs('device')}
558
559PFN_vkVoidFunction
560vk_instance_dispatch_table_get_if_supported(
561    const struct vk_instance_dispatch_table *table,
562    const char *name,
563    uint32_t core_version,
564    const struct vk_instance_extension_table *instance_exts)
565{
566    int entry_index = instance_string_map_lookup(name);
567    if (entry_index < 0)
568        return NULL;
569
570    if (!vk_instance_entrypoint_is_enabled(entry_index, core_version,
571                                           instance_exts))
572        return NULL;
573
574    return vk_instance_dispatch_table_get_for_entry_index(table, entry_index);
575}
576
577PFN_vkVoidFunction
578vk_physical_device_dispatch_table_get_if_supported(
579    const struct vk_physical_device_dispatch_table *table,
580    const char *name,
581    uint32_t core_version,
582    const struct vk_instance_extension_table *instance_exts)
583{
584    int entry_index = physical_device_string_map_lookup(name);
585    if (entry_index < 0)
586        return NULL;
587
588    if (!vk_physical_device_entrypoint_is_enabled(entry_index, core_version,
589                                                  instance_exts))
590        return NULL;
591
592    return vk_physical_device_dispatch_table_get_for_entry_index(table, entry_index);
593}
594
595PFN_vkVoidFunction
596vk_device_dispatch_table_get_if_supported(
597    const struct vk_device_dispatch_table *table,
598    const char *name,
599    uint32_t core_version,
600    const struct vk_instance_extension_table *instance_exts,
601    const struct vk_device_extension_table *device_exts)
602{
603    int entry_index = device_string_map_lookup(name);
604    if (entry_index < 0)
605        return NULL;
606
607    if (!vk_device_entrypoint_is_enabled(entry_index, core_version,
608                                         instance_exts, device_exts))
609        return NULL;
610
611    return vk_device_dispatch_table_get_for_entry_index(table, entry_index);
612}
613""")
614
615U32_MASK = 2**32 - 1
616
617PRIME_FACTOR = 5024183
618PRIME_STEP = 19
619
620class StringIntMapEntry:
621    def __init__(self, string, num):
622        self.string = string
623        self.num = num
624
625        # Calculate the same hash value that we will calculate in C.
626        h = 0
627        for c in string:
628            h = ((h * PRIME_FACTOR) + ord(c)) & U32_MASK
629        self.hash = h
630
631        self.offset = None
632
633def round_to_pow2(x):
634    return 2**int(math.ceil(math.log(x, 2)))
635
636class StringIntMap:
637    def __init__(self):
638        self.baked = False
639        self.strings = {}
640
641    def add_string(self, string, num):
642        assert not self.baked
643        assert string not in self.strings
644        assert 0 <= num < 2**31
645        self.strings[string] = StringIntMapEntry(string, num)
646
647    def bake(self):
648        self.sorted_strings = \
649            sorted(self.strings.values(), key=lambda x: x.string)
650        offset = 0
651        for entry in self.sorted_strings:
652            entry.offset = offset
653            offset += len(entry.string) + 1
654
655        # Save off some values that we'll need in C
656        self.hash_size = round_to_pow2(len(self.strings) * 1.25)
657        self.hash_mask = self.hash_size - 1
658        self.prime_factor = PRIME_FACTOR
659        self.prime_step = PRIME_STEP
660
661        self.mapping = [-1] * self.hash_size
662        self.collisions = [0] * 10
663        for idx, s in enumerate(self.sorted_strings):
664            level = 0
665            h = s.hash
666            while self.mapping[h & self.hash_mask] >= 0:
667                h = h + PRIME_STEP
668                level = level + 1
669            self.collisions[min(level, 9)] += 1
670            self.mapping[h & self.hash_mask] = idx
671
672def main():
673    parser = argparse.ArgumentParser()
674    parser.add_argument('--out-c', help='Output C file.')
675    parser.add_argument('--out-h', help='Output H file.')
676    parser.add_argument('--beta', required=True, help='Enable beta extensions.')
677    parser.add_argument('--xml',
678                        help='Vulkan API XML file.',
679                        required=True,
680                        action='append',
681                        dest='xml_files')
682    args = parser.parse_args()
683
684    entrypoints = get_entrypoints_from_xml(args.xml_files, args.beta)
685
686    device_entrypoints = []
687    physical_device_entrypoints = []
688    instance_entrypoints = []
689    for e in entrypoints:
690        if e.is_device_entrypoint():
691            device_entrypoints.append(e)
692        elif e.is_physical_device_entrypoint():
693            physical_device_entrypoints.append(e)
694        else:
695            instance_entrypoints.append(e)
696
697    for i, e in enumerate(e for e in device_entrypoints if not e.alias):
698        e.disp_table_index = i
699
700    device_strmap = StringIntMap()
701    for i, e in enumerate(device_entrypoints):
702        e.entry_table_index = i
703        device_strmap.add_string("vk" + e.name, e.entry_table_index)
704    device_strmap.bake()
705
706    for i, e in enumerate(e for e in physical_device_entrypoints if not e.alias):
707        e.disp_table_index = i
708
709    physical_device_strmap = StringIntMap()
710    for i, e in enumerate(physical_device_entrypoints):
711        e.entry_table_index = i
712        physical_device_strmap.add_string("vk" + e.name, e.entry_table_index)
713    physical_device_strmap.bake()
714
715    for i, e in enumerate(e for e in instance_entrypoints if not e.alias):
716        e.disp_table_index = i
717
718    instance_strmap = StringIntMap()
719    for i, e in enumerate(instance_entrypoints):
720        e.entry_table_index = i
721        instance_strmap.add_string("vk" + e.name, e.entry_table_index)
722    instance_strmap.bake()
723
724    # For outputting entrypoints.h we generate a anv_EntryPoint() prototype
725    # per entry point.
726    try:
727        if args.out_h:
728            with open(args.out_h, 'w') as f:
729                f.write(TEMPLATE_H.render(instance_entrypoints=instance_entrypoints,
730                                          physical_device_entrypoints=physical_device_entrypoints,
731                                          device_entrypoints=device_entrypoints,
732                                          filename=os.path.basename(__file__)))
733        if args.out_c:
734            with open(args.out_c, 'w') as f:
735                f.write(TEMPLATE_C.render(instance_entrypoints=instance_entrypoints,
736                                          physical_device_entrypoints=physical_device_entrypoints,
737                                          device_entrypoints=device_entrypoints,
738                                          instance_strmap=instance_strmap,
739                                          physical_device_strmap=physical_device_strmap,
740                                          device_strmap=device_strmap,
741                                          filename=os.path.basename(__file__)))
742    except Exception:
743        # In the event there's an error, this imports some helpers from mako
744        # to print a useful stack trace and prints it, then exits with
745        # status 1, if python is run with debug; otherwise it just raises
746        # the exception
747        import sys
748        from mako import exceptions
749        print(exceptions.text_error_template().render(), file=sys.stderr)
750        sys.exit(1)
751
752
753if __name__ == '__main__':
754    main()
755