Skip to content

[FuncAttr] Incorrect norecurse inference in both PostOrderFunctionAttrsPass and ReversePostOrderFunctionAttrsPass #157081

@usha1830

Description

@usha1830

While working on improving the norecurse attribute inference in #139943, we came across a couple of cases of incorrect inference in PO and RPO FunctionAttr pass.

As per my understanding, both of these cases are not possible to reach through compiler analysis/transformations unless forced using
-mllvm -force-attribute=<fname>:norecurse

  1. https://godbolt.org/z/cMxYE8qaW
define dso_local void @norecurse_fn() norecurse {
entry:
  tail call fastcc void @a(i32 0)
  ret void
}

define internal fastcc void @a(i32 range(i32 0, 2) %c) {
entry:
  %tobool.not = icmp eq i32 %c, 0
  br i1 %tobool.not, label %if.end, label %if.then

if.then:                                          ; preds = %entry
  tail call void @calls_norecurse()
  br label %if.end

if.end:                                           ; preds = %if.then, %entry
  ret void
}

define dso_local noundef i32 @main() norecurse {
entry:
  tail call fastcc void @foo()
  ret i32 1
}

define internal fastcc void @foo() {
entry:
  tail call void @norecurse_fn()
  tail call fastcc void @a(i32 1)
  ret void
}

declare void @calls_norecurse()

ReversePostOrderPass adds the norecurse attribute to function a, even when there is a possible recursion as a -> calls_norecurse (external function without definition) -> norecurse_fn -> a

This issue occurs because RPO norecurse attribute inference solely work based on the fact that all callers of function being examined are marked norecurse, which happens to be true when walking in top-down manner.

In this case, the norecurse attribute on norecurse_fn is not correct as it is not an internal function and can be called from external function which may introduce some indirect recursion.

  1. https://godbolt.org/z/WcTvdTaa1
define void @a(i32 %c) {
  %tobool.not = icmp ne i32 %c, 0
  br i1 %tobool.not, label %if.then, label %if.end

if.then:
  call void @calls_b()
  br label %if.end

if.end:
  ret void
}

declare void @calls_b() norecurse

;define void @b() norecurse {
;  call void @a(i32 0)
;  ret void
;}

In this case, PostOrderFunctionAttrsPass infers norecurse on a which is incorrect. It relies on the fact that if all callees are norecurse, the caller is also norecurse.

Here as well, the norecurse attribute on calls_b is incorrect as its definition is not known.

CC: @nikic @david-arm

Metadata

Metadata

Assignees

Labels

llvm:transformsquestionA question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions