From 0bc4b2d33731279e41c4ce581a26a9f4386e08bb Mon Sep 17 00:00:00 2001 From: Yaxun Liu Date: Thu, 28 Jul 2016 19:26:30 +0000 Subject: [OpenCL] Generate opaque type for sampler_t and function call for the initializer Currently Clang use int32 to represent sampler_t, which have been a source of issue for some backends, because in some backends sampler_t cannot be represented by int32. They have to depend on kernel argument metadata and use IPA to find the sampler arguments and global variables and transform them to target specific sampler type. This patch uses opaque pointer type opencl.sampler_t* for sampler_t. For each use of file-scope sampler variable, it generates a function call of __translate_sampler_initializer. For each initialization of function-scope sampler variable, it generates a function call of __translate_sampler_initializer. Each builtin library can implement its own __translate_sampler_initializer(). Since the real sampler type tends to be architecture dependent, allowing it to be initialized by a library function simplifies backend design. A typical implementation of __translate_sampler_initializer could be a table lookup of real sampler literal values. Since its argument is always a literal, the returned pointer is known at compile time and easily optimized to finally become some literal values directly put into image read instructions. This patch is partially based on Alexey Sotkin's work in Khronos Clang (https://github.com/KhronosGroup/SPIR/commit/3d4eec61623502fc306e8c67c9868be2b136e42b). Differential Revision: https://reviews.llvm.org/D21567 llvm-svn: 277024 --- clang/lib/AST/ASTContext.cpp | 9 +-- clang/lib/AST/Expr.cpp | 4 +- clang/lib/AST/ExprConstant.cpp | 2 + clang/lib/CodeGen/CGDebugInfo.cpp | 5 +- clang/lib/CodeGen/CGDebugInfo.h | 1 + clang/lib/CodeGen/CGExpr.cpp | 1 + clang/lib/CodeGen/CGExprAgg.cpp | 1 + clang/lib/CodeGen/CGExprComplex.cpp | 1 + clang/lib/CodeGen/CGExprConstant.cpp | 3 + clang/lib/CodeGen/CGExprScalar.cpp | 5 +- clang/lib/CodeGen/CGOpenCLRuntime.cpp | 11 +++- clang/lib/CodeGen/CGOpenCLRuntime.h | 6 +- clang/lib/CodeGen/CodeGenModule.cpp | 17 ++++- clang/lib/CodeGen/CodeGenModule.h | 3 + clang/lib/Edit/RewriteObjCFoundationAPI.cpp | 1 + clang/lib/Frontend/CompilerInvocation.cpp | 7 +++ clang/lib/Sema/SemaExpr.cpp | 5 ++ clang/lib/Sema/SemaInit.cpp | 91 ++++++++++++++++++++++++--- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 1 + 19 files changed, 154 insertions(+), 20 deletions(-) (limited to 'clang/lib') diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index cf7f5947a2f..0d6ad9d0d60 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1734,11 +1734,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(0); Align = Target->getPointerAlign(0); break; - case BuiltinType::OCLSampler: - // Samplers are modeled as integers. - Width = Target->getIntWidth(); - Align = Target->getIntAlign(); + case BuiltinType::OCLSampler: { + auto AS = getTargetAddressSpace(LangAS::opencl_constant); + Width = Target->getPointerWidth(AS); + Align = Target->getPointerAlign(AS); break; + } case BuiltinType::OCLEvent: case BuiltinType::OCLClkEvent: case BuiltinType::OCLQueue: diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 71bbfdc3369..e835686a1a9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1569,6 +1569,7 @@ bool CastExpr::CastConsistency() const { case CK_ARCReclaimReturnedObject: case CK_ARCExtendBlockObject: case CK_ZeroToOCLEvent: + case CK_IntToOCLSampler: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); goto CheckNoBasePath; @@ -2747,7 +2748,8 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, CE->getCastKind() == CK_ToUnion || CE->getCastKind() == CK_ConstructorConversion || CE->getCastKind() == CK_NonAtomicToAtomic || - CE->getCastKind() == CK_AtomicToNonAtomic) + CE->getCastKind() == CK_AtomicToNonAtomic || + CE->getCastKind() == CK_IntToOCLSampler) return CE->getSubExpr()->isConstantInitializer(Ctx, false, Culprit); break; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 760cda99c54..176f1ba8422 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -8056,6 +8056,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_ZeroToOCLEvent: case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -8547,6 +8548,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_ZeroToOCLEvent: case CK_NonAtomicToAtomic: case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 9763e9b51a8..b99b519a966 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -518,9 +518,8 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { SingletonId); #include "clang/Basic/OpenCLImageTypes.def" case BuiltinType::OCLSampler: - return DBuilder.createBasicType( - "opencl_sampler_t", CGM.getContext().getTypeSize(BT), - CGM.getContext().getTypeAlign(BT), llvm::dwarf::DW_ATE_unsigned); + return getOrCreateStructPtrType("opencl_sampler_t", + OCLSamplerDITy); case BuiltinType::OCLEvent: return getOrCreateStructPtrType("opencl_event_t", OCLEventDITy); case BuiltinType::OCLClkEvent: diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 70c16462965..71c0df4cbf0 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -67,6 +67,7 @@ class CGDebugInfo { #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ llvm::DIType *SingletonId = nullptr; #include "clang/Basic/OpenCLImageTypes.def" + llvm::DIType *OCLSamplerDITy = nullptr; llvm::DIType *OCLEventDITy = nullptr; llvm::DIType *OCLClkEventDITy = nullptr; llvm::DIType *OCLQueueDITy = nullptr; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 3e1ae3604f9..039936a3a64 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3601,6 +3601,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_ARCExtendBlockObject: case CK_CopyAndAutoreleaseBlockObject: case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: return EmitUnsupportedLValue(E, "unexpected cast lvalue"); case CK_Dependent: diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 6d18843591f..f51330c8b19 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -750,6 +750,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: llvm_unreachable("cast kind invalid for aggregate types"); } } diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index e0d3fc4c1c6..af7f190f793 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -481,6 +481,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: llvm_unreachable("invalid cast kind for complex value"); case CK_FloatingRealToComplex: diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 803b39907dd..0e818e99eec 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -690,6 +690,9 @@ public: case CK_ConstructorConversion: return C; + case CK_IntToOCLSampler: + llvm_unreachable("global sampler variables are not generated"); + case CK_Dependent: llvm_unreachable("saw dependent cast!"); case CK_BuiltinFnToFnPtr: diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 064bc9532a6..343d3fb5aa6 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1573,7 +1573,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return llvm::Constant::getNullValue(ConvertType(DestTy)); } - } + case CK_IntToOCLSampler: + return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF); + + } // end of switch llvm_unreachable("unknown scalar cast"); } diff --git a/clang/lib/CodeGen/CGOpenCLRuntime.cpp b/clang/lib/CodeGen/CGOpenCLRuntime.cpp index c0af6deae2d..e4541d59c25 100644 --- a/clang/lib/CodeGen/CGOpenCLRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenCLRuntime.cpp @@ -48,7 +48,7 @@ llvm::Type *CGOpenCLRuntime::convertOpenCLSpecificType(const Type *T) { ImgAddrSpc); #include "clang/Basic/OpenCLImageTypes.def" case BuiltinType::OCLSampler: - return llvm::IntegerType::get(Ctx, 32); + return getSamplerType(); case BuiltinType::OCLEvent: return llvm::PointerType::get(llvm::StructType::create( Ctx, "opencl.event_t"), 0); @@ -77,3 +77,12 @@ llvm::Type *CGOpenCLRuntime::getPipeType() { return PipeTy; } + +llvm::PointerType *CGOpenCLRuntime::getSamplerType() { + if (!SamplerTy) + SamplerTy = llvm::PointerType::get(llvm::StructType::create( + CGM.getLLVMContext(), "opencl.sampler_t"), + CGM.getContext().getTargetAddressSpace( + LangAS::opencl_constant)); + return SamplerTy; +} diff --git a/clang/lib/CodeGen/CGOpenCLRuntime.h b/clang/lib/CodeGen/CGOpenCLRuntime.h index f1a7a310644..41ead1010a2 100644 --- a/clang/lib/CodeGen/CGOpenCLRuntime.h +++ b/clang/lib/CodeGen/CGOpenCLRuntime.h @@ -33,9 +33,11 @@ class CGOpenCLRuntime { protected: CodeGenModule &CGM; llvm::Type *PipeTy; + llvm::PointerType *SamplerTy; public: - CGOpenCLRuntime(CodeGenModule &CGM) : CGM(CGM), PipeTy(nullptr) {} + CGOpenCLRuntime(CodeGenModule &CGM) : CGM(CGM), PipeTy(nullptr), + SamplerTy(nullptr) {} virtual ~CGOpenCLRuntime(); /// Emit the IR required for a work-group-local variable declaration, and add @@ -47,6 +49,8 @@ public: virtual llvm::Type *convertOpenCLSpecificType(const Type *T); virtual llvm::Type *getPipeType(); + + llvm::PointerType *getSamplerType(); }; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 96bb5bfe027..2affa57ca11 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2384,8 +2384,13 @@ void CodeGenModule::maybeSetTrivialComdat(const Decl &D, /// Pass IsTentative as true if you want to create a tentative definition. void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative) { - llvm::Constant *Init = nullptr; + // OpenCL global variables of sampler type are translated to function calls, + // therefore no need to be translated. QualType ASTTy = D->getType(); + if (getLangOpts().OpenCL && ASTTy->isSamplerT()) + return; + + llvm::Constant *Init = nullptr; CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); bool NeedsGlobalCtor = false; bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor(); @@ -4317,3 +4322,13 @@ llvm::SanitizerStatReport &CodeGenModule::getSanStats() { return *SanStats; } +llvm::Value * +CodeGenModule::createOpenCLIntToSamplerConversion(const Expr *E, + CodeGenFunction &CGF) { + llvm::Constant *C = EmitConstantExpr(E, E->getType(), &CGF); + auto SamplerT = getOpenCLRuntime().getSamplerType(); + auto FTy = llvm::FunctionType::get(SamplerT, {C->getType()}, false); + return CGF.Builder.CreateCall(CreateRuntimeFunction(FTy, + "__translate_sampler_initializer"), + {C}); +} diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 94904997d62..54354451b85 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1145,6 +1145,9 @@ public: llvm::SanitizerStatReport &getSanStats(); + llvm::Value * + createOpenCLIntToSamplerConversion(const Expr *E, CodeGenFunction &CGF); + private: llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index 482c0f6f856..0ae1ec7e4b1 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1076,6 +1076,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_CopyAndAutoreleaseBlockObject: case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: + case CK_IntToOCLSampler: return false; case CK_BooleanToSignedIntegral: diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 04da24870f1..386e5773f90 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2399,6 +2399,13 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ParsePreprocessorArgs(Res.getPreprocessorOpts(), Args, FileMgr, Diags); ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), Args, Res.getFrontendOpts().ProgramAction); + + // Turn on -Wspir-compat for SPIR target. + llvm::Triple T(Res.getTargetOpts().Triple); + auto Arch = T.getArch(); + if (Arch == llvm::Triple::spir || Arch == llvm::Triple::spir64) { + Res.getDiagnosticOpts().Warnings.push_back("spir-compat"); + } return Success; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 42c44b183e5..19a3d035d37 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7661,6 +7661,11 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, } } + if (LHSType->isSamplerT() && RHSType->isIntegerType()) { + Kind = CK_IntToOCLSampler; + return Compatible; + } + return Incompatible; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index af7fdc40c7b..fc896941df0 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4885,7 +4885,8 @@ static bool TryOCLSamplerInitialization(Sema &S, QualType DestType, Expr *Initializer) { if (!S.getLangOpts().OpenCL || !DestType->isSamplerT() || - !Initializer->isIntegerConstantExpr(S.getASTContext())) + (!Initializer->isIntegerConstantExpr(S.Context) && + !Initializer->getType()->isSamplerT())) return false; Sequence.AddOCLSamplerInitStep(DestType); @@ -6903,19 +6904,93 @@ InitializationSequence::Perform(Sema &S, } case SK_OCLSamplerInit: { - assert(Step->Type->isSamplerT() && + // Sampler initialzation have 5 cases: + // 1. function argument passing + // 1a. argument is a file-scope variable + // 1b. argument is a function-scope variable + // 1c. argument is one of caller function's parameters + // 2. variable initialization + // 2a. initializing a file-scope variable + // 2b. initializing a function-scope variable + // + // For file-scope variables, since they cannot be initialized by function + // call of __translate_sampler_initializer in LLVM IR, their references + // need to be replaced by a cast from their literal initializers to + // sampler type. Since sampler variables can only be used in function + // calls as arguments, we only need to replace them when handling the + // argument passing. + assert(Step->Type->isSamplerT() && "Sampler initialization on non-sampler type."); - - QualType SourceType = CurInit.get()->getType(); - + Expr *Init = CurInit.get(); + QualType SourceType = Init->getType(); + // Case 1 if (Entity.isParameterKind()) { - if (!SourceType->isSamplerT()) + if (!SourceType->isSamplerT()) { S.Diag(Kind.getLocation(), diag::err_sampler_argument_required) << SourceType; - } else if (Entity.getKind() != InitializedEntity::EK_Variable) { - llvm_unreachable("Invalid EntityKind!"); + break; + } else if (const DeclRefExpr *DRE = dyn_cast(Init)) { + auto Var = cast(DRE->getDecl()); + // Case 1b and 1c + // No cast from integer to sampler is needed. + if (!Var->hasGlobalStorage()) { + CurInit = ImplicitCastExpr::Create(S.Context, Step->Type, + CK_LValueToRValue, Init, + /*BasePath=*/nullptr, VK_RValue); + break; + } + // Case 1a + // For function call with a file-scope sampler variable as argument, + // get the integer literal. + // Do not diagnose if the file-scope variable does not have initializer + // since this has already been diagnosed when parsing the variable + // declaration. + if (!Var->getInit() || !isa(Var->getInit())) + break; + Init = cast(const_cast( + Var->getInit()))->getSubExpr(); + SourceType = Init->getType(); + } + } else { + // Case 2 + // Check initializer is 32 bit integer constant. + // If the initializer is taken from global variable, do not diagnose since + // this has already been done when parsing the variable declaration. + if (!Init->isConstantInitializer(S.Context, false)) + break; + + if (!SourceType->isIntegerType() || + 32 != S.Context.getIntWidth(SourceType)) { + S.Diag(Kind.getLocation(), diag::err_sampler_initializer_not_integer) + << SourceType; + break; + } + + llvm::APSInt Result; + Init->EvaluateAsInt(Result, S.Context); + const uint64_t SamplerValue = Result.getLimitedValue(); + // 32-bit value of sampler's initializer is interpreted as + // bit-field with the following structure: + // |unspecified|Filter|Addressing Mode| Normalized Coords| + // |31 6|5 4|3 1| 0| + // This structure corresponds to enum values of sampler properties + // defined in SPIR spec v1.2 and also opencl-c.h + unsigned AddressingMode = (0x0E & SamplerValue) >> 1; + unsigned FilterMode = (0x30 & SamplerValue) >> 4; + if (FilterMode != 1 && FilterMode != 2) + S.Diag(Kind.getLocation(), + diag::warn_sampler_initializer_invalid_bits) + << "Filter Mode"; + if (AddressingMode > 4) + S.Diag(Kind.getLocation(), + diag::warn_sampler_initializer_invalid_bits) + << "Addressing Mode"; } + // Cases 1a, 2a and 2b + // Insert cast from integer to sampler. + CurInit = S.ImpCastExprToType(Init, S.Context.OCLSamplerTy, + CK_IntToOCLSampler); break; } case SK_OCLZeroEvent: { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 175225ba0de..542399d4bcb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -341,6 +341,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_AnyPointerToBlockPointerCast: case CK_ObjCObjectLValueCast: case CK_ZeroToOCLEvent: + case CK_IntToOCLSampler: case CK_LValueBitCast: { // Delegate to SValBuilder to process. SVal V = state->getSVal(Ex, LCtx); -- cgit v1.2.3