[cfe-commits] [PATCH] OpenCL sampler type

Tanya Lattner lattner at apple.com
Fri Jun 22 10:59:48 PDT 2012


On Jun 22, 2012, at 9:21 AM, Douglas Gregor wrote:

> 
> On Jun 21, 2012, at 4:51 PM, Tanya Lattner wrote:
> 
>> Attached is a patch that adds the OpenCL sampler type to clang. This is a combined patch from the one submitted by Anton Lokhmotov, changes from my tree, and correcting some issues that I saw. 
>> 
>> I've purposely separated out the event and image types from the original patch as they should be made in other separate patches.
>> 
>> Please review.
> 
> Index: include/clang/AST/Type.h
> ===================================================================
> --- include/clang/AST/Type.h	(revision 158757)
> +++ include/clang/AST/Type.h	(working copy)
> @@ -4533,6 +4533,21 @@
>   QualType apply(const ASTContext &Context, const Type* T) const;
> };
> 
> +// OpenCL specific types.
> +class OpenCLSamplerType : public Type {
> +public:
> +  OpenCLSamplerType() :  Type(OpenCLSampler, QualType(), false, false,
> +                              /*VariablyModified=*/false,
> +                              /*Unexpanded parameter pack=*/false) { }
> +  bool isSugared() const { return false; }
> +  QualType desugar() const { return QualType(this, 0); }
> +  
> +  static bool classof(const Type *T) {
> +    return T->getTypeClass() == OpenCLSampler;
> +  }
> +  
> +  static bool classof(const OpenCLSamplerType *) { return true; }
> +};
> 
> // Inline function definitions.
> 
> Apologies if I've missed/forgotten the discussion, but why is this a separate type rather than simply another kind of BuiltinType?
> 


I'll just comment on this before I go through the rest of your comments. This is from Anton back when we were trying to converge on sampler_t in Clang. I had it as a builtin type but from his perspective it should not be one:

"Hi Tanya,

I've invested a lot of time trying to understand the sampler type.  Here's a
summary.

The sampler type (sampler_t) is used for sampler objects, either created via
the OpenCL API and set as a kernel argument, or declared in the program
source (6.11.13.1).  A sampler value is a 32-bit unsigned integer constant,
interpreted as a bit-field for the image read properties: addressing mode,
filtering mode, normalised coordinates.

== Allowed use ==
* The sampler type can be used as the type of a function argument.
* The sampler type can be used to declare a variable in the program scope.
* The sampler type can be used to declare a variable in a kernel function
scope.

== Disallowed use ==
* The sampler type cannot be used to declare an array of samplers.
* The sampler type cannot be used to declare a pointer to a sampler.
* The sampler type cannot be the return type of a function.
* A sampler function argument cannot be modified.
* A sampler variable cannot be modified.

All in all, the sampler type does not behave like a normal integer.  It's
actually an opaque type which backends can implement in a target-specific
way.

I'm going to submit our first patch for the image and sampler types shortly.
Hope we can do a merge in some way.

Best regards,
Anton."

Some other references:
My original patch and discussion:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-March/013865.html
Anton's response:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-March/014118.html
His patch:
http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-March/014121.html

Let me know what you think and if this makes sense to you. 

-Tanya





> Index: include/clang/AST/TypeLoc.h
> ===================================================================
> --- include/clang/AST/TypeLoc.h	(revision 158757)
> +++ include/clang/AST/TypeLoc.h	(working copy)
> @@ -1360,6 +1360,12 @@
>                                                         ComplexType> {
> };
> 
> +// FIXME: location of the OpenCL sampler type.
> +class OpenCLSamplerTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
> +                                                        OpenCLSamplerTypeLoc,
> +                                                        OpenCLSamplerType> {
> +};
> +  
> 
> TypeSpecTypeLoc has the right storage already for the sampler_t type, so there really isn't anything to FIXME here. (Or, you'd get this for free if sampler_t was jut another builtin type).
> 
> Index: lib/Sema/TreeTransform.h
> ===================================================================
> --- lib/Sema/TreeTransform.h	(revision 158757)
> +++ lib/Sema/TreeTransform.h	(working copy)
> @@ -4524,6 +4524,13 @@
> }
> 
> template<typename Derived>
> +QualType TreeTransform<Derived>::TransformOpenCLSamplerType(TypeLocBuilder &TLB,
> +                                                    OpenCLSamplerTypeLoc TL) {
> +  QualType Result = TL.getType();
> +  return Result;
> +}
> +  
> 
> You'll actually have to push an OpenCLSamplerTypeLoc into TLB here; otherwise, the TypeLocBuilder won't actually have the type-location information, and anything based on tree transformation will break with sampler_t types. See TransformBuiltinType for an indication of how to fix this.
> 
> Index: lib/Sema/SemaDecl.cpp
> ===================================================================
> --- lib/Sema/SemaDecl.cpp	(revision 158759)
> +++ lib/Sema/SemaDecl.cpp	(working copy)
> @@ -4098,6 +4098,33 @@
>     // OpenCL __local address space.
>     if (R.getAddressSpace() == LangAS::opencl_local)
>       SC = SC_OpenCLWorkGroupLocal;
> +    
> +    if (isa<OpenCLSamplerType>(R.getTypePtr())) {
> 
> isa<> doesn't look through sugar. Please use R->getAs<OpenCLSamplerType>() to test.
> 
> +      //  The sampler type can be used to declare a variable only
> +      //  in the scope of a kernel function or in the program scope.
> +      if (DC->isFunctionOrMethod()) {
> +        FunctionDecl *FD = dyn_cast<FunctionDecl>(DC);
> +        if (FD->hasAttr<OpenCLKernelAttr>()) {
> +          valid = true;
> +        }
> +      } else if (DC->isTranslationUnit()) {
> +        valid = true;
> +      }
> 
> I know OpenCL doesn't have namespaces or ObjC methods, but I think this code should still be robust against those possibilities. In the hasAttr<OpenCLKernelAttr>() check, please check that 'FD' is non-NULL first (to catch the ObjC method case). I also suggest replacing the DC->isTranslationUnit() check with DC->isFileContext() (to handle namespaces).
> 
> +        if (R.hasQualifiers()
> +            && !((R.getAddressSpace() == LangAS::opencl_constant)
> +            || R.isConstQualified())) {
> +          Diag(D.getIdentifierLoc(), diag::err_opencl_sampler_qualifier);
> +          D.setInvalidType();
> +        }
> 
> This is a very generic diagnostic, although it's testing for something very specific: why not emit something like 'non-const sampler type can only be in the constant address space (not address space %0)'?
> 
> @@ -9222,6 +9249,14 @@
>     }
>   }
> 
> +  if (!InvalidDecl) {
> +    // Sampler types cannot be used to declare a field.
> +    if (isa<OpenCLSamplerType>(T)) {
> +      Diag(Loc, diag::err_opencl_type_field) << II << T;
> +      InvalidDecl = true;
> +    }
> +  }
> +  
> 
> isa -> getAs
> 
> Index: include/clang/Basic/DiagnosticSemaKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticSemaKinds.td	(revision 158759)
> +++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
> @@ -5626,6 +5626,13 @@
>   "kernel functions cannot be declared static">;
> def err_static_function_scope : Error<
>   "variables in function scope cannot be declared static">;
> +def err_opencl_sampler_declaration : Error<
> +  "declaring sampler variable in this context is not allowed">;
> +def err_opencl_sampler_qualifier : Error<"using invalid qualifier with sampler type">;
> +def err_opencl_type_pointer : Error<"%0: declaring pointer to type %1 is not allowed">;
> +def err_opencl_type_array : Error<"%0: declaring array of type %1 is not allowed">;
> +def err_opencl_type_return : Error<"declaring return value of type %0 is not allowed">;
> +  def err_opencl_type_field : Error<"%0: declaring field of type %1 is not allowed">;
> 
> I find the prefixing of '%0:' in these diagnostics to be a bit awkward, for two reasons: first, Clang tends to put the names of things in their English context when it uses names, e.g., 'variable %0 declared as a pointer to type %1'. Moreover, in most of the cases where these diagnostics are emitted, there may not be any name, which is going to leave a very awkward ':'. Why not drop the names entirely, and follow what other things in SemaType do, e.g., "cannot declare field of type %0" or "cannot declare an array of type %0"?
> 
> @@ -1132,6 +1135,13 @@
>     return QualType();
>   }
> 
> +  // Using the sampler types to declare a pointer is not allowed.
> +  if (isa<OpenCLSamplerType>(T)) {
> +    Diag(Loc, diag::err_opencl_type_pointer)
> +    << getPrintableNameForEntity(Entity) << T;
> +    return QualType();
> +  }
> 
> isa -> getAs
> 
> @@ -1281,6 +1291,13 @@
>     return QualType();
>   }
> 
> +  // Using the image and sampler types to declare an array is not allowed
> +  if (isa<OpenCLSamplerType>(T)) {
> +    Diag(Loc, diag::err_opencl_type_array)
> +    << getPrintableNameForEntity(Entity) << T;
> +    return QualType();
> +  }
> +  
> 
> isa -> getAs
> 
> @@ -2411,6 +2428,14 @@
>           // Only the outermost chunk is marked noexcept, of course.
>           EPI.ExceptionSpecType = EST_BasicNoexcept;
>         }
> +        
> +        // Using the sampler types to declare a return value
> +        // is not allowed.
> +        if (LangOpts.OpenCL) {
> +          if (isa<OpenCLSamplerType>(T.getTypePtr())) {
> +            S.Diag(DeclType.Loc, diag::err_opencl_type_return) << T;
> +          }
> +        }
> 
> There's no need for the OpenCL check here; also, isa -> getAs
> 
> Index: lib/Sema/SemaExpr.cpp
> ===================================================================
> --- lib/Sema/SemaExpr.cpp	(revision 158757)
> +++ lib/Sema/SemaExpr.cpp	(working copy)
> @@ -5511,6 +5511,12 @@
>   LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
>   RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();
> 
> +  // A special case: sampler constructor.
> +  if (getLangOpts().OpenCL) {
> +    if (isa<OpenCLSamplerType>(LHSType)) {
> +      if (RHSType->isIntegerType()) return Compatible;
> +    }
> +  }
> 
> No need for the OpenCL check. isa -> getAs. Also, should there be an equivalent standard conversion on the C++ side of the world?
> 
> @@ -7466,6 +7472,10 @@
>   case Expr::MLV_ConstQualified:
>     Diag = diag::err_typecheck_assign_const;
> 
> +    // OpenCL sampler check, no need to do anything further.
> +    if (S.getLangOpts().OpenCL && isa<OpenCLSamplerType>(E->getType()))
> +      break;
> +
> 
> No need for the OpenCL check. isa -> getAs.
> 
> Index: lib/Sema/Sema.cpp
> ===================================================================
> --- lib/Sema/Sema.cpp	(revision 158757)
> +++ lib/Sema/Sema.cpp	(working copy)
> @@ -273,6 +273,15 @@
>   if (ExprTy == TypeTy)
>     return Owned(E);
> 
> +  // A special case: attempting to initialize a sampler.
> +  if (getLangOpts().OpenCL) {
> +    const OpenCLSamplerType *tp = Ty.getTypePtr()->getAs<OpenCLSamplerType>();
> +    if (tp != NULL) {
> +      // Casting is not required - CodeGen will take care of it
> +      return Owned(E); 
> +    }
> +  }
> +
> 
> We don't want to do this. It's a conversion in the language, and should be represented in the AST. The meta-point here is that CodeGen is supposed to be "simple", in the sense that it doesn't have to reason about language semantics. The cast kind is there to tell CodeGen what to do.
> 
> @@ -2063,6 +2064,10 @@
>   mangleType(T->getElementType());
> }
> 
> +void CXXNameMangler::mangleType(const OpenCLSamplerType *T) {
> +  Out << "uSampler";
> +}
> 
> This should be "u7Sampler", because 'Sampler' is not a source-name.
> 
> 'twould be nice if the libc++abi demangler knew about this mangling, but that's just gravy.
> 
> +  // OpenCL specific types.
> +  if (Ctx.getLangOpts().OpenCL) {
> +    const QualType ETy = E->getType();
> +    const Type *ETyP = ETy.getTypePtr();
> +    if (isa<OpenCLSamplerType>(ETyP)) {
> +      return Cl::CM_ConstQualified;
> +    }
> +  }
> 
> This has the isa -> getAs issue.
> 
> However, I don't understand the purpose, because it seems like this should fall out from the fact that the lvalue will either have 'const' type (which is handled above) or is in the constant address space (which seems like a missing general case in this code). 
> 
> Index: lib/CodeGen/CGRTTI.cpp
> ===================================================================
> --- lib/CodeGen/CGRTTI.cpp	(revision 158757)
> +++ lib/CodeGen/CGRTTI.cpp	(working copy)
> @@ -397,6 +397,9 @@
> #include "clang/AST/TypeNodes.def"
>     llvm_unreachable("Non-canonical and dependent types shouldn't get here");
> 
> +  case Type::OpenCLSampler:
> +    llvm_unreachable("OpenCL types shouldn't get here");
> +      
>   case Type::LValueReference:
>   case Type::RValueReference:
>     llvm_unreachable("References shouldn't get here");
> 
> OpenCL and RTTI, like oil and water.
> 
> Index: lib/CodeGen/CGDebugInfo.cpp
> ===================================================================
> --- lib/CodeGen/CGDebugInfo.cpp	(revision 158757)
> +++ lib/CodeGen/CGDebugInfo.cpp	(working copy)
> @@ -1769,6 +1769,10 @@
>   case Type::Atomic:
>     return CreateType(cast<AtomicType>(Ty), Unit);
> 
> +      
> +  case Type::OpenCLSampler:
> +      llvm_unreachable("unsupported");
> +      
> 
> Rather than aborting, could we simply use 'Int32' here and add a FIXME if anyone ever cares to improve the situation? 
> 
> Index: lib/CodeGen/CGDecl.cpp
> ===================================================================
> --- lib/CodeGen/CGDecl.cpp	(revision 158757)
> +++ lib/CodeGen/CGDecl.cpp	(working copy)
> @@ -111,6 +111,12 @@
>   case SC_None:
>   case SC_Auto:
>   case SC_Register:
> +      if (isa<OpenCLSamplerType>(D.getType())) {
> +        // Emit a global "static" sampler declaration.
> +        llvm::GlobalValue::LinkageTypes Linkage =
> +        llvm::GlobalValue::InternalLinkage;
> +        return EmitStaticVarDecl(D, Linkage);
> +      }
>     return EmitAutoVarDecl(D);
>   case SC_Static: {
>     llvm::GlobalValue::LinkageTypes Linkage =
> 
> CodeGen shouldn't be making this decision. I suspect there's a more general rule at work here that should be handled by Sema and encoded in the AST. For example, perhaps local variables in the __constant address space effectively have 'static' storage? And that a 'const sampler_t' local variable is effectively in the __constant address space?
> 
> Also, isa -> getAs.
> 
> @@ -184,7 +190,20 @@
>     Name = GetStaticDeclName(*this, D, Separator);
> 
>   llvm::Type *LTy = CGM.getTypes().ConvertTypeForMem(Ty);
> -  llvm::GlobalVariable *GV =
> +  llvm::GlobalVariable *GV = 0;
> +  
> +  // Handle OpenCL sampler types.
> +  if (getLangOpts().OpenCL) {
> +    if (isa<OpenCLSamplerType>(D.getType().getTypePtr())) {
> +      GV = new llvm::GlobalVariable(CGM.getModule(), LTy,
> +                                    true, llvm::GlobalValue::ExternalLinkage,
> +                                    0, Name, 0,
> +                                    D.isThreadSpecified(),
> +                                    CGM.getContext().getTargetAddressSpace(Ty));
> +    }
> +  }
> +  
> 
> Unnecessary OpenCL check; isa -> getAs.
> 
> However, I suspect this code shouldn't be needed at all, if we modeled the storage of variables of type sampler_t in the AST (as mentioned above). However, I could certainly be missing something.
> 
> 	- Doug




More information about the cfe-commits mailing list