xazax.hun created this revision.
Herald added a subscriber: mgorny.

This patch adds support for naive cross translational unit analysis.

The aim of this patch is to be minimal to enable the development of the feature 
on the top of tree. This patch should be an NFC in case XTUDir is not provided 
by the user.

When XTUDir is provided:

- In case a function definition is not available it will be looked up from a 
textual index, whether it was available in another TU.
- The AST dump of the other TU will be loaded and the function definition will 
be inlined.

One of the main limitations is that the coverage pattern of the analysis will 
change when this feature is enabled. For this reason in the future it might be 
better to include some heuristics to prefer examining shorter execution paths 
to the longer ones. Until than this feature is not recommended to be turned on 
by users unless they already fixed the important issues with this feature 
turned off.

We will cover more detailed analysis of this patch soon in our EuroLLVM talk: 
http://llvm.org/devmtg/2017-03//2017/02/20/accepted-sessions.html#7
We will talk about how this works and the memory usage, analysis time, coverage 
pattern change, limitations of the ASTImporter, how the length of the bugpaths 
changed and a lot more.

Feel free to skip the review after the talk, but we wanted to make the code 
available in an easy to review format before the conference.


Repository:
  rL LLVM

https://reviews.llvm.org/D30691

Files:
  include/clang/AST/ASTContext.h
  include/clang/AST/Decl.h
  include/clang/AST/Mangle.h
  include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
  include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/AST/ASTContext.cpp
  lib/AST/ASTImporter.cpp
  lib/AST/ItaniumMangle.cpp
  lib/Basic/SourceManager.cpp
  lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
  lib/StaticAnalyzer/Core/CMakeLists.txt
  lib/StaticAnalyzer/Core/CallEvent.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
  test/Analysis/Inputs/externalFnMap.txt
  test/Analysis/Inputs/xtu-chain.cpp
  test/Analysis/Inputs/xtu-other.cpp
  test/Analysis/xtu-main.cpp
  tools/CMakeLists.txt
  tools/clang-cmdline-arch-extractor/CMakeLists.txt
  tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
  tools/clang-func-mapping/CMakeLists.txt
  tools/clang-func-mapping/ClangFnMapGen.cpp
  tools/scan-build-py/libscanbuild/runner.py
  tools/xtu-analysis/xtu-analyze.py
  tools/xtu-analysis/xtu-build.py

Index: tools/xtu-analysis/xtu-build.py
===================================================================
--- /dev/null
+++ tools/xtu-analysis/xtu-build.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+
+import argparse
+import io
+import json
+import multiprocessing
+import os
+import re
+import signal
+import subprocess
+import string
+
+threading_factor = int(multiprocessing.cpu_count() * 1.5)
+timeout = 86400
+
+parser = argparse.ArgumentParser(
+    description='Executes 1st pass of XTU analysis')
+parser.add_argument('-b', required=True, dest='buildlog',
+                    metavar='build.json',
+                    help='Use a JSON Compilation Database')
+parser.add_argument('-p', metavar='preanalyze-dir', dest='xtuindir',
+                    help='Use directory for generating preanalyzation data '
+                         '(default=".xtu")',
+                    default='.xtu')
+parser.add_argument('-j', metavar='threads', dest='threads',
+                    help='Number of threads used (default=' +
+                    str(threading_factor) + ')',
+                    default=threading_factor)
+parser.add_argument('-v', dest='verbose', action='store_true',
+                    help='Verbose output of every command executed')
+parser.add_argument('--clang-path', metavar='clang-path', dest='clang_path',
+                    help='Set path of clang binaries to be used '
+                         '(default taken from CLANG_PATH envvar)',
+                    default=os.environ.get('CLANG_PATH'))
+parser.add_argument('--timeout', metavar='N',
+                    help='Timeout for build in seconds (default: %d)' %
+                    timeout,
+                    default=timeout)
+mainargs = parser.parse_args()
+
+if mainargs.clang_path is None:
+    clang_path = ''
+else:
+    clang_path = os.path.abspath(mainargs.clang_path)
+if mainargs.verbose:
+    print 'XTU uses clang dir: ' + \
+        (clang_path if clang_path != '' else '<taken from PATH>')
+
+buildlog_file = open(mainargs.buildlog, 'r')
+buildlog = json.load(buildlog_file)
+buildlog_file.close()
+
+src_pattern = re.compile('.*\.(C|c|cc|cpp|cxx|ii|m|mm)$', re.IGNORECASE)
+src_2_dir = {}
+src_2_cmd = {}
+src_order = []
+cmd_2_src = {}
+cmd_order = []
+for step in buildlog:
+    if src_pattern.match(step['file']):
+        if step['file'] not in src_2_dir:
+            src_2_dir[step['file']] = step['directory']
+        if step['file'] not in src_2_cmd:
+            src_2_cmd[step['file']] = step['command']
+            src_order.append(step['file'])
+        if step['command'] not in cmd_2_src:
+            cmd_2_src[step['command']] = [step['file']]
+            cmd_order.append(step['command'])
+        else:
+            cmd_2_src[step['command']].append(step['file'])
+
+
+def clear_file(filename):
+    try:
+        os.remove(filename)
+    except OSError:
+        pass
+
+
+def get_command_arguments(cmd):
+    had_command = False
+    args = []
+    for arg in cmd.split():
+        if had_command and not src_pattern.match(arg):
+            args.append(arg)
+        if not had_command and arg.find('=') == -1:
+            had_command = True
+    return args
+
+
+def generate_ast(source):
+    cmd = src_2_cmd[source]
+    args = get_command_arguments(cmd)
+    arch_command = os.path.join(clang_path, 'clang-cmdline-arch-extractor') + \
+        ' ' + string.join(args, ' ') + ' ' + source
+    if mainargs.verbose:
+        print arch_command
+    arch_output = subprocess.check_output(arch_command, shell=True)
+    arch = arch_output[arch_output.rfind('@')+1:].strip()
+    ast_joined_path = os.path.join(mainargs.xtuindir,
+                                   os.path.join('/ast/' + arch,
+                                                os.path.realpath(source)[1:] +
+                                                '.ast')[1:])
+    ast_path = os.path.abspath(ast_joined_path)
+    try:
+        os.makedirs(os.path.dirname(ast_path))
+    except OSError:
+        if os.path.isdir(os.path.dirname(ast_path)):
+            pass
+        else:
+            raise
+    dir_command = 'cd ' + src_2_dir[source]
+    ast_command = os.path.join(clang_path, 'clang') + ' -emit-ast ' + \
+        string.join(args, ' ') + ' -w ' + source + ' -o ' + ast_path
+    if mainargs.verbose:
+        print dir_command + " && " + ast_command
+    subprocess.call(dir_command + " && " + ast_command, shell=True)
+
+
+def map_functions(command):
+    args = get_command_arguments(command)
+    sources = cmd_2_src[command]
+    dir_command = 'cd ' + src_2_dir[sources[0]]
+    funcmap_command = os.path.join(clang_path, 'clang-func-mapping') + \
+        ' --xtu-dir ' + os.path.abspath(mainargs.xtuindir) + ' ' + \
+        string.join(sources, ' ') + ' -- ' + string.join(args, ' ')
+    if mainargs.verbose:
+        print funcmap_command
+    subprocess.call(dir_command + " && " + funcmap_command, shell=True)
+
+clear_file(os.path.join(mainargs.xtuindir, 'cfg.txt'))
+clear_file(os.path.join(mainargs.xtuindir, 'definedFns.txt'))
+clear_file(os.path.join(mainargs.xtuindir, 'externalFns.txt'))
+clear_file(os.path.join(mainargs.xtuindir, 'externalFnMap.txt'))
+
+original_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+ast_workers = multiprocessing.Pool(processes=int(mainargs.threads))
+signal.signal(signal.SIGINT, original_handler)
+try:
+    res = ast_workers.map_async(generate_ast, src_order)
+    # Block with timeout so that signals don't get ignored, python bug 8296
+    res.get(mainargs.timeout)
+except KeyboardInterrupt:
+    ast_workers.terminate()
+    ast_workers.join()
+    exit(1)
+else:
+    ast_workers.close()
+    ast_workers.join()
+
+original_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+funcmap_workers = multiprocessing.Pool(processes=int(mainargs.threads))
+signal.signal(signal.SIGINT, original_handler)
+try:
+    res = funcmap_workers.map_async(map_functions, cmd_order)
+    res.get(mainargs.timeout)
+except KeyboardInterrupt:
+    funcmap_workers.terminate()
+    funcmap_workers.join()
+    exit(1)
+else:
+    funcmap_workers.close()
+    funcmap_workers.join()
+
+
+# Generate externalFnMap.txt
+
+func_2_file = {}
+extfunc_2_file = {}
+func_2_fileset = {}
+
+defined_fns_filename = os.path.join(mainargs.xtuindir, 'definedFns.txt')
+with open(defined_fns_filename,  'r') as defined_fns_file:
+    for line in defined_fns_file:
+        funcname, filename = line.strip().split(' ')
+        if funcname.startswith('!'):
+            funcname = funcname[1:]  # main function
+        if funcname not in func_2_file.keys():
+            func_2_fileset[funcname] = set([filename])
+        else:
+            func_2_fileset[funcname].add(filename)
+        func_2_file[funcname] = filename
+
+extern_fns_filename = os.path.join(mainargs.xtuindir, 'externalFns.txt')
+with open(extern_fns_filename,  'r') as extern_fns_file:
+    for line in extern_fns_file:
+        line = line.strip()
+        if line in func_2_file and line not in extfunc_2_file:
+            extfunc_2_file[line] = func_2_file[line]
+
+extern_fns_map_filename = os.path.join(mainargs.xtuindir, 'externalFnMap.txt')
+with open(extern_fns_map_filename, 'w') as out_file:
+    for func, fname in extfunc_2_file.items():
+        if len(func_2_fileset[func]) == 1:
+            out_file.write('%s %s.ast\n' % (func, fname))
Index: tools/xtu-analysis/xtu-analyze.py
===================================================================
--- /dev/null
+++ tools/xtu-analysis/xtu-analyze.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python
+
+import argparse
+import io
+import json
+import multiprocessing
+import os
+import re
+import shutil
+import signal
+import subprocess
+import string
+import sys
+import threading
+import time
+import uuid
+import sys
+
+reload(sys)
+sys.setdefaultencoding('utf8')
+
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             '..', '..', 'utils', 'analyzer'))
+try:
+    import MergeCoverage
+except:
+    raise
+
+threading_factor = int(multiprocessing.cpu_count() * 1.5)
+analyser_output_formats = ['plist-multi-file', 'plist', 'plist-html',
+                           'html', 'text']
+analyser_output_format = analyser_output_formats[0]
+gcov_outdir = 'gcov'
+gcov_tmpdir = gcov_outdir + '_tmp'
+
+parser = argparse.ArgumentParser(
+            description='Executes 2nd pass of XTU analysis')
+parser.add_argument('-b', required=True, dest='buildlog', metavar='build.json',
+                    help='Use a JSON Compilation Database')
+parser.add_argument('-p', metavar='preanalyze-dir', dest='xtuindir',
+                    help='Use directory for reading preanalyzation data '
+                         '(default=".xtu")',
+                    default='.xtu')
+parser.add_argument('-o', metavar='output-dir', dest='xtuoutdir',
+                    help='Use directory for output analyzation results '
+                         '(default=".xtu-out")',
+                    default='.xtu-out')
+parser.add_argument('-e', metavar='enabled-checker', nargs='+',
+                    dest='enabled_checkers',
+                    help='List all enabled checkers')
+parser.add_argument('-d', metavar='disabled-checker', nargs='+',
+                    dest='disabled_checkers',
+                    help='List all disabled checkers')
+parser.add_argument('-j', metavar='threads', dest='threads',
+                    help='Number of threads used (default=' +
+                    str(threading_factor) + ')',
+                    default=threading_factor)
+parser.add_argument('-v', dest='verbose', action='store_true',
+                    help='Verbose output of every command executed')
+parser.add_argument('--clang-path', metavar='clang-path', dest='clang_path',
+                    help='Set path of clang binaries to be used (default '
+                         'taken from CLANG_PATH environment variable)',
+                    default=os.environ.get('CLANG_PATH'))
+parser.add_argument('--analyze-cc-path', metavar='analyze-cc-path',
+                    dest='analyze_path',
+                    help='Set path of analyze-cc to be used '
+                         '(default is taken from CLANG_ANALYZE_CC_PATH '
+                         'environment variable)',
+                    default=os.environ.get('CLANG_ANALYZE_CC_PATH'))
+parser.add_argument('--output-format', metavar='format',
+                    choices=analyser_output_formats,
+                    default=analyser_output_format,
+                    help='Format for analysis reports '
+                         '(one of %s; default is "%s").' %
+                    (', '.join(analyser_output_formats),
+                     analyser_output_format))
+parser.add_argument('--no-xtu', dest='no_xtu', action='store_true',
+                    help='Do not use XTU at all, '
+                         'only do normal static analysis')
+parser.add_argument('--record-coverage', dest='record_coverage',
+                    action='store_true',
+                    help='Generate coverage information during analysis')
+mainargs = parser.parse_args()
+
+concurrent_threads = 0
+concurrent_thread_times = [0.0]
+concurrent_thread_last_clock = time.time()
+
+if mainargs.clang_path is None:
+    clang_path = ''
+else:
+    clang_path = os.path.abspath(mainargs.clang_path)
+if mainargs.verbose:
+    print 'XTU uses clang dir: ' + (clang_path if clang_path != ''
+                                    else '<taken from PATH>')
+
+if mainargs.analyze_path is None:
+    analyze_path = ''
+else:
+    analyze_path = os.path.abspath(mainargs.analyze_path)
+if mainargs.verbose:
+    print 'XTU uses analyze-cc dir: ' + (analyze_path if analyze_path != ''
+                                         else '<taken from PATH>')
+
+analyzer_params = []
+if mainargs.enabled_checkers:
+    analyzer_params += ['-analyzer-checker', mainargs.enabled_checkers]
+if mainargs.disabled_checkers:
+    analyzer_params += ['-analyzer-disable-checker', mainargs.disable_checkers]
+if not mainargs.no_xtu:
+    analyzer_params += ['-analyzer-config',
+                        'xtu-dir=' + os.path.abspath(mainargs.xtuindir)]
+analyzer_params += ['-analyzer-config', 'reanalyze-xtu-visited=true']
+if mainargs.record_coverage:
+    gcov_tmppath = os.path.abspath(os.path.join(mainargs.xtuoutdir,
+                                                gcov_tmpdir))
+    gcov_finalpath = os.path.abspath(os.path.join(mainargs.xtuoutdir,
+                                                  gcov_outdir))
+    shutil.rmtree(gcov_tmppath, True)
+    # analyzer_params += ['-analyzer-config',
+    #                     'record-coverage=' + gcov_tmppath]
+analyzer_params += ['-analyzer-stats']
+# analyzer_params += ['-analyzer-output ' + mainargs.output_format]
+passthru_analyzer_params = []
+for param in analyzer_params:
+    passthru_analyzer_params += ['-Xanalyzer']
+    passthru_analyzer_params += [param]
+passthru_analyzer_params += ['--analyzer-output ' + mainargs.output_format]
+
+analyzer_env = os.environ.copy()
+analyzer_env['ANALYZE_BUILD_CLANG'] = os.path.join(clang_path, 'clang')
+analyzer_env['ANALYZE_BUILD_REPORT_DIR'] = os.path.abspath(mainargs.xtuoutdir)
+analyzer_env['ANALYZE_BUILD_PARAMETERS'] = ' '.join(passthru_analyzer_params)
+analyzer_env['ANALYZE_BUILD_REPORT_FORMAT'] = mainargs.output_format
+# analyzer_env['ANALYZE_BUILD_VERBOSE'] = 'DEBUG'
+
+graph_lock = threading.Lock()
+
+buildlog_file = open(mainargs.buildlog, 'r')
+buildlog = json.load(buildlog_file)
+buildlog_file.close()
+
+src_pattern = re.compile('.*\.(C|c|cc|cpp|cxx|ii|m|mm)$', re.IGNORECASE)
+dircmd_separator = ': '
+dircmd_2_orders = {}
+src_build_steps = 0
+for step in buildlog:
+    if src_pattern.match(step['file']):
+        uid = step['directory'] + dircmd_separator + step['command']
+        if uid not in dircmd_2_orders:
+            dircmd_2_orders[uid] = [src_build_steps]
+        else:
+            dircmd_2_orders[uid].append(src_build_steps)
+        src_build_steps += 1
+
+
+def get_compiler_and_arguments(cmd):
+    had_command = False
+    args = []
+    for arg in cmd.split():
+        if had_command:
+            args.append(arg)
+        if not had_command and arg.find('=') == -1:
+            had_command = True
+            compiler = arg
+    return compiler, args
+
+
+def analyze(directory, command):
+    compiler, args = get_compiler_and_arguments(command)
+
+    last_src = None
+    for cmdpart in command.split():
+        if src_pattern.match(cmdpart):
+            last_src = cmdpart
+    tu_name = ''
+    if last_src:
+        tu_name += last_src.split(os.sep)[-1]
+    tu_name += '_' + str(uuid.uuid4())
+
+    cmdenv = analyzer_env.copy()
+    cmdenv['ANALYZE_BUILD_CC'] = compiler
+    cmdenv['ANALYZE_BUILD_CXX'] = compiler
+    if mainargs.record_coverage:
+        cmdenv['ANALYZE_BUILD_PARAMETERS'] += \
+            ' -Xanalyzer -analyzer-config -Xanalyzer record-coverage=' + \
+            os.path.join(gcov_tmppath, tu_name)
+    analyze_cmd = os.path.join(analyze_path, 'analyze-cc') + \
+        ' ' + string.join(args, ' ')
+    if mainargs.verbose:
+        print analyze_cmd
+
+    # Buffer output of subprocess and dump it out at the end, so that
+    # the subprocess doesn't continue to write output after the user
+    # sends SIGTERM
+    runOK = True
+    out = '******* Error running command'
+    try:
+        po = subprocess.Popen(analyze_cmd, shell=True,
+                              stderr=subprocess.STDOUT,
+                              stdout=subprocess.PIPE,
+                              universal_newlines=True,
+                              cwd=directory,
+                              env=cmdenv)
+        out, _ = po.communicate()
+        runOK = not po.returncode
+    except OSError:
+        runOK = False
+    if mainargs.verbose:
+        sys.stdout.write(out)
+    if not runOK:
+        prefix = os.path.join(os.path.abspath(mainargs.xtuoutdir), "fails")
+    else:
+        prefix = os.path.join(os.path.abspath(mainargs.xtuoutdir), "passes")
+    with open(os.path.join(prefix, "%s.out" % tu_name), "w") as f:
+        f.write("%s\n%s" % (analyze_cmd, out))
+
+
+def analyze_work():
+    global concurrent_threads
+    global concurrent_thread_times
+    global concurrent_thread_last_clock
+    global graph_lock
+    global dircmd_2_orders
+    while len(dircmd_2_orders) > 0:
+        graph_lock.acquire()
+        found_dircmd_orders = None
+        found_dircmd = None
+        found_orders = None
+        for dircmd_orders in dircmd_2_orders.items():
+            dircmd = dircmd_orders[0].split(dircmd_separator, 2)
+            orders = dircmd_orders[1]
+            assert len(dircmd) == 2 and len(dircmd[0]) > 0 and \
+                len(dircmd[1]) > 0
+            assert len(orders) > 0
+            found_dircmd_orders = dircmd_orders
+            found_dircmd = dircmd
+            found_orders = orders
+            break
+        if found_dircmd_orders is not None:
+            del dircmd_2_orders[found_dircmd_orders[0]]
+
+            concurrent_thread_current_clock = time.time()
+            concurrent_thread_times[concurrent_threads] += \
+                concurrent_thread_current_clock - concurrent_thread_last_clock
+            concurrent_thread_last_clock = concurrent_thread_current_clock
+            concurrent_threads += 1
+            if len(concurrent_thread_times) == concurrent_threads:
+                concurrent_thread_times.append(0.0)
+
+            graph_lock.release()
+            analyze(found_dircmd[0], found_dircmd[1])
+            graph_lock.acquire()
+
+            concurrent_thread_current_clock = time.time()
+            concurrent_thread_times[concurrent_threads] += \
+                concurrent_thread_current_clock - concurrent_thread_last_clock
+            concurrent_thread_last_clock = concurrent_thread_current_clock
+            concurrent_threads -= 1
+            assert concurrent_threads >= 0
+
+            graph_lock.release()
+        else:
+            graph_lock.release()
+            time.sleep(0.125)
+
+try:
+    os.makedirs(os.path.abspath(mainargs.xtuoutdir))
+except OSError:
+    print 'Output directory %s already exists!' % \
+        os.path.abspath(mainargs.xtuoutdir)
+    sys.exit(1)
+
+os.makedirs(os.path.join(os.path.abspath(mainargs.xtuoutdir), "passes"))
+os.makedirs(os.path.join(os.path.abspath(mainargs.xtuoutdir), "fails"))
+
+original_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+signal.signal(signal.SIGINT, original_handler)
+
+analyze_workers = []
+for i in range(int(mainargs.threads)):
+    analyze_workers.append(threading.Thread(target=analyze_work))
+for worker in analyze_workers:
+    worker.start()
+try:
+    for worker in analyze_workers:
+        worker.join(9999999999)
+except KeyboardInterrupt:
+    exit(1)
+
+try:
+    os.removedirs(os.path.abspath(mainargs.xtuoutdir))
+    print 'Removing directory %s because it contains no reports' % \
+        os.path.abspath(mainargs.xtuoutdir)
+except OSError:
+    pass
+
+if mainargs.record_coverage:
+    MergeCoverage.main(gcov_tmppath, gcov_finalpath)
+    shutil.rmtree(gcov_tmppath, True)
+
+assert concurrent_threads == 0
+concurrent_thread_times[0] += time.time() - concurrent_thread_last_clock
+sumtime = 0.0
+for i in range(len(concurrent_thread_times)):
+    sumtime += concurrent_thread_times[i]
+print '--- Total running time: %.2fs' % sumtime
+for i in range(len(concurrent_thread_times)):
+    print '----- ' + \
+        (('using %d processes' % i) if i != 0 else 'self time') + \
+        ' for %.2fs (%.0f%%)' % (concurrent_thread_times[i],
+                                 concurrent_thread_times[i] * 100.0 / sumtime)
Index: tools/scan-build-py/libscanbuild/runner.py
===================================================================
--- tools/scan-build-py/libscanbuild/runner.py
+++ tools/scan-build-py/libscanbuild/runner.py
@@ -162,7 +162,8 @@
 
     def target():
         """ Creates output file name for reports. """
-        if opts['output_format'] in {'plist', 'plist-html'}:
+        if opts['output_format'] in {'plist', 'plist-html',
+                                     'plist-multi-file'}:
             (handle, name) = tempfile.mkstemp(prefix='report-',
                                               suffix='.plist',
                                               dir=opts['output_dir'])
Index: tools/clang-func-mapping/ClangFnMapGen.cpp
===================================================================
--- /dev/null
+++ tools/clang-func-mapping/ClangFnMapGen.cpp
@@ -0,0 +1,257 @@
+//===- ClangFnMapGen.cpp -----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// Clang tool which creates a list of defined functions and the files in which
+// they are defined.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include <assert.h>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <sys/file.h>
+#include <unistd.h>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::tooling;
+
+typedef StringSet<> StrSet;
+typedef StringMap<StrSet> CallGraph;
+
+static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options");
+static cl::opt<std::string> XTUDir(
+    "xtu-dir",
+    cl::desc(
+        "Directory that contains the XTU related files (e.g.: AST dumps)."),
+    cl::init(""), cl::cat(ClangFnMapGenCategory));
+
+static void lockedWrite(const std::string &fileName,
+                        const std::string &content) {
+  if (!content.empty()) {
+    int fd = open(fileName.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
+    flock(fd, LOCK_EX);
+    ssize_t written = write(fd, content.c_str(), content.length());
+    assert(written == static_cast<ssize_t>(content.length()));
+    (void)written;
+    flock(fd, LOCK_UN);
+    close(fd);
+  }
+}
+
+static std::string getTripleSuffix(ASTContext &Ctx) {
+  // We are not going to support vendor and don't support OS and environment.
+  // FIXME: support OS and environment correctly.
+  Triple::ArchType T = Ctx.getTargetInfo().getTriple().getArch();
+  if (T == Triple::thumb)
+    T = Triple::arm;
+  return Ctx.getTargetInfo().getTriple().getArchTypeName(T);
+}
+
+class MapFunctionNamesConsumer : public ASTConsumer {
+private:
+  ASTContext &Ctx;
+  ItaniumMangleContext *ItaniumCtx;
+  std::stringstream DefinedFuncsStr;
+  std::stringstream ExternFuncStr;
+  CallGraph CG;
+  const std::string Triple;
+
+public:
+  MapFunctionNamesConsumer(ASTContext &Context, ItaniumMangleContext *MangleCtx)
+      : Ctx(Context), ItaniumCtx(MangleCtx),
+        Triple(std::string("@") + getTripleSuffix(Context)) {}
+  std::string CurrentFileName;
+
+  ~MapFunctionNamesConsumer();
+  virtual void HandleTranslationUnit(ASTContext &Ctx) {
+    handleDecl(Ctx.getTranslationUnitDecl());
+  }
+
+private:
+  std::string getMangledName(const FunctionDecl *FD, MangleContext *Ctx);
+  std::string getMangledName(const FunctionDecl *FD) {
+    return getMangledName(FD, ItaniumCtx);
+  }
+
+  bool isCLibraryFunction(const FunctionDecl *FD);
+  void handleDecl(const Decl *D);
+
+  class WalkAST : public ConstStmtVisitor<WalkAST> {
+    MapFunctionNamesConsumer &Parent;
+    std::string CurrentFuncName;
+    MangleContext *MangleCtx;
+    const std::string Triple;
+
+  public:
+    WalkAST(MapFunctionNamesConsumer &parent, const std::string &FuncName,
+            MangleContext *Ctx, const std::string &triple)
+        : Parent(parent), CurrentFuncName(FuncName), MangleCtx(Ctx),
+          Triple(triple) {}
+    void VisitCallExpr(const CallExpr *CE);
+    void VisitStmt(const Stmt *S) { VisitChildren(S); }
+    void VisitChildren(const Stmt *S);
+  };
+};
+
+std::string MapFunctionNamesConsumer::getMangledName(const FunctionDecl *FD,
+                                                     MangleContext *Ctx) {
+  std::string MangledName;
+  llvm::raw_string_ostream os(MangledName);
+  if (const auto *CCD = dyn_cast<CXXConstructorDecl>(FD))
+    // FIXME: Use correct Ctor/DtorType.
+    Ctx->mangleCXXCtor(CCD, Ctor_Complete, os);
+  else if (const auto *CDD = dyn_cast<CXXDestructorDecl>(FD))
+    Ctx->mangleCXXDtor(CDD, Dtor_Complete, os);
+  else
+    Ctx->mangleName(FD, os);
+  os.flush();
+  return MangledName;
+}
+
+void MapFunctionNamesConsumer::handleDecl(const Decl *D) {
+  if (!D)
+    return;
+
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    if (const Stmt *Body = FD->getBody()) {
+      std::string MangledName = getMangledName(FD);
+      const SourceManager &SM = Ctx.getSourceManager();
+      if (CurrentFileName.empty()) {
+        StringRef SMgrName =
+            SM.getFileEntryForID(SM.getMainFileID())->getName();
+        char *Path = realpath(SMgrName.str().c_str(), nullptr);
+        CurrentFileName = Path;
+        free(Path);
+      }
+
+      std::string FileName =
+          std::string("/ast/") + getTripleSuffix(Ctx) + CurrentFileName;
+      std::string FullName = MangledName + Triple;
+
+      if (!FileName.empty())
+        switch (FD->getLinkageInternal()) {
+        case ExternalLinkage:
+        case VisibleNoLinkage:
+        case UniqueExternalLinkage:
+          if (SM.isInMainFile(Body->getLocStart()))
+            DefinedFuncsStr << "!";
+          DefinedFuncsStr << FullName << " " << FileName << "\n";
+        default:
+          break;
+        }
+
+      WalkAST Walker(*this, FullName, ItaniumCtx, Triple);
+      Walker.Visit(Body);
+    } else if (!FD->getBody() && !FD->getBuiltinID()) {
+      std::string MangledName = getMangledName(FD);
+      ExternFuncStr << MangledName << Triple << "\n";
+    }
+  }
+
+  if (const auto *DC = dyn_cast<DeclContext>(D))
+    for (const Decl *D : DC->decls())
+      handleDecl(D);
+}
+
+bool MapFunctionNamesConsumer::isCLibraryFunction(const FunctionDecl *FD) {
+  SourceManager &SM = Ctx.getSourceManager();
+  if (!FD)
+    return false;
+  SourceLocation Loc = FD->getLocation();
+  if (Loc.isValid())
+    return SM.isInSystemHeader(Loc);
+  return true;
+}
+
+MapFunctionNamesConsumer::~MapFunctionNamesConsumer() {
+  // Flush results to files.
+  std::string BuildDir = XTUDir;
+  lockedWrite(BuildDir + "/externalFns.txt", ExternFuncStr.str());
+  lockedWrite(BuildDir + "/definedFns.txt", DefinedFuncsStr.str());
+  std::stringstream CFGStr;
+  for (auto &Entry : CG) {
+    CFGStr << CurrentFileName << Triple << "::" << Entry.getKey().data();
+    for (auto &E : Entry.getValue())
+      CFGStr << ' ' << E.getKey().data();
+    CFGStr << '\n';
+  }
+
+  lockedWrite(BuildDir + "/cfg.txt", CFGStr.str());
+}
+
+void MapFunctionNamesConsumer::WalkAST::VisitChildren(const Stmt *S) {
+  for (const Stmt *CS : S->children())
+    if (CS)
+      Visit(CS);
+}
+
+void MapFunctionNamesConsumer::WalkAST::VisitCallExpr(const CallExpr *CE) {
+  const auto *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
+  if (FD && !FD->getBuiltinID()) {
+    std::string FuncName = (FD->hasBody() ? "::" : "") +
+                           Parent.getMangledName(FD, MangleCtx) + Triple;
+    Parent.CG[CurrentFuncName].insert(FuncName);
+  }
+  VisitChildren(CE);
+}
+
+class MapFunctionNamesAction : public ASTFrontendAction {
+protected:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 llvm::StringRef) {
+    ItaniumMangleContext *ItaniumCtx =
+        ItaniumMangleContext::create(CI.getASTContext(), CI.getDiagnostics());
+    ItaniumCtx->setShouldForceMangleProto(true);
+    std::unique_ptr<ASTConsumer> PFC(
+        new MapFunctionNamesConsumer(CI.getASTContext(), ItaniumCtx));
+    return PFC;
+  }
+};
+
+int main(int argc, const char **argv) {
+  // Print a stack trace if we signal out.
+  sys::PrintStackTraceOnErrorSignal(argv[0], false);
+  PrettyStackTraceProgram X(argc, argv);
+
+  SmallVector<std::string, 4> Sources;
+  CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory,
+                                    cl::ZeroOrMore);
+
+  if (XTUDir.getNumOccurrences() != 1) {
+    errs() << "Exactly one XTU dir should be provided\n";
+    return 1;
+  }
+  const StringRef cppFile = ".cpp", ccFile = ".cc", cFile = ".c",
+                  cxxFile = ".cxx";
+  for (int i = 1; i < argc; i++) {
+    StringRef arg = argv[i];
+    if (arg.endswith(cppFile) || arg.endswith(ccFile) || arg.endswith(cFile) ||
+        arg.endswith(cxxFile)) {
+      Sources.push_back(arg);
+    }
+  }
+  ClangTool Tool(OptionsParser.getCompilations(), Sources);
+  Tool.run(newFrontendActionFactory<MapFunctionNamesAction>().get());
+}
Index: tools/clang-func-mapping/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/clang-func-mapping/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  asmparser
+  support
+  mc
+  )
+
+add_clang_executable(clang-func-mapping
+  ClangFnMapGen.cpp
+  )
+
+target_link_libraries(clang-func-mapping
+  clangTooling
+  clangAST
+  clangBasic
+  clangFrontend
+  clangRewriteFrontend
+  )
+
+install(TARGETS clang-func-mapping
+  RUNTIME DESTINATION bin)
Index: tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
===================================================================
--- /dev/null
+++ tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
@@ -0,0 +1,73 @@
+//===- ClangCmdlineArchExtractor.cpp ------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// Clang tool which prints architecture type for a given command line.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+
+static std::string getTripleSuffix(const Triple &Triple) {
+  // We are not going to support vendor and don't support OS and environment.
+  // FIXME: support OS and environment correctly
+  Triple::ArchType T = Triple.getArch();
+  if (T == Triple::thumb)
+    T = Triple::arm;
+  return Triple.getArchTypeName(T);
+}
+
+int main(int argc, const char **argv) {
+  // Print a stack trace if we signal out.
+  sys::PrintStackTraceOnErrorSignal(argv[0], true);
+  PrettyStackTraceProgram X(argc, argv);
+
+  SmallVector<StringRef, 4> Sources;
+  std::vector<const char*> Args;
+  const StringRef cppFile = ".cpp", ccFile = ".cc", cFile = ".c",
+                  cxxFile = ".cxx";
+  for (int i = 1; i < argc; i++) {
+    StringRef Arg = argv[i];
+    if (Arg.endswith(cppFile) || Arg.endswith(ccFile) || Arg.endswith(cFile) ||
+        Arg.endswith(cxxFile)) {
+      Sources.push_back(argv[i]);
+    } else {
+      Args.push_back(argv[i]);
+    }
+  }
+
+  if (Sources.empty())
+    return 1;
+
+  Args.push_back(Sources[0].data());
+  std::unique_ptr<CompilerInvocation> CI(
+      createInvocationFromCommandLine(Args));
+
+  const std::string Suffix =
+      "@" + getTripleSuffix(llvm::Triple(CI->getTargetOpts().Triple));
+
+  for (StringRef SourceFile : Sources) {
+    char *Path = realpath(SourceFile.data(), nullptr);
+    if (Path)
+      outs() << Path << Suffix << " ";
+    free(Path);
+  }
+
+  return 0;
+}
Index: tools/clang-cmdline-arch-extractor/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/clang-cmdline-arch-extractor/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+  ${LLVM_TARGETS_TO_BUILD}
+  asmparser
+  support
+  mc
+  )
+
+add_clang_executable(clang-cmdline-arch-extractor
+  ClangCmdlineArchExtractor.cpp
+  )
+
+target_link_libraries(clang-cmdline-arch-extractor
+  clangTooling
+  clangBasic
+  clangFrontend
+  clangRewriteFrontend
+  )
+
+install(TARGETS clang-cmdline-arch-extractor
+  RUNTIME DESTINATION bin)
Index: tools/CMakeLists.txt
===================================================================
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -19,6 +19,8 @@
   add_clang_subdirectory(clang-check)
   add_clang_subdirectory(scan-build)
   add_clang_subdirectory(scan-view)
+  add_clang_subdirectory(clang-func-mapping)
+  add_clang_subdirectory(clang-cmdline-arch-extractor)
 endif()
 
 # We support checking out the clang-tools-extra repository into the 'extra'
Index: test/Analysis/xtu-main.cpp
===================================================================
--- /dev/null
+++ test/Analysis/xtu-main.cpp
@@ -0,0 +1,58 @@
+// RUN: mkdir -p %T/xtudir
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/xtudir/xtu-other.cpp.ast %S/Inputs/xtu-other.cpp
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/xtudir/xtu-chain.cpp.ast %S/Inputs/xtu-chain.cpp
+// RUN: cp %S/Inputs/externalFnMap.txt %T/xtudir/
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config xtu-dir=%T/xtudir -analyzer-config reanalyze-xtu-visited=true -verify %s
+
+void clang_analyzer_eval(int);
+
+int f(int);
+int g(int);
+int h(int);
+
+int callback_to_main(int x) { return x + 1; }
+
+namespace myns {
+int fns(int x);
+
+namespace embed_ns {
+int fens(int x);
+}
+
+class embed_cls {
+public:
+  int fecl(int x);
+};
+}
+
+class mycls {
+public:
+  int fcl(int x);
+  static int fscl(int x);
+
+  class embed_cls2 {
+  public:
+    int fecl2(int x);
+  };
+};
+
+namespace chns {
+int chf1(int x);
+}
+
+int main() {
+  clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}}
+  clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}}
+  clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}}
+  clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}}
+  clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(myns::fns(2) == 9);                   // expected-warning{{TRUE}}
+  clang_analyzer_eval(myns::embed_ns::fens(2) == -1);       // expected-warning{{TRUE}}
+  clang_analyzer_eval(mycls().fcl(1) == 6);                 // expected-warning{{TRUE}}
+  clang_analyzer_eval(mycls::fscl(1) == 7);                 // expected-warning{{TRUE}}
+  clang_analyzer_eval(myns::embed_cls().fecl(1) == -6);     // expected-warning{{TRUE}}
+  clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}}
+
+  clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}}
+}
Index: test/Analysis/Inputs/xtu-other.cpp
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/xtu-other.cpp
@@ -0,0 +1,67 @@
+int callback_to_main(int x);
+int f(int x) {
+  return x - 1;
+}
+
+int g(int x) {
+  return callback_to_main(x) + 1;
+}
+
+int h_chain(int);
+
+int h(int x) {
+  return 2 * h_chain(x);
+}
+
+namespace myns {
+int fns(int x) {
+  return x + 7;
+}
+
+namespace embed_ns {
+int fens(int x) {
+  return x - 3;
+}
+}
+
+class embed_cls {
+public:
+  int fecl(int x) {
+    return x - 7;
+  }
+};
+}
+
+class mycls {
+public:
+  int fcl(int x) {
+    return x + 5;
+  }
+  static int fscl(int x) {
+    return x + 6;
+  }
+
+  class embed_cls2 {
+  public:
+    int fecl2(int x) {
+      return x - 11;
+    }
+  };
+};
+
+namespace chns {
+int chf2(int x);
+
+class chcls {
+public:
+  int chf4(int x);
+};
+
+int chf3(int x) {
+  return chcls().chf4(x);
+}
+
+int chf1(int x) {
+  return chf2(x);
+}
+}
Index: test/Analysis/Inputs/xtu-chain.cpp
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/xtu-chain.cpp
@@ -0,0 +1,20 @@
+int h_chain(int x) {
+  return x * 2;
+}
+
+namespace chns {
+int chf3(int x);
+
+int chf2(int x) {
+  return chf3(x);
+}
+
+class chcls {
+public:
+  int chf4(int x);
+};
+
+int chcls::chf4(int x) {
+  return x * 3;
+}
+}
Index: test/Analysis/Inputs/externalFnMap.txt
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/externalFnMap.txt
@@ -0,0 +1,14 @@
+_Z7h_chaini@x86_64 xtu-chain.cpp.ast
+_ZN4chns4chf2Ei@x86_64 xtu-chain.cpp.ast
+_ZN4chns5chcls4chf4Ei@x86_64 xtu-chain.cpp.ast
+_Z1fi@x86_64 xtu-other.cpp.ast
+_Z1gi@x86_64 xtu-other.cpp.ast
+_Z1hi@x86_64 xtu-other.cpp.ast
+_ZN4myns9embed_cls4feclEi@x86_64 xtu-other.cpp.ast
+_ZN4myns3fnsEi@x86_64 xtu-other.cpp.ast
+_ZN5mycls10embed_cls25fecl2Ei@x86_64 xtu-other.cpp.ast
+_ZN5mycls4fsclEi@x86_64 xtu-other.cpp.ast
+_ZN4chns4chf3Ei@x86_64 xtu-other.cpp.ast
+_ZN5mycls3fclEi@x86_64 xtu-other.cpp.ast
+_ZN4chns4chf1Ei@x86_64 xtu-other.cpp.ast
+_ZN4myns8embed_ns4fensEi@x86_64 xtu-other.cpp.ast
Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -40,9 +40,17 @@
 #include "llvm/Support/Program.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
+#include <assert.h>
 #include <memory>
 #include <queue>
 #include <utility>
+#include <sys/file.h>
+#include <unistd.h>
+#include <fstream>
+#include <time.h>
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Basic/TargetInfo.h"
 
 using namespace clang;
 using namespace ento;
@@ -168,6 +176,7 @@
   AnalyzerOptionsRef Opts;
   ArrayRef<std::string> Plugins;
   CodeInjector *Injector;
+  CompilerInstance &CI;
 
   /// \brief Stores the declarations from the local translation unit.
   /// Note, we pre-compute the local declarations at parse time as an
@@ -192,12 +201,12 @@
   /// translation unit.
   FunctionSummariesTy FunctionSummaries;
 
-  AnalysisConsumer(const Preprocessor &pp, const std::string &outdir,
-                   AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
-                   CodeInjector *injector)
+  AnalysisConsumer(CompilerInstance &CI, const Preprocessor &pp,
+                   const std::string &outdir, AnalyzerOptionsRef opts,
+                   ArrayRef<std::string> plugins, CodeInjector *injector)
       : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
         OutDir(outdir), Opts(std::move(opts)), Plugins(plugins),
-        Injector(injector) {
+        Injector(injector), CI(CI) {
     DigestAnalyzerOptions();
     if (Opts->PrintStats) {
       llvm::EnableStatistics(false);
@@ -415,6 +424,21 @@
   }
 }
 
+extern std::string getMangledName(const NamedDecl *ND,
+                                  MangleContext *MangleCtx);
+
+void lockedWrite(const std::string &fileName, const std::string &content) {
+  if (content.empty()) 
+    return;
+  int fd = open(fileName.c_str(), O_CREAT|O_WRONLY|O_APPEND, 0666);
+  flock(fd, LOCK_EX);
+  ssize_t written = write(fd, content.c_str(), content.length());
+  assert(written == static_cast<ssize_t>(content.length()));
+  (void)written;
+  flock(fd, LOCK_UN);
+  close(fd);
+}
+
 static bool shouldSkipFunction(const Decl *D,
                                const SetOfConstDecls &Visited,
                                const SetOfConstDecls &VisitedAsTopLevel) {
@@ -706,7 +730,8 @@
   if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
     return;
 
-  ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
+  ExprEngine Eng(CI, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,
+                 IMode);
 
   // Set the graph auditor.
   std::unique_ptr<ExplodedNode::Auditor> Auditor;
@@ -764,7 +789,7 @@
   bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
 
   return llvm::make_unique<AnalysisConsumer>(
-      CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
+      CI, CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
       CI.getFrontendOpts().Plugins,
       hasModelPath ? new ModelInjector(CI) : nullptr);
 }
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -67,25 +67,19 @@
 
 static const char* TagProviderName = "ExprEngine";
 
-ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
-                       SetOfConstDecls *VisitedCalleesIn,
-                       FunctionSummariesTy *FS,
-                       InliningModes HowToInlineIn)
-  : AMgr(mgr),
-    AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
-    Engine(*this, FS),
-    G(Engine.getGraph()),
-    StateMgr(getContext(), mgr.getStoreManagerCreator(),
-             mgr.getConstraintManagerCreator(), G.getAllocator(),
-             this),
-    SymMgr(StateMgr.getSymbolManager()),
-    svalBuilder(StateMgr.getSValBuilder()),
-    currStmtIdx(0), currBldrCtx(nullptr),
-    ObjCNoRet(mgr.getASTContext()),
-    ObjCGCEnabled(gcEnabled), BR(mgr, *this),
-    VisitedCallees(VisitedCalleesIn),
-    HowToInline(HowToInlineIn)
-{
+ExprEngine::ExprEngine(CompilerInstance &CI, AnalysisManager &mgr,
+                       bool gcEnabled, SetOfConstDecls *VisitedCalleesIn,
+                       FunctionSummariesTy *FS, InliningModes HowToInlineIn)
+    : CI(CI), AMgr(mgr),
+      AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+      Engine(*this, FS), G(Engine.getGraph()),
+      StateMgr(getContext(), mgr.getStoreManagerCreator(),
+               mgr.getConstraintManagerCreator(), G.getAllocator(), this),
+      SymMgr(StateMgr.getSymbolManager()),
+      svalBuilder(StateMgr.getSValBuilder()), currStmtIdx(0),
+      currBldrCtx(nullptr), ObjCNoRet(mgr.getASTContext()),
+      ObjCGCEnabled(gcEnabled), BR(mgr, *this),
+      VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
   unsigned TrimInterval = mgr.options.getGraphTrimInterval();
   if (TrimInterval != 0) {
     // Enable eager node reclaimation when constructing the ExplodedGraph.
Index: lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CallEvent.cpp
+++ lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -16,6 +16,9 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/AST/ParentMap.h"
 #include "clang/Analysis/ProgramPoint.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
 #include "llvm/ADT/SmallSet.h"
@@ -25,6 +28,10 @@
 using namespace clang;
 using namespace ento;
 
+CallEvent::FileASTUnitMapping CallEvent::FileASTUnitMap;
+CallEvent::FunctionAstUnitMapping CallEvent::FunctionAstUnitMap;
+CallEvent::FunctionFileMapping CallEvent::FunctionFileMap;
+
 QualType CallEvent::getResultType() const {
   const Expr *E = getOriginExpr();
   assert(E && "Calls without origin expressions do not have results");
@@ -355,6 +362,44 @@
                                D->parameters());
 }
 
+RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
+  const FunctionDecl *FD = getDecl();
+  // Note that the AnalysisDeclContext will have the FunctionDecl with
+  // the definition (if one exists).
+  if (!FD)
+    return RuntimeDefinition();
+
+  AnalysisDeclContext *AD =
+    getLocationContext()->getAnalysisDeclContext()->
+    getManager()->getContext(FD);
+  if (AD->getBody())
+    return RuntimeDefinition(AD->getDecl());
+
+  auto Engine = static_cast<ExprEngine *>(
+      getState()->getStateManager().getOwningEngine());
+  CompilerInstance &CI = Engine->getCompilerInstance();
+
+  auto ASTLoader = [&](StringRef ASTFileName) {
+    IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+    TextDiagnosticPrinter *DiagClient =
+        new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
+    IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+    IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+        new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
+    return ASTUnit::LoadFromASTFile(
+               ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
+               Diags, CI.getFileSystemOpts())
+        .release();
+  };
+
+  const FunctionDecl *XTUDecl = AD->getASTContext().getXTUDefinition(
+      FD, CI, Engine->getAnalysisManager().options.getXTUDir(),
+      CI.getDiagnostics(), ASTLoader);
+
+  return RuntimeDefinition(XTUDecl);
+}
+
+
 bool AnyFunctionCall::argumentsMayEscape() const {
   if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg())
     return true;
Index: lib/StaticAnalyzer/Core/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Core/CMakeLists.txt
+++ lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -48,6 +48,7 @@
   clangAST
   clangAnalysis
   clangBasic
+  clangFrontend
   clangLex
   clangRewrite
   )
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -370,3 +370,10 @@
         getBooleanOption("notes-as-events", /*Default=*/false);
   return DisplayNotesAsEvents.getValue();
 }
+
+StringRef AnalyzerOptions::getXTUDir() {
+  if (!XTUDir.hasValue())
+    XTUDir = getOptionAsString("xtu-dir", "");
+  return XTUDir.getValue();
+}
+
Index: lib/Basic/SourceManager.cpp
===================================================================
--- lib/Basic/SourceManager.cpp
+++ lib/Basic/SourceManager.cpp
@@ -2025,6 +2025,8 @@
 }
 
 /// \brief Determines the order of 2 source locations in the translation unit.
+/// FIXME: It also works when two locations are from different translation unit.
+///        In that case it will return *some* order.
 ///
 /// \returns true if LHS source location comes before RHS, false otherwise.
 bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
@@ -2121,7 +2123,8 @@
       return LIsScratch;
     return LOffs.second < ROffs.second;
   }
-  llvm_unreachable("Unsortable locations found");
+  // FIXME: Source locations from different translation unit.
+  return LOffs.first < ROffs.first;
 }
 
 void SourceManager::PrintStats() const {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -79,6 +79,17 @@
     if (FD->isExternC())
       return FD->getASTContext().getTranslationUnitDecl();
 
+  // Avoid infinite recursion with code like:
+  //   void f(struct S* p);
+  // Where this is the first declaration of S. This is only valid for C.
+  // For some tools it makes sense to mangle C functions (e.g. avoid collisions
+  // when indexing). It might be nicer to check whether the Decl is in
+  // FunctionPrototypeScope, but this information is lost after the Sema is
+  // done.
+  if (!D->getASTContext().getLangOpts().CPlusPlus && DC->isFunctionOrMethod() &&
+      isa<TagDecl>(D))
+    return D->getASTContext().getTranslationUnitDecl();
+
   return DC->getRedeclContext();
 }
 
@@ -653,9 +664,11 @@
   // <encoding> ::= <function name> <bare-function-type>
 
   // Don't mangle in the type if this isn't a decl we should typically mangle.
-  if (!Context.shouldMangleDeclName(FD)) {
-    mangleName(FD);
-    return;
+  if (!Context.shouldMangleDeclName(FD) &&
+      !(Context.shouldForceMangleProto() &&
+        FD->getType()->getAs<FunctionProtoType>())){
+	 mangleName(FD);
+     return;
   }
 
   AbiTagList ReturnTypeAbiTags = makeFunctionReturnTypeTags(FD);
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -3142,6 +3142,7 @@
   DeclarationName Name;
   SourceLocation Loc;
   NamedDecl *ToD;
+
   if (ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc))
     return nullptr;
   if (ToD)
@@ -3206,6 +3207,8 @@
   if (ToD)
     return ToD;
 
+  const FunctionDecl *FoundWithoutBody = nullptr;
+
   // Try to find a function in our own ("to") context with the same name, same
   // type, and in the same context as the function we're importing.
   if (!LexicalDC->isFunctionOrMethod()) {
@@ -3217,12 +3220,19 @@
       if (!FoundDecls[I]->isInIdentifierNamespace(IDNS))
         continue;
     
-      if (FunctionDecl *FoundFunction = dyn_cast<FunctionDecl>(FoundDecls[I])) {
+      if (auto *FoundFunction = dyn_cast<FunctionDecl>(FoundDecls[I])) {
         if (FoundFunction->hasExternalFormalLinkage() &&
             D->hasExternalFormalLinkage()) {
           if (Importer.IsStructurallyEquivalent(D->getType(), 
                                                 FoundFunction->getType())) {
             // FIXME: Actually try to merge the body and other attributes.
+            const FunctionDecl *FromBodyDecl = nullptr;
+            D->hasBody(FromBodyDecl);
+            if (D == FromBodyDecl && !FoundFunction->hasBody()) {
+              // This function is needed to merge completely.
+              FoundWithoutBody = FoundFunction;
+              break;
+            }
             return Importer.Imported(D, FoundFunction);
           }
         
@@ -3261,8 +3271,7 @@
   QualType FromTy = D->getType();
   bool usedDifferentExceptionSpec = false;
 
-  if (const FunctionProtoType *
-        FromFPT = D->getType()->getAs<FunctionProtoType>()) {
+  if (const auto *FromFPT = D->getType()->getAs<FunctionProtoType>()) {
     FunctionProtoType::ExtProtoInfo FromEPI = FromFPT->getExtProtoInfo();
     // FunctionProtoType::ExtProtoInfo's ExceptionSpecDecl can point to the
     // FunctionDecl that we are importing the FunctionProtoType for.
@@ -3297,7 +3306,7 @@
   TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo());
   FunctionDecl *ToFunction = nullptr;
   SourceLocation InnerLocStart = Importer.Import(D->getInnerLocStart());
-  if (CXXConstructorDecl *FromConstructor = dyn_cast<CXXConstructorDecl>(D)) {
+  if (auto *FromConstructor = dyn_cast<CXXConstructorDecl>(D)) {
     ToFunction = CXXConstructorDecl::Create(Importer.getToContext(),
                                             cast<CXXRecordDecl>(DC),
                                             InnerLocStart,
@@ -3329,17 +3338,16 @@
                                            NameInfo, T, TInfo,
                                            D->isInlineSpecified(),
                                            D->isImplicit());
-  } else if (CXXConversionDecl *FromConversion
-                                           = dyn_cast<CXXConversionDecl>(D)) {
+  } else if (auto *FromConversion = dyn_cast<CXXConversionDecl>(D)) {
     ToFunction = CXXConversionDecl::Create(Importer.getToContext(), 
                                            cast<CXXRecordDecl>(DC),
                                            InnerLocStart,
                                            NameInfo, T, TInfo,
                                            D->isInlineSpecified(),
                                            FromConversion->isExplicit(),
                                            D->isConstexpr(),
                                            Importer.Import(D->getLocEnd()));
-  } else if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
+  } else if (auto *Method = dyn_cast<CXXMethodDecl>(D)) {
     ToFunction = CXXMethodDecl::Create(Importer.getToContext(), 
                                        cast<CXXRecordDecl>(DC),
                                        InnerLocStart,
@@ -3373,6 +3381,12 @@
   }
   ToFunction->setParams(Parameters);
 
+  if (FoundWithoutBody) {
+    auto *Recent = const_cast<FunctionDecl *>(
+          FoundWithoutBody->getMostRecentDecl());
+    ToFunction->setPreviousDecl(Recent);
+  }
+
   if (usedDifferentExceptionSpec) {
     // Update FunctionProtoType::ExtProtoInfo.
     QualType T = Importer.Import(D->getType());
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/AST/ASTContext.h"
 #include "CXXABI.h"
+#include "clang/AST/ASTImporter.h"
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/CharUnits.h"
@@ -34,11 +35,15 @@
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Support/Capacity.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include <fstream>
 #include <map>
 
 using namespace clang;
@@ -1418,6 +1423,136 @@
   }
 }
 
+
+//===----------------------------------------------------------------------===//
+//                         Cross-translation unit support
+//===----------------------------------------------------------------------===//
+
+std::string getMangledName(const NamedDecl *ND, MangleContext *MangleCtx) {
+  std::string MangledName;
+  llvm::raw_string_ostream OS(MangledName);
+  if (const auto *CCD = dyn_cast<CXXConstructorDecl>(ND))
+    // FIXME: Use correct Ctor/DtorType
+    MangleCtx->mangleCXXCtor(CCD, Ctor_Complete, OS);
+  else if (const auto *CDD = dyn_cast<CXXDestructorDecl>(ND))
+    MangleCtx->mangleCXXDtor(CDD, Dtor_Complete, OS);
+  else
+    MangleCtx->mangleName(ND, OS);
+  ASTContext &Ctx = ND->getASTContext();
+  // We are not going to support vendor and don't support OS and environment.
+  // FIXME: support OS and environment correctly
+  llvm::Triple::ArchType T = Ctx.getTargetInfo().getTriple().getArch();
+  if (T == llvm::Triple::thumb)
+    T = llvm::Triple::arm;
+  OS << "@" << Ctx.getTargetInfo().getTriple().getArchTypeName(T);
+  return OS.str();
+}
+
+const FunctionDecl* iterateContextDecls(const DeclContext *DC,
+                                const std::string &MangledFnName,
+                                std::unique_ptr<MangleContext> &MangleCtx) {
+  //FIXME: Use ASTMatcher instead.
+  if (!DC)
+    return nullptr;
+  for (Decl *D : DC->decls()) {
+    const auto *SubDC = dyn_cast<DeclContext>(D);
+    if (const auto *FD = iterateContextDecls(SubDC, MangledFnName, MangleCtx))
+      return FD;
+
+    const auto *ND = dyn_cast<FunctionDecl>(D);
+    const FunctionDecl *ResultDecl;
+    if (!ND || !ND->hasBody(ResultDecl)) {
+      continue;
+    }
+    std::string LookupMangledName = getMangledName(ResultDecl, MangleCtx.get());
+    // We are already sure that the triple is correct here.
+    if (LookupMangledName != MangledFnName)
+      continue;
+    return ResultDecl;
+  }
+  return nullptr;
+}
+
+const FunctionDecl *
+ASTContext::getXTUDefinition(const FunctionDecl *FD, CompilerInstance &CI,
+                             StringRef XTUDir, DiagnosticsEngine &Diags,
+                             std::function<ASTUnit *(StringRef)> Loader) {
+  assert(!FD->hasBody() && "FD has a definition in current translation unit!");
+  if (!FD->getType()->getAs<FunctionProtoType>())
+    return nullptr; // Cannot even mangle that.
+  ImportMapping::const_iterator FoundImport = ImportMap.find(FD);
+  if (FoundImport != ImportMap.end())
+    return FoundImport->second;
+
+  std::unique_ptr<MangleContext> MangleCtx(
+      ItaniumMangleContext::create(FD->getASTContext(), Diags));
+  MangleCtx->setShouldForceMangleProto(true);
+  std::string MangledFnName = getMangledName(FD, MangleCtx.get());
+  std::string ExternalFunctionMap = (XTUDir + "/externalFnMap.txt").str();
+  ASTUnit *Unit = nullptr;
+  StringRef ASTFileName;
+  FunctionAstUnitMapping::const_iterator FnUnitCacheEntry =
+      FunctionAstUnitMap.find(MangledFnName);
+  if (FnUnitCacheEntry == FunctionAstUnitMap.end()) {
+    if (FunctionFileMap.empty()) {
+      std::ifstream ExternalFnMapFile(ExternalFunctionMap);
+      std::string FunctionName, FileName;
+      while (ExternalFnMapFile >> FunctionName >> FileName)
+        FunctionFileMap[FunctionName] = (XTUDir + "/" + FileName).str();
+      ExternalFnMapFile.close();
+    }
+
+    FunctionFileMapping::iterator it = FunctionFileMap.find(MangledFnName);
+    if (it != FunctionFileMap.end())
+      ASTFileName = it->second;
+    else // No definition found even in some other build unit.
+      return nullptr;
+    FileASTUnitMapping::iterator ASTCacheEntry =
+        FileASTUnitMap.find(ASTFileName);
+    if (ASTCacheEntry == FileASTUnitMap.end()) {
+      Unit = Loader(ASTFileName);
+      FileASTUnitMap[ASTFileName] = Unit;
+      FunctionAstUnitMap[MangledFnName] = Unit;
+    } else {
+      Unit = ASTCacheEntry->second;
+      FunctionAstUnitMap[MangledFnName] = Unit;
+    }
+  } else {
+    Unit = FnUnitCacheEntry->second;
+  }
+
+  if (!Unit)
+    return nullptr;
+  assert(&Unit->getFileManager() ==
+         &Unit->getASTContext().getSourceManager().getFileManager());
+  ASTImporter &Importer = getOrCreateASTImporter(Unit->getASTContext());
+  TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
+  if (const FunctionDecl *ResultDecl =
+            iterateContextDecls(TU, MangledFnName, MangleCtx)) {
+    llvm::errs() << "Importing function " << MangledFnName << " from "
+                 << ASTFileName << "\n";
+    // FIXME: Refactor const_cast
+    auto *ToDecl = cast<FunctionDecl>(
+        Importer.Import(const_cast<FunctionDecl *>(ResultDecl)));
+    assert(ToDecl->hasBody());
+    ImportMap[FD] = ToDecl;
+    return ToDecl;
+  }
+  return nullptr;
+}
+
+ASTImporter &ASTContext::getOrCreateASTImporter(ASTContext &From) {
+  ASTUnitImporterMapping::iterator I = ASTUnitImporterMap.find(
+        From.getTranslationUnitDecl());
+  if (I != ASTUnitImporterMap.end())
+    return *I->second;
+  ASTImporter *NewImporter = new ASTImporter(
+        *this, getSourceManager().getFileManager(),
+        From, From.getSourceManager().getFileManager(), false);
+  ASTUnitImporterMap[From.getTranslationUnitDecl()] = NewImporter;
+  return *NewImporter;
+}
+
 CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const {
   unsigned Align = Target->getCharWidth();
 
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -56,6 +56,8 @@
   };
 
 private:
+  CompilerInstance &CI;
+
   AnalysisManager &AMgr;
   
   AnalysisDeclContextManager &AnalysisDeclContexts;
@@ -97,9 +99,8 @@
   InliningModes HowToInline;
 
 public:
-  ExprEngine(AnalysisManager &mgr, bool gcEnabled,
-             SetOfConstDecls *VisitedCalleesIn,
-             FunctionSummariesTy *FS,
+  ExprEngine(CompilerInstance &CI, AnalysisManager &mgr, bool gcEnabled,
+             SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS,
              InliningModes HowToInlineIn);
 
   ~ExprEngine() override;
@@ -132,6 +133,8 @@
 
   BugReporter& getBugReporter() { return BR; }
 
+  CompilerInstance &getCompilerInstance() { return CI; }
+
   const NodeBuilderContext &getBuilderContext() {
     assert(currBldrCtx);
     return *currBldrCtx;
Index: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -165,6 +165,10 @@
   void Release() const;
 
 protected:
+  typedef llvm::StringMap<std::string> FunctionFileMapping;
+  typedef llvm::StringMap<clang::ASTUnit*> FunctionAstUnitMapping;
+  typedef llvm::StringMap<clang::ASTUnit*> FileASTUnitMapping;
+
   friend class CallEventManager;
 
   CallEvent(const Expr *E, ProgramStateRef state, const LocationContext *lctx)
@@ -384,6 +388,11 @@
   // Iterator access to formal parameters and their types.
 private:
   typedef std::const_mem_fun_t<QualType, ParmVarDecl> get_type_fun;
+  
+protected:
+  static FileASTUnitMapping FileASTUnitMap;
+  static FunctionAstUnitMapping FunctionAstUnitMap;
+  static FunctionFileMapping FunctionFileMap;
 
 public:
   /// Return call's formal parameters.
@@ -436,20 +445,7 @@
     return cast<FunctionDecl>(CallEvent::getDecl());
   }
 
-  RuntimeDefinition getRuntimeDefinition() const override {
-    const FunctionDecl *FD = getDecl();
-    // Note that the AnalysisDeclContext will have the FunctionDecl with
-    // the definition (if one exists).
-    if (FD) {
-      AnalysisDeclContext *AD =
-        getLocationContext()->getAnalysisDeclContext()->
-        getManager()->getContext(FD);
-      if (AD->getBody())
-        return RuntimeDefinition(AD->getDecl());
-    }
-
-    return RuntimeDefinition();
-  }
+  RuntimeDefinition getRuntimeDefinition() const override;
 
   bool argumentsMayEscape() const override;
 
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -272,6 +272,9 @@
   /// \sa shouldDisplayNotesAsEvents
   Optional<bool> DisplayNotesAsEvents;
 
+  /// \sa getXTUDir
+  Optional<StringRef> XTUDir;
+
   /// A helper function that retrieves option for a given full-qualified
   /// checker name.
   /// Options for checkers can be specified via 'analyzer-config' command-line
@@ -548,6 +551,9 @@
   /// to false when unset.
   bool shouldDisplayNotesAsEvents();
 
+  /// Returns the directory containing the XTU related files.
+  StringRef getXTUDir();
+
 public:
   AnalyzerOptions() :
     AnalysisStoreOpt(RegionStoreModel),
Index: include/clang/AST/Mangle.h
===================================================================
--- include/clang/AST/Mangle.h
+++ include/clang/AST/Mangle.h
@@ -52,6 +52,11 @@
   ASTContext &Context;
   DiagnosticsEngine &Diags;
   const ManglerKind Kind;
+  // Used for cross tranlsation unit analysis.
+  // To reduce the risk of function name collision in C projects, we force
+  // name mangling for C functions when generating lookup identifiers for
+  // the static analyzer.
+  bool ShouldForceMangleProto;
 
   llvm::DenseMap<const BlockDecl*, unsigned> GlobalBlockIds;
   llvm::DenseMap<const BlockDecl*, unsigned> LocalBlockIds;
@@ -87,6 +92,11 @@
     return Result.first->second;
   }
 
+  bool shouldForceMangleProto() const { return ShouldForceMangleProto; }
+  void setShouldForceMangleProto(bool ForceMangleArguments) {
+    ShouldForceMangleProto = ForceMangleArguments;
+  }
+
   /// @name Mangler Entry Points
   /// @{
 
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -50,6 +50,7 @@
 class TypeLoc;
 class UnresolvedSetImpl;
 class VarTemplateDecl;
+class CompilerInstance;
 
 /// \brief A container of type source information.
 ///
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -39,6 +39,7 @@
 #include "clang/Basic/SanitizerBlacklist.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/VersionTuple.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
@@ -62,6 +63,7 @@
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
+#include <functional>
 #include <iterator>
 #include <memory>
 #include <new>
@@ -77,12 +79,14 @@
 } // end namespace llvm
 
 namespace clang {
-
+class ASTImporter;
 class ASTMutationListener;
 class ASTRecordLayout;
+class ASTUnit;
 class AtomicExpr;
 class BlockExpr;
 class CharUnits;
+class CompilerInstance;
 class CXXABI;
 class DiagnosticsEngine;
 class Expr;
@@ -1893,6 +1897,28 @@
   }
 
   //===--------------------------------------------------------------------===//
+  //                         Cross-translation unit support
+  //===--------------------------------------------------------------------===//
+private:
+  typedef llvm::StringMap<std::string> FunctionFileMapping;
+  typedef llvm::StringMap<clang::ASTUnit *> FunctionAstUnitMapping;
+  typedef llvm::StringMap<clang::ASTUnit *> FileASTUnitMapping;
+  typedef std::map<TranslationUnitDecl *, ASTImporter *> ASTUnitImporterMapping;
+  typedef std::map<const FunctionDecl *, const FunctionDecl *> ImportMapping;
+  FileASTUnitMapping FileASTUnitMap;
+  FunctionAstUnitMapping FunctionAstUnitMap;
+  FunctionFileMapping FunctionFileMap;
+  ASTUnitImporterMapping ASTUnitImporterMap;
+  ImportMapping ImportMap;
+  ASTImporter &getOrCreateASTImporter(ASTContext &From);
+
+public:
+  const FunctionDecl *
+  getXTUDefinition(const FunctionDecl *FD, CompilerInstance &CI,
+                   StringRef XTUDir, DiagnosticsEngine &Diags,
+                   std::function<ASTUnit *(StringRef)> Loader);
+
+  //===--------------------------------------------------------------------===//
   //                         Type Sizing and Analysis
   //===--------------------------------------------------------------------===//
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to