Skip to content

Commit d0f6c8b

Browse files
committed
[clang][modules] Avoid serializing all diag mappings in non-deterministic order
When writing a pcm, we serialize diagnostic mappings in order to accurately reproduce the diagnostic environment inside any headers from that module. However, the diagnostic state mapping table contains entries for every diagnostic ID ever accessed, while we only want to serialize the ones that are actually modified from their default value. Futher, we need to serialize them in a deterministic order. rdar://111477511 Differential Revision: https://reviews.llvm.org/D154016 (cherry picked from commit 1ede7b4)
1 parent 0e17e27 commit d0f6c8b

File tree

5 files changed

+141
-22
lines changed

5 files changed

+141
-22
lines changed

clang/include/clang/Basic/DiagnosticIDs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ class DiagnosticMapping {
160160
Result.Severity = Bits & 0x7;
161161
return Result;
162162
}
163+
164+
bool operator==(DiagnosticMapping Other) const {
165+
return serialize() == Other.serialize();
166+
}
163167
};
164168

165169
/// Used for handling and querying diagnostic IDs.
@@ -209,6 +213,9 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
209213
/// default.
210214
static bool isDefaultMappingAsError(unsigned DiagID);
211215

216+
/// Get the default mapping for this diagnostic.
217+
static DiagnosticMapping getDefaultMapping(unsigned DiagID);
218+
212219
/// Determine whether the given built-in diagnostic ID is a Note.
213220
static bool isBuiltinNote(unsigned DiagID);
214221

clang/lib/Basic/Diagnostic.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ void DiagnosticsEngine::ReportDelayed() {
184184
Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3;
185185
}
186186

187+
DiagnosticMapping &
188+
DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
189+
std::pair<iterator, bool> Result =
190+
DiagMap.insert(std::make_pair(Diag, DiagnosticMapping()));
191+
192+
// Initialize the entry if we added it.
193+
if (Result.second)
194+
Result.first->second = DiagnosticIDs::getDefaultMapping(Diag);
195+
196+
return Result.first->second;
197+
}
198+
187199
void DiagnosticsEngine::DiagStateMap::appendFirst(DiagState *State) {
188200
assert(Files.empty() && "not first");
189201
FirstDiagState = CurDiagState = State;

clang/lib/Basic/DiagnosticIDs.cpp

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ CATEGORY(CAS, REFACTORING)
261261
return Found;
262262
}
263263

264-
static DiagnosticMapping GetDefaultDiagMapping(unsigned DiagID) {
264+
DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) {
265265
DiagnosticMapping Info = DiagnosticMapping::Make(
266266
diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false);
267267

@@ -298,21 +298,6 @@ namespace {
298298
};
299299
}
300300

301-
// Unfortunately, the split between DiagnosticIDs and Diagnostic is not
302-
// particularly clean, but for now we just implement this method here so we can
303-
// access GetDefaultDiagMapping.
304-
DiagnosticMapping &
305-
DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
306-
std::pair<iterator, bool> Result =
307-
DiagMap.insert(std::make_pair(Diag, DiagnosticMapping()));
308-
309-
// Initialize the entry if we added it.
310-
if (Result.second)
311-
Result.first->second = GetDefaultDiagMapping(Diag);
312-
313-
return Result.first->second;
314-
}
315-
316301
static const StaticDiagCategoryRec CategoryNameTable[] = {
317302
#define GET_CATEGORY_TABLE
318303
#define CATEGORY(X, ENUM) { X, STR_SIZE(X, uint8_t) },
@@ -454,15 +439,15 @@ bool DiagnosticIDs::isBuiltinExtensionDiag(unsigned DiagID,
454439
return false;
455440

456441
EnabledByDefault =
457-
GetDefaultDiagMapping(DiagID).getSeverity() != diag::Severity::Ignored;
442+
getDefaultMapping(DiagID).getSeverity() != diag::Severity::Ignored;
458443
return true;
459444
}
460445

461446
bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) {
462447
if (DiagID >= diag::DIAG_UPPER_LIMIT)
463448
return false;
464449

465-
return GetDefaultDiagMapping(DiagID).getSeverity() >= diag::Severity::Error;
450+
return getDefaultMapping(DiagID).getSeverity() >= diag::Severity::Error;
466451
}
467452

468453
/// getDescription - Given a diagnostic ID, return a description of the

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,20 +3010,41 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag,
30103010
assert(Flags == EncodeDiagStateFlags(State) &&
30113011
"diag state flags vary in single AST file");
30123012

3013+
// If we ever serialize non-pragma mappings outside the initial state, the
3014+
// code below will need to consider more than getDefaultMapping.
3015+
assert(!IncludeNonPragmaStates ||
3016+
State == Diag.DiagStatesByLoc.FirstDiagState);
3017+
30133018
unsigned &DiagStateID = DiagStateIDMap[State];
30143019
Record.push_back(DiagStateID);
30153020

30163021
if (DiagStateID == 0) {
30173022
DiagStateID = ++CurrID;
3023+
SmallVector<std::pair<unsigned, DiagnosticMapping>> Mappings;
30183024

30193025
// Add a placeholder for the number of mappings.
30203026
auto SizeIdx = Record.size();
30213027
Record.emplace_back();
30223028
for (const auto &I : *State) {
3023-
if (I.second.isPragma() || IncludeNonPragmaStates) {
3024-
Record.push_back(I.first);
3025-
Record.push_back(I.second.serialize());
3026-
}
3029+
// Maybe skip non-pragmas.
3030+
if (!I.second.isPragma() && !IncludeNonPragmaStates)
3031+
continue;
3032+
// Skip default mappings. We have a mapping for every diagnostic ever
3033+
// emitted, regardless of whether it was customized.
3034+
if (!I.second.isPragma() &&
3035+
I.second == DiagnosticIDs::getDefaultMapping(I.first))
3036+
continue;
3037+
Mappings.push_back(I);
3038+
}
3039+
3040+
// Sort by diag::kind for deterministic output.
3041+
llvm::sort(Mappings, [](const auto &LHS, const auto &RHS) {
3042+
return LHS.first < RHS.first;
3043+
});
3044+
3045+
for (const auto &I : Mappings) {
3046+
Record.push_back(I.first);
3047+
Record.push_back(I.second.serialize());
30273048
}
30283049
// Update the placeholder.
30293050
Record[SizeIdx] = (Record.size() - SizeIdx) / 2;

clang/test/Modules/diag-mappings.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Test that diagnostic mappings are emitted only when needed and in order of
2+
// diagnostic ID rather than non-deterministically. This test passes 3
3+
// -W options and expects exactly 3 mappings to be emitted in the pcm. The -W
4+
// options are chosen to be far apart in ID (see DiagnosticIDs.h) so we can
5+
// check they are ordered. We also intentionally trigger several other warnings
6+
// inside the module and ensure they do not show up in the pcm as mappings.
7+
8+
// RUN: rm -rf %t
9+
// RUN: split-file %s %t
10+
11+
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \
12+
// RUN: -fmodules-cache-path=%t/cache -triple x86_64-apple-macosx10.11.0 \
13+
// RUN: %t/main.m -fdisable-module-hash \
14+
// RUN: -Werror=stack-protector -Werror=empty-translation-unit -Werror=float-equal
15+
16+
// RUN: mv %t/cache/A.pcm %t/A1.pcm
17+
18+
// RUN: llvm-bcanalyzer --dump --disable-histogram %t/A1.pcm | FileCheck %s
19+
20+
// CHECK: <DIAG_PRAGMA_MAPPINGS
21+
22+
// == Initial mappings
23+
// Number of mappings = 3
24+
// CHECK-SAME: op2=3
25+
// Common diag id is < 1000 (see DiagnosticIDs.h)
26+
// CHECK-SAME: op3=[[STACK_PROT:[0-9][0-9]?[0-9]?]] op4=
27+
// Parse diag id is somewhere in 1000..2999, leaving room for changes
28+
// CHECK-SAME: op5=[[EMPTY_TU:[12][0-9][0-9][0-9]]] op6=
29+
// Sema diag id is > 2000
30+
// CHECK-SAME: op7=[[FLOAT_EQ:[2-9][0-9][0-9][0-9]]] op8=
31+
32+
// == Pragmas:
33+
// Each pragma creates a mapping table; and each copies the previous table. The
34+
// initial mappings are copied as well, but are not serialized since they have
35+
// isPragma=false.
36+
37+
// == ignored "-Wfloat-equal"
38+
// CHECK-SAME: op{{[0-9]+}}=1
39+
// CHECK-SAME: op{{[0-9]+}}=[[FLOAT_EQ]] op{{[0-9]+}}=
40+
41+
// == ignored "-Wstack-protector"
42+
// CHECK-SAME: op{{[0-9]+}}=2
43+
// CHECK-SAME: op{{[0-9]+}}=[[STACK_PROT]] op{{[0-9]+}}=
44+
// CHECK-SAME: op{{[0-9]+}}=[[FLOAT_EQ]] op{{[0-9]+}}=
45+
46+
// == warning "-Wempty-translation-unit"
47+
// CHECK-SAME: op{{[0-9]+}}=3
48+
// CHECK-SAME: op{{[0-9]+}}=[[STACK_PROT]] op{{[0-9]+}}=
49+
// CHECK-SAME: op{{[0-9]+}}=[[EMPTY_TU]] op{{[0-9]+}}=
50+
// CHECK-SAME: op{{[0-9]+}}=[[FLOAT_EQ]] op{{[0-9]+}}=
51+
52+
// == warning "-Wstack-protector"
53+
// CHECK-SAME: op{{[0-9]+}}=3
54+
// CHECK-SAME: op{{[0-9]+}}=[[STACK_PROT]] op{{[0-9]+}}=
55+
// CHECK-SAME: op{{[0-9]+}}=[[EMPTY_TU]] op{{[0-9]+}}=
56+
// CHECK-SAME: op{{[0-9]+}}=[[FLOAT_EQ]] op{{[0-9]+}}=
57+
58+
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \
59+
// RUN: -fmodules-cache-path=%t/cache -triple x86_64-apple-macosx10.11.0 \
60+
// RUN: %t/main.m -fdisable-module-hash \
61+
// RUN: -Werror=stack-protector -Werror=empty-translation-unit -Werror=float-equal
62+
63+
// RUN: diff %t/cache/A.pcm %t/A1.pcm
64+
65+
//--- module.modulemap
66+
module A { header "a.h" }
67+
68+
//--- a.h
69+
// Lex warning
70+
#warning "w"
71+
72+
static inline void f() {
73+
// Parse warning
74+
;
75+
}
76+
77+
#pragma clang diagnostic push
78+
#pragma clang diagnostic ignored "-Wfloat-equal"
79+
#pragma clang diagnostic ignored "-Wstack-protector"
80+
81+
static inline void g() {
82+
// Sema warning
83+
int x;
84+
}
85+
86+
#pragma clang diagnostic push
87+
#pragma clang diagnostic warning "-Wempty-translation-unit"
88+
#pragma clang diagnostic warning "-Wstack-protector"
89+
90+
#pragma clang diagnostic pop
91+
#pragma clang diagnostic pop
92+
93+
//--- main.m
94+
#import "a.h"

0 commit comments

Comments
 (0)