Index: include/clang/Basic/Builtins.h =================================================================== --- include/clang/Basic/Builtins.h +++ include/clang/Basic/Builtins.h @@ -143,6 +143,12 @@ return strchr(GetRecord(ID).Attributes, 't') != nullptr; } + /// \brief Determines whether this builtin has a result or any arguments which + /// are pointer types. + bool hasPtrArgsOrResult(unsigned ID) const { + return strchr(GetRecord(ID).Type, '*') != nullptr; + } + /// \brief Completely forget that the given ID was ever considered a builtin, /// e.g., because the user provided a conflicting signature. void ForgetBuiltin(unsigned ID, IdentifierTable &Table); Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -56,7 +56,8 @@ // I -> Required to constant fold to an integer constant expression. // // Types may be postfixed with the following modifiers: -// * -> pointer (optionally followed by an address space number) +// * -> pointer (optionally followed by an address space number, if no address +// space is specified than any address space will be accepted) // & -> reference (optionally followed by an address space number) // C -> const // D -> volatile Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4563,6 +4563,83 @@ return hasInvalid; } +/// If a builtin function has a pointer argument with no explicit address +/// space, than it should be able to accept a pointer to any address +/// space as input. In order to do this, we need to replace the +/// standard builtin declaration with one that uses the same address space +/// as the call. +/// +/// \returns nullptr If this builtin is not a candidate for a rewrite i.e. +/// it does not contain any pointer arguments without +/// an address space qualifer. Otherwise the rewritten +/// FunctionDecl is returned. +/// TODO: Handle pointer return types. +static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, + const FunctionDecl *FDecl, + MultiExprArg ArgExprs) { + + QualType DeclType = FDecl->getType(); + const FunctionProtoType *FT = dyn_cast(DeclType); + + if (!Context.BuiltinInfo.hasPtrArgsOrResult(FDecl->getBuiltinID()) || + !FT || FT->isVariadic() || ArgExprs.size() != FT->getNumParams()) + return nullptr; + + bool NeedsNewDecl = false; + unsigned i = 0; + SmallVector OverloadParams; + + for (QualType ParamType : FT->param_types()) { + + // Convert array arguments to pointer to simplify type lookup. + Expr *Arg = Sema->DefaultFunctionArrayLvalueConversion(ArgExprs[i++]).get(); + QualType ArgType = Arg->getType(); + if (!ParamType->isPointerType() || + ParamType.getQualifiers().hasAddressSpace() || + !ArgType->isPointerType() || + !ArgType->getPointeeType().getQualifiers().hasAddressSpace()) { + OverloadParams.push_back(ParamType); + continue; + } + + NeedsNewDecl = true; + unsigned AS = ArgType->getPointeeType().getQualifiers().getAddressSpace(); + + QualType PointeeType = ParamType->getPointeeType(); + PointeeType = Context.getAddrSpaceQualType(PointeeType, AS); + OverloadParams.push_back(Context.getPointerType(PointeeType)); + } + + if (!NeedsNewDecl) + return nullptr; + + FunctionProtoType::ExtProtoInfo EPI; + QualType OverloadTy = Context.getFunctionType(FT->getReturnType(), + OverloadParams, EPI); + DeclContext *Parent = Context.getTranslationUnitDecl(); + FunctionDecl *OverloadDecl = FunctionDecl::Create(Context, Parent, + FDecl->getLocation(), + FDecl->getLocation(), + FDecl->getIdentifier(), + OverloadTy, + /*TInfo=*/nullptr, + SC_Extern, false, + /*hasPrototype=*/true); + SmallVector Params; + FT = cast(OverloadTy); + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + QualType ParamType = FT->getParamType(i); + ParmVarDecl *Parm = + ParmVarDecl::Create(Context, OverloadDecl, SourceLocation(), + SourceLocation(), nullptr, ParamType, + /*TInfo=*/nullptr, SC_None, nullptr); + Parm->setScopeInfo(0, i); + Params.push_back(Parm); + } + OverloadDecl->setParams(Params); + return OverloadDecl; +} + /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments. /// This provides the location of the left/right parens and a list of comma /// locations. @@ -4666,10 +4743,24 @@ if (UnaryOperator *UnOp = dyn_cast(NakedFn)) if (UnOp->getOpcode() == UO_AddrOf) NakedFn = UnOp->getSubExpr()->IgnoreParens(); - - if (isa(NakedFn)) + + if (isa(NakedFn)) { NDecl = cast(NakedFn)->getDecl(); - else if (isa(NakedFn)) + + FunctionDecl *FDecl = dyn_cast(NDecl); + if (FDecl && FDecl->getBuiltinID()) { + // Rewrite the function decl for this builtin by replacing paramaters + // with no explicit address space with the address space of the arguments + // in ArgExprs. + if ((FDecl = rewriteBuiltinFunctionDecl(this, Context, FDecl, ArgExprs))) { + NDecl = FDecl; + Fn = DeclRefExpr::Create(Context, FDecl->getQualifierLoc(), + SourceLocation(), FDecl, false, + SourceLocation(), FDecl->getType(), + Fn->getValueKind(), FDecl); + } + } + } else if (isa(NakedFn)) NDecl = cast(NakedFn)->getMemberDecl(); if (FunctionDecl *FD = dyn_cast_or_null(NDecl)) { Index: test/CodeGenOpenCL/memcpy.cl =================================================================== --- /dev/null +++ test/CodeGenOpenCL/memcpy.cl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -ffake-address-space-map -emit-llvm -o - | FileCheck %s + +// CHECK-LABEL: @test +// CHECK-NOT: addrspacecast +// CHECK: call void @llvm.memcpy.p1i8.p3i8 +kernel void test(global float *g, constant float *c) { + __builtin_memcpy(g, c, 32); +} Index: test/Sema/builtins.cl =================================================================== --- /dev/null +++ test/Sema/builtins.cl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic +// expected-no-diagnostics + +kernel void test(global float *out, global float *in, global int* in2) { + out[0] = __builtin_nanf(""); + __builtin_memcpy(out, in, 32); + out[0] = __builtin_frexpf(in[0], in2); +}