Skip to content

[llvm:optimizations] [missed-optimization] Unable to hoist out call to readonly function in a loop #163097

@mattgodbolt

Description

@mattgodbolt

In clang 21.1.0 (and earlier), in the following code, which is about as reduced as I can get:

#include <cstddef>

// [[gnu::noinline]]
// [[gnu::const]]
// [[gnu::noinline, gnu::const]]
static std::size_t count_ints(const int *ints) {
  std::size_t num{};
  while (*ints) {
    num++;
    ints++;
  }
  return num;
}

std::size_t num_compares{};

// Returns if a zero-termed list of ints has 1234
bool has_1234(const int *ints) {
  for (std::size_t index = 0; index < count_ints(ints); ++index) {
    ++num_compares;
    if (ints[index] == 1234) {
      return true;
    }
  }
  return false;
}

CE link: https://godbolt.org/z/raPrvq9aT

Here I would expect:

  • the count_ints function to be noted as const (or similar) Certainly "pure". TBAA should note that it only reads through an int* if at all. Maybe argmemonly (I think?)
  • as a result the count_ints is called inside the loop (even though it's cleverly been turned into a wcslen. I'd hope LICM could yank it out
  • that results in poor performance (O(n2)) though naturally this code is awful (on purpose!)

Interestingly, marking it gnu::const (not strictly true) still causes the wcslen to be inlined and called every iteration. Making it noinline and const "fixes" this but I think is not strictly true (as I understand it the read through *ints makes it non-const which is to say "readnone" in llvm).

Similar reported to gcc.

I discussed this over Discord with a few folks more familiar with LLVM and they said to file this with the tags in the title. Sorry if this is not the correct way to do this.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions