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,216 @@
From 59b991088e64b983464a5e481dc3f25d611954fc Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he@intel.com>
Date: Tue, 23 Jun 2026 08:38:37 +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 16 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 c88f25209fc0..6a3327ba0bd7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10394,6 +10394,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 3752a23faa85..c85d3f306bbf 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6147,6 +6147,8 @@ public:
SourceLocation BuiltinLoc,
SourceLocation RParenLoc);

+ void checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call);
+
//===---------------------------- HLSL Features -------------------------===//
Decl *ActOnStartHLSLBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a94f009f3fa6..34e237a44a52 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -19187,3 +19187,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 1b5417a5b2ee..391e8d03fffb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7515,6 +7515,11 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
}
}

+ // 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