kavon/ghc-llvm

Ensure IR->IR CSE does not break CPS calls

Opened this issue · 2 comments

kavon commented

As seen in #11 , CSE for constants is something that is valid to do across a vanilla call in LLVM.
Consider the following program:

declare void @foo(i64)

@extern_glob = external global i8

define void @bar() {
  %x_addr = ptrtoint i8* @extern_glob to i64
  %x = add i64 %x_addr, 2
  call void @foo(i64 %x)
  %y_addr = ptrtoint i8* @extern_glob to i64
  %y = add i64 %y_addr, 2
  call void @foo(i64 %y)
  ret void
}

Currently, LLVM's optimizations, like early-cse or gvn always inline the address calculation like so:

define void @bar() {
  call void @foo(i64 add (i64 ptrtoint (i8* @extern_glob to i64), i64 2))
  call void @foo(i64 add (i64 ptrtoint (i8* @extern_glob to i64), i64 2))
  ret void
}

But, it's valid to also produce the following:

define void @bar() {
  %x_addr = ptrtoint i8* @extern_glob to i64
  %x = add i64 %x_addr, 2
  call void @foo(i64 %x)
  call void @foo(i64 %x)
  ret void
}

If the calls were specifically cpscalls, this type of CSE introduces a value that is live across the cpscall, which will be allocated an LLVM stack slot.

Questions:

  • How do we know the latter will not happen, or rather, why does the latter transformation not occur?
  • Can we rely on it never occurring?
  • Can we check liveness information during cpscallprep to enforce our requirement?
kavon commented

Asked an LLVM dev and apparently the former always happens because it recognizes it as a const expression, and the goal is to reduce the number of instructions during IR->IR simplification.

Thus, it should be fine, but not technically guaranteed. "If the latter does happen, it's probably an LLVM bug."

kavon commented

Note that any common-subexpression issues should always be with constants due to our guarentees about variable liveness.