Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1252,6 +1252,32 @@ BUILTIN(__builtin_nontemporal_store, "v.", "t") BUILTIN(__builtin_nontemporal_load, "v.", "t") +// OpenCL 2.0 Pipe functions. +// We need the variadic prototype, since the packet type could be anything. +BUILTIN(read_pipe, "i.", "tn") +BUILTIN(write_pipe, "i.", "tn") + +BUILTIN(reserve_read_pipe, "i.", "tn") +BUILTIN(reserve_write_pipe, "i.", "tn") + +BUILTIN(commit_write_pipe, "v.", "tn") +BUILTIN(commit_read_pipe, "v.", "tn") + +BUILTIN(sub_group_reserve_read_pipe, "i.", "tn") +BUILTIN(sub_group_reserve_write_pipe, "i.", "tn") + +BUILTIN(sub_group_commit_read_pipe, "v.", "tn") +BUILTIN(sub_group_commit_write_pipe, "v.", "tn") + +BUILTIN(work_group_reserve_read_pipe, "i.", "tn") +BUILTIN(work_group_reserve_write_pipe, "i.", "tn") + +BUILTIN(work_group_commit_read_pipe, "v.", "tn") +BUILTIN(work_group_commit_write_pipe, "v.", "tn") + +BUILTIN(get_pipe_num_packets, "Ui.", "tn") +BUILTIN(get_pipe_max_packets, "Ui.", "tn") + #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7640,6 +7640,17 @@ "missing actual type specifier for pipe">; def err_reference_pipe_type : Error < "pipes packet types cannot be of reference type">; +// Builtin pipe +def err_builtin_pipe_first_arg : Error< + "first argument to %0 must be a pipe type">; +def err_builtin_pipe_argument_type_mismatch : Error< + "argument type doesn't match pipe type">; +def err_builtin_pipe_args_num_mismatch : Error< + "invalid number of arguments to function: %0">; +def err_builtin_pipe_invalid_arg : Error< + "invalid argument type to function %0 (expecting: %1)">; +def err_builtin_pipe_invalid_access_modifier : Error< + "invalid pipe access modifier (expecting %0)">; def err_opencl_no_main : Error<"%select{function|kernel}0 cannot be called 'main'">; def err_opencl_kernel_attr : Error<"attribute %0 can only be applied to a kernel function">; Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -14,6 +14,7 @@ #include "CodeGenFunction.h" #include "CGCXXABI.h" #include "CGObjCRuntime.h" +#include "CGOpenCLRuntime.h" #include "CodeGenModule.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -1963,6 +1964,194 @@ return RValue::get(llvm::ConstantExpr::getBitCast(GV, CGM.Int8PtrTy)); break; } + + case Builtin::BIread_pipe: + case Builtin::BIwrite_pipe: { + const char *NamePrefix = + (BuiltinID == Builtin::BIread_pipe) ? "_Z9read_pipe" : "_Z10write_pipe"; + llvm::LLVMContext &Ctxt = getLLVMContext(); + Value *Arg0 = EmitScalarExpr(E->getArg(0)), + *Arg1 = EmitScalarExpr(E->getArg(1)); + Value *PacketSize = CGOpenCLRuntime(CGM).getPipeElemSize(E->getArg(0)); + llvm::SmallString<128> Out; + + // Type of the packet parameter. + llvm::Type *I8PTy = llvm::PointerType::get(llvm::Type::getInt8Ty(Ctxt), 4U); + + // Testing which overloaded version we should generate the call for. + if (2U == E->getNumArgs()) { + // The name is mangled with hard-coded, since we would like to avoid the + // AST rewrite. + SmallString<64> SS; + Ocl20Mangler Mangler(SS); + Mangler.appendPointer(1) + .appendPipe() + .appendPointer(4) + .appendVoid() + .appendInt(); + + llvm::StringRef ManglerRef = SS.str(); + Twine NameTwine(NamePrefix, ManglerRef); + StringRef Name = NameTwine.toStringRef(Out); + // Re-Creating the function type for this call, since the original type + // is variadic, which we convert to a specific type to match this call. + llvm::Type *ArgTys[] = {Arg0->getType(), I8PTy, Int32Ty}; + llvm::FunctionType *FTy = llvm::FunctionType::get( + Int32Ty, llvm::ArrayRef(ArgTys), false); + Value *BCast = Builder.CreatePointerCast(Arg1, I8PTy); + return RValue::get(Builder.CreateCall( + CGM.CreateRuntimeFunction(FTy, Name), {Arg0, BCast, PacketSize})); + } else { + assert(4 == E->getNumArgs() && + "Illegal number of parameters to pipe function"); + + SmallString<64> SS; + Ocl20Mangler Mangler(SS); + Mangler.appendPointer(1) + .appendPipe() + .appendReservedId() + .appendUint() + .appendPointer(4) + .appendVoid() + .appendInt(); + + llvm::StringRef ManglerRef = SS.str(); + Twine NameTwine(NamePrefix, ManglerRef); + StringRef Name = NameTwine.toStringRef(Out); + llvm::Type *ArgTys[] = {Arg0->getType(), Arg1->getType(), Int32Ty, I8PTy, + Int32Ty}; + Value *Arg2 = EmitScalarExpr(E->getArg(2)), + *Arg3 = EmitScalarExpr(E->getArg(3)); + llvm::FunctionType *FTy = llvm::FunctionType::get( + Int32Ty, llvm::ArrayRef(ArgTys), false); + Value *BCast = Builder.CreatePointerCast(Arg3, I8PTy); + // We know the third argument is an integer type (Verified by Sema, but + // we may need to cast it. + if (Arg2->getType() != Int32Ty) + Arg2 = Builder.CreateZExtOrTrunc(Arg2, Int32Ty); + return RValue::get( + Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), + {Arg0, Arg1, Arg2, BCast, PacketSize})); + } + } + case Builtin::BIreserve_read_pipe: + case Builtin::BIreserve_write_pipe: + case Builtin::BIwork_group_reserve_read_pipe: + case Builtin::BIwork_group_reserve_write_pipe: + case Builtin::BIsub_group_reserve_read_pipe: + case Builtin::BIsub_group_reserve_write_pipe: { + // Composing the mangled name for the function. + const char *NamePrefix; + if (BuiltinID == Builtin::BIreserve_read_pipe) + NamePrefix = "_Z17reserve_read_pipe"; + else if (BuiltinID == Builtin::BIreserve_write_pipe) + NamePrefix = "_Z18reserve_write_pipe"; + else if (BuiltinID == Builtin::BIwork_group_reserve_read_pipe) + NamePrefix = "_Z28work_group_reserve_read_pipe"; + else if (BuiltinID == Builtin::BIwork_group_reserve_write_pipe) + NamePrefix = "_Z29work_group_reserve_write_pipe"; + else if (BuiltinID == Builtin::BIsub_group_reserve_read_pipe) + NamePrefix = "_Z27sub_group_reserve_read_pipe"; + else + NamePrefix = "_Z28sub_group_reserve_write_pipe"; + + SmallString<64> SS; + Ocl20Mangler Mangler(SS); + Mangler.appendPointer(1).appendPipe().appendUint().appendInt(); + llvm::StringRef ManglerRef = SS.str(); + llvm::Twine NameTwine(NamePrefix, ManglerRef); + + llvm::SmallString<128> Out; + llvm::StringRef Name = NameTwine.toStringRef(Out); + CGOpenCLRuntime CGOcl(CGM); + + Value *PacketSize = CGOcl.getPipeElemSize(E->getArg(0)); + Value *Arg0 = EmitScalarExpr(E->getArg(0)), + *Arg1 = EmitScalarExpr(E->getArg(1)); + llvm::Type *ReservedIDTy = ConvertType(getContext().OCLReserveIDTy); + + // Building the fucntion's prototype. + llvm::Type *ArgTys[] = {Arg0->getType(), Int32Ty, Int32Ty}; + llvm::FunctionType *FTy = llvm::FunctionType::get( + ReservedIDTy, llvm::ArrayRef(ArgTys), false); + // We know the third argument is an integer type (Verified by Sema, but + // we may need to cast it. + if (Arg1->getType() != Int32Ty) + Arg1 = Builder.CreateZExtOrTrunc(Arg1, Int32Ty); + return RValue::get(Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), + {Arg0, Arg1, PacketSize})); + } + case Builtin::BIcommit_read_pipe: + case Builtin::BIcommit_write_pipe: + case Builtin::BIwork_group_commit_read_pipe: + case Builtin::BIwork_group_commit_write_pipe: + case Builtin::BIsub_group_commit_read_pipe: + case Builtin::BIsub_group_commit_write_pipe: { + // Composing the mangled name for the function. + const char *NamePrefix; + if (BuiltinID == Builtin::BIcommit_read_pipe) + NamePrefix = "_Z16commit_read_pipe"; + else if (BuiltinID == Builtin::BIcommit_write_pipe) + NamePrefix = "_Z17commit_write_pipe"; + else if (BuiltinID == Builtin::BIwork_group_commit_read_pipe) + NamePrefix = "_Z27work_group_commit_read_pipe"; + else if (BuiltinID == Builtin::BIwork_group_commit_write_pipe) + NamePrefix = "_Z28work_group_commit_write_pipe"; + else if (BuiltinID == Builtin::BIsub_group_commit_read_pipe) + NamePrefix = "_Z26sub_group_commit_read_pipe"; + else + NamePrefix = "_Z27sub_group_commit_write_pipe"; + + SmallString<64> SS; + Ocl20Mangler Mangler(SS); + Mangler.appendPointer(1).appendPipe().appendReservedId().appendInt(); + llvm::StringRef ManglerRef = SS.str(); + llvm::Twine NameTwine(NamePrefix, ManglerRef); + + llvm::SmallString<128> Out; + llvm::StringRef Name = NameTwine.toStringRef(Out); + CGOpenCLRuntime CGOcl(CGM); + + Value *PacketSize = CGOcl.getPipeElemSize(E->getArg(0)); + Value *Arg0 = EmitScalarExpr(E->getArg(0)), + *Arg1 = EmitScalarExpr(E->getArg(1)); + + // Building the function's prototype. + llvm::Type *ArgTys[] = {Arg0->getType(), Arg1->getType(), Int32Ty}; + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), + llvm::ArrayRef(ArgTys), false); + + return RValue::get(Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), + {Arg0, Arg1, PacketSize})); + } + case Builtin::BIget_pipe_num_packets: + case Builtin::BIget_pipe_max_packets: { + // Composing the builtin's name. + const char *NamePrefix; + if (BuiltinID == Builtin::BIget_pipe_num_packets) + NamePrefix = "_Z20get_pipe_num_packets"; + else + NamePrefix = "_Z20get_pipe_max_packets"; + SmallString<64> SS; + Ocl20Mangler Mangler(SS); + Mangler.appendPointer(1).appendPipe().appendInt(); + llvm::StringRef ManglerRef = SS.str(); + llvm::Twine NameTwine(NamePrefix, ManglerRef); + llvm::SmallString<128> Out; + llvm::StringRef Name = NameTwine.toStringRef(Out); + + // Building the function's prototype. + CGOpenCLRuntime CGOcl(CGM); + Value *PacketSize = CGOcl.getPipeElemSize(E->getArg(0)); + Value *Arg0 = EmitScalarExpr(E->getArg(0)); + llvm::Type *ArgTys[] = {Arg0->getType(), Int32Ty}; + llvm::FunctionType *FTy = llvm::FunctionType::get( + Int32Ty, llvm::ArrayRef(ArgTys), false); + + return RValue::get(Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), + {Arg0, PacketSize})); + } } // If this is an alias for a lib function (e.g. __builtin_sin), emit Index: lib/CodeGen/CGOpenCLRuntime.h =================================================================== --- lib/CodeGen/CGOpenCLRuntime.h +++ lib/CodeGen/CGOpenCLRuntime.h @@ -47,8 +47,50 @@ virtual llvm::Type *convertOpenCLSpecificType(const Type *T); virtual llvm::Type *getPipeType(); + + // \brief Returnes a value which indicates the size in bytes of the pipe + // element. + virtual llvm::Value *getPipeElemSize(const Expr *PipeArg); }; +class Ocl20Mangler { +public: + Ocl20Mangler(llvm::SmallVectorImpl &); + + // \brief Appends the mangled representation of reserve_id_t parameter to the + // mangled string. + Ocl20Mangler &appendReservedId(); + + // \brief Appends the mangled representation of pipe_t parameter to the + // mangled string. + Ocl20Mangler &appendPipe(); + + // \brief Appends the mangled representation of 'int' parameter to the + // mangled string. + Ocl20Mangler &appendInt(); + + // \brief Appends the mangled representation of 'unsigned int' parameter to + // the + // mangled string. + Ocl20Mangler &appendUint(); + // \brief Appends the mangled representation of a pointer. + Ocl20Mangler &appendPointer(); + + // \brief Appends the mangled representation of void. + Ocl20Mangler &appendVoid(); + + // \brief Appends the mangled representation of a pointer with a given address + // space. + // \param addressSapace The address space of the pointer. Valid values are + // [0,4]. + Ocl20Mangler &appendPointer(int addressSapace); + +private: + // \brief Appends the given string to the mangled prototype. + Ocl20Mangler &appendString(llvm::StringRef); + + llvm::SmallVectorImpl *MangledString; +}; } } Index: lib/CodeGen/CGOpenCLRuntime.cpp =================================================================== --- lib/CodeGen/CGOpenCLRuntime.cpp +++ lib/CodeGen/CGOpenCLRuntime.cpp @@ -110,3 +110,65 @@ return PipeTy; } + +llvm::Value *CGOpenCLRuntime::getPipeElemSize(const Expr *PipeArg) { + const PipeType *PipeTy = PipeArg->getType()->getAs(); + // The type of the last (implicit) argument to be passed. + llvm::Type *Int32Ty = llvm::IntegerType::getInt32Ty(CGM.getLLVMContext()); + unsigned TypeSizeInBits = + CGM.getContext().getTypeSize(PipeTy->getElementType()); + return llvm::ConstantInt::get(Int32Ty, + TypeSizeInBits / 8, // Size in bytes. + false); +} + +// Ocl20Mangler +// TODO:Some explation or new name? +Ocl20Mangler::Ocl20Mangler(llvm::SmallVectorImpl &SS) + : MangledString(&SS) {} + +Ocl20Mangler &Ocl20Mangler::appendReservedId() { + this->appendString("16ocl_reserve_id_t"); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendPipe() { + this->appendString("10ocl_pipe_t"); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendInt() { + MangledString->push_back('i'); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendUint() { + MangledString->push_back('j'); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendVoid() { + MangledString->push_back('v'); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendPointer() { + this->appendString("P"); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendPointer(int addressSpace) { + assert(addressSpace >= 0 && addressSpace <= 4 && + "Illegal address space for OpenCL"); + if (!addressSpace) + return appendPointer(); + + this->appendString("PU3AS"); + MangledString->push_back('0' + addressSpace); + return *this; +} + +Ocl20Mangler &Ocl20Mangler::appendString(llvm::StringRef S) { + MangledString->append(S.begin(), S.end()); + return *this; +} Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -258,6 +258,194 @@ return false; } +/// Returns readable name for a call. +static StringRef getFunctionName(CallExpr *call) { + return cast(call->getCalleeDecl())->getName(); +} + +/// Returns OpenCL access qual. +static OpenCLImageAccessAttr *getOpenCLImageAcces(const Decl *D) { + if (D->hasAttr()) + return D->getAttr(); + return nullptr; +} + +/// Returns true if pipe element type is different from the pointer. +static bool checkOpenCLPipeArg(Sema &S, CallExpr *call) { + const Expr *Arg0 = call->getArg(0); + // First argument type should always be pipe. + if (!Arg0->getType()->isPipeType()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_first_arg) + << getFunctionName(call) << Arg0->getSourceRange(); + return true; + } + OpenCLImageAccessAttr *AccessQual = + getOpenCLImageAcces(cast(Arg0)->getDecl()); + // Validates the access modifier is compatible with the call. + // From OpenCL C Specification 6.13.16 the access qualifiers for + // pipe should only be read_only and write_only, and assumed to + // be read_only if no qualifier is specified. + bool isValid = true; + // TODO: For all pipe built-in read is for read_only? + if (getFunctionName(call).find("read") != StringRef::npos) + // if (getFunctionName(call).startswith("read")) + isValid = AccessQual == nullptr || AccessQual->isReadOnly(); + else + isValid = AccessQual != nullptr && AccessQual->isWriteOnly(); + if (!isValid) { + bool ReadOnly = getFunctionName(call).startswith("read"); + const char *AM = ReadOnly ? "read_only" : "write_only"; + S.Diag(Arg0->getLocStart(), diag::err_builtin_pipe_invalid_access_modifier) + << AM << Arg0->getSourceRange(); + return true; + } + + return false; +} + +/// Returns true if pipe element type is different from the pointer. +static bool checkOpenCLPipePacketType(Sema &S, CallExpr *call, unsigned idx) { + const Expr *Arg0 = call->getArg(0); + const Expr *Argidx = call->getArg(idx); + const PipeType *PipeTy = cast(Arg0->getType()); + const Type *EltTy = PipeTy->getElementType().getTypePtr(); + const PointerType *ArgTy = + dyn_cast(Argidx->getType().getTypePtr()); + // The idx argument should be a pointer and the type of the pointer and + // the type of pipe element should also be the same. + if (!Argidx->getType()->isPointerType() || !ArgTy || + EltTy != ArgTy->getPointeeType().getTypePtr()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_invalid_arg) + << getFunctionName(call) + << S.Context.getPointerType(PipeTy->getElementType()) + << Argidx->getSourceRange(); + return true; + } + return false; +} + +// \brief Performs semantic analysis for the read/write_pipe call. +// \param S Reference to the semantic analyzer. +// \param call A pointer to the builtin call. +// \return True if a semantic error has been found, false otherwise. +static bool SemaBuiltinRWPipe(Sema &S, CallExpr *call) { + + if (checkOpenCLPipeArg(S, call)) + return true; + + // Two kinds of read/write pipe + // From OpenCL C Specification 6.13.16.2 the built-in read/write + // functions have following forms. + switch (call->getNumArgs()) { + case 2: { + // The call with 2 arguments should be + // read/write_pipe(pipe T, T*) + // check packet type T + if (checkOpenCLPipePacketType(S, call, 1)) + return true; + } break; + + case 4: { + // The call with 4 arguments should be + // read/write_pipe(pipe T, reserve_id_t, uint, T*) + // check reserve_id_t + if (!call->getArg(1)->getType()->isReserveIDT()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_invalid_arg) + << getFunctionName(call) << S.Context.OCLReserveIDTy + << call->getArg(1)->getSourceRange(); + return true; + } + + // check the index + const Expr *Arg2 = call->getArg(2); + if (!Arg2->getType()->isIntegerType() && + !Arg2->getType()->isUnsignedIntegerType()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_invalid_arg) + << getFunctionName(call) << S.Context.UnsignedIntTy + << Arg2->getSourceRange(); + return true; + } + + // check packet type T + if (checkOpenCLPipePacketType(S, call, 3)) + return true; + } break; + default: + S.Diag(call->getLocStart(), diag::err_builtin_pipe_args_num_mismatch) + << getFunctionName(call) << call->getSourceRange(); + return true; + } + call->setType(S.getASTContext().IntTy); + return false; +} + +// \brief Performs a semantic analysis on the {work_group_/sub_group_ +// /_}reserve_{read/write}_pipe +// \param S Reference to the semantic analyzer. +// \param call The call to the builtin function to be analyzed. +// \return True if a semantic error was found, false otherwise. +static bool SemaBuiltinReserveRWPipe(Sema &S, CallExpr *call) { + if (checkArgCount(S, call, 2)) + return true; + + if (checkOpenCLPipeArg(S, call)) + return true; + + // check the reverse size + if (!call->getArg(1)->getType()->isIntegerType() && + !call->getArg(1)->getType()->isUnsignedIntegerType()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_invalid_arg) + << getFunctionName(call) << S.Context.UnsignedIntTy + << call->getArg(1)->getSourceRange(); + return true; + } + + call->setType(S.getASTContext().IntTy); + return false; +} + +// \brief Performs a semantic analysis on {work_group_/sub_group_ +// /_}commit_{read/write}_pipe +// \param S Reference to the semantic analyzer. +// \param call The call to the builtin function to be analyzed. +// \return True if a semantic error was found, false otherwise. +static bool SemaBuiltinCommitRWPipe(Sema &S, CallExpr *call) { + if (checkArgCount(S, call, 2)) + return true; + + if (checkOpenCLPipeArg(S, call)) + return true; + + // check reserve_id_t + if (!call->getArg(1)->getType()->isReserveIDT()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_invalid_arg) + << getFunctionName(call) << S.Context.OCLReserveIDTy + << call->getArg(1)->getSourceRange(); + return true; + } + + call->setType(S.getASTContext().VoidTy); + return false; +} + +// \brief Performs a semantic analysis on the call to built-in Pipe +// Query Functions. +// \param S Reference to the semantic analyzer. +// \param call The call to the builtin function to be analyzed. +// \return True if a semantic error was found, false otherwise. +static bool SemaBuiltinPipePacketes(Sema &S, CallExpr *call) { + if (checkArgCount(S, call, 1)) + return true; + + if (!call->getArg(0)->getType()->isPipeType()) { + S.Diag(call->getLocStart(), diag::err_builtin_pipe_first_arg) + << getFunctionName(call) << call->getArg(0)->getSourceRange(); + return true; + } + + return false; +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -563,6 +751,38 @@ TheCall->setType(Context.VoidPtrTy); break; + case Builtin::BIread_pipe: + case Builtin::BIwrite_pipe: + if (SemaBuiltinRWPipe(*this, TheCall)) + return ExprError(); + break; + case Builtin::BIreserve_read_pipe: + case Builtin::BIreserve_write_pipe: + case Builtin::BIwork_group_reserve_read_pipe: + case Builtin::BIwork_group_reserve_write_pipe: + case Builtin::BIsub_group_reserve_read_pipe: + case Builtin::BIsub_group_reserve_write_pipe: + // Since those two functions are declared with var args, therefore we need + // a semantic check for the argument. + if (SemaBuiltinReserveRWPipe(*this, TheCall)) + return ExprError(); + // We need to override the return type of the call expression. + TheCall->setType(Context.OCLReserveIDTy); + break; + case Builtin::BIcommit_read_pipe: + case Builtin::BIcommit_write_pipe: + case Builtin::BIwork_group_commit_read_pipe: + case Builtin::BIwork_group_commit_write_pipe: + case Builtin::BIsub_group_commit_read_pipe: + case Builtin::BIsub_group_commit_write_pipe: + if (SemaBuiltinCommitRWPipe(*this, TheCall)) + return ExprError(); + break; + case Builtin::BIget_pipe_num_packets: + case Builtin::BIget_pipe_max_packets: + if (SemaBuiltinPipePacketes(*this, TheCall)) + return ExprError(); + break; }