Skip to content

Commit

Permalink
[LTO] Improve diagnostics handling when parsing module-level inline a…
Browse files Browse the repository at this point in the history
…ssembly (#75726)

Non-LTO compiles set the buffer name to "<inline asm>"
(`AsmPrinter::addInlineAsmDiagBuffer`) and pass diagnostics to
`ClangDiagnosticHandler` (through the `MCContext` handler in
`MachineModuleInfoWrapperPass::doInitialization`) to ensure that
the exit code is 1 in the presence of errors. In contrast, LTO compiles
spuriously succeed even if error messages are printed.

```
% cat a.c
void _start() {}
asm("unknown instruction");
% clang -c a.c
<inline asm>:1:1: error: invalid instruction mnemonic 'unknown'
    1 | unknown instruction
      | ^
1 error generated.
% clang -c -flto a.c; echo $?  # -flto=thin is the same
error: invalid instruction mnemonic 'unknown'
unknown instruction
^~~~~~~
error: invalid instruction mnemonic 'unknown'
unknown instruction
^~~~~~~
0
```

`CollectAsmSymbols` parses inline assembly and is transitively called by
both `ModuleSummaryIndexAnalysis::run` and `WriteBitcodeToFile`, leading
to duplicate diagnostics.

This patch updates `CollectAsmSymbols` to be similar to non-LTO
compiles.
```
% clang -c -flto=thin a.c; echo $?
<inline asm>:1:1: error: invalid instruction mnemonic 'unknown'
    1 | unknown instruction
      | ^
1 errors generated.
1
```

The `HasErrors` check does not prevent duplicate warnings but assembler
warnings are very uncommon.
  • Loading branch information
MaskRay authored Dec 18, 2023
1 parent 672f1a0 commit 96aca7c
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 6 deletions.
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ void BackendConsumer::anchor() { }
} // namespace clang

bool ClangDiagnosticHandler::handleDiagnostics(const DiagnosticInfo &DI) {
if (DI.getSeverity() == DS_Error)
HasErrors = true;
BackendCon->DiagnosticHandlerImpl(DI);
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions clang/test/CodeGen/invalid_global_asm.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// REQUIRES: arm-registered-target
// RUN: not %clang_cc1 -emit-obj -triple armv6-unknown-unknown -o %t %s 2>&1 | FileCheck %s
// RUN: not %clang_cc1 -emit-obj -triple armv6-unknown-unknown -flto -o %t %s 2>&1 | FileCheck %s

/// Test the diagnostic behavior considering the whole system including the driver.
// RUN: not %clang --target=armv6-unknown-unknown -c -flto=thin -o %t %s 2>&1 | FileCheck %s
#pragma clang diagnostic ignored "-Wmissing-noreturn"
__asm__(".Lfoo: movw r2, #:lower16:.Lbar - .Lfoo");
// CHECK: <inline asm>:1:8: error: instruction requires: armv6t2
// CHECK-NOT: error:
8 changes: 3 additions & 5 deletions lld/test/MachO/lto-module-asm-err.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
; RUN: not %lld %t.bc -o /dev/null 2>&1 | FileCheck %s --check-prefix=REGULAR

;; For regular LTO, the original module name is lost.
;; TODO Fix the line number
; REGULAR: error: ld-temp.o <inline asm>:3:1: invalid instruction mnemonic 'invalid'
; REGULAR: error: <inline asm>:2:1: invalid instruction mnemonic 'invalid'

; RUN: opt -module-summary %s -o %t.bc
; RUN: not %lld %t.bc -o /dev/null 2>&1 | FileCheck %s --check-prefix=THIN
; RUN: not opt -module-summary %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=THIN

; THIN: error: {{.*}}.bc <inline asm>:2:1: invalid instruction mnemonic 'invalid'
; THIN: error: <inline asm>:2:1: invalid instruction mnemonic 'invalid'

target triple = "x86_64-apple-macosx10.15.0"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/DiagnosticHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class DiagnosticInfo;
/// which remarks are enabled.
struct DiagnosticHandler {
void *DiagnosticContext = nullptr;
bool HasErrors = false;
DiagnosticHandler(void *DiagContext = nullptr)
: DiagnosticContext(DiagContext) {}
virtual ~DiagnosticHandler() = default;
Expand Down
16 changes: 15 additions & 1 deletion llvm/lib/Object/ModuleSymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "RecordStreamer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalValue.h"
Expand Down Expand Up @@ -68,6 +69,11 @@ void ModuleSymbolTable::addModule(Module *M) {
static void
initializeRecordStreamer(const Module &M,
function_ref<void(RecordStreamer &)> Init) {
// This function may be called twice, once for ModuleSummaryIndexAnalysis and
// the other when writing the IR symbol table. If parsing inline assembly has
// caused errors in the first run, suppress the second run.
if (M.getContext().getDiagHandlerPtr()->HasErrors)
return;
StringRef InlineAsm = M.getModuleInlineAsm();
if (InlineAsm.empty())
return;
Expand Down Expand Up @@ -95,7 +101,8 @@ initializeRecordStreamer(const Module &M,
if (!MCII)
return;

std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer(InlineAsm));
std::unique_ptr<MemoryBuffer> Buffer(
MemoryBuffer::getMemBuffer(InlineAsm, "<inline asm>"));
SourceMgr SrcMgr;
SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());

Expand All @@ -115,6 +122,13 @@ initializeRecordStreamer(const Module &M,
if (!TAP)
return;

MCCtx.setDiagnosticHandler([&](const SMDiagnostic &SMD, bool IsInlineAsm,
const SourceMgr &SrcMgr,
std::vector<const MDNode *> &LocInfos) {
M.getContext().diagnose(
DiagnosticInfoSrcMgr(SMD, M.getName(), IsInlineAsm, /*LocCookie=*/0));
});

// Module-level inline asm is assumed to use At&t syntax (see
// AsmPrinter::doInitialization()).
Parser->setAssemblerDialect(InlineAsm::AD_ATT);
Expand Down

0 comments on commit 96aca7c

Please sign in to comment.