///
/// \returns true to indicate the target options are invalid, or false
/// otherwise.
- virtual bool ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) {
+ virtual bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain,
+ bool AllowCompatibleDifferences) {
return false;
}
void ReadModuleMapFile(StringRef ModuleMapPath) override;
bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain,
bool AllowCompatibleDifferences) override;
- bool ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) override;
+ bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain,
+ bool AllowCompatibleDifferences) override;
bool ReadDiagnosticOptions(IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
bool Complain) override;
bool ReadFileSystemOptions(const FileSystemOptions &FSOpts,
bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain,
bool AllowCompatibleDifferences) override;
- bool ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) override;
+ bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain,
+ bool AllowCompatibleDifferences) override;
bool ReadDiagnosticOptions(IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
bool Complain) override;
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, bool Complain,
ASTReaderListener &Listener,
bool AllowCompatibleDifferences);
static bool ParseTargetOptions(const RecordData &Record, bool Complain,
- ASTReaderListener &Listener);
+ ASTReaderListener &Listener,
+ bool AllowCompatibleDifferences);
static bool ParseDiagnosticOptions(const RecordData &Record, bool Complain,
ASTReaderListener &Listener);
static bool ParseFileSystemOptions(const RecordData &Record, bool Complain,
Second->ReadLanguageOptions(LangOpts, Complain,
AllowCompatibleDifferences);
}
-bool
-ChainedASTReaderListener::ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) {
- return First->ReadTargetOptions(TargetOpts, Complain) ||
- Second->ReadTargetOptions(TargetOpts, Complain);
+bool ChainedASTReaderListener::ReadTargetOptions(
+ const TargetOptions &TargetOpts, bool Complain,
+ bool AllowCompatibleDifferences) {
+ return First->ReadTargetOptions(TargetOpts, Complain,
+ AllowCompatibleDifferences) ||
+ Second->ReadTargetOptions(TargetOpts, Complain,
+ AllowCompatibleDifferences);
}
bool ChainedASTReaderListener::ReadDiagnosticOptions(
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, bool Complain) {
/// \returns true if the target options mis-match, false otherwise.
static bool checkTargetOptions(const TargetOptions &TargetOpts,
const TargetOptions &ExistingTargetOpts,
- DiagnosticsEngine *Diags) {
+ DiagnosticsEngine *Diags,
+ bool AllowCompatibleDifferences = true) {
#define CHECK_TARGET_OPT(Field, Name) \
if (TargetOpts.Field != ExistingTargetOpts.Field) { \
if (Diags) \
return true; \
}
+ // The triple and ABI must match exactly.
CHECK_TARGET_OPT(Triple, "target");
- CHECK_TARGET_OPT(CPU, "target CPU");
CHECK_TARGET_OPT(ABI, "target ABI");
+
+ // We can tolerate different CPUs in many cases, notably when one CPU
+ // supports a strict superset of another. When allowing compatible
+ // differences skip this check.
+ if (!AllowCompatibleDifferences)
+ CHECK_TARGET_OPT(CPU, "target CPU");
+
#undef CHECK_TARGET_OPT
// Compare feature sets.
std::sort(ExistingFeatures.begin(), ExistingFeatures.end());
std::sort(ReadFeatures.begin(), ReadFeatures.end());
- unsigned ExistingIdx = 0, ExistingN = ExistingFeatures.size();
- unsigned ReadIdx = 0, ReadN = ReadFeatures.size();
- while (ExistingIdx < ExistingN && ReadIdx < ReadN) {
- if (ExistingFeatures[ExistingIdx] == ReadFeatures[ReadIdx]) {
- ++ExistingIdx;
- ++ReadIdx;
- continue;
- }
-
- if (ReadFeatures[ReadIdx] < ExistingFeatures[ExistingIdx]) {
- if (Diags)
- Diags->Report(diag::err_pch_targetopt_feature_mismatch)
- << false << ReadFeatures[ReadIdx];
- return true;
- }
-
- if (Diags)
- Diags->Report(diag::err_pch_targetopt_feature_mismatch)
- << true << ExistingFeatures[ExistingIdx];
- return true;
- }
+ // We compute the set difference in both directions explicitly so that we can
+ // diagnose the differences differently.
+ SmallVector<StringRef, 4> UnmatchedExistingFeatures, UnmatchedReadFeatures;
+ std::set_difference(
+ ExistingFeatures.begin(), ExistingFeatures.end(), ReadFeatures.begin(),
+ ReadFeatures.end(), std::back_inserter(UnmatchedExistingFeatures));
+ std::set_difference(ReadFeatures.begin(), ReadFeatures.end(),
+ ExistingFeatures.begin(), ExistingFeatures.end(),
+ std::back_inserter(UnmatchedReadFeatures));
+
+ // If we are allowing compatible differences and the read feature set is
+ // a strict subset of the existing feature set, there is nothing to diagnose.
+ if (AllowCompatibleDifferences && UnmatchedReadFeatures.empty())
+ return false;
- if (ExistingIdx < ExistingN) {
- if (Diags)
+ if (Diags) {
+ for (StringRef Feature : UnmatchedReadFeatures)
Diags->Report(diag::err_pch_targetopt_feature_mismatch)
- << true << ExistingFeatures[ExistingIdx];
- return true;
- }
-
- if (ReadIdx < ReadN) {
- if (Diags)
+ << /* is-existing-feature */ false << Feature;
+ for (StringRef Feature : UnmatchedExistingFeatures)
Diags->Report(diag::err_pch_targetopt_feature_mismatch)
- << false << ReadFeatures[ReadIdx];
- return true;
+ << /* is-existing-feature */ true << Feature;
}
- return false;
+ return !UnmatchedReadFeatures.empty() || !UnmatchedExistingFeatures.empty();
}
bool
}
bool PCHValidator::ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) {
+ bool Complain,
+ bool AllowCompatibleDifferences) {
const TargetOptions &ExistingTargetOpts = PP.getTargetInfo().getTargetOpts();
return checkTargetOptions(TargetOpts, ExistingTargetOpts,
- Complain? &Reader.Diags : nullptr);
+ Complain ? &Reader.Diags : nullptr,
+ AllowCompatibleDifferences);
}
namespace {
case TARGET_OPTIONS: {
bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch)==0;
if (Listener && &F == *ModuleMgr.begin() &&
- ParseTargetOptions(Record, Complain, *Listener) &&
+ ParseTargetOptions(Record, Complain, *Listener,
+ AllowCompatibleConfigurationMismatch) &&
!DisableValidation && !AllowConfigurationMismatch)
return ConfigurationMismatch;
break;
return checkLanguageOptions(ExistingLangOpts, LangOpts, nullptr,
AllowCompatibleDifferences);
}
- bool ReadTargetOptions(const TargetOptions &TargetOpts,
- bool Complain) override {
- return checkTargetOptions(ExistingTargetOpts, TargetOpts, nullptr);
+ bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain,
+ bool AllowCompatibleDifferences) override {
+ return checkTargetOptions(ExistingTargetOpts, TargetOpts, nullptr,
+ AllowCompatibleDifferences);
}
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef SpecificModuleCachePath,
break;
case TARGET_OPTIONS:
- if (ParseTargetOptions(Record, false, Listener))
+ if (ParseTargetOptions(Record, false, Listener,
+ /*AllowCompatibleConfigurationMismatch*/ false))
return true;
break;
AllowCompatibleDifferences);
}
-bool ASTReader::ParseTargetOptions(const RecordData &Record,
- bool Complain,
- ASTReaderListener &Listener) {
+bool ASTReader::ParseTargetOptions(const RecordData &Record, bool Complain,
+ ASTReaderListener &Listener,
+ bool AllowCompatibleDifferences) {
unsigned Idx = 0;
TargetOptions TargetOpts;
TargetOpts.Triple = ReadString(Record, Idx);
TargetOpts.Features.push_back(ReadString(Record, Idx));
}
- return Listener.ReadTargetOptions(TargetOpts, Complain);
+ return Listener.ReadTargetOptions(TargetOpts, Complain,
+ AllowCompatibleDifferences);
}
bool ASTReader::ParseDiagnosticOptions(const RecordData &Record, bool Complain,
--- /dev/null
+// RUN: rm -rf %t
+// RUN: cd %S
+//
+// RUN: %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN: -iquote Inputs/merge-target-features \
+// RUN: -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN: -fmodule-map-file-home-is-cwd \
+// RUN: -emit-module -fmodule-name=foo -o %t/foo.pcm \
+// RUN: -triple i386-unknown-unknown \
+// RUN: -target-cpu i386 -target-feature +sse2 \
+// RUN: Inputs/merge-target-features/module.modulemap
+//
+// RUN: not %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN: -iquote Inputs/merge-target-features \
+// RUN: -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN: -fmodule-map-file-home-is-cwd \
+// RUN: -fmodule-map-file=Inputs/merge-target-features/module.modulemap \
+// RUN: -fmodule-file=%t/foo.pcm \
+// RUN: -triple i386-unknown-unknown \
+// RUN: -target-cpu i386 \
+// RUN: -fsyntax-only merge-target-features.cpp 2>&1 \
+// RUN: | FileCheck --check-prefix=SUBSET %s
+// SUBSET: AST file was compiled with the target feature'+sse2' but the current translation unit is not
+//
+// RUN: %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN: -iquote Inputs/merge-target-features \
+// RUN: -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN: -fmodule-map-file-home-is-cwd \
+// RUN: -fmodule-map-file=Inputs/merge-target-features/module.modulemap \
+// RUN: -fmodule-file=%t/foo.pcm \
+// RUN: -triple i386-unknown-unknown \
+// RUN: -target-cpu i386 -target-feature +sse2 \
+// RUN: -fsyntax-only merge-target-features.cpp 2>&1 \
+// RUN: | FileCheck --allow-empty --check-prefix=SAME %s
+// SAME-NOT: error:
+//
+// RUN: %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN: -iquote Inputs/merge-target-features \
+// RUN: -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN: -fmodule-map-file-home-is-cwd \
+// RUN: -fmodule-map-file=Inputs/merge-target-features/module.modulemap \
+// RUN: -fmodule-file=%t/foo.pcm \
+// RUN: -triple i386-unknown-unknown \
+// RUN: -target-cpu i386 -target-feature +sse2 -target-feature +sse3 \
+// RUN: -fsyntax-only merge-target-features.cpp 2>&1 \
+// RUN: | FileCheck --allow-empty --check-prefix=SUPERSET %s
+// SUPERSET-NOT: error:
+//
+// RUN: not %clang_cc1 -fmodules -x c++ -fmodules-cache-path=%t \
+// RUN: -iquote Inputs/merge-target-features \
+// RUN: -fno-implicit-modules -fno-modules-implicit-maps \
+// RUN: -fmodule-map-file-home-is-cwd \
+// RUN: -fmodule-map-file=Inputs/merge-target-features/module.modulemap \
+// RUN: -fmodule-file=%t/foo.pcm \
+// RUN: -triple i386-unknown-unknown \
+// RUN: -target-cpu i386 -target-feature +cx16 \
+// RUN: -fsyntax-only merge-target-features.cpp 2>&1 \
+// RUN: | FileCheck --check-prefix=MISMATCH %s
+// MISMATCH: AST file was compiled with the target feature'+sse2' but the current translation unit is not
+// MISMATCH: current translation unit was compiled with the target feature'+cx16' but the AST file was not
+
+#include "foo.h"
+
+int test(int x) {
+ return foo(x);
+}