blob: 31c2f7c5a6c3b7f9dafc1fa5ceaf185e091ea6f0 [file] [log] [blame]
#!/bin/bash
##############################################################################
# Build LLVM code analyzer and analyze torch code dependency.
##############################################################################
#
# Example usage:
#
# 1. Analyze torch and generate yaml file of op dependency transitive closure:
# LLVM_DIR=${HOME}/src/llvm8/build/install \
# ANALYZE_TORCH=1 tools/code_analyzer/build.sh
#
# 2. Analyze test project and compare with expected result:
# LLVM_DIR=${HOME}/src/llvm8/build/install \
# ANALYZE_TEST=1 tools/code_analyzer/build.sh
#
# 3. Analyze torch and generate yaml file of op dependency with debug path:
# LLVM_DIR=${HOME}/src/llvm8/build/install \
# ANALYZE_TORCH=1 tools/code_analyzer/build.sh -debug_path=true
#
# If you're a Facebook employee, chances are you're running on CentOS 8.
# If that's the case, you can install all the dependencies you need with:
#
# sudo dnf install llvm-devel llvm-static clang ncurses-devel
#
# and then set LLVM_DIR=/usr
set -ex
SRC_ROOT="$( cd "$(dirname "$0")"/../.. ; pwd -P)"
ANALYZER_SRC_HOME="${SRC_ROOT}/tools/code_analyzer"
# Clang/LLVM path
export LLVM_DIR="${LLVM_DIR:-/usr/lib/llvm-8}"
export CC="${LLVM_DIR}/bin/clang"
export CXX="${LLVM_DIR}/bin/clang++"
EXTRA_ANALYZER_FLAGS=$@
BUILD_ROOT="${BUILD_ROOT:-${SRC_ROOT}/build_code_analyzer}"
WORK_DIR="${BUILD_ROOT}/work"
rm -rf "${BUILD_ROOT}"
mkdir -p "${BUILD_ROOT}"
mkdir -p "${WORK_DIR}"
cd "${BUILD_ROOT}"
build_analyzer() {
cmake "${ANALYZER_SRC_HOME}" -DCMAKE_BUILD_TYPE=Release
if [ -z "${MAX_JOBS}" ]; then
if [ "$(uname)" == 'Darwin' ]; then
MAX_JOBS=$(sysctl -n hw.ncpu)
else
MAX_JOBS=$(nproc)
fi
fi
make "-j${MAX_JOBS}"
}
build_torch_mobile() {
TORCH_BUILD_ROOT="${BUILD_ROOT}/build_mobile"
TORCH_INSTALL_PREFIX="${TORCH_BUILD_ROOT}/install"
BUILD_ROOT="${TORCH_BUILD_ROOT}" "${SRC_ROOT}/scripts/build_mobile.sh" \
-DCMAKE_CXX_FLAGS="-S -emit-llvm -DSTRIP_ERROR_MESSAGES" \
${MOBILE_BUILD_FLAGS}
}
build_test_project() {
TEST_SRC_ROOT="${SRC_ROOT}/test/mobile/op_deps"
TEST_BUILD_ROOT="${BUILD_ROOT}/build_test"
TEST_INSTALL_PREFIX="${TEST_BUILD_ROOT}/install"
BUILD_ROOT="${TEST_BUILD_ROOT}" \
TORCH_INSTALL_PREFIX="${TORCH_INSTALL_PREFIX}" \
"${TEST_SRC_ROOT}/build.sh" \
-DCMAKE_CXX_FLAGS="-S -emit-llvm -DSTRIP_ERROR_MESSAGES"
}
call_analyzer() {
ANALYZER_BIN="${BUILD_ROOT}/analyzer" \
INPUT="${INPUT}" OUTPUT="${OUTPUT}" \
EXTRA_ANALYZER_FLAGS="${EXTRA_ANALYZER_FLAGS}" \
"${ANALYZER_SRC_HOME}/run_analyzer.sh"
}
analyze_torch_mobile() {
INPUT="${WORK_DIR}/torch.ll"
OUTPUT="${WORK_DIR}/torch_result.yaml"
if [ ! -f "${INPUT}" ]; then
# Link libtorch into a single module
# TODO: invoke llvm-link from cmake directly to avoid this hack.
# TODO: include *.c.o when there is meaningful fan-out from pure-c code.
"${LLVM_DIR}/bin/llvm-link" -S \
$(find "${TORCH_BUILD_ROOT}" -name '*.cpp.o' -o -name '*.cc.o') \
-o "${INPUT}"
fi
# Analyze dependency
call_analyzer
}
print_output_file_path() {
echo "Deployed file at: ${OUTPUT}"
}
analyze_test_project() {
INPUT="${WORK_DIR}/test.ll"
OUTPUT="${WORK_DIR}/test_result.yaml"
# Link into a single module (only need c10 and OpLib srcs)
# TODO: invoke llvm-link from cmake directly to avoid this hack.
"${LLVM_DIR}/bin/llvm-link" -S \
$(find "${TORCH_BUILD_ROOT}" -path '*/c10*' \( -name '*.cpp.o' -o -name '*.cc.o' \)) \
$(find "${TEST_BUILD_ROOT}" -path '*/OpLib*' \( -name '*.cpp.o' -o -name '*.cc.o' \)) \
-o "${INPUT}"
# Analyze dependency
call_analyzer
}
check_test_result() {
if cmp -s "${OUTPUT}" "${TEST_SRC_ROOT}/expected_deps.yaml"; then
echo "Test result is the same as expected."
else
echo "Test result is DIFFERENT from expected!"
diff -u "${TEST_SRC_ROOT}/expected_deps.yaml" "${OUTPUT}"
exit 1
fi
}
build_analyzer
if [ -n "${ANALYZE_TORCH}" ]; then
build_torch_mobile
analyze_torch_mobile
if [ -n "${DEPLOY}" ]; then
print_output_file_path
fi
fi
if [ -n "${ANALYZE_TEST}" ]; then
build_torch_mobile
build_test_project
analyze_test_project
check_test_result
fi