How to know if a parameter of a function is "nofree"?

I’m recently learning the attribute system of llvm. But sometimes it doesn’t works as I expected. For example:

#include <stdio.h>
#include <malloc.h>
__attribute__((noinline)) void print(int *p) {
  printf("%d\n", *p);
}

int main() {
  int *p = (int *)malloc(sizeof(int));
  print(p);
  return *p;
}

and the corresponding IR is:

; Function Attrs: nofree noinline nounwind uwtable
define dso_local void @print(i32* nocapture readonly %0) local_unnamed_addr #0 {
  %2 = load i32, i32* %0, align 4, !tbaa !2
  %3 = tail call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
  ret void
}

; Function Attrs: nofree nounwind
declare dso_local noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #1

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #2 {
  %1 = tail call noalias dereferenceable_or_null(4) i8* @malloc(i64 4) #4
  %2 = bitcast i8* %1 to i32*
  tail call void @print(i32* %2)
  ret i32 undef
}

I think the parameter of function print should has a “nofree” attribute, but it actually only has “nocapture” and “readonly”. Is there any pass that can infer the “nofree” parameter attribute?

I think that was because the nofree attached on print subsumed the case you described.

1 Like

Yes, it is. It seems that I should first check the function attribute and then check the parameter attribute. But considering this example:

__attribute__((noinline)) void dealloc2(int *p, int *q) {
  printf("%d\n", *p);
  free(q);
}

What I expect is the *p has nofree attribute and *q doesn’t. but its coresponding IR is opposite:

; Function Attrs: noinline nounwind uwtable
define dso_local void @dealloc2(i32* nocapture readonly %0, i32* nocapture %1) local_unnamed_addr #0 {
  %3 = load i32, i32* %0, align 4, !tbaa !2
  %4 = tail call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %3)
  %5 = bitcast i32* %1 to i8*
  tail call void @free(i8* %5) #5
  ret void
}

I guess the readonly attribute has already implied that the parameter is nofree. Am I right? Is there any other attribute which also implies nofree?

I think perhaps there is a pass for adding nofree to parameters but I didn’t use it. Could anyone help?