Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -611,6 +611,9 @@ : Error<"always_inline function %1 requires target feature '%2', but would " "be inlined into function %0 that is compiled without support for " "'%2'">; +def err_function_needs_cpu + : Error<"always_inline function %1 requires target CPU '%2', but would " + "be inlined into function %0 that is compiled for '%3'">; def warn_builtin_unknown : Warning<"use of unknown builtin %0">, InGroup, DefaultError; def warn_cstruct_memaccess : Warning< Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -2267,35 +2267,6 @@ CGF->InsertHelper(I, Name, BB, InsertPt); } -static bool hasRequiredFeatures(const SmallVectorImpl &ReqFeatures, - CodeGenModule &CGM, const FunctionDecl *FD, - std::string &FirstMissing) { - // If there aren't any required features listed then go ahead and return. - if (ReqFeatures.empty()) - return false; - - // Now build up the set of caller features and verify that all the required - // features are there. - llvm::StringMap CallerFeatureMap; - CGM.getFunctionFeatureMap(CallerFeatureMap, FD); - - // If we have at least one of the features in the feature list return - // true, otherwise return false. - return std::all_of( - ReqFeatures.begin(), ReqFeatures.end(), [&](StringRef Feature) { - SmallVector OrFeatures; - Feature.split(OrFeatures, '|'); - return std::any_of(OrFeatures.begin(), OrFeatures.end(), - [&](StringRef Feature) { - if (!CallerFeatureMap.lookup(Feature)) { - FirstMissing = Feature.str(); - return false; - } - return true; - }); - }); -} - // Emits an error if we don't have a valid set of target features for the // called function. void CodeGenFunction::checkTargetFeatures(const CallExpr *E, @@ -2313,34 +2284,68 @@ // Grab the required features for the call. For a builtin this is listed in // the td file with the default cpu, for an always_inline function this is any // listed cpu and any listed features. + SmallVector ReqFeatures; + // CalleeFeatureMap needs to be at this scope to prevent ReqFeatures from + // having dangling StringRefs. + llvm::StringMap CalleeFeatureMap; + StringRef RequiredCPU; unsigned BuiltinID = TargetDecl->getBuiltinID(); - std::string MissingFeature; if (BuiltinID) { - SmallVector ReqFeatures; const char *FeatureList = CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID); // Return if the builtin doesn't have any required features. if (!FeatureList || StringRef(FeatureList) == "") return; StringRef(FeatureList).split(ReqFeatures, ','); - if (!hasRequiredFeatures(ReqFeatures, CGM, FD, MissingFeature)) - CGM.getDiags().Report(E->getLocStart(), diag::err_builtin_needs_feature) - << TargetDecl->getDeclName() - << CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID); - } else if (TargetDecl->hasAttr()) { - // Get the required features for the callee. - SmallVector ReqFeatures; - llvm::StringMap CalleeFeatureMap; - CGM.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); + CGM.getFunctionCPUAndFeatureMap(CalleeFeatureMap, RequiredCPU, TargetDecl); for (const auto &F : CalleeFeatureMap) { // Only positive features are "required". if (F.getValue()) ReqFeatures.push_back(F.getKey()); } - if (!hasRequiredFeatures(ReqFeatures, CGM, FD, MissingFeature)) - CGM.getDiags().Report(E->getLocStart(), diag::err_function_needs_feature) - << FD->getDeclName() << TargetDecl->getDeclName() << MissingFeature; + } + // TODO: What if the caller has a TargetAttr, but the callee doesn't. + + // Now build up the set of caller features and verify that all the required + // features are there. + llvm::StringMap CallerFeatureMap; + StringRef CallerCPU; + CGM.getFunctionCPUAndFeatureMap(CallerFeatureMap, CallerCPU, FD); + + // Make sure the CPUs match unless this is a builtin. + if (!BuiltinID && CallerCPU != RequiredCPU) { + CGM.getDiags().Report(E->getLocStart(), diag::err_function_needs_cpu) + << FD->getDeclName() << TargetDecl->getDeclName() << RequiredCPU + << CallerCPU; + return; + } + + // If we have all of the required features, we're done. + std::string FirstMissing; + if (llvm::all_of(ReqFeatures, + [&](StringRef Feature) { + SmallVector OrFeatures; + Feature.split(OrFeatures, '|'); + return llvm::any_of(OrFeatures, + [&](StringRef Feature) { + if (!CallerFeatureMap.lookup(Feature)) { + FirstMissing = Feature.str(); + return false; + } + return true; + }); + })) + return; + + // We have a missing feature, diagnose it. + if (BuiltinID) { + CGM.getDiags().Report(E->getLocStart(), diag::err_builtin_needs_feature) + << TargetDecl->getDeclName() + << CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID); + } else { + CGM.getDiags().Report(E->getLocStart(), diag::err_function_needs_feature) + << FD->getDeclName() << TargetDecl->getDeclName() << FirstMissing; } } Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1083,9 +1083,10 @@ void AddDefaultFnAttrs(llvm::Function &F); // Fills in the supplied string map with the set of target features for the - // passed in function. - void getFunctionFeatureMap(llvm::StringMap &FeatureMap, - const FunctionDecl *FD); + // passed in function. Also fills in the target CPU. + void getFunctionCPUAndFeatureMap(llvm::StringMap &FeatureMap, + StringRef &TargetCPU, + const FunctionDecl *FD); StringRef getMangledName(GlobalDecl GD); StringRef getBlockMangledName(GlobalDecl GD, const BlockDecl *BD); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1282,23 +1282,15 @@ bool AddedAttr = false; if (TD) { llvm::StringMap FeatureMap; - getFunctionFeatureMap(FeatureMap, FD); + getFunctionCPUAndFeatureMap(FeatureMap, TargetCPU, FD); // Produce the canonical string for this set of features. for (const llvm::StringMap::value_type &Entry : FeatureMap) Features.push_back((Entry.getValue() ? "+" : "-") + Entry.getKey().str()); - - // Now add the target-cpu and target-features to the function. - // While we populated the feature map above, we still need to - // get and parse the target attribute so we can get the cpu for - // the function. - TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); - if (ParsedAttr.Architecture != "" && - getTarget().isValidCPUName(ParsedAttr.Architecture)) - TargetCPU = ParsedAttr.Architecture; } else { // Otherwise just add the existing target cpu and target features to the // function. + TargetCPU = getTarget().getTargetOpts().CPU; Features = getTarget().getTargetOpts().Features; } @@ -4988,9 +4980,11 @@ // Fills in the supplied string map with the set of target features for the // passed in function. -void CodeGenModule::getFunctionFeatureMap(llvm::StringMap &FeatureMap, - const FunctionDecl *FD) { - StringRef TargetCPU = Target.getTargetOpts().CPU; +void +CodeGenModule::getFunctionCPUAndFeatureMap(llvm::StringMap &FeatureMap, + StringRef &TargetCPU, + const FunctionDecl *FD) { + TargetCPU = Target.getTargetOpts().CPU; if (const auto *TD = FD->getAttr()) { // If we have a TargetAttr build up the feature map based on that. TargetAttr::ParsedTargetAttr ParsedAttr = TD->parse(); Index: test/CodeGen/target-cpu-error-2.c =================================================================== --- /dev/null +++ test/CodeGen/target-cpu-error-2.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -S -verify -o - -target-cpu nehalem +int __attribute__((target("arch=silvermont"), always_inline)) foo(int a) { + return a + 4; +} +int bar() { + return foo(4); // expected-error {{always_inline function 'foo' requires target CPU 'silvermont', but would be inlined into function 'bar' that is compiled for 'nehalem'}} +} + Index: test/CodeGen/target-cpu-error-3.c =================================================================== --- /dev/null +++ test/CodeGen/target-cpu-error-3.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -S -verify -o - -target-cpu silvermont +int __attribute__((target("sse4.2"), always_inline)) foo(int a) { + return a + 4; +} +int __attribute__((target("arch=nehalem"))) bar() { + return foo(4); // expected-error {{always_inline function 'foo' requires target CPU 'silvermont', but would be inlined into function 'bar' that is compiled for 'nehalem'}} +} + Index: test/CodeGen/target-cpu-error.c =================================================================== --- /dev/null +++ test/CodeGen/target-cpu-error.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -S -verify -o - +int __attribute__((target("arch=silvermont"), always_inline)) foo(int a) { + return a + 4; +} +int __attribute__((target("arch=nehalem"))) bar() { + return foo(4); // expected-error {{always_inline function 'foo' requires target CPU 'silvermont', but would be inlined into function 'bar' that is compiled for 'nehalem'}} +} +