Skip to content

Commit 52e0005

Browse files
authored
[OpenCL] Warn if filter_mode is linear in read_image{i|ui} (#204086)
Per OpenCL spec: The read_image{i|ui} calls support a nearest filter only. The filter_mode specified in sampler must be set to CLK_FILTER_NEAREST; otherwise the values returned are undefined. Warn users when they apply a linear filter accidentally. Address intel/compute-runtime#379 (comment) Assisted-by: Claude Sonnet 4.6
1 parent c7e815b commit 52e0005

5 files changed

Lines changed: 139 additions & 0 deletions

File tree

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11838,6 +11838,8 @@ def err_sampler_initializer_not_integer : Error<
1183811838
"sampler_t initialization requires 32-bit integer, not %0">;
1183911839
def warn_sampler_initializer_invalid_bits : Warning<
1184011840
"sampler initializer has invalid %0 bits">, InGroup<SpirCompat>, DefaultIgnore;
11841+
def warn_sampler_argument_invalid_filter : Warning<
11842+
"'%0' sampler must use CLK_FILTER_NEAREST">, InGroup<SpirCompat>;
1184111843
def err_sampler_argument_required : Error<
1184211844
"sampler_t variable required - got %0">;
1184311845
def err_wrong_sampler_addressspace: Error<

clang/include/clang/Sema/SemaOpenCL.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class SemaOpenCL : public SemaBase {
100100
bool checkBuiltinKernelWorkGroupSize(CallExpr *TheCall);
101101

102102
bool checkBuiltinNDRangeAndBlock(CallExpr *TheCall);
103+
104+
void checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call);
103105
};
104106

105107
} // namespace clang

clang/lib/Sema/SemaExpr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "clang/Sema/SemaFixItUtils.h"
6060
#include "clang/Sema/SemaHLSL.h"
6161
#include "clang/Sema/SemaObjC.h"
62+
#include "clang/Sema/SemaOpenCL.h"
6263
#include "clang/Sema/SemaOpenMP.h"
6364
#include "clang/Sema/SemaPseudoObject.h"
6465
#include "clang/Sema/Template.h"
@@ -7287,6 +7288,12 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
72877288
}
72887289
}
72897290

7291+
// Check read_image{i|ui} sampler argument before ConvertArgumentsForCall
7292+
// replaces sampler DeclRefExprs with their integer initializers.
7293+
if (getLangOpts().OpenCL && FDecl) {
7294+
OpenCL().checkBuiltinReadImage(FDecl, TheCall);
7295+
}
7296+
72907297
if (Proto) {
72917298
if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc,
72927299
IsExecConfig))

clang/lib/Sema/SemaOpenCL.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
#include "clang/Sema/SemaOpenCL.h"
1414
#include "clang/AST/Attr.h"
15+
#include "clang/AST/Decl.h"
1516
#include "clang/AST/DeclBase.h"
17+
#include "clang/AST/Expr.h"
1618
#include "clang/Basic/DiagnosticSema.h"
1719
#include "clang/Sema/ParsedAttr.h"
1820
#include "clang/Sema/Sema.h"
@@ -576,4 +578,50 @@ bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) {
576578
return false;
577579
}
578580

581+
void SemaOpenCL::checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call) {
582+
IdentifierInfo *II = FDecl->getIdentifier();
583+
if (!II)
584+
return;
585+
StringRef Name = II->getName();
586+
if (Name != "read_imagei" && Name != "read_imageui")
587+
return;
588+
589+
if (FDecl->getNumParams() < 2)
590+
return;
591+
QualType ParamTy = FDecl->getParamDecl(1)->getType().getCanonicalType();
592+
if (!ParamTy->isSamplerT())
593+
return;
594+
Expr *SamplerArg = Call->getArg(1);
595+
596+
Expr *IntExpr = nullptr;
597+
Expr *Inner = SamplerArg->IgnoreParenCasts();
598+
599+
if (auto *DRE = dyn_cast<DeclRefExpr>(Inner)) {
600+
if (auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
601+
if (const Expr *Init = Var->getAnyInitializer()) {
602+
Init = Init->IgnoreParenImpCasts();
603+
if (Init->getType()->isIntegerType())
604+
IntExpr = const_cast<Expr *>(Init);
605+
}
606+
}
607+
} else if (Inner->getType()->isIntegerType()) {
608+
IntExpr = Inner;
609+
}
610+
611+
if (!IntExpr)
612+
return;
613+
614+
Expr::EvalResult EVResult;
615+
if (!IntExpr->EvaluateAsInt(EVResult, getASTContext()))
616+
return;
617+
618+
uint64_t SamplerValue = EVResult.Val.getInt().getLimitedValue();
619+
// Must stay in sync with CLK_FILTER_* defines in opencl-c-base.h.
620+
constexpr unsigned FilterModeMask = 0x30u;
621+
constexpr unsigned FilterModeLinear = 0x20u;
622+
if ((SamplerValue & FilterModeMask) == FilterModeLinear)
623+
Diag(SamplerArg->getExprLoc(), diag::warn_sampler_argument_invalid_filter)
624+
<< Name << SamplerArg->getSourceRange();
625+
}
626+
579627
} // namespace clang
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %clang_cc1 %s -verify -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header
2+
// RUN: %clang_cc1 %s -verify=nowarn -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header -Wno-spir-compat
3+
// nowarn-no-diagnostics
4+
5+
// OpenCL spec: read_imagei and read_imageui support nearest filter only.
6+
// CLK_FILTER_LINEAR in the sampler results in undefined behavior; warn.
7+
8+
// Program scope samplers.
9+
__constant sampler_t glb_linear =
10+
CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_MIRRORED_REPEAT;
11+
__constant sampler_t glb_nearest =
12+
CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP_TO_EDGE;
13+
14+
kernel void test_read_imageui_global_sampler(read_only image2d_t img) {
15+
int2 coord = (int2)(0, 0);
16+
uint4 u = read_imageui(img, glb_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
17+
u = read_imageui(img, glb_nearest, coord); // no warning
18+
}
19+
20+
kernel void test_read_imagei_global_sampler(read_only image2d_t img) {
21+
int2 coord = (int2)(0, 0);
22+
int4 i = read_imagei(img, glb_linear, coord); // expected-warning{{'read_imagei' sampler must use CLK_FILTER_NEAREST}}
23+
}
24+
25+
kernel void test_read_imageui_local_constant(read_only image2d_t img) {
26+
__constant sampler_t s_linear =
27+
CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
28+
int2 coord = (int2)(0, 0);
29+
uint4 u = read_imageui(img, s_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
30+
}
31+
32+
kernel void test_read_imageui_nearest_constant(read_only image2d_t img) {
33+
__constant sampler_t s_nearest =
34+
CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
35+
int2 coord = (int2)(0, 0);
36+
uint4 u = read_imageui(img, s_nearest, coord); // no warning
37+
}
38+
39+
kernel void test_read_imageui_local(read_only image2d_t img) {
40+
sampler_t s_linear =
41+
CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
42+
int2 coord = (int2)(0, 0);
43+
uint4 u = read_imageui(img, s_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
44+
}
45+
46+
kernel void test_read_imageui_nearest(read_only image2d_t img) {
47+
sampler_t s_nearest =
48+
CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
49+
int2 coord = (int2)(0, 0);
50+
uint4 u = read_imageui(img, s_nearest, coord); // no warning
51+
}
52+
53+
kernel void test_read_imageui_literal(read_only image2d_t img) {
54+
int2 coord = (int2)(0, 0);
55+
// CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP = 0x21
56+
uint4 u = read_imageui(img, CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
57+
// CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE = 0x10
58+
u = read_imageui(img, CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE, coord); // no warning
59+
}
60+
61+
kernel void test_read_imageui_parameter(read_only image2d_t img, sampler_t smp) {
62+
int2 coord = (int2)(0, 0);
63+
uint4 u = read_imageui(img, smp, coord); // no warning
64+
}
65+
66+
kernel void test_read_imagef_linear(read_only image2d_t img) {
67+
// read_imagef supports linear filtering: no warning.
68+
float2 coord = (float2)(0.5f, 0.5f);
69+
float4 f = read_imagef(img, glb_linear, coord); // no warning
70+
}
71+
72+
// Samplerless 1D image reads: integer coordinate must not be mistaken for a
73+
// sampler value even when it looks like CLK_FILTER_LINEAR (e.g. 0x20).
74+
kernel void test_read_imageui_samplerless(read_only image1d_t img) {
75+
uint4 u = read_imageui(img, 0x10); // no warning
76+
u = read_imageui(img, 0x20); // no warning
77+
u = read_imageui(img, 0x30); // no warning
78+
int4 i = read_imagei(img, 0x10); // no warning
79+
i = read_imagei(img, 0x20); // no warning
80+
}

0 commit comments

Comments
 (0)