Skip to content

Commit

Permalink
Provide a frontend based error for always_inline functions that require
Browse files Browse the repository at this point in the history
target features that the caller function doesn't provide. This matches
the existing backend failure to inline functions that don't have
matching target features - and diagnoses earlier in the case of
always_inline.

Fix up a few test cases that were, in fact, invalid if you tried
to generate code from the backend with the specified target features
and add a couple of tests to illustrate what's going on.

This should fix PR25246.

llvm-svn: 252834
  • Loading branch information
echristo committed Nov 12, 2015
1 parent fbfd97e commit 2b2d56f
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 123 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ def err_builtin_definition : Error<"definition of builtin function %0">;
def err_arm_invalid_specialreg : Error<"invalid special register for builtin">;
def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">;
def err_builtin_needs_feature : Error<"%0 needs target feature %1">;
def err_function_needs_feature
: Error<"function %0 and always_inline callee function %1 are required to "
"have matching target features">;
def warn_builtin_unknown : Warning<"use of unknown builtin %0">,
InGroup<ImplicitFunctionDeclare>, DefaultError;
def warn_dyn_class_memaccess : Warning<
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3747,6 +3747,15 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
assert(CalleeType->isFunctionPointerType() &&
"Call must have function pointer type!");

if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl))
// If this isn't an always_inline function we can't guarantee that any
// function isn't being used correctly so only check if we have the
// attribute and a set of target attributes that might be different from
// our default.
if (TargetDecl->hasAttr<AlwaysInlineAttr>() &&
TargetDecl->hasAttr<TargetAttr>())
checkTargetFeatures(E, FD);

CalleeType = getContext().getCanonicalType(CalleeType);

const auto *FnType =
Expand Down
90 changes: 65 additions & 25 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1843,7 +1843,8 @@ template void CGBuilderInserter<PreserveNames>::InsertHelper(
llvm::BasicBlock::iterator InsertPt) const;
#undef PreserveNames

// Returns true if we have a valid set of target features.
// Emits an error if we don't have a valid set of target features for the
// called function.
void CodeGenFunction::checkTargetFeatures(const CallExpr *E,
const FunctionDecl *TargetDecl) {
// Early exit if this is an indirect call.
Expand All @@ -1856,31 +1857,70 @@ void CodeGenFunction::checkTargetFeatures(const CallExpr *E,
if (!FD)
return;

// 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.
unsigned BuiltinID = TargetDecl->getBuiltinID();
const char *FeatureList =
CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID);
if (BuiltinID) {
SmallVector<StringRef, 1> 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 (!FeatureList || StringRef(FeatureList) == "")
return;
// If there aren't any required features listed then go ahead and return.
if (ReqFeatures.empty())
return;

llvm::StringMap<bool> FeatureMap;
CGM.getFunctionFeatureMap(FeatureMap, FD);

// If we have at least one of the features in the feature list return
// true, otherwise return false.
SmallVector<StringRef, 1> AttrFeatures;
StringRef(FeatureList).split(AttrFeatures, ",");
if (!std::all_of(AttrFeatures.begin(), AttrFeatures.end(),
[&](StringRef &Feature) {
SmallVector<StringRef, 1> OrFeatures;
Feature.split(OrFeatures, "|");
return std::any_of(OrFeatures.begin(), OrFeatures.end(),
[&](StringRef &Feature) {
return FeatureMap[Feature];
});
}))
CGM.getDiags().Report(E->getLocStart(), diag::err_builtin_needs_feature)
<< TargetDecl->getDeclName()
<< CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID);
}
// Now build up the set of caller features and verify that all the required
// features are there.
llvm::StringMap<bool> CallerFeatureMap;
CGM.getFunctionFeatureMap(CallerFeatureMap, FD);

// If we have at least one of the features in the feature list return
// true, otherwise return false.
if (!std::all_of(
ReqFeatures.begin(), ReqFeatures.end(), [&](StringRef &Feature) {
SmallVector<StringRef, 1> OrFeatures;
Feature.split(OrFeatures, "|");
return std::any_of(OrFeatures.begin(), OrFeatures.end(),
[&](StringRef &Feature) {
return CallerFeatureMap.lookup(Feature);
});
}))
CGM.getDiags().Report(E->getLocStart(), diag::err_builtin_needs_feature)
<< TargetDecl->getDeclName()
<< CGM.getContext().BuiltinInfo.getRequiredFeatures(BuiltinID);

} else if (TargetDecl->hasAttr<TargetAttr>()) {
// Get the required features for the callee.
SmallVector<StringRef, 1> ReqFeatures;
llvm::StringMap<bool> CalleeFeatureMap;
CGM.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl);
for (const auto &F : CalleeFeatureMap)
ReqFeatures.push_back(F.getKey());
// If there aren't any required features listed then go ahead and return.
if (ReqFeatures.empty())
return;

// Now get the features that the caller provides.
llvm::StringMap<bool> CallerFeatureMap;
CGM.getFunctionFeatureMap(CallerFeatureMap, FD);

// If we have at least one of the features in the feature list return
// true, otherwise return false.
if (!std::all_of(
ReqFeatures.begin(), ReqFeatures.end(), [&](StringRef &Feature) {
SmallVector<StringRef, 1> OrFeatures;
Feature.split(OrFeatures, "|");
return std::any_of(OrFeatures.begin(), OrFeatures.end(),
[&](StringRef &Feature) {
return CallerFeatureMap.lookup(Feature);
});
}))
CGM.getDiags().Report(E->getLocStart(), diag::err_function_needs_feature)
<< FD->getDeclName() << TargetDecl->getDeclName();
}
}
4 changes: 2 additions & 2 deletions clang/test/CodeGen/3dnow-builtins.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// REQUIRES: x86-registered-target
// RUN: %clang_cc1 %s -triple=x86_64-unknown-unknown -target-feature +3dnow -emit-llvm -o - -Werror | FileCheck %s
// RUN: %clang_cc1 %s -triple=x86_64-unknown-unknown -target-feature +3dnow -S -o - -Werror | FileCheck %s --check-prefix=CHECK-ASM
// RUN: %clang_cc1 %s -triple=x86_64-unknown-unknown -target-feature +3dnowa -emit-llvm -o - -Werror | FileCheck %s
// RUN: %clang_cc1 %s -triple=x86_64-unknown-unknown -target-feature +3dnowa -S -o - -Werror | FileCheck %s --check-prefix=CHECK-ASM

// Don't include mm_malloc.h, it's system specific.
#define __MM_MALLOC_H
Expand Down
96 changes: 0 additions & 96 deletions clang/test/CodeGen/avx512vl-builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,102 +5,6 @@

#include <immintrin.h>

__mmask8 test_mm256_cmpeq_epi32_mask(__m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_cmpeq_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.d.256
return (__mmask8)_mm256_cmpeq_epi32_mask(__a, __b);
}

__mmask8 test_mm256_mask_cmpeq_epi32_mask(__mmask8 __u, __m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_mask_cmpeq_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.d.256
return (__mmask8)_mm256_mask_cmpeq_epi32_mask(__u, __a, __b);
}

__mmask8 test_mm_cmpeq_epi32_mask(__m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_cmpeq_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.d.128
return (__mmask8)_mm_cmpeq_epi32_mask(__a, __b);
}

__mmask8 test_mm_mask_cmpeq_epi32_mask(__mmask8 __u, __m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_mask_cmpeq_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.d.128
return (__mmask8)_mm_mask_cmpeq_epi32_mask(__u, __a, __b);
}

__mmask8 test_mm256_cmpeq_epi64_mask(__m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_cmpeq_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.q.256
return (__mmask8)_mm256_cmpeq_epi64_mask(__a, __b);
}

__mmask8 test_mm256_mask_cmpeq_epi64_mask(__mmask8 __u, __m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_mask_cmpeq_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.q.256
return (__mmask8)_mm256_mask_cmpeq_epi64_mask(__u, __a, __b);
}

__mmask8 test_mm_cmpeq_epi64_mask(__m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_cmpeq_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.q.128
return (__mmask8)_mm_cmpeq_epi64_mask(__a, __b);
}

__mmask8 test_mm_mask_cmpeq_epi64_mask(__mmask8 __u, __m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_mask_cmpeq_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpeq.q.128
return (__mmask8)_mm_mask_cmpeq_epi64_mask(__u, __a, __b);
}

__mmask8 test_mm256_cmpgt_epi32_mask(__m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_cmpgt_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.d.256
return (__mmask8)_mm256_cmpgt_epi32_mask(__a, __b);
}

__mmask8 test_mm256_mask_cmpgt_epi32_mask(__mmask8 __u, __m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_mask_cmpgt_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.d.256
return (__mmask8)_mm256_mask_cmpgt_epi32_mask(__u, __a, __b);
}

__mmask8 test_mm_cmpgt_epi32_mask(__m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_cmpgt_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.d.128
return (__mmask8)_mm_cmpgt_epi32_mask(__a, __b);
}

__mmask8 test_mm_mask_cmpgt_epi32_mask(__mmask8 __u, __m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_mask_cmpgt_epi32_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.d.128
return (__mmask8)_mm_mask_cmpgt_epi32_mask(__u, __a, __b);
}

__mmask8 test_mm256_cmpgt_epi64_mask(__m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_cmpgt_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.q.256
return (__mmask8)_mm256_cmpgt_epi64_mask(__a, __b);
}

__mmask8 test_mm256_mask_cmpgt_epi64_mask(__mmask8 __u, __m256i __a, __m256i __b) {
// CHECK-LABEL: @test_mm256_mask_cmpgt_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.q.256
return (__mmask8)_mm256_mask_cmpgt_epi64_mask(__u, __a, __b);
}

__mmask8 test_mm_cmpgt_epi64_mask(__m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_cmpgt_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.q.128
return (__mmask8)_mm_cmpgt_epi64_mask(__a, __b);
}

__mmask8 test_mm_mask_cmpgt_epi64_mask(__mmask8 __u, __m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_mask_cmpgt_epi64_mask
// CHECK: @llvm.x86.avx512.mask.pcmpgt.q.128
return (__mmask8)_mm_mask_cmpgt_epi64_mask(__u, __a, __b);
}

__mmask8 test_mm_cmpeq_epu32_mask(__m128i __a, __m128i __b) {
// CHECK-LABEL: @test_mm_cmpeq_epu32_mask
// CHECK: @llvm.x86.avx512.mask.ucmp.d.128(<4 x i32> {{.*}}, <4 x i32> {{.*}}, i32 0, i8 -1)
Expand Down
7 changes: 7 additions & 0 deletions clang/test/CodeGen/target-features-error-2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -S -verify -o -
#define __MM_MALLOC_H
#include <x86intrin.h>

int baz(__m256i a) {
return _mm256_extract_epi32(a, 3); // expected-error {{function 'baz' and always_inline callee function '_mm256_extract_epi32' are required to have matching target features}}
}
8 changes: 8 additions & 0 deletions clang/test/CodeGen/target-features-error.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// RUN: %clang_cc1 %s -triple=x86_64-linux-gnu -S -verify -o -
int __attribute__((target("avx"), always_inline)) foo(int a) {
return a + 4;
}
int bar() {
return foo(4); // expected-error {{function 'bar' and always_inline callee function 'foo' are required to have matching target features}}
}

0 comments on commit 2b2d56f

Please sign in to comment.