Appendix B: Clang library and tools¶
This chapter shows that Using Clang as a Library and Tools [clang-doc].
Install llvm with clang tools¶
exlbt/clang-tools/install-llvm.sh
#!/usr/bin/env bash
#export LLVM_VER=14.x
#export LLVM_VER=15.x
export LLVM_VER=16.x
export LLVM_SRC_DIR=$HOME/llvm/$LLVM_VER
get_llvm() {
if [ ! -d "$LLVM_SRC_DIR" ]; then
echo "LLVM_SRC_DIR: $LLVM_SRC_DIR not exist"
exit 1
fi
pushd $LLVM_SRC_DIR
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout -b $LLVM_VER origin/release/$LLVM_VER
popd
}
release_build_llvm_toolchain() {
pushd $LLVM_SRC_DIR/llvm-project
if [ -d "$LLVM_SRC_DIR/llvm-project/release-build" ]; then
echo "$LLVM_SRC_DIR/build exist already. Please remove it before run this bash"
exit 1
fi
mkdir release-build
cd release-build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DLLVM_PARALLEL_COMPILE_JOBS=4 -DLLVM_PARALLEL_LINK_JOBS=1 -G "Ninja" ../llvm \
-DCLANG_BUILD_EXAMPLES=ON
ninja
popd
}
debug_build_llvm_toolchain() {
pushd $LLVM_SRC_DIR/llvm-project
if [ -d "$LLVM_SRC_DIR/llvm-project/build" ]; then
echo "$LLVM_SRC_DIR/build exist already. Please remove it before run this bash"
exit 1
fi
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DLLVM_PARALLEL_COMPILE_JOBS=4 -DLLVM_PARALLEL_LINK_JOBS=1 -G "Ninja" ../llvm \
-DCLANG_BUILD_EXAMPLES=ON
ninja
popd
}
get_llvm;
release_build_llvm_toolchain;
debug_build_llvm_toolchain;
$ pwd
$ $HOME/lbt/exlbt/clang-tools
$ bash install-llvm.sh
Using Clang as a Library¶
Clang Plugins¶
References clang/examples/PrintFunctionNames [clang-plugins] [clang-ast-fa].
exlbt/clang-tools/Rewriter/plugin/README.txt
Clang example:
- Converter from cpp to cpp.
- Modified from clang/examples/PrintFunctionNames.
- Work on llvm 088f33605d8a61ff519c580a71b1dd57d16a03f8 of 15.x,
f28c006a5895fc0e329fe15fead81e37457cb1d1 of 14.x and
e8a397203c67adbeae04763ce25c6a5ae76af52c of 12.x.
Install:
~/git/lbt/exlbt/clang-tools/Rewriter/plugin$ cp -rf Rewriter ~/llvm/15.x/llvm-project/clang/examples/.
echo 'add_subdirectory(Rewriter)' >> ~/llvm/15.x/llvm-project/clang/examples/CMakeLists.txt
Run command:
Check Rewriter/README.txt
exlbt/clang-tools/Rewriter/plugin/Rewriter/README.txt
This is a simple example demonstrating how to use clang's facility for
providing AST consumers using a plugin.
Once the plugin is built, you can run it using:
--
Linux:
$ <llvm-build-path>/bin/clang++ -Xclang -load -Xclang <llvm-build-path>/lib/Rewriter.so -Xclang -add-plugin -Xclang ex1-act -Xclang -emit-llvm -S input.cpp -o -
for example:
~/llvm/15.x/llvm-project/build/bin/clang++ -Xclang -load -Xclang ~/llvm/15.x/llvm-project/build/lib/Rewriter.so -Xclang -add-plugin -Xclang ex1-act -Xclang -ast-dump -fsyntax-only input.cpp -o -
MacOS:
Use Rewriter.dylib instead of .so.
Rewriter/exe/Linux and Rewriter/exe/macOS applies on Linux and macOS as follows,
exlbt/clang-tools/Rewriter/exe/Linux/README.txt
Clang example:
- Converter from cpp to cpp
- Work on llvm 088f33605d8a61ff519c580a71b1dd57d16a03f8 of 15.x.
build:
exe/Linux$ make clean; make
run:
./build/Rewriter ../input.cpp
check:
cat output.cpp
gdb:
exe/Linux$ gdb --args ./build/Rewriter ../input.cpp
(gdb) b MyASTConsumer.cpp:61
(gdb) r
61 TheRewriter.ReplaceText(CallerSR, StringRef(sBuiltinFunc));
exlbt/clang-tools/Rewriter/exe/macOS/README.txt
Clang example:
- Converter from cpp to cpp
- Work on llvm 088f33605d8a61ff519c580a71b1dd57d16a03f8 of 15.x.
install:
% brew install llvm
build:
exe/macOS% make clean; make
run:
./build/Rewriter ../input.cpp
check:
cat output.cpp
gdb:
exe/macOS% gdb --args ./build/Rewriter ../input.cpp
(gdb) b MyASTConsumer.cpp:61
(gdb) r
61 TheRewriter.ReplaceText(CallerSR, StringRef(sBuiltinFunc));
Using LibTooling and LibASTMatchers¶
References clang-tools-extra/loop-convert [clang-lamt] [clang-lam].
exlbt/clang-tools/loop-convert2/README.txt
Work on llvm 088f33605d8a61ff519c580a71b1dd57d16a03f8 of 15.x
and f28c006a5895fc0e329fe15fead81e37457cb1d1 of 14.x.
install:
~/git/lbt/exlbt/clang-tools$ cp -rf loop-convert2 ~/llvm/15.x/llvm-project/clang-tools-extra/.
echo 'add_subdirectory(loop-convert2)' >> ~/llvm/15.x/llvm-project/clang-tools-extra/CMakeLists.txt
build:
~/llvm/14.x/llvm-project/build$ ninja
run:
~/git/lbt/exlbt/clang-tools$ ~/llvm/15.x/llvm-project/build/bin/loop-convert2 loop-test.cpp --
Potential array-based loop discovered.
ForStmt begin at 8:3
ForStmt end at 10:3
~/git/lbt/exlbt/clang-tools$ ~/llvm/15.x/llvm-project/build/bin/loop-convert2 -debug loop-test.cpp --
Potential array-based loop discovered.
ForStmt 0x1378c7018
|-DeclStmt 0x1378c6dc0
| `-VarDecl 0x1378c6d38 used i 'int' cinit
| `-IntegerLiteral 0x1378c6da0 'int' 0
|-<<<NULL>>>
|-BinaryOperator 0x1378c6ed8 '_Bool' '<'
| |-ImplicitCastExpr 0x1378c6ec0 'int' <LValueToRValue>
| | `-DeclRefExpr 0x1378c6dd8 'int' lvalue Var 0x1378c6d38 'i' 'int'
| `-CallExpr 0x1378c6ea0 'int'
| `-ImplicitCastExpr 0x1378c6e88 'int (*)(void)' <FunctionToPointerDecay>
| `-DeclRefExpr 0x1378c6e40 'int (void)' lvalue Function 0x1378c6a40 'expr' 'int (void)'
|-UnaryOperator 0x1378c6f18 'int' lvalue prefix '++'
| `-DeclRefExpr 0x1378c6ef8 'int' lvalue Var 0x1378c6d38 'i' 'int'
`-CompoundStmt 0x1378c7000
`-BinaryOperator 0x1378c6fe0 'int' lvalue '='
|-DeclRefExpr 0x1378c6f30 'int' lvalue Var 0x1378c6c80 'res' 'int'
`-BinaryOperator 0x1378c6fc0 'int' '+'
|-ImplicitCastExpr 0x1378c6f90 'int' <LValueToRValue>
| `-DeclRefExpr 0x1378c6f50 'int' lvalue Var 0x1378c6c80 'res' 'int'
`-ImplicitCastExpr 0x1378c6fa8 'int' <LValueToRValue>
`-DeclRefExpr 0x1378c6f70 'int' lvalue Var 0x1378c6d38 'i' 'int'
ForStmt begin at 8:3
ForStmt end at 10:3
Using Clang as Tools¶
clang-query¶
References [1] [2] [3]. RVV reference ~/riscv/riscv_newlib/lib/clang/13.0.1/include/riscv_vector.h.
exlbt/clang-tools/vecotor-dsl.cpp
/*
~/riscv/riscv_newlib/bin/clang++ vector-dsl.cpp -menable-experimental-extensions \
-march=rv64gcv0p10 -O0 -mllvm --riscv-v-vector-bits-min=256 -DUSE_RVV -v
~/riscv/git/qemu/build/qemu-riscv64 -cpu rv64,v=true a.out
~/riscv/riscv_newlib/bin/riscv64-unknown-elf-objdump -d a.out|grep vmul
The following work for #undef USE_RVV
~/llvm/15.x/llvm-project/build/bin/clang++ vector-dsl.cpp -emit-llvm -S -Xclang -ast-dump
~/llvm/15.x/llvm-project/build/bin/clang-query vector-dsl.cpp --
clang-query> set traversal IgnoreUnlessSpelledInSource
clang-query> m cxxOperatorCallExpr(binaryOperation(hasOperatorName("="),hasLHS(expr(hasType(cxxRecordDecl(hasName("UVec32")))).bind("lhs")),hasRHS(expr(cxxOperatorCallExpr(binaryOperation(hasOperatorName("+"),hasLHS(expr(cxxOperatorCallExpr(binaryOperation(hasOperatorName("*"),hasLHS(expr().bind("lhs2")),hasRHS(expr().bind("rhs2"))))).bind("lhs1")),hasRHS(expr().bind("rhs1"))))).bind("rhs"))))
Match #1:
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:3: note: "lhs" binds here
A = alpha*B + C;
^
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "lhs1" binds here
A = alpha*B + C;
^~~~~~~
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "lhs2" binds here
A = alpha*B + C;
^~~~~
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "rhs" binds here
A = alpha*B + C;
^~~~~~~~~~~
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:17: note: "rhs1" binds here
A = alpha*B + C;
^
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:13: note: "rhs2" binds here
A = alpha*B + C;
^
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:3: note: "root" binds here
A = alpha*B + C;
^~~~~~~~~~~~~~~
1 match.
More:
clang-query> m cxxOperatorCallExpr(binaryOperation(hasOperatorName("="),hasLHS(expr(hasType(cxxRecordDecl(hasName("UVec32")))).bind("lhs")),hasRHS(expr(cxxOperatorCallExpr(binaryOperation(hasOperatorName("+"),hasLHS(expr().bind("lhs1")),hasRHS(expr().bind("rhs1"))))).bind("rhs"))))
...
Match #2:
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:3: note: "lhs" binds here
A = alpha*B + C;
^
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "lhs1" binds here
A = alpha*B + C;
^~~~~~~
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "rhs" binds here
A = alpha*B + C;
^~~~~~~~~~~
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:17: note: "rhs1" binds here
A = alpha*B + C;
^
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:3: note: "root" binds here
A = alpha*B + C;
^~~~~~~~~~~~~~~
clang-query> m cxxOperatorCallExpr(binaryOperation(hasOperatorName("*"),hasLHS(expr().bind("lhs2")),hasRHS(expr().bind("rhs2"))))
...
Match #4:
/home/cschen/git/lbt/exlbt/clang-tools/vector-dsl.cpp:154:7: note: "lhs2" binds here
A = alpha*B + C;
^~~~~
...
Ref. https://clang.llvm.org/docs/LibASTMatchersReference.html#narrowing-matchers
*/
#include "vector-dsl.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#ifdef USE_RVV
#include <riscv_vector.h>
#endif
#define array_size(a) (sizeof(a) / sizeof((a)[0]))
const Precision bit16 = Bit16;
uint32_t t[33] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
};
UVec32::UVec32(uint32_t *A, size_t aSize) {
data = A;
size = aSize;
}
UVec32& UVec32::Mul(const uint32_t Scalar) {
static UVec32 res(t, size);
size_t n = size;
uint32_t *a = res.data;
uint32_t *b = data;
#ifdef USE_RVV
while (n > 0) {
size_t vl = vsetvl_e32m8(n);
vuint32m8_t vb = vle32_v_u32m8(b, vl);
vuint32m8_t va = vmul(vb, Scalar, vl);
vse32(a, va, vl);
a += vl;
b += vl;
n -= vl;
}
#else
for (int i=0; i < size; i++) {
a[i] = Scalar*b[i];
}
#endif
return res;
}
UVec32& UVec32::Mul(const UVec32 &arg) {
static UVec32 res(t, size);
size_t n = size;
uint32_t *a = res.data;
uint32_t *b = data;
uint32_t *c = arg.data;
assert(size == arg.size);
#ifdef USE_RVV
while (n > 0) {
size_t vl = vsetvl_e32m8(n);
vuint32m8_t vb = vle32_v_u32m8(b, vl);
vuint32m8_t vc = vle32_v_u32m8(c, vl);
vuint32m8_t va = vmul(vb, vc, vl);
vse32(a, va, vl);
a += vl;
b += vl;
c += vl;
n -= vl;
}
#else
for (int i=0; i < size; i++) {
a[i] = b[i]*c[i];
}
#endif
return res;
}
UVec32& UVec32::operator*(const uint32_t Scalar) {
return Mul(Scalar);
}
UVec32& UVec32::operator*(const UVec32 &arg) {
return Mul(arg);
}
UVec32& UVec32::operator+(const UVec32 &arg) {
static UVec32 res(t, size);
size_t n = size;
uint32_t *a = res.data;
uint32_t *b = data;
uint32_t *c = arg.data;
assert(size == arg.size);
#ifdef USE_RVV
while (n > 0) {
size_t vl = vsetvl_e32m8(n);
vuint32m8_t vb = vle32_v_u32m8(b, vl);
vuint32m8_t vc = vle32_v_u32m8(c, vl);
vuint32m8_t va = vadd(vb, vc, vl);
vse32(a, va, vl);
a += vl;
b += vl;
c += vl;
n -= vl;
}
#else
for (int i=0; i < size; i++) {
a[i] = b[i]+c[i];
}
#endif
return res;
}
void UVec32::Print() {
for (int i=0; i < size; i++) {
printf("%u ", data[i]);
}
printf("\n");
}
UVec32& operator*(const uint32_t Scalar, UVec32 &B) {
return B * Scalar;
}
uint32_t gV1[33] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
};
uint32_t gV2[33] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
};
uint32_t gV3[33] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
};
int main() {
UVec32 A(gV1,array_size(gV1)), B(gV2,array_size(gV2)), C(gV3,array_size(gV3));
uint32_t alpha = (uint32_t)2;
A = B + C;
A = B * C;
A = alpha*B;
A = alpha*B + C;
uint32_t a = 0; uint32_t b = 2; uint32_t c = 3;
a = alpha*b + c;
printf("B: "); B.Print();
printf("C: "); C.Print();
printf("A: "); A.Print();
printf("\n\n");
// multiply and add, madd
// A = B * C + D;
// Solve above by class is not efficient, class does not use madd.
// DSL: extend clang to parsing the left and calling A = madd(B,C,D); can get better performance.
return 0;
}
References [4] .