1"""This file contains BUILD extensions for generating source code from LLVM's table definition files using the TableGen tool.
2
3See http://llvm.org/cmds/tblgen.html for more information on the TableGen
4tool.
5TODO(chandlerc): Currently this expresses include-based dependencies as
6"sources", and has no transitive understanding due to these files not being
7correctly understood by the build system.
8"""
9
10def _dict_add(*dictionaries):
11    """Returns a new `dict` that has all the entries of the given dictionaries.
12
13    If the same key is present in more than one of the input dictionaries, the
14    last of them in the argument list overrides any earlier ones.
15
16    This function is designed to take zero or one arguments as well as multiple
17    dictionaries, so that it follows arithmetic identities and callers can avoid
18    special cases for their inputs: the sum of zero dictionaries is the empty
19    dictionary, and the sum of a single dictionary is a copy of itself.
20
21    Re-implemented here to avoid adding a dependency on skylib.
22
23    Args:
24      *dictionaries: Zero or more dictionaries to be added.
25
26    Returns:
27      A new `dict` that has all the entries of the given dictionaries.
28    """
29    result = {}
30    for d in dictionaries:
31        result.update(d)
32    return result
33
34def gentbl(name, tblgen, td_file, td_srcs, tbl_outs, library = True, **kwargs):
35    """gentbl() generates tabular code from a table definition file.
36
37    Args:
38      name: The name of the build rule for use in dependencies.
39      tblgen: The binary used to produce the output.
40      td_file: The primary table definitions file.
41      td_srcs: A list of table definition files included transitively.
42      tbl_outs: A list of tuples (opts, out), where each opts is a string of
43        options passed to tblgen, and the out is the corresponding output file
44        produced.
45      library: Whether to bundle the generated files into a library.
46      **kwargs: Keyword arguments to pass to subsidiary cc_library() rule.
47    """
48    if td_file not in td_srcs:
49        td_srcs += [td_file]
50    includes = []
51    for (opts, out) in tbl_outs:
52        outdir = out[:out.rindex("/")]
53        if outdir not in includes:
54            includes.append(outdir)
55        rule_suffix = "_".join(opts.replace("-", "_").replace("=", "_").split(" "))
56        native.genrule(
57            name = "%s_%s_genrule" % (name, rule_suffix),
58            srcs = td_srcs,
59            outs = [out],
60            tools = [tblgen],
61            message = "Generating code from table: %s" % td_file,
62            cmd = (("$(location %s) " + "-I external/llvm-project/llvm/include " +
63                    "-I external/llvm-project/clang/include " +
64                    "-I $$(dirname $(location %s)) " + ("%s $(location %s) --long-string-literals=0 " +
65                                                        "-o $@")) % (
66                tblgen,
67                td_file,
68                opts,
69                td_file,
70            )),
71        )
72
73    # For now, all generated files can be assumed to comprise public interfaces.
74    # If this is not true, you should specify library = False
75    # and list the generated '.inc' files in "srcs".
76    if library:
77        native.cc_library(
78            name = name,
79            textual_hdrs = [f for (_, f) in tbl_outs],
80            includes = includes,
81            **kwargs
82        )
83
84def llvm_target_cmake_vars(native_arch, target_triple):
85    return {
86        "LLVM_HOST_TRIPLE": target_triple,
87        "LLVM_DEFAULT_TARGET_TRIPLE": target_triple,
88        "LLVM_NATIVE_ARCH": native_arch,
89    }
90
91def _quote(s):
92    """Quotes the given string for use in a shell command.
93
94    This function double-quotes the given string (in case it contains spaces or
95    other special characters) and escapes any special characters (dollar signs,
96    double-quotes, and backslashes) that may be present.
97
98    Args:
99      s: The string to quote.
100
101    Returns:
102      An escaped and quoted version of the string that can be passed to a shell
103      command.
104    """
105    return ('"' +
106            s.replace("\\", "\\\\").replace("$", "\\$").replace('"', "\\\"") +
107            '"')
108
109def cmake_var_string(cmake_vars):
110    """Converts a dictionary to an input suitable for expand_cmake_vars.
111
112    Ideally we would jist stringify in the expand_cmake_vars() rule, but select()
113    interacts badly with genrules.
114
115    TODO(phawkins): replace the genrule() with native rule and delete this rule.
116
117    Args:
118      cmake_vars: a dictionary with string keys and values that are convertable to
119        strings.
120
121    Returns:
122      cmake_vars in a form suitable for passing to expand_cmake_vars.
123    """
124    return " ".join([
125        _quote("{}={}".format(k, str(v)))
126        for (k, v) in cmake_vars.items()
127    ])
128
129def expand_cmake_vars(name, src, dst, cmake_vars):
130    """Expands #cmakedefine, #cmakedefine01, and CMake variables in a text file.
131
132    Args:
133      name: the name of the rule
134      src: the input of the rule
135      dst: the output of the rule
136      cmake_vars: a string containing the CMake variables, as generated by
137        cmake_var_string.
138    """
139    expand_cmake_vars_tool = "@org_tensorflow//third_party/llvm:expand_cmake_vars"
140    native.genrule(
141        name = name,
142        srcs = [src],
143        tools = [expand_cmake_vars_tool],
144        outs = [dst],
145        cmd = ("$(location {}) ".format(expand_cmake_vars_tool) + cmake_vars +
146               "< $< > $@"),
147    )
148
149# TODO(phawkins): the set of CMake variables was hardcoded for expediency.
150# However, we should really detect many of these via configure-time tests.
151
152# The set of CMake variables common to all targets.
153cmake_vars = {
154    # LLVM features
155    "ENABLE_BACKTRACES": 1,
156    "LLVM_BINDIR": "/dev/null",
157    "LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING": 0,
158    "LLVM_ENABLE_ABI_BREAKING_CHECKS": 0,
159    "LLVM_ENABLE_THREADS": 1,
160    "LLVM_ENABLE_ZLIB": 1,
161    "LLVM_HAS_ATOMICS": 1,
162    "LLVM_INCLUDEDIR": "/dev/null",
163    "LLVM_INFODIR": "/dev/null",
164    "LLVM_MANDIR": "/dev/null",
165    "LLVM_NATIVE_TARGET": 1,
166    "LLVM_NATIVE_TARGETINFO": 1,
167    "LLVM_NATIVE_TARGETMC": 1,
168    "LLVM_NATIVE_ASMPRINTER": 1,
169    "LLVM_NATIVE_ASMPARSER": 1,
170    "LLVM_NATIVE_DISASSEMBLER": 1,
171    "LLVM_PREFIX": "/dev/null",
172    "LLVM_VERSION_MAJOR": 0,
173    "LLVM_VERSION_MINOR": 0,
174    "LLVM_VERSION_PATCH": 0,
175    "PACKAGE_NAME": "llvm",
176    "PACKAGE_STRING": "llvm tensorflow-trunk",
177    "PACKAGE_VERSION": "tensorflow-trunk",
178    "RETSIGTYPE": "void",
179}
180
181# The set of CMake variables common to POSIX targets.
182posix_cmake_vars = {
183    # Headers
184    "HAVE_DIRENT_H": 1,
185    "HAVE_DLFCN_H": 1,
186    "HAVE_ERRNO_H": 1,
187    "HAVE_EXECINFO_H": 1,
188    "HAVE_FCNTL_H": 1,
189    "HAVE_INTTYPES_H": 1,
190    "HAVE_PTHREAD_H": 1,
191    "HAVE_SIGNAL_H": 1,
192    "HAVE_STDINT_H": 1,
193    "HAVE_SYSEXITS_H": 1,
194    "HAVE_SYS_IOCTL_H": 1,
195    "HAVE_SYS_MMAN_H": 1,
196    "HAVE_SYS_PARAM_H": 1,
197    "HAVE_SYS_RESOURCE_H": 1,
198    "HAVE_SYS_STAT_H": 1,
199    "HAVE_SYS_TIME_H": 1,
200    "HAVE_SYS_TYPES_H": 1,
201    "HAVE_TERMIOS_H": 1,
202    "HAVE_UNISTD_H": 1,
203    "HAVE_ZLIB_H": 1,
204
205    # Features
206    "HAVE_BACKTRACE": 1,
207    "BACKTRACE_HEADER": "execinfo.h",
208    "HAVE_DLOPEN": 1,
209    "HAVE_FUTIMES": 1,
210    "HAVE_GETCWD": 1,
211    "HAVE_GETPAGESIZE": 1,
212    "HAVE_GETRLIMIT": 1,
213    "HAVE_GETRUSAGE": 1,
214    "HAVE_GETTIMEOFDAY": 1,
215    "HAVE_INT64_T": 1,
216    "HAVE_ISATTY": 1,
217    "HAVE_LIBEDIT": 1,
218    "HAVE_LIBPTHREAD": 1,
219    "HAVE_LIBZ": 1,
220    "HAVE_MKDTEMP": 1,
221    "HAVE_MKSTEMP": 1,
222    "HAVE_MKTEMP": 1,
223    "HAVE_PREAD": 1,
224    "HAVE_PTHREAD_GETSPECIFIC": 1,
225    "HAVE_PTHREAD_MUTEX_LOCK": 1,
226    "HAVE_PTHREAD_RWLOCK_INIT": 1,
227    "HAVE_REALPATH": 1,
228    "HAVE_SBRK": 1,
229    "HAVE_SETENV": 1,
230    "HAVE_SETRLIMIT": 1,
231    "HAVE_SIGALTSTACK": 1,
232    "HAVE_STRERROR": 1,
233    "HAVE_STRERROR_R": 1,
234    "HAVE_STRTOLL": 1,
235    "HAVE_SYSCONF": 1,
236    "HAVE_UINT64_T": 1,
237    "HAVE__UNWIND_BACKTRACE": 1,
238
239    # LLVM features
240    "LLVM_ON_UNIX": 1,
241    "LTDL_SHLIB_EXT": ".so",
242}
243
244# CMake variables specific to the Linux platform
245linux_cmake_vars = {
246    "HAVE_MALLOC_H": 1,
247    "HAVE_LINK_H": 1,
248    "HAVE_MALLINFO": 1,
249    "HAVE_FUTIMENS": 1,
250}
251
252# CMake variables specific to the FreeBSD platform
253freebsd_cmake_vars = {
254    "HAVE_MALLOC_H": 1,
255    "HAVE_LINK_H": 1,
256}
257
258# CMake variables specific to the Darwin (Mac OS X) platform.
259darwin_cmake_vars = {
260    "HAVE_MALLOC_MALLOC_H": 1,
261    "HAVE_MALLOC_ZONE_STATISTICS": 1,
262}
263
264# CMake variables specific to the Windows platform.
265win32_cmake_vars = {
266    # Headers
267    "HAVE_ERRNO_H": 1,
268    "HAVE_EXECINFO_H": 1,
269    "HAVE_FCNTL_H": 1,
270    "HAVE_FENV_H": 1,
271    "HAVE_INTTYPES_H": 1,
272    "HAVE_MALLOC_H": 1,
273    "HAVE_SIGNAL_H": 1,
274    "HAVE_STDINT_H": 1,
275    "HAVE_SYS_STAT_H": 1,
276    "HAVE_SYS_TYPES_H": 1,
277    "HAVE_ZLIB_H": 1,
278
279    # Features
280    "BACKTRACE_HEADER": "execinfo.h",
281    "HAVE_GETCWD": 1,
282    "HAVE_INT64_T": 1,
283    "HAVE_STRERROR": 1,
284    "HAVE_STRTOLL": 1,
285    "HAVE_SYSCONF": 1,
286    "HAVE_UINT64_T": 1,
287    "HAVE__CHSIZE_S": 1,
288    "HAVE___CHKSTK": 1,
289
290    # MSVC specific
291    "stricmp": "_stricmp",
292    "strdup": "_strdup",
293
294    # LLVM features
295    "LTDL_SHLIB_EXT": ".dll",
296}
297
298# Select a set of CMake variables based on the platform.
299# TODO(phawkins): use a better method to select the right host triple, rather
300# than hardcoding x86_64.
301llvm_all_cmake_vars = select({
302    "@org_tensorflow//tensorflow:macos_x86_64": cmake_var_string(
303        _dict_add(
304            cmake_vars,
305            llvm_target_cmake_vars("X86", "x86_64-apple-darwin"),
306            posix_cmake_vars,
307            darwin_cmake_vars,
308        ),
309    ),
310    "@org_tensorflow//tensorflow:macos_arm64": cmake_var_string(
311        _dict_add(
312            cmake_vars,
313            llvm_target_cmake_vars("AArch64", "arm64-apple-darwin"),
314            posix_cmake_vars,
315            darwin_cmake_vars,
316        ),
317    ),
318    "@org_tensorflow//tensorflow:linux_aarch64": cmake_var_string(
319        _dict_add(
320            cmake_vars,
321            llvm_target_cmake_vars("AArch64", "aarch64-unknown-linux_gnu"),
322            posix_cmake_vars,
323            linux_cmake_vars,
324        ),
325    ),
326    "@org_tensorflow//tensorflow:linux_ppc64le": cmake_var_string(
327        _dict_add(
328            cmake_vars,
329            llvm_target_cmake_vars("PowerPC", "powerpc64le-unknown-linux_gnu"),
330            posix_cmake_vars,
331            linux_cmake_vars,
332        ),
333    ),
334    "@org_tensorflow//tensorflow:windows": cmake_var_string(
335        _dict_add(
336            cmake_vars,
337            llvm_target_cmake_vars("X86", "x86_64-pc-win32"),
338            win32_cmake_vars,
339        ),
340    ),
341    "@org_tensorflow//tensorflow:freebsd": cmake_var_string(
342        _dict_add(
343            cmake_vars,
344            llvm_target_cmake_vars("X86", "x86_64-unknown-freebsd"),
345            posix_cmake_vars,
346        ),
347    ),
348    "@org_tensorflow//tensorflow:linux_s390x": cmake_var_string(
349        _dict_add(
350            cmake_vars,
351            llvm_target_cmake_vars("SystemZ", "systemz-unknown-linux_gnu"),
352            posix_cmake_vars,
353            linux_cmake_vars,
354        ),
355    ),
356    "//conditions:default": cmake_var_string(
357        _dict_add(
358            cmake_vars,
359            llvm_target_cmake_vars("X86", "x86_64-unknown-linux_gnu"),
360            posix_cmake_vars,
361            linux_cmake_vars,
362        ),
363    ),
364})
365
366llvm_linkopts = select({
367    "@org_tensorflow//tensorflow:windows": [],
368    "@org_tensorflow//tensorflow:freebsd": ["-ldl", "-lm", "-lpthread", "-lexecinfo"],
369    "//conditions:default": ["-ldl", "-lm", "-lpthread"],
370})
371
372llvm_defines = select({
373    "@org_tensorflow//tensorflow:windows": [
374        "_CRT_SECURE_NO_DEPRECATE",
375        "_CRT_SECURE_NO_WARNINGS",
376        "_CRT_NONSTDC_NO_DEPRECATE",
377        "_CRT_NONSTDC_NO_WARNINGS",
378        "_SCL_SECURE_NO_DEPRECATE",
379        "_SCL_SECURE_NO_WARNINGS",
380        "UNICODE",
381        "_UNICODE",
382    ],
383    "//conditions:default": [],
384}) + [
385    "LLVM_ENABLE_STATS",
386    "__STDC_LIMIT_MACROS",
387    "__STDC_CONSTANT_MACROS",
388    "__STDC_FORMAT_MACROS",
389    "LLVM_BUILD_GLOBAL_ISEL",
390]
391
392llvm_copts = select({
393    "@org_tensorflow//tensorflow:windows": [
394        "-Zc:inline",
395        "-Zc:strictStrings",
396        "-Zc:rvalueCast",
397        "-Oi",
398        "-wd4141",
399        "-wd4146",
400        "-wd4180",
401        "-wd4244",
402        "-wd4258",
403        "-wd4267",
404        "-wd4291",
405        "-wd4345",
406        "-wd4351",
407        "-wd4355",
408        "-wd4456",
409        "-wd4457",
410        "-wd4458",
411        "-wd4459",
412        "-wd4503",
413        "-wd4624",
414        "-wd4722",
415        "-wd4800",
416        "-wd4100",
417        "-wd4127",
418        "-wd4512",
419        "-wd4505",
420        "-wd4610",
421        "-wd4510",
422        "-wd4702",
423        "-wd4245",
424        "-wd4706",
425        "-wd4310",
426        "-wd4701",
427        "-wd4703",
428        "-wd4389",
429        "-wd4611",
430        "-wd4805",
431        "-wd4204",
432        "-wd4577",
433        "-wd4091",
434        "-wd4592",
435        "-wd4319",
436        "-wd4324",
437        "-w14062",
438        "-we4238",
439    ],
440    "//conditions:default": [],
441})
442
443# Platform specific sources for libSupport.
444
445def llvm_support_platform_specific_srcs_glob():
446    return select({
447        "@org_tensorflow//tensorflow:windows": native.glob([
448            "lib/Support/Windows/*.inc",
449            "lib/Support/Windows/*.h",
450        ]),
451        "//conditions:default": native.glob([
452            "lib/Support/Unix/*.inc",
453            "lib/Support/Unix/*.h",
454        ]),
455    })
456