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] .