Possibility of Obtaining Array Length in Clang AST Phase

Hello,

I’m currently working on HLS(high level synthesis) project that involves using the llvm (version 10) In my analysis, I need to determine the length of arrays in C function parameters to generate BRAM.

However, I noticed that when Clang generates IR, array parameters are represented as pointers (e.g., i32* for int array[4]). As a result, the array length information is lost during the Clang AST phase.

My question is whether there is any possibility to obtain the array length (only in situations like int a[4]) in C function parameters or to somehow preserve this information in the LLVM IR.

I have considered custom attribute or metadata to store the array length information, but I’m unsure if this is feasible or if there are any existing mechanisms in Clang to achieve this.

To clarify, I’m looking for a way to access the array length (only in situations like int a[4]) in the Clang AST phase before the generation of LLVM IR. If there’s a way to accomplish this or if you have any suggestions or insights, I’d greatly appreciate your help.

Thank you for your time and expertise.

This information is tracked by the type system for constant-sized arrays. Assuming you’ve got access to a QualType named QT, you could do something like:

if (const auto *CAT = dyn_cast<ConstantArrayType>(QT.getTypePtr())) {
  APInt APExtent = CAT->getSize();
  uint64_t Extent = APExtent.getZExtValue();
  ...
}

I tried this approach, but it didn’t work. I obtained the QualType using FunctionDeclparameters()getType().Is this correct?

My example code:

int main(int argc, char **argv) {
  int bar[4];
}

You can try: clang -Xclang -ast-dump -fsyntax-only foo.c to see the small array.
For LLVM clang -S -emit-llvm -c foo.c creates a small foo.ll:

define i32 @main(i32 noundef %0, i8** noundef %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i8**, align 8
  %5 = alloca [4 x i32], align 16
  store i32 %0, i32* %3, align 4
  store i8** %1, i8*** %4, align 8
  ret i32 0
}

Notice the alloca of four elements of type i32. You have an old LLVM. Today pointers are typeless, e.g., ptr.

For function variables, this is accurate, but for function parameters, it will be converted to a pointer

Indeed:

int foo(int bar[4]) {
}
define i32 @foo(i32* noundef %0) #0 {
  %2 = alloca i32, align 4
  %3 = alloca i32*, align 8
  store i32* %0, i32** %3, align 8
  %4 = load i32, i32* %2, align 4
  ret i32 %4
}

But even in C how do you know the size of the parameter? Side-channel? sizeof()?

Indeed, in this situation, C also cannot obtain the array’s length since it has already been converted into a pointer. However, users write this kind of code with the intention of using an array of length 4, which is why I asked if it is possible to obtain the array’s length.

Yup, that’s correct.

I had missed the fact that this was in a function parameter. In C, function parameters of array type are decayed into a pointer type: Compiler Explorer

Try out:

if (const auto *DT = dyn_cast<DecayedType>(getType().getTypePtr())) {
  if (const auto *CAT = Context.getAsConstantArrayType(DT->getOriginalType())) {
    ...
  }
}

I tried this method, and it worked perfectly! Thank you for resolving the issue for me! I really appreciate it!

1 Like