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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
