Skip to content

DeadArgumentElimination creates invalid code with musttail calls returning structs #107569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ostannard opened this issue Sep 6, 2024 · 8 comments · Fixed by #127366
Closed
Labels
crash-on-valid ipo Interprocedural optimizations

Comments

@ostannard
Copy link
Collaborator

The DeadArgumentElimination pass creates code which fails the IR verifier when run on this input:

%struct.S = type { i32 }

define internal %struct.S @F38()  {
  ret %struct.S { i32 0 }
}

define %struct.S @F36() {
  %3 = musttail call %struct.S @F38()
  ret %struct.S %3
}
$ /work/llvm/build/bin/opt test.ll -passes=deadargelim -S
cannot guarantee tail call due to mismatched return types
  %1 = musttail call i32 @F38()
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /work/llvm/build/bin/opt test.ll -passes=deadargelim -S
1.      Running pass "verify" on module "test.ll"
 #0 0x000055c16a99ca37 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/work/llvm/build/bin/opt+0x4133a37)
 #1 0x000055c16a99a57e llvm::sys::RunSignalHandlers() (/work/llvm/build/bin/opt+0x413157e)
 #2 0x000055c16a99d27a SignalHandler(int) Signals.cpp:0:0
 #3 0x00007f38e3a42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007f38e3a969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007f38e3a969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x00007f38e3a969fc pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x00007f38e3a42476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x00007f38e3a287f3 abort ./stdlib/abort.c:81:7
 #9 0x000055c16a9857a3 llvm::report_fatal_error(llvm::Twine const&, bool) (/work/llvm/build/bin/opt+0x411c7a3)
#10 0x000055c16a9855f6 (/work/llvm/build/bin/opt+0x411c5f6)
#11 0x000055c16afe2d4a (/work/llvm/build/bin/opt+0x4779d4a)
#12 0x000055c16bd1b53d llvm::detail::PassModel<llvm::Module, llvm::VerifierPass, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) NewPMDriver.cpp:0:0
#13 0x000055c16abad88a llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/work/llvm/build/bin/opt+0x434488a)
#14 0x000055c16bd146eb llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/work/llvm/build/bin/opt+0x54ab6eb)
#15 0x000055c16a964609 optMain (/work/llvm/build/bin/opt+0x40fb609)
#16 0x00007f38e3a29d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#17 0x00007f38e3a29e40 call_init ./csu/../csu/libc-start.c:128:20
#18 0x00007f38e3a29e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#19 0x000055c16a95e0a5 _start (/work/llvm/build/bin/opt+0x40f50a5)
fish: Job 1, '/work/llvm/build/bin/opt test.l…' terminated by signal SIGABRT (Abort)

Found using a fuzzer while testing #102896.

@ostannard
Copy link
Collaborator Author

Related case with a different assertion message:

%struct.S = type { double }

define internal %struct.S @F38() {
  ret %struct.S { double 0.0 }
}

define internal %struct.S @F36() {
  %1 = alloca %struct.S, align 8
  %3 = musttail call %struct.S @F38()
  ret %struct.S %3
}

define double @foo() {
  %3 = call %struct.S @F36()
  %5 = extractvalue %struct.S %3, 0
  ret double %5
}
$ /work/llvm/build/bin/opt test2.ll -passes=deadargelim -S -print-after-all
; *** IR Dump After DeadArgumentEliminationPass on [module] ***
; ModuleID = 'test2.ll'
source_filename = "test2.ll"

%struct.S = type { double }

define internal double @F38() {
  %oldret = extractvalue %struct.S zeroinitializer, 0
  ret double %oldret
}

define internal double @F36() {
  %1 = alloca %struct.S, align 8
  %2 = musttail call double @F38()
  %oldret = insertvalue %struct.S poison, double %2, 0
  %oldret1 = extractvalue %struct.S %oldret, 0
  ret double %oldret1
}

define double @foo() {
  %1 = call double @F36()
  %oldret = insertvalue %struct.S poison, double %1, 0
  %2 = extractvalue %struct.S %oldret, 0
  ret double %2
}
musttail call must precede a ret with an optional bitcast
  %2 = musttail call double @F38()
LLVM ERROR: Broken module found, compilation aborted!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.      Program arguments: /work/llvm/build/bin/opt test2.ll -passes=deadargelim -S -print-after-all
1.      Running pass "verify" on module "test2.ll"
 #0 0x000055c5f980ca37 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/work/llvm/build/bin/opt+0x4133a37)
 #1 0x000055c5f980a57e llvm::sys::RunSignalHandlers() (/work/llvm/build/bin/opt+0x413157e)
 #2 0x000055c5f980d27a SignalHandler(int) Signals.cpp:0:0
 #3 0x00007faf4da42520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007faf4da969fc __pthread_kill_implementation ./nptl/pthread_kill.c:44:76
 #5 0x00007faf4da969fc __pthread_kill_internal ./nptl/pthread_kill.c:78:10
 #6 0x00007faf4da969fc pthread_kill ./nptl/pthread_kill.c:89:10
 #7 0x00007faf4da42476 gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #8 0x00007faf4da287f3 abort ./stdlib/abort.c:81:7
 #9 0x000055c5f97f57a3 llvm::report_fatal_error(llvm::Twine const&, bool) (/work/llvm/build/bin/opt+0x411c7a3)
#10 0x000055c5f97f55f6 (/work/llvm/build/bin/opt+0x411c5f6)
#11 0x000055c5f9e52d4a (/work/llvm/build/bin/opt+0x4779d4a)
#12 0x000055c5fab8b53d llvm::detail::PassModel<llvm::Module, llvm::VerifierPass, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) NewPMDriver.cpp:0:0
#13 0x000055c5f9a1d88a llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/work/llvm/build/bin/opt+0x434488a)
#14 0x000055c5fab846eb llvm::runPassPipeline(llvm::StringRef, llvm::Module&, llvm::TargetMachine*, llvm::TargetLibraryInfoImpl*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::ToolOutputFile*, llvm::StringRef, llvm::ArrayRef<llvm::PassPlugin>, llvm::ArrayRef<std::function<void (llvm::PassBuilder&)>>, llvm::opt_tool::OutputKind, llvm::opt_tool::VerifierKind, bool, bool, bool, bool, bool, bool, bool) (/work/llvm/build/bin/opt+0x54ab6eb)
#15 0x000055c5f97d4609 optMain (/work/llvm/build/bin/opt+0x40fb609)
#16 0x00007faf4da29d90 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#17 0x00007faf4da29e40 call_init ./csu/../csu/libc-start.c:128:20
#18 0x00007faf4da29e40 __libc_start_main ./csu/../csu/libc-start.c:379:5
#19 0x000055c5f97ce0a5 _start (/work/llvm/build/bin/opt+0x40f50a5)
fish: Job 1, '/work/llvm/build/bin/opt test2.…' terminated by signal SIGABRT (Abort)

@ParkHanbum
Copy link
Contributor

ParkHanbum commented Sep 7, 2024

we have the test for second case at musttail-invalid.ll

define i32 @not_tail_pos() {
; CHECK: musttail call must precede a ret with an optional bitcast
  %v = musttail call i32 @not_tail_pos_callee()
  %w = add i32 %v, 1
  ret i32 %w
}

but first case is not. maybe we need to add this into testcase.

first case

  Check(!CI.isInlineAsm(), "cannot use musttail call with inline asm", &CI);

  Function *F = CI.getParent()->getParent();
  FunctionType *CallerTy = F->getFunctionType();
  FunctionType *CalleeTy = CI.getFunctionType();
  Check(CallerTy->isVarArg() == CalleeTy->isVarArg(),
        "cannot guarantee tail call due to mismatched varargs", &CI);
  Check(isTypeCongruent(CallerTy->getReturnType(), CalleeTy->getReturnType()),
        "cannot guarantee tail call due to mismatched return types", F, CallerTy->getReturnType(), &CI, CalleeTy->getReturnType());

CallInst: %1 = musttail call i32 @f38()
Return type of Caller : %struct.S = type { i32 }
Return type of Callee: i32

This is weird.
I couldn't understand why %struct.S = type { i32 } is changed to i32 at %3 = musttail call %struct.S @F38()

but after change it to i32 then it works.

%struct.S = type { i32 }

define internal %struct.S @F38()  {
  ret %struct.S { i32 0 }
}

define i32 @F36() {
  %3 = musttail call i32 @F38()
  ret i32 %3
}
% bin/opt --passes=deadargelim  test.ll -S       
; ModuleID = 'test.ll'
source_filename = "test.ll"

%struct.S = type { i32 }

define internal %struct.S @F38() {
  ret %struct.S zeroinitializer
}

define i32 @F36() {
  %1 = musttail call i32 @F38()
  ret i32 %1
}

is this normal?

@ParkHanbum
Copy link
Contributor

ParkHanbum commented Sep 7, 2024

I found why it causes, maybe.

source_filename = "test.ll"

%struct.S = type { i32 }

define internal i32 @F38() {
  %oldret = extractvalue %struct.S zeroinitializer, 0
  ret i32 %oldret
}

define %struct.S @F36() {
  %1 = musttail call i32 @F38()
  %oldret = insertvalue %struct.S poison, i32 %1, 0
  ret %struct.S %oldret
}

@ParkHanbum
Copy link
Contributor

ParkHanbum commented Sep 7, 2024

we create new return type in here

// Look at each of the original return values individually.
    for (unsigned Ri = 0; Ri != RetCount; ++Ri) {
      RetOrArg Ret = createRet(F, Ri);
      if (LiveValues.erase(Ret)) {
        RetTypes.push_back(getRetComponentType(F, Ri));
        NewRetIdxs[Ri] = RetTypes.size() - 1;
      } else {
        ++NumRetValsEliminated;
        LLVM_DEBUG(
            dbgs() << "DeadArgumentEliminationPass - Removing return value "
                   << Ri << " from " << F->getName() << "\n");
      }
    

I tested disabling the code that creates the new return type to see if it would make a quick improvement

NRetTy = RetTy;

it seems to work

; ModuleID = 'test.ll'
source_filename = "test.ll"

%struct.S = type { i32 }

define internal %struct.S @F38() {
  ret %struct.S zeroinitializer
}

define %struct.S @F36() {
  %1 = musttail call %struct.S @F38()
  ret %struct.S %1
}

If this is a defect,

  1. do not change return type if it conflict with caller's return type when CB was musttail call.
  2. if caller's return type is conflict with new return type then change all of uses type.

we can fix it by choosing one of above. IMO
how do you think?

please let me know this issue need to fix or not~

@dtcxzyw Can you please review this issue?

@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 9, 2024

cc @nikic @efriedma-quic

@efriedma-quic
Copy link
Collaborator

I'm not sure why we're trying to run DAE on functions where we can't change the signature; why doesn't the current check for musttail callers handle this?

@ParkHanbum
Copy link
Contributor

@efriedma-quic Don't think it needs to be fix?

@efriedma-quic
Copy link
Collaborator

Not sure what you're saying. I'm asking about the structure of the existing code. There are already checks for musttail calls in DeadArgumentElimination, which at first glance should exclude transforming cases like this. Can those checks be extended to also exclude this case?

@rnk rnk closed this as completed in 5978bb2 Apr 10, 2025
@EugeneZelenko EugeneZelenko added ipo Interprocedural optimizations and removed llvm:optimizations labels Apr 10, 2025
var-const pushed a commit to ldionne/llvm-project that referenced this issue Apr 17, 2025
…signature (llvm#127366)

This commit is for llvm#107569 and llvm#126817.

Stop changing musttail's caller and callee's function signature when
calling convention is not swifttailcc nor tailcc. Verifier makes sure
musttail's caller and callee shares exactly the same signature, see
commit 9ff2eb1 and llvm#54964.

Otherwise just make sure the return type is the same and then process
musttail like usual calls.

close llvm#107569, llvm#126817
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crash-on-valid ipo Interprocedural optimizations
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants