...]
324 | """
325 | assert(len(tokens) > 2)
326 |
327 | # The top-level TranslationUnitDecl node has no nesting
328 | if tokens[1].text.startswith('Translation'):
329 | nesting = ''
330 | itok = 1
331 | else:
332 | nesting = tokens[1].text
333 | itok = 2
334 |
335 | # The name is a concat of the following non-empty tokens, until something
336 | # that looks like the ID is encountered, or the line ends.
337 | name_parts = []
338 | while itok < len(tokens):
339 | t = tokens[itok].text.strip()
340 | if len(t) > 0:
341 | if ADDR_PATTERN.match(t):
342 | # Found an ID; bail out
343 | break
344 | else:
345 | # Part of the name
346 | name_parts.append(t)
347 | itok += 1
348 | name = ' '.join(name_parts)
349 |
350 | # Here itok is either past the end of the list, or it points to the ID.
351 | id = tokens[itok].text.strip() if itok < len(tokens) else ''
352 | itok += 1
353 |
354 | # Gather all uses
355 | uses = []
356 | while itok < len(tokens):
357 | t = tokens[itok].text.strip()
358 | if ADDR_PATTERN.match(t):
359 | uses.append(t)
360 | itok += 1
361 |
362 | nesting_level = len(nesting)
363 | return id, name, nesting_level, uses
364 |
365 |
366 | def prepare_nav_data(line_info):
367 | """Given a list of tuples from analyze_line, prepares navigation data.
368 |
369 | Navigation data is a dictionary mapping an id to its children ids, paren id
370 | and user ids.
371 |
372 | It's important for line_info to be in the order gathered from the input. The
373 | order is essential for determining parent/child relationships.
374 | """
375 | # ZZZ: in the end, add 'users' fields...
376 | nav_data = {}
377 | def new_data_entry(line_entry):
378 | """Create a new entry with empty parent and child info."""
379 | nonlocal nav_data
380 | id, name, nesting_level, uselist = line_entry
381 | nav_data[id] = {'id': id, 'name': name,
382 | 'uses': uselist, 'users': [],
383 | 'nesting_level': nesting_level,
384 | 'parent': None, 'children': []}
385 | return nav_data[id]
386 |
387 | # Keep a stack of parents. The topmost parent on the stack is the one
388 | # collecting the current children, and their parent ID is mapped to it. The
389 | # stack is popped when the nesting level decreases (popped until the topmost
390 | # parent has a lower nesting level). Every entry is eventually pushed onto
391 | # the stack because it may have children.
392 | assert len(line_info) > 0
393 | assert line_info[0][2] == 0, "Expect top-level entry at nesting level 0"
394 |
395 | # Initialize the parent stack to the first entry
396 | parent_stack = [new_data_entry(line_info[0])]
397 |
398 | for line_entry in line_info[1:]:
399 | data_entry = new_data_entry(line_entry)
400 |
401 | # Pop the stack until the topmost entry is a suitable parent for this
402 | # one.
403 | while parent_stack[-1]['nesting_level'] >= data_entry['nesting_level']:
404 | # Note: no entry except the toplevel has nesting 0, so this will
405 | # always terminate with at most 1 entry remaining on the stack.
406 | parent_stack.pop()
407 |
408 | # Now parent_stack[-1] is the parent of this entry. Update the entries
409 | # accordingly.
410 | data_entry['parent'] = parent_stack[-1]['id']
411 | parent_stack[-1]['children'].append(data_entry['id'])
412 |
413 | # At this point, we push the current entry onto the stack.
414 | parent_stack.append(data_entry)
415 |
416 | # Finally, add 'users' fields to all entries. This is an inversion of 'uses'
417 | for id, entry in nav_data.items():
418 | for used_id in entry['uses']:
419 | if used_id in nav_data:
420 | nav_data[used_id]['users'].append(id)
421 |
422 | return nav_data
423 |
424 |
425 | def htmlize(input):
426 | """HTML-ize the input text, producing output.
427 |
428 | input: stream / file-like object with textual AST dump.
429 | Returns a string with HTML-ized dump.
430 | """
431 | html_lines = []
432 |
433 | # collected list of line analysis info
434 | line_info = []
435 |
436 | for text_line in input:
437 | html_line_chunks = []
438 | tokens = list(tokenize_line(text_line))
439 | line_info.append(analyze_line(tokens))
440 | for tok in tokens:
441 | style = tok.style
442 | klass = 'ansi-{}'.format(style.color.name.lower())
443 | if style.bold:
444 | klass += ' ansi-bold'
445 | html_line_chunks.append(SPAN_TEMPLATE.format(
446 | klass=klass,
447 | text=html.escape(tok.text)))
448 | html_line_chunks.append('
')
449 | inject_links(html_line_chunks)
450 | html_lines.append(''.join(html_line_chunks))
451 |
452 | nav_data = prepare_nav_data(line_info)
453 |
454 | return HTML_OUTPUT_TEMPLATE.format(lines='\n'.join(html_lines),
455 | nav_data=json.dumps(nav_data),
456 | js_code=JS_CODE)
457 |
458 |
459 | def main():
460 | argparser = argparse.ArgumentParser(
461 | description='HTML output is emitted to stdout')
462 | argparser.add_argument('dump_file',
463 | help='AST dump file, "-" for reading from stdin')
464 | args = argparser.parse_args()
465 |
466 | try:
467 | # Argh: it would be nice to use argparse's FileType to do this
468 | # automatically, but unfortunately it's broken for binary mode
469 | # (http://bugs.python.org/issue14156)
470 | input_stream = (open(sys.argv[1], 'rb') if args.dump_file != '-' else
471 | io.BufferedReader(sys.stdin.buffer))
472 | print(htmlize(input_stream))
473 | #tokens = list(tokenize_line(l) for l in input_stream)
474 | #print(list(tokens[0]))
475 | finally:
476 | input_stream.close()
477 |
478 |
479 | if __name__ == '__main__':
480 | main()
481 |
--------------------------------------------------------------------------------
/tools/sample-js.js:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | ...
10 | ...
11 |
12 |
13 | TranslationUnitDecl 0x3511670 <<invalid sloc>> <invalid sloc>
14 |
15 |
--------------------------------------------------------------------------------
/tools/show-cfg.py:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------
2 | # Script for showing the CFG of the given LLVM IR file.
3 | # 'dot' must be installed. If 'eog' is installed, it's popped to show the
4 | # produced image.
5 | #
6 | # Eli Bendersky (eliben@gmail.com)
7 | # This code is in the public domain
8 | #-------------------------------------------------------------------------------
9 | import os, re, subprocess, sys
10 | import tempfile
11 | import os.path
12 |
13 | LLVMDIR = os.path.expanduser('~/llvm/build/svn-make-debug/Debug+Asserts/bin')
14 | OPT = os.path.join(LLVMDIR, 'opt')
15 |
16 | def show_dot(filename):
17 | outpng = os.path.join(tempfile.gettempdir(), filename + '.png')
18 | subprocess.check_call(['dot', '-Tpng', filename, '-o', outpng])
19 | print('Created', outpng, '... Running eog')
20 | subprocess.call('eog ' + outpng + ' &', shell=True)
21 |
22 | def main():
23 | opt_out = subprocess.check_output(
24 | [OPT, '-dot-cfg-only', '-disable-output', sys.argv[1]],
25 | stderr=subprocess.STDOUT) # opt prints stuff to stderr too o_O
26 | for line in opt_out.decode('utf-8').splitlines():
27 | m = re.search("Writing '(cfg\.[^\.]+\.dot)", line)
28 | if m:
29 | show_dot(m.group(1))
30 | else:
31 | print('Cannot parse line:', line)
32 |
33 | if __name__ == '__main__':
34 | main()
35 |
--------------------------------------------------------------------------------
/tools/template.html:
--------------------------------------------------------------------------------
1 |
2 |
39 |
40 |
41 |
42 |
43 | hello
44 | hello
45 |
46 |
47 |
48 |
49 |
50 | TranslationUnitDecl 0x38a3670 implicit
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/using_clang_toolchain/Makefile:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------------------------------------
2 | # Eli Bendersky (eliben@gmail.com)
3 | # This code is in the public domain
4 | #-------------------------------------------------------------------------------
5 |
6 | # The following variables will likely need to be modified, depending on where
7 | # and how you built LLVM & Clang.
8 |
9 | # LLVM_BUILD_PATH is the directory in which you built LLVM - where you ran
10 | # configure or cmake.
11 | # For linking vs. a binary build of LLVM, point to the main untarred directory.
12 | LLVM_BUILD_PATH = $$HOME/llvm/build/svn-make-debug
13 | #LLVM_BUILD_PATH := $$HOME/llvm/build/gh-ninja-debug
14 | #LLVM_BUILD_PATH := $$HOME/llvm/build/svn-ninja-release
15 |
16 | # LLVM_BIN_PATH is the directory where binaries are placed by the LLVM build
17 | # process. It should contain the tools like opt, llc and clang. The default
18 | # reflects a debug build with autotools (configure & make).
19 | LLVM_BIN_PATH = $(LLVM_BUILD_PATH)/Debug+Asserts/bin
20 | #LLVM_BIN_PATH := $(LLVM_BUILD_PATH)/bin
21 |
22 | CLANG := $(LLVM_BIN_PATH)/clang
23 | CLANGXX := $(LLVM_BIN_PATH)/clang++
24 | LLVMLINK := $(LLVM_BIN_PATH)/llvm-link
25 | LLC := $(LLVM_BIN_PATH)/llc
26 | OPT := $(LLVM_BIN_PATH)/opt
27 | LD := $(LLVM_BIN_PATH)/clang++
28 |
29 | CFLAGS := -std=c99 -Wall
30 | CXXFLAGS := -Wall
31 | CXX11FLAG := -std=c++11
32 |
33 | define colorecho
34 | @tput setaf 6
35 | @echo $1
36 | @tput sgr0
37 | endef
38 |
39 | BUILDDIR := build
40 |
41 | all: make_builddir \
42 | $(BUILDDIR)/simple \
43 | $(BUILDDIR)/exceptions1 \
44 | $(BUILDDIR)/simple-from-bitcode \
45 | $(BUILDDIR)/cxx11-range-sample
46 |
47 | make_builddir:
48 | @test -d $(BUILDDIR) || mkdir $(BUILDDIR)
49 |
50 | $(BUILDDIR)/simple: simple.cpp
51 | $(CLANGXX) $(CXXFLAGS) $^ -o $@
52 |
53 | $(BUILDDIR)/exceptions1: exceptions1.cpp
54 | $(CLANGXX) $(CXXFLAGS) $^ -o $@
55 |
56 | # The following rules split the Clang+LLVM compilation process to discrete
57 | # parts, generating an intermediate LLVM bitcode file and performing the backend
58 | # compilation separately with llc.
59 | $(BUILDDIR)/simple-from-bitcode.bc: simple.cpp
60 | $(call colorecho,"Compiling C++ to LLVM bitcode")
61 | $(CLANGXX) $(CXXFLAGS) $^ -o $@ -c -emit-llvm
62 |
63 | $(BUILDDIR)/simple-from-bitcode.o: $(BUILDDIR)/simple-from-bitcode.bc
64 | $(call colorecho,"Emitting native object file from LLVM bitcode")
65 | $(LLC) $^ -o $@ -filetype=obj
66 |
67 | $(BUILDDIR)/simple-from-bitcode: $(BUILDDIR)/simple-from-bitcode.o
68 | $(call colorecho,"Linking with" $(LD))
69 | $(LD) $^ -o $@
70 |
71 | # The following rules compile a library "memcaller" to bitcode, as well as a
72 | # "main" object that invokes it. It then uses llvm-link to link the two bitcodes
73 | # into one and proceeds with llc to compile them to native code.
74 | $(BUILDDIR)/memmain.bc: memmain.c
75 | $(CLANG) $(CFLAGS) $^ -o $@ -c -emit-llvm
76 |
77 | $(BUILDDIR)/memcaller.bc: memcaller.c
78 | $(CLANG) $(CFLAGS) $^ -o $@ -c -emit-llvm
79 |
80 | $(BUILDDIR)/memmain_exe.bc: $(BUILDDIR)/memmain.bc $(BUILDDIR)/memcaller.bc
81 | $(LLVMLINK) -o=$@ $^
82 |
83 | $(BUILDDIR)/memmain_exe: $(BUILDDIR)/memmain_exe.bc
84 | $(LLC) $^ -o $(BUILDDIR)/memmain_exe.o -filetype=obj
85 | $(LD) $(BUILDDIR)/memmain_exe.o -o $@
86 |
87 | $(BUILDDIR)/cxx11-range-sample: cxx11-range-sample.cpp
88 | $(CLANGXX) $(CXXFLAGS) $(CXX11FLAG) $^ -o $@
89 |
90 | # Not adding this one to 'all' because it generates warnings
91 | $(BUILDDIR)/warn_in_macro: warn_in_macro.cpp
92 | $(CLANGXX) $(CXXFLAGS) $^ -o $@
93 |
94 | clean:
95 | rm -rf $(BUILDDIR)/* *.dot test/*.pyc test/__pycache__
96 |
--------------------------------------------------------------------------------
/using_clang_toolchain/README.rst:
--------------------------------------------------------------------------------
1 | These are samples of using the Clang+LLVM toolchain for building actual
2 | executables.
3 |
--------------------------------------------------------------------------------
/using_clang_toolchain/cxx11-range-sample.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | int main(int argc, char **argv) {
5 | // Container initialization - shiny!
6 | std::vector nums = {1, 2, 3, 4, 5};
7 |
8 | // auto + for-range loop - shiny^2!
9 | for (auto &n : nums) {
10 | std::cout << n << " ";
11 | }
12 |
13 | std::cout << std::endl;
14 | return 0;
15 | }
16 |
--------------------------------------------------------------------------------
/using_clang_toolchain/exceptions1.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | void foo(int i) {
6 | if (i > 2) {
7 | throw std::runtime_error("Expected i <= 2");
8 | }
9 | }
10 |
11 | int main(int argc, char **argv) {
12 | try {
13 | foo(argc);
14 | } catch (std::exception &e) {
15 | std::cerr << "Got exception: " << e.what() << std::endl;
16 | }
17 |
18 | return 0;
19 | }
20 |
--------------------------------------------------------------------------------
/using_clang_toolchain/memcaller.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void* memcpy_impl(void* dst, const void* src, size_t n) {
4 | unsigned char* d = dst;
5 | const unsigned char* s = src;
6 | for (size_t i = 0; i < n; ++i) {
7 | d[i] = s[i];
8 | }
9 | return dst;
10 | }
11 |
12 | void* memmove_impl(void* dst, const void* src, size_t n) {
13 | unsigned char* d = dst;
14 | const unsigned char* s = src;
15 | if (s < d) {
16 | while (n--) {
17 | d[n] = s[n];
18 | }
19 | } else {
20 | for (size_t i = 0; i < n; ++i) {
21 | d[i] = s[i];
22 | }
23 | }
24 | return dst;
25 | }
26 |
27 | void* memcpy_caller(void* dst, const void* src, size_t n) {
28 | return memcpy(dst, src, n);
29 | //return memcpy_impl(dst, src, n);
30 | }
31 |
32 | void* memcpy_caller_casting(int* dst, const int* src, size_t n) {
33 | return memcpy(dst, src, n);
34 | }
35 |
36 | void* memmove_caller(void* dst, const void* src, size_t n) {
37 | //return memmove(dst, src, n);
38 | return memmove_impl(dst, src, n);
39 | }
40 |
41 | void* memset_caller(void* dst, int c, size_t n) {
42 | return memset(dst, c, n);
43 | }
44 |
--------------------------------------------------------------------------------
/using_clang_toolchain/memmain.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void* memcpy_caller(void* dst, const void* src, size_t n);
4 | void* memmove_caller(void* dst, const void* src, size_t n);
5 | void* memset_caller(void* dst, int c, size_t n);
6 |
7 | int main(int argc, char** argv) {
8 | const size_t N = 256;
9 | char buf[N];
10 |
11 | for (size_t i = 0; i < N; ++i) {
12 | buf[i] = i;
13 | }
14 |
15 | memmove_caller(&buf[4], &buf[0], 0);
16 | //memmove_caller(&buf[1], &buf[5], 0);
17 |
18 | for (size_t i = 0; i < 30; ++i) {
19 | printf("0x%x ", buf[i]);
20 | }
21 | printf("\n");
22 |
23 | return 0;
24 | }
25 |
--------------------------------------------------------------------------------
/using_clang_toolchain/simple.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | int main(int argc, char **argv) {
4 | std::cout << "Hello world\n";
5 | return 0;
6 | }
7 |
--------------------------------------------------------------------------------
/using_clang_toolchain/warn_in_macro.cpp:
--------------------------------------------------------------------------------
1 | #define MYCHECK(a) (0 && 4)
2 |
3 | int main(int argc, char **argv) {
4 |
5 | // Warning here
6 | while (0 && 4) {
7 | }
8 |
9 | // But no warning here, since 4 comes from a macro
10 | while (MYCHECK(argc)) {
11 | }
12 |
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------