[clang] [OpenCL] Warn if filter_mode is linear in read_image{i|ui} (PR #204086)
Wenju He via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 16 03:14:54 PDT 2026
https://github.com/wenju-he updated https://github.com/llvm/llvm-project/pull/204086
>From f90642ac5da3d3da7d2cc648a88121e80520ab95 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Tue, 16 Jun 2026 01:08:58 -0700
Subject: [PATCH 1/3] [OpenCL] Warn if filter_mode is linear in
read_image{i|ui}
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.
Assisted-by: Claude Sonnet 4.6
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/SemaOpenCL.h | 2 +
clang/lib/Sema/SemaExpr.cpp | 7 ++
clang/lib/Sema/SemaOpenCL.cpp | 49 +++++++++++++
.../read-image-integer-linear-filter.cl | 68 +++++++++++++++++++
5 files changed, 128 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 f84cd8dca6d4c..57c32216a2f1e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11834,6 +11834,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/SemaOpenCL.h b/clang/include/clang/Sema/SemaOpenCL.h
index 04b2b617fb12f..51c2e1703b504 100644
--- a/clang/include/clang/Sema/SemaOpenCL.h
+++ b/clang/include/clang/Sema/SemaOpenCL.h
@@ -100,6 +100,8 @@ class SemaOpenCL : public SemaBase {
bool checkBuiltinKernelWorkGroupSize(CallExpr *TheCall);
bool checkBuiltinNDRangeAndBlock(CallExpr *TheCall);
+
+ void checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call);
};
} // namespace clang
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f2745425588f5..345d093aed88a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -59,6 +59,7 @@
#include "clang/Sema/SemaFixItUtils.h"
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaObjC.h"
+#include "clang/Sema/SemaOpenCL.h"
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/SemaPseudoObject.h"
#include "clang/Sema/Template.h"
@@ -7283,6 +7284,12 @@ 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) {
+ OpenCL().checkBuiltinReadImage(FDecl, TheCall);
+ }
+
if (Proto) {
if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc,
IsExecConfig))
diff --git a/clang/lib/Sema/SemaOpenCL.cpp b/clang/lib/Sema/SemaOpenCL.cpp
index f11a40e3964ff..d307858e1660f 100644
--- a/clang/lib/Sema/SemaOpenCL.cpp
+++ b/clang/lib/Sema/SemaOpenCL.cpp
@@ -12,7 +12,9 @@
#include "clang/Sema/SemaOpenCL.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/Expr.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Sema.h"
@@ -576,4 +578,51 @@ bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) {
return false;
}
+void SemaOpenCL::checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call) {
+ IdentifierInfo *II = FDecl->getIdentifier();
+ if (!II)
+ return;
+ StringRef Name = II->getName();
+ if (Name != "read_imagei" && Name != "read_imageui")
+ return;
+
+ // read_image{i|ui} take (image, sampler, coord); sampler is arg[1].
+ if (Call->getNumArgs() < 2)
+ return;
+ Expr *SamplerArg = Call->getArg(1);
+ QualType ArgTy = SamplerArg->getType().getCanonicalType();
+ if (!ArgTy->isSamplerT() && !ArgTy->isIntegerType())
+ return;
+
+ 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, getASTContext()))
+ return;
+
+ uint64_t SamplerValue = EVResult.Val.getInt().getLimitedValue();
+ // Bit layout: |...|FilterMode[5:4]|AddressMode[3:1]|NormalizedCoords[0]|
+ // CLK_FILTER_LINEAR = 0x20 => FilterMode bits = 2
+ if (((SamplerValue & 0x30u) >> 4) == 2)
+ Diag(SamplerArg->getExprLoc(),
+ diag::warn_sampler_argument_invalid_filter)
+ << Name << SamplerArg->getSourceRange();
+}
+
} // namespace clang
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 0000000000000..2a7acff8a8298
--- /dev/null
+++ b/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only -cl-std=CL2.0 -finclude-default-header
+
+// 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, global uint *out) {
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, glb_linear, coord).s0; // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+ *out = read_imageui(img, glb_nearest, coord).s0; // no warning
+}
+
+kernel void test_read_imagei_global_sampler(read_only image2d_t img, global int *out) {
+ int2 coord = (int2)(0, 0);
+ *out = read_imagei(img, glb_linear, coord).s0; // expected-warning{{'read_imagei' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_local_constant(read_only image2d_t img, global uint *out) {
+ __constant sampler_t s_linear =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, s_linear, coord).s0; // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_nearest_constant(read_only image2d_t img, global uint *out) {
+ __constant sampler_t s_nearest =
+ CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, s_nearest, coord).s0; // no warning
+}
+
+kernel void test_read_imageui_local(read_only image2d_t img, global uint *out) {
+ sampler_t s_linear =
+ CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP;
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, s_linear, coord).s0; // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+}
+
+kernel void test_read_imageui_nearest(read_only image2d_t img, global uint *out) {
+ sampler_t s_nearest =
+ CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE;
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, s_nearest, coord).s0; // no warning
+}
+
+kernel void test_read_imageui_literal(read_only image2d_t img, global uint *out) {
+ int2 coord = (int2)(0, 0);
+ // CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP = 0x21
+ *out = read_imageui(img, CLK_NORMALIZED_COORDS_TRUE | CLK_FILTER_LINEAR | CLK_ADDRESS_CLAMP, coord).s0; // expected-warning{{'read_imageui' sampler must use CLK_FILTER_NEAREST}}
+ // CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE = 0x10
+ *out = read_imageui(img, CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST | CLK_ADDRESS_NONE, coord).s0; // no warning
+}
+
+kernel void test_read_imageui_parameter(read_only image2d_t img, global uint *out, sampler_t smp) {
+ int2 coord = (int2)(0, 0);
+ *out = read_imageui(img, smp, coord).s0; // no warning
+}
+
+kernel void test_read_imagef_linear(read_only image2d_t img, global float *out) {
+ // read_imagef supports linear filtering: no warning.
+ float2 coord = (float2)(0.5f, 0.5f);
+ *out = read_imagef(img, glb_linear, coord).s0; // no warning
+}
>From be35e55a2c073b300b405297178b2f73ccab7004 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Tue, 16 Jun 2026 01:49:29 -0700
Subject: [PATCH 2/3] clang-format
---
clang/lib/Sema/SemaOpenCL.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaOpenCL.cpp b/clang/lib/Sema/SemaOpenCL.cpp
index d307858e1660f..832c8fd66ba40 100644
--- a/clang/lib/Sema/SemaOpenCL.cpp
+++ b/clang/lib/Sema/SemaOpenCL.cpp
@@ -620,8 +620,7 @@ void SemaOpenCL::checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call) {
// Bit layout: |...|FilterMode[5:4]|AddressMode[3:1]|NormalizedCoords[0]|
// CLK_FILTER_LINEAR = 0x20 => FilterMode bits = 2
if (((SamplerValue & 0x30u) >> 4) == 2)
- Diag(SamplerArg->getExprLoc(),
- diag::warn_sampler_argument_invalid_filter)
+ Diag(SamplerArg->getExprLoc(), diag::warn_sampler_argument_invalid_filter)
<< Name << SamplerArg->getSourceRange();
}
>From fd22a864e8c55963f90582a1444583c6e030b809 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Tue, 16 Jun 2026 03:10:02 -0700
Subject: [PATCH 3/3] add -fdeclare-opencl-builtins to fix test on aarch64
---
clang/test/SemaOpenCL/read-image-integer-linear-filter.cl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl b/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
index 2a7acff8a8298..3654908f7b925 100644
--- a/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
+++ b/clang/test/SemaOpenCL/read-image-integer-linear-filter.cl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -verify -fsyntax-only -cl-std=CL2.0 -finclude-default-header
+// RUN: %clang_cc1 %s -verify -fsyntax-only -cl-std=CL2.0 -fdeclare-opencl-builtins -finclude-default-header
// OpenCL spec: read_imagei and read_imageui support nearest filter only.
// CLK_FILTER_LINEAR in the sampler results in undefined behavior; warn.
More information about the cfe-commits
mailing list