llvm-project icon indicating copy to clipboard operation
llvm-project copied to clipboard

Clang assertions trunk crash when use inline asm

Open int6 opened this issue 2 years ago • 5 comments

struct {
  unsigned bf : 1;
} t34_s;

void t34() {
  __asm__ volatile("movl %%eax, %0" :: "*"(t34_s.bf));
}
Failure value returned from cantFail wrapped call
number of input constraints does not match number of parameters
UNREACHABLE executed at /root/llvm-project/llvm/include/llvm/Support/Error.h:755!
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/clang++ -gdwarf-4 -g -o /app/output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot -fcolor-diagnostics -fno-crash-diagnostics <source>
1.	<eof> parser at end of file
2.	<source>:5:6: LLVM IR generation of declaration 't34'
3.	<source>:5:6: Generating code for declaration 't34'
 #0 0x000055648ec47a34 PrintStackTraceSignalHandler(void*) Signals.cpp:0:0
 #1 0x000055648ec457dc llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3faf7dc)
 #2 0x000055648eb7b7a8 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x00007ff1c2e39420 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x14420)
 #4 0x00007ff1c290600b raise (/lib/x86_64-linux-gnu/libc.so.6+0x4300b)
 #5 0x00007ff1c28e5859 abort (/lib/x86_64-linux-gnu/libc.so.6+0x22859)
 #6 0x000055648eb8555e (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3eef55e)
 #7 0x000055648e31103c llvm::InlineAsm::InlineAsm(llvm::FunctionType*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, bool, bool, llvm::InlineAsm::AsmDialect, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x367b03c)
 #8 0x000055648e311504 llvm::ConstantUniqueMap<llvm::InlineAsm>::getOrCreate(llvm::PointerType*, llvm::InlineAsmKeyType) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x367b504)
 #9 0x000055648e31247d llvm::InlineAsm::get(llvm::FunctionType*, llvm::StringRef, llvm::StringRef, bool, bool, llvm::InlineAsm::AsmDialect, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x367c47d)
#10 0x000055648f0bad4b clang::CodeGen::CodeGenFunction::EmitAsmStmt(clang::AsmStmt const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4424d4b)
#11 0x000055648f0bfd3b clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt const*, llvm::ArrayRef<clang::Attr const*>) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4429d3b)
#12 0x000055648f0c6084 clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope(clang::CompoundStmt const&, bool, clang::CodeGen::AggValueSlot) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4430084)
#13 0x000055648f1242fe clang::CodeGen::CodeGenFunction::EmitFunctionBody(clang::Stmt const*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x448e2fe)
#14 0x000055648f136894 clang::CodeGen::CodeGenFunction::GenerateCode(clang::GlobalDecl, llvm::Function*, clang::CodeGen::CGFunctionInfo const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x44a0894)
#15 0x000055648f194d59 clang::CodeGen::CodeGenModule::EmitGlobalFunctionDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x44fed59)
#16 0x000055648f190015 clang::CodeGen::CodeGenModule::EmitGlobalDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x44fa015)
#17 0x000055648f1906b3 clang::CodeGen::CodeGenModule::EmitGlobal(clang::GlobalDecl) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x44fa6b3)
#18 0x000055648f199b32 clang::CodeGen::CodeGenModule::EmitTopLevelDecl(clang::Decl*) (.part.0) CodeGenModule.cpp:0:0
#19 0x000055648ff70a19 (anonymous namespace)::CodeGeneratorImpl::HandleTopLevelDecl(clang::DeclGroupRef) ModuleBuilder.cpp:0:0
#20 0x000055648ff62a20 clang::BackendConsumer::HandleTopLevelDecl(clang::DeclGroupRef) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x52cca20)
#21 0x00005564911cd234 clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x6537234)
#22 0x000055648ff6d938 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x52d7938)
#23 0x000055648f803f09 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4b6df09)
#24 0x000055648f78affe clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4af4ffe)
#25 0x000055648f8e9523 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4c53523)
#26 0x000055648c0ddde4 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x1447de4)
#27 0x000055648c0d6f18 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&) driver.cpp:0:0
#28 0x000055648f5f7e19 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#29 0x000055648eb7bf4a llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3ee5f4a)
#30 0x000055648f5f866f clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.0) Job.cpp:0:0
#31 0x000055648f5c1799 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x492b799)
#32 0x000055648f5c221d clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x492c21d)
#33 0x000055648f5cbc6c clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4935c6c)
#34 0x000055648c0dc1c3 clang_main(int, char**) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x14461c3)
#35 0x00007ff1c28e7083 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24083)
#36 0x000055648c0d6b2e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x1440b2e)
clang-16: error: clang frontend command failed with exit code 134 (use -v to see invocation)
Compiler returned: 134

https://godbolt.org/z/sPnfhnMYo

int6 avatar Oct 16 '22 03:10 int6

@llvm/issue-subscribers-clang-frontend

llvmbot avatar Oct 16 '22 03:10 llvmbot

@llvm/issue-subscribers-clang-codegen

llvmbot avatar Oct 16 '22 04:10 llvmbot

CC @tahonermann Could this be related to you fix for https://github.com/llvm/llvm-project/issues/57791

shafik avatar Oct 16 '22 16:10 shafik

@shafik, I don’t think so. The changes made for issue #57791 are specific to the Microsoft style inline assembly enabled with the -fasm-blocks option. I can take a closer look tomorrow to make sure though.

tahonermann avatar Oct 16 '22 17:10 tahonermann

clang 10.0.0 also fails with an assertion, so this is not due to a recent change.

clang++: /home/nicolas/llvm-project/llvm/lib/IR/InlineAsm.cpp:39: llvm::InlineAsm::InlineAsm(llvm::FunctionType *, const std::string &, const std::string &, bool, bool, llvm::InlineAsm::AsmDialect): Assertion `Verify(getFunctionType(), constraints) && "Function type not legal for constraints!"' failed.
...
clang-10: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 10.0.0 (https://github.com/llvm/llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28)
Target: aarch64-unknown-linux-gnu
Thread model: posix

nicolas17 avatar Oct 17 '22 03:10 nicolas17

I spent a little time debugging this.

First, I had to research the semantics of a "*" constraint. I wasn't able to find documentation, but the TargetInfo::validateInputConstraint() in lib/Basic/TargetInfo.cpp indicates that it means that any register can be used (https://github.com/llvm/llvm-project/blob/release/15.x/clang/lib/Basic/TargetInfo.cpp#L836-L837). Changing the constraint to "r" sufficed to avoid the problem. Changing it to "g" produced an appropriate error.

Second, I determined that the issue is not dependent on use of a bit-field; the problem reproduces with this example:

struct S {
  int dm;
} s;
void f() {
  __asm__ volatile("movl %%eax, %0" :: "*"(s.dm));
}

The assertion failure occurs at line 39 of llvm::InlineAsm::InlineAsm() as shown below:

llvm/lib/IR/InlineAsm.cpp:
 30 InlineAsm::InlineAsm(FunctionType *FTy, const std::string &asmString,
 31                      const std::string &constraints, bool hasSideEffects,
 32                      bool isAlignStack, AsmDialect asmDialect, bool canThrow)
 33     : Value(PointerType::getUnqual(FTy), Value::InlineAsmVal),
 34       AsmString(asmString), Constraints(constraints), FTy(FTy),
 35       HasSideEffects(hasSideEffects), IsAlignStack(isAlignStack),
 36       Dialect(asmDialect), CanThrow(canThrow) {
 37 #ifndef NDEBUG
 38   // Do various checks on the constraint string and type.
 39   cantFail(verify(getFunctionType(), constraints));
 40 #endif
 41 }

llvm::InlineAsm::verify() returns in error after having determined that the number of inputs (0) doesn't match the number of expected parameters (1) for the Ty parameter.

llvm/lib/IR/InlineAsm.cpp
260 Error InlineAsm::verify(FunctionType *Ty, StringRef ConstStr) {
...
322   if (Ty->getNumParams() != NumInputs)
323     return makeStringError("number of input constraints does not match number "
324                            "of parameters");
...
328 }

The arguments passed are:

(gdb) p Ty->dump()
void (i32)
.....
(gdb) p ConstStr
$2 = {
  static npos = 18446744073709551615,
  Data = 0x14cd33b0 "~{dirflag},~{fpsr},~{flags}",
  Length = 27
}

The miscalculation of the number of inputs occurs because ConstStr doesn't include a constraint for the input variable (the ~{dirflag}, ~{fpsr}, and ~{flags} all correspond to implicit machine dependent clobber constraints). The input constraint goes missing because the call to SimplifyConstraint() at line 2558 of CodeGenFunction::EmitAsmStmt() below simplifies the constraint from "*" to "". Since no output variables are specified, Constraints is empty at this point and the append of InputConstraint to it at line 2613 ends up having no effect.

clang/lib/CodeGen/CGStmt.cpp:
2328 void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) {
....
2545   for (unsigned i = 0, e = S.getNumInputs(); i != e; i++) {
....
2557     std::string InputConstraint(S.getInputConstraint(i));
2558     InputConstraint = SimplifyConstraint(InputConstraint.c_str(), getTarget(),
2559                                          &OutputConstraintInfos);
....
2613     Constraints += InputConstraint;
2614   }

Since Constraints remains empty, no comma is appended before the clobber constraints are appended; the input constraint is therefore not reflected in the list of constraints.

SimplifyConstraint() is explicit in its intention to ignore "*" (and some other) constraints, but its method of ignoring them has the effect of breaking the constraint validation performed by llvm::InlineAsm::verify().

clang/lib/CodeGen/CGStmt.cpp:
2092 static std::string
2093 SimplifyConstraint(const char *Constraint, const TargetInfo &Target,
2094                  SmallVectorImpl<TargetInfo::ConstraintInfo> *OutCons=nullptr) {
2095   std::string Result;
2096
2097   while (*Constraint) {
2098     switch (*Constraint) {
....
2102     // Ignore these
2103     case '*':
2104     case '?':
2105     case '!':
2106     case '=': // Will see this and the following in mult-alt constraints.
2107     case '+':
2108       break;
....
2134     }
2135
2136     Constraint++;
2137   }
2138
2139   return Result;
2140 }

tahonermann avatar Oct 29 '22 23:10 tahonermann

This issue appears to be strongly related to https://github.com/llvm/llvm-project/issues/50219.

tahonermann avatar Oct 30 '22 02:10 tahonermann

@nikic @xiangzh1 given some of your past work in this area, do you have any thoughts on how to address this? Please see the analysis in the previous comment.

tahonermann avatar Nov 02 '22 19:11 tahonermann

Very good/careful analysis! So the key problem is why SimplifyConstraint() ignore " * " . I think the reason is that current llvm didn't implement the " * " constraint for an independent constraint (it mainly use with other char together, for example "* m" to imply indirection). I'd like to come the the problem self: do we need to support the independent constraint " * " ? Why we not write " r " in the source code.

xiangzh1 avatar Nov 03 '22 06:11 xiangzh1

@xiangzh1:

I'd like to come the the problem self: do we need to support the independent constraint " * " ? Why we not write " r " in the source code.

Good questions that I don't know the answer to.

gcc and icc both reject the original test case. Here is gcc's diagnostic:

<source>: In function 'void f()':
<source>:5:3: warning: 'asm' operand 0 probably does not match constraints
    5 |   __asm__ volatile("movl %%eax, %0" :: "*"(s.dm));
      |   ^~~~~~~
<source>:5:3: error: impossible constraint in 'asm'

Here is icc's diagnostic:

<source>(5): catastrophic error: Cannot match asm operand constraint
compilation aborted for <source> (code 1)

The same results occur if the constraint is changed from "*" to "?" or "!". Likewise for "#" except that icc then accepts the test case. I guess Clang should be diagnosing these constraints as invalid? I'm afraid I'm not sufficiently familiar with these constraints to know what the restrictions for their use are. Pairing them with "r" results in all three compilers accepting the test case.

tahonermann avatar Nov 03 '22 19:11 tahonermann

It's normal that you're not familiar with it, because it very rarely used (may be only shortly used in history). I +1 for it should be sync with gcc (diagnosing these independent constraints as invalid). For these special constraints, only we meet strong requirement from users we should implement them.

xiangzh1 avatar Nov 04 '22 01:11 xiangzh1