https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106725

            Bug ID: 106725
           Summary: LTO semantics for __attribute__((leaf))
           Product: gcc
           Version: 12.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: dthorn at google dot com
  Target Milestone: ---

After implementing GCC's `__attribute__((leaf))` in Clang/LLVM, discussion
arose (https://reviews.llvm.org/D131628#3740602) about the semantics of LTO.

It seems like merging together object files in LTO might or might notproduce
erroneous results, depending on how the term "compilation unit" in the docs are
interpreted.

A minimal example consists of three source files:

main.c:
  __attribute__((leaf)) void foo(void);
  int main(void) {
    foo();
    return 0;
  }

foo.c:
  void bar(void);
  void foo(void) {
    bar();
  }

bar.c:
  void bar(void) {}

If "compilation unit" in the `__attribute__((leaf))` manual means "translation
unit", `__attribute__((leaf))` should be valid in main.c, since foo() only
calls bar(), which is not in the main.c translation unit.

Compile main.c and bar.c to LTO GIMPLE, but compile foo.c without LTO:
  $ gcc -flto -c main.c bar.c
  $ gcc -c foo.c

The resulting GIMPLE and object files can then be linked together and the LTO
trees dumped (Here I'm using -nostdlib just because I built gcc in isolation).
I installed a simple printf hack to gimple-pretty-print to print "leaf" if
ECF_LEAF is set on a printed call. This gives the following results:

  $ gcc -fdump-tree-all main.o bar.o foo.o

  $ cat a.ltrans0.ltrans.252t.optimized
<...>
int main ()
{
<...>
  foo (); check 1 1024 leaf
<...>
}

;; Function bar (bar, funcdef_no=1, decl_uid=4720, cgraph_uid=1,
symbol_order=2)
<...>

>From the above, it appears that the main and bar translation units are merged
together by LTO to form a new module without altering the
`__attribute__((leaf))` semantics given on the call to foo() in main().

If "compilation unit" is interpreted as "translation unit", this may be
incorrect behavior, since any post-LTO passes that are expecting that foo()
cannot call bar() (since it's in the same TU) would have their expectations
violated.

If instead "compilation unit" is interpreted as "LTO unit", `then the above
program has undefined behavior, since `__attribute__((leaf)) should never have
been used on foo(), since it calls something (bar()) in the same LTO unit as
main.c.

Accordingly, if "compilation unit" means "translation unit" and this case is
incorrect behavior, this bug is to report this.

If "compilation unit" instead means "LTO unit", this bug is to request
clarification in the GCC manual about the precise behavior in case of LTO.

Finally, if "compilation unit" means "translation unit", but GCC has some other
internal mechanisms for dealing with this, feel free to close this. Please do
report back with those mechanisms though, as we'll have to do something similar
in LLVM in that case. It still may be worth altering the docs, given the
ambiguity in the term "compilation unit" in the presence of LTO.

System: Debian rodete

GCC build commands used:
  ../configure --disable-bootstrap  --enable-languages=c,c++,lto \
    --prefix=$(realpath ..)/gcc-install
  make -j96 -l96 all-gcc
  make install-gcc

Reply via email to