Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
From d3201b716efacba3c771f9abb58f9dfeb8c0b8bb Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he@intel.com>
Date: Tue, 23 Jun 2026 09:33:44 +0200
Subject: [PATCH] [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 https://github.com/intel/compute-runtime/issues/379#issuecomment-4592083032

Assisted-by: Claude Sonnet 4.6

--

Additional note:
Ported from upstream to clang 14 branch (no SemaOpenCL subsema):
declaration in Sema.h, implementation in SemaChecking.cpp.
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/Sema.h | 2 +
clang/lib/Sema/SemaChecking.cpp | 46 +++++++++++
clang/lib/Sema/SemaExpr.cpp | 5 ++
.../read-image-integer-linear-filter.cl | 80 +++++++++++++++++++
5 files changed, 135 insertions(+)
create mode 100644 clang/test/SemaOpenCL/read-image-integer-linear-filter.cl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 33cbf51ae305..074968b70203 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10240,6 +10240,8 @@ def err_sampler_initializer_not_integer : Error<
"sampler_t initialization requires 32-bit integer, not %0">;
def warn_sampler_initializer_invalid_bits : Warning<
"sampler initializer has invalid %0 bits">, InGroup<SpirCompat>, DefaultIgnore;
+def warn_sampler_argument_invalid_filter : Warning<
+ "'%0' sampler must use CLK_FILTER_NEAREST">, InGroup<SpirCompat>;
def err_sampler_argument_required : Error<
"sampler_t variable required - got %0">;
def err_wrong_sampler_addressspace: Error<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 681a76dfa56a..f6cd3421a69e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5926,6 +5926,8 @@ public:
SourceLocation BuiltinLoc,
SourceLocation RParenLoc);

+ void checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call);
+
//===---------------------------- C++ Features --------------------------===//

// Act on C++ namespaces
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index dae51d0690e6..219545f722fd 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -17886,3 +17886,49 @@ void Sema::CheckTCBEnforcement(const SourceLocation CallExprLoc,
}
}
}
+
+void Sema::checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call) {
+ IdentifierInfo *II = FDecl->getIdentifier();
+ if (!II)
+ return;
+ StringRef Name = II->getName();
+ if (Name != "read_imagei" && Name != "read_imageui")
+ return;
+
+ if (FDecl->getNumParams() < 2)
+ return;
+ QualType ParamTy = FDecl->getParamDecl(1)->getType().getCanonicalType();
+ if (!ParamTy->isSamplerT())
+ return;
+ Expr *SamplerArg = Call->getArg(1);
+
+ Expr *IntExpr = nullptr;
+ Expr *Inner = SamplerArg->IgnoreParenCasts();
+
+ if (auto *DRE = dyn_cast<DeclRefExpr>(Inner)) {
+ if (auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
+ if (const Expr *Init = Var->getAnyInitializer()) {
+ Init = Init->IgnoreParenImpCasts();
+ if (Init->getType()->isIntegerType())
+ IntExpr = const_cast<Expr *>(Init);
+ }
+ }
+ } else if (Inner->getType()->isIntegerType()) {
+ IntExpr = Inner;
+ }
+
+ if (!IntExpr)
+ return;
+
+ Expr::EvalResult EVResult;
+ if (!IntExpr->EvaluateAsInt(EVResult, Context))
+ return;
+
+ uint64_t SamplerValue = EVResult.Val.getInt().getLimitedValue();
+ // Must stay in sync with CLK_FILTER_* defines in opencl-c-base.h.
+ constexpr unsigned FilterModeMask = 0x30u;
+ constexpr unsigned FilterModeLinear = 0x20u;
+ if ((SamplerValue & FilterModeMask) == FilterModeLinear)
+ Diag(SamplerArg->getExprLoc(), diag::warn_sampler_argument_invalid_filter)
+ << Name << SamplerArg->getSourceRange();
+}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 35d84df00ea9..85260f9c045a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7089,6 +7089,11 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
TheCall->setType(FuncT->getCallResultType(Context));
TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType()));

+ // Check read_image{i|ui} sampler argument before ConvertArgumentsForCall
+ // replaces sampler DeclRefExprs with their integer initializers.
+ if (getLangOpts().OpenCL && FDecl)
+ checkBuiltinReadImage(FDecl, TheCall);
+
if (Proto) {
if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc,
IsExecConfig))
diff --git a/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl b/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
new file mode 100644
index 000000000000..a90f689a307d
--- /dev/null
+++ b/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header
+// RUN: %clang_cc1 %s -verify=nowarn -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header -Wno-spir-compat
+// nowarn-no-diagnostics
+
+// OpenCL spec: read_imagei and read_imageui support nearest filter only.
+// CLK_FILTER_LINEAR in the sampler results in undefined behavior; warn.
+
+// Program scope samplers.
+__constant sampler_t glb_linear =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_MIRRORED_REPEAT;
+__constant sampler_t glb_nearest =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_NEAREST | CLK_ADDRESS_CLAMP_TO_EDGE;
+
+kernel void test_read_imageui_global_sampler(read_only image2d_t img) {
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, glb_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+ u = read_imageui(img, glb_nearest, coord); // no warning
+}
+
+kernel void test_read_imagei_global_sampler(read_only image2d_t img) {
+ int2 coord = (int2)(0, 0);
+ int4 i = read_imagei(img, glb_linear, coord); // expected-warning{{'read_imagei' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_local_constant(read_only image2d_t img) {
+ __constant sampler_t s_linear =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, s_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_nearest_constant(read_only image2d_t img) {
+ __constant sampler_t s_nearest =
+ CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, s_nearest, coord); // no warning
+}
+
+kernel void test_read_imageui_local(read_only image2d_t img) {
+ sampler_t s_linear =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, s_linear, coord); // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_nearest(read_only image2d_t img) {
+ sampler_t s_nearest =
+ CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, s_nearest, coord); // no warning
+}
+
+kernel void test_read_imageui_literal(read_only image2d_t img) {
+ int2 coord = (int2)(0, 0);
+ // CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP = 0x21
+ 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}}
+ // CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE = 0x10
+ u = read_imageui(img, CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE, coord); // no warning
+}
+
+kernel void test_read_imageui_parameter(read_only image2d_t img, sampler_t smp) {
+ int2 coord = (int2)(0, 0);
+ uint4 u = read_imageui(img, smp, coord); // no warning
+}
+
+kernel void test_read_imagef_linear(read_only image2d_t img) {
+ // read_imagef supports linear filtering: no warning.
+ float2 coord = (float2)(0.5f, 0.5f);
+ float4 f = read_imagef(img, glb_linear, coord); // no warning
+}
+
+// Samplerless 1D image reads: integer coordinate must not be mistaken for a
+// sampler value even when it looks like CLK_FILTER_LINEAR (e.g. 0x20).
+kernel void test_read_imageui_samplerless(read_only image1d_t img) {
+ uint4 u = read_imageui(img, 0x10); // no warning
+ u = read_imageui(img, 0x20); // no warning
+ u = read_imageui(img, 0x30); // no warning
+ int4 i = read_imagei(img, 0x10); // no warning
+ i = read_imagei(img, 0x20); // no warning
+}
--
2.53.0

Loading