Skip to content

LLVM FAQ

Arseny Bochkarev edited this page Jun 1, 2025 · 17 revisions

This document briefly describes issues that may occur during LLVM/Clang development for RISC-V target.

Building binary for RISC-V

To build a binary for RISC-V with linux using Clang, you need to pass it the option --target=riscv64-unknown-linux. If after that you see errors, see this section.

Running LLVM-Exegesis

Example for vmerge instruction

  • Generate the benchmark itself using llvm-exegesis: ./build/Release/bin/llvm-exegesis --mode=latency --mtriple=riscv64 --opcode-name=PseudoVMERGE_VVM_M1 --mcpu=sifive-p670 --benchmark-phase=assemble-measured-code -mattr="+d" --dump-object-to-disk=PseudoVMERGE_VVM_M1.o
    • One can disable the -mattr flag, but then it'll require -mabi=lp64 flag on the build stage
  • Take the following two files:

wrapper-PseudoVMERGE_VVM_M1.c:

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <riscv_vector.h>

#include "arch.h"
extern void foo(void *); // PLEASE MIND THE NAME OF THIS FUNCTION

void testPseudoVMERGE_VVM_M1() {
  // Storage space for generated instructions to read/write to.
  char arr[128] = {0};
  char *arr_addr = arr;

  // Special hack, used for ld a0, 0(a0) sequences.
  memcpy(arr, &arr_addr, sizeof(arr_addr));
  size_t vl = __riscv_vsetvl_e8m1(16); // Required for RVV set up and can be adjusted
  unsigned long start = arch_cycle(), fin;
  foo(arr);
  fin = arch_cycle();

#if defined(DETAILED)
  printf("start: %lx\n", start);
  printf("fin: %lx\n", fin);
#endif
  printf("PseudoVMERGE_VVM_M1: cycles elapsed: %ld\n", fin - start);
}

common1.c:

extern void testPseudoVMERGE_VVM_M1();

int main() {
  testPseudoVMERGE_VVM_M1();
}
  • Next, compile the binary: clang -I <paths containing arch.h and some other required headers> --target=riscv64-unknown-elf -march=rv64gcv -mabi=lp64 common1.c PseudoVMERGE_VVM_M1.o wrapper-PseudoVMERGE_VVM_M1.c -o benchmark-VMERGE.elf
    • arch.h and supporting files can be found in HAL, for example, in this toolkit
    • If no function foo was found in PseudoVMERGE_VVM_M1.o, disassemble it (using the llvm-objdump for example), and rename the function called in wrapper-PseudoVMERGE_VVM_M1.c accordingly

Running LLVM-MCA

Please refer to this document. For example:

./build/Release/bin/clang --target=riscv32-unknown-linux ./test.c -O2 --gcc-toolchain=<path to gcc toolchain> --sysroot=<path to gcc toolchain>/sysroot -mcpu=syntacore-scr1-max -S -o - | ./build/Release/bin/llvm-mca -march=riscv32 -mcpu=syntacore-scr1-max

Debugging Clang

Basic pipeline is:

gdb clang++
<set breakpoint>
set follow-fork-mode child
r <additional flags> test.cpp

OR it is also possible to use -fintegrated-cc1 option:

gdb clang++
<set breakpoint>
r -fintegrated-cc1 <additional flags> test.cpp

Tips:

  • almost everything in LLVM has dump() method, so it should be used as much as you can to understand what's going on
  • utilize the Text User Interface from GDB. To use it, simply type the tui enable. To exit, use tui disable or Ctrl+X and A (subsequently)
  • utilize useful LLVM/Clang command-line options: -print-changed, -stop-after, -debug-only, etc.

Useful links:

LLVM compilation

Which generator to use

Usually we use Ninja for speed and faster incremental rebuilds. For example:

cmake -S llvm -G Ninja -B build/Release \
        -DCMAKE_BUILD_TYPE=Release \
        -DLLVM_ENABLE_PROJECTS=clang \

cmake --build build/Release -j$(nproc)

Missing headers

One may encounter error for standard library headers missing when cross-compiling:

fatal error: 'iostream' file not found
    1 | #include <iostream>
      |          ^~~~~~~~~~
1 error generated.

The error means that the system doesn't see GCC cross-compiler from x86 to RISC-V (yes, even if you're using clang the GCC dependency is still there). You should either:

  1. Somehow install cross-compiler GCC in your system: set it in your $PATH, OR
  2. Download or build GCC cross-compiler from x86 to RISC-V and pass two options for clang:
  • --gcc-toolchain=<path to GCC folder>, and
  • --sysroot=<path to GCC folder>/sysroot

So your build command will look like: ./build/bin/clang++ --target=riscv64-unknown-linux --gcc-toolchain=/home/<gcc path>/ --sysroot=<gcc path>/sysroot/ ./test.cpp

GCC folder should contain sysroot and riscv64-unknown-linux-gnu -- precompiled system libraries (for example libstdc++.so)

llc usage

One may need to use llc to trigger certain pass, and then compile it to ELF format. To do so the following actions should be performed:

  1. Emit bitcode with clang: ./build/bin/clang --target=riscv64-unknown-linux-gnu -march=rv64g -emit-llvm -c ~/test.cpp
  2. Use llc, but with some additional options: ./build/bin/llc -march=riscv64 -mcpu=generic-rv64 -mattr=+d -filetype=asm test.bc. Note that it is not always necessary to add these attributes.
  3. Use clang again: ./build/bin/clang --target=riscv64-unknown-linux-gnu test.s

Note: it is possible you'll need to specify the --gcc-toolchain and --sysroot for clang in steps 1 and 3 above

GLIBC version

GCC 13 introduced new GLIBC, which may work poorly on some of the systems. In case of such an error, try to compile with different (previous) GCC toolchain.

Clone this wiki locally