From: Nate Begeman Date: Thu, 17 Jan 2008 17:46:27 +0000 (+0000) Subject: Implement basic overload support via a new builtin, __builtin_overload. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e2ce1d9440186cf3332368291cd884a6e3ae8946;p=clang Implement basic overload support via a new builtin, __builtin_overload. __builtin_overload takes 2 or more arguments: 0) a non-zero constant-expr for the number of arguments the overloaded functions will take 1) the arguments to pass to the matching overloaded function 2) a list of functions to match. The return type of __builtin_overload is inferred from the function whose args match the types of the arguments passed to the builtin. For example: float a; float sinf(float); int sini(int); float b = __builtin_overload(1, a, sini, sinf); Says that we are overloading functions that take one argument, and trying to pass an argument of the same type as 'a'. sini() does not match since it takes and argument of type int. sinf does match, so at codegen time this will turn into float b = sinf(a); git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46132 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/AST/Expr.cpp b/AST/Expr.cpp index 0e1eecd7d7..8aa5eed80b 100644 --- a/AST/Expr.cpp +++ b/AST/Expr.cpp @@ -78,6 +78,7 @@ const char *UnaryOperator::getOpcodeStr(Opcode Op) { // Postfix Operators. //===----------------------------------------------------------------------===// + CallExpr::CallExpr(Expr *fn, Expr **args, unsigned numargs, QualType t, SourceLocation rparenloc) : Expr(CallExprClass, t), NumArgs(numargs) { @@ -1238,6 +1239,14 @@ Stmt::child_iterator ChooseExpr::child_end() { return reinterpret_cast(&SubExprs)+END_EXPR; } +// OverloadExpr +Stmt::child_iterator OverloadExpr::child_begin() { + return reinterpret_cast(&SubExprs[0]); +} +Stmt::child_iterator OverloadExpr::child_end() { + return reinterpret_cast(&SubExprs[NumArgs]); +} + // VAArgExpr Stmt::child_iterator VAArgExpr::child_begin() { return reinterpret_cast(&Val); diff --git a/AST/StmtPrinter.cpp b/AST/StmtPrinter.cpp index 6bfe735211..81bad22a7e 100644 --- a/AST/StmtPrinter.cpp +++ b/AST/StmtPrinter.cpp @@ -733,6 +733,15 @@ void StmtPrinter::VisitChooseExpr(ChooseExpr *Node) { OS << ")"; } +void StmtPrinter::VisitOverloadExpr(OverloadExpr *Node) { + OS << "__builtin_overload("; + for (unsigned i = 0, e = Node->getNumArgs(); i != e; ++i) { + if (i) OS << ", "; + PrintExpr(Node->getArg(i)); + } + OS << ")"; +} + void StmtPrinter::VisitInitListExpr(InitListExpr* Node) { OS << "{ "; for (unsigned i = 0, e = Node->getNumInits(); i != e; ++i) { diff --git a/CodeGen/CGBuiltin.cpp b/CodeGen/CGBuiltin.cpp index cd3c2e6cf2..b782b223f6 100644 --- a/CodeGen/CGBuiltin.cpp +++ b/CodeGen/CGBuiltin.cpp @@ -29,7 +29,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { switch (BuiltinID) { default: { if (getContext().BuiltinInfo.isLibFunction(BuiltinID)) - return EmitCallExpr(CGM.getBuiltinLibFunction(BuiltinID), E); + return EmitCallExpr(CGM.getBuiltinLibFunction(BuiltinID), E->getType(), + E->arg_begin()); // See if we have a target specific intrinsic. Intrinsic::ID IntrinsicID; diff --git a/CodeGen/CGExpr.cpp b/CodeGen/CGExpr.cpp index 0fe25c8d16..ad9e15a7ca 100644 --- a/CodeGen/CGExpr.cpp +++ b/CodeGen/CGExpr.cpp @@ -450,7 +450,12 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E) { return EmitBuiltinExpr(builtinID, E); llvm::Value *Callee = EmitScalarExpr(E->getCallee()); - return EmitCallExpr(Callee, E); + return EmitCallExpr(Callee, E->getType(), E->arg_begin()); +} + +RValue CodeGenFunction::EmitCallExpr(Expr *FnExpr, Expr *const *Args) { + llvm::Value *Callee = EmitScalarExpr(FnExpr); + return EmitCallExpr(Callee, FnExpr->getType(), Args); } LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { @@ -459,45 +464,42 @@ LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) { return LValue::MakeAddr(RV.getAggregateAddr()); } -RValue CodeGenFunction::EmitCallExpr(llvm::Value *Callee, const CallExpr *E) { +RValue CodeGenFunction::EmitCallExpr(llvm::Value *Callee, QualType FnType, + Expr *const *ArgExprs) { // The callee type will always be a pointer to function type, get the function // type. - QualType CalleeTy = E->getCallee()->getType(); - CalleeTy = cast(CalleeTy.getCanonicalType())->getPointeeType(); - - // Get information about the argument types. - FunctionTypeProto::arg_type_iterator ArgTyIt = 0, ArgTyEnd = 0; + FnType = cast(FnType.getCanonicalType())->getPointeeType(); + QualType ResultType = cast(FnType)->getResultType(); // Calling unprototyped functions provides no argument info. - if (const FunctionTypeProto *FTP = dyn_cast(CalleeTy)) { - ArgTyIt = FTP->arg_type_begin(); - ArgTyEnd = FTP->arg_type_end(); - } + unsigned NumArgs = 0; + if (const FunctionTypeProto *FTP = dyn_cast(FnType)) + NumArgs = FTP->getNumArgs(); llvm::SmallVector Args; // Handle struct-return functions by passing a pointer to the location that // we would like to return into. - if (hasAggregateLLVMType(E->getType())) { + if (hasAggregateLLVMType(ResultType)) { // Create a temporary alloca to hold the result of the call. :( - Args.push_back(CreateTempAlloca(ConvertType(E->getType()))); + Args.push_back(CreateTempAlloca(ConvertType(ResultType))); // FIXME: set the stret attribute on the argument. } - for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { - QualType ArgTy = E->getArg(i)->getType(); + for (unsigned i = 0, e = NumArgs; i != e; ++i) { + QualType ArgTy = ArgExprs[i]->getType(); if (!hasAggregateLLVMType(ArgTy)) { // Scalar argument is passed by-value. - Args.push_back(EmitScalarExpr(E->getArg(i))); + Args.push_back(EmitScalarExpr(ArgExprs[i])); } else if (ArgTy->isComplexType()) { // Make a temporary alloca to pass the argument. llvm::Value *DestMem = CreateTempAlloca(ConvertType(ArgTy)); - EmitComplexExprIntoAddr(E->getArg(i), DestMem, false); + EmitComplexExprIntoAddr(ArgExprs[i], DestMem, false); Args.push_back(DestMem); } else { llvm::Value *DestMem = CreateTempAlloca(ConvertType(ArgTy)); - EmitAggExpr(E->getArg(i), DestMem, false); + EmitAggExpr(ArgExprs[i], DestMem, false); Args.push_back(DestMem); } } @@ -505,14 +507,14 @@ RValue CodeGenFunction::EmitCallExpr(llvm::Value *Callee, const CallExpr *E) { llvm::Value *V = Builder.CreateCall(Callee, &Args[0], &Args[0]+Args.size()); if (V->getType() != llvm::Type::VoidTy) V->setName("call"); - else if (E->getType()->isComplexType()) + else if (ResultType->isComplexType()) return RValue::getComplex(LoadComplexFromAddr(Args[0], false)); - else if (hasAggregateLLVMType(E->getType())) + else if (hasAggregateLLVMType(ResultType)) // Struct return. return RValue::getAggregate(Args[0]); else { // void return. - assert(E->getType()->isVoidType() && "Should only have a void expr here"); + assert(ResultType->isVoidType() && "Should only have a void expr here"); V = 0; } diff --git a/CodeGen/CGExprScalar.cpp b/CodeGen/CGExprScalar.cpp index a76f1e1550..410284a7b8 100644 --- a/CodeGen/CGExprScalar.cpp +++ b/CodeGen/CGExprScalar.cpp @@ -282,6 +282,7 @@ public: // Other Operators. Value *VisitConditionalOperator(const ConditionalOperator *CO); Value *VisitChooseExpr(ChooseExpr *CE); + Value *VisitOverloadExpr(OverloadExpr *OE); Value *VisitVAArgExpr(VAArgExpr *VE); Value *VisitObjCStringLiteral(const ObjCStringLiteral *E) { return CGF.EmitObjCStringLiteral(E); @@ -991,6 +992,10 @@ Value *ScalarExprEmitter::VisitChooseExpr(ChooseExpr *E) { Visit(E->isConditionTrue(CGF.getContext()) ? E->getLHS() : E->getRHS()); } +Value *ScalarExprEmitter::VisitOverloadExpr(OverloadExpr *E) { + return CGF.EmitCallExpr(E->getFn(), E->arg_begin()).getScalarVal(); +} + Value *ScalarExprEmitter::VisitVAArgExpr(VAArgExpr *VE) { llvm::Value *ArgValue = EmitLValue(VE->getSubExpr()).getAddress(); diff --git a/CodeGen/CodeGenFunction.h b/CodeGen/CodeGenFunction.h index 216b7cd71a..55a02a89b9 100644 --- a/CodeGen/CodeGenFunction.h +++ b/CodeGen/CodeGenFunction.h @@ -388,7 +388,8 @@ public: //===--------------------------------------------------------------------===// RValue EmitCallExpr(const CallExpr *E); - RValue EmitCallExpr(llvm::Value *Callee, const CallExpr *E); + RValue EmitCallExpr(Expr *FnExpr, Expr *const *Args); + RValue EmitCallExpr(llvm::Value *Callee, QualType FnType, Expr *const *Args); RValue EmitBuiltinExpr(unsigned BuiltinID, const CallExpr *E); llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E); diff --git a/Parse/ParseExpr.cpp b/Parse/ParseExpr.cpp index 5046f28d46..c7b8ea62e7 100644 --- a/Parse/ParseExpr.cpp +++ b/Parse/ParseExpr.cpp @@ -550,6 +550,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) { case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: + case tok::kw___builtin_overload: case tok::kw___builtin_types_compatible_p: return ParseBuiltinPrimaryExpression(); case tok::plusplus: // unary-expression: '++' unary-expression @@ -774,6 +775,7 @@ Parser::ExprResult Parser::ParseSizeofAlignofExpression() { /// [GNU] '__builtin_choose_expr' '(' assign-expr ',' assign-expr ',' /// assign-expr ')' /// [GNU] '__builtin_types_compatible_p' '(' type-name ',' type-name ')' +/// [CLANG] '__builtin_overload' '(' expr (',' expr)* ')' /// /// [GNU] offsetof-member-designator: /// [GNU] identifier @@ -909,6 +911,44 @@ Parser::ExprResult Parser::ParseBuiltinPrimaryExpression() { ConsumeParen()); break; } + case tok::kw___builtin_overload: { + llvm::SmallVector ArgExprs; + llvm::SmallVector CommaLocs; + + // For each iteration through the loop look for assign-expr followed by a + // comma. If there is no comma, break and attempt to match r-paren. + if (Tok.isNot(tok::r_paren)) { + while (1) { + ExprResult ArgExpr = ParseAssignmentExpression(); + if (ArgExpr.isInvalid) { + SkipUntil(tok::r_paren); + return ExprResult(true); + } else + ArgExprs.push_back(ArgExpr.Val); + + if (Tok.isNot(tok::comma)) + break; + // Move to the next argument, remember where the comma was. + CommaLocs.push_back(ConsumeToken()); + } + } + + // Attempt to consume the r-paren + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected_rparen); + SkipUntil(tok::r_paren); + return ExprResult(true); + } + + // __builtin_overload requires at least 2 arguments + if (ArgExprs.size() < 2) { + Diag(Tok, diag::err_typecheck_call_too_few_args); + return ExprResult(true); + } + Res = Actions.ActOnOverloadExpr(&ArgExprs[0], ArgExprs.size(), + &CommaLocs[0], StartLoc, ConsumeParen()); + break; + } case tok::kw___builtin_types_compatible_p: TypeTy *Ty1 = ParseTypeName(); diff --git a/Sema/Sema.h b/Sema/Sema.h index 7bac8b2465..38a37671b5 100644 --- a/Sema/Sema.h +++ b/Sema/Sema.h @@ -465,6 +465,12 @@ public: ExprTy *cond, ExprTy *expr1, ExprTy *expr2, SourceLocation RPLoc); + // __builtin_overload(...) + virtual ExprResult ActOnOverloadExpr(ExprTy **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + // __builtin_va_arg(expr, type) virtual ExprResult ActOnVAArg(SourceLocation BuiltinLoc, ExprTy *expr, TypeTy *type, diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp index 5d446dd01e..0c8e5e2315 100644 --- a/Sema/SemaExpr.cpp +++ b/Sema/SemaExpr.cpp @@ -892,7 +892,7 @@ void Sema::DefaultFunctionArrayConversion(Expr *&e) { ImpCastExprToType(e, Context.getPointerType(ary->getElementType())); } -/// UsualUnaryConversion - Performs various conversions that are common to most +/// UsualUnaryConversions - Performs various conversions that are common to most /// operators (C99 6.3). The conversions of array and function types are /// sometimes surpressed. For example, the array->pointer conversion doesn't /// apply if the array is an argument to the sizeof or address (&) operators. @@ -2074,6 +2074,76 @@ Sema::ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, ExprTy *cond, return new ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, resType, RPLoc); } +/// ExprsCompatibleWithFnType - return true if the Exprs in array Args have +/// QualTypes that match the QualTypes of the arguments of the FnType. +static bool ExprsCompatibleWithFnType(Expr **Args, FunctionTypeProto *FnType) { + unsigned NumParams = FnType->getNumArgs(); + for (unsigned i = 0; i != NumParams; ++i) + if (Args[i]->getType() != FnType->getArgType(i)) + return false; + return true; +} + +Sema::ExprResult Sema::ActOnOverloadExpr(ExprTy **args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + assert((NumArgs > 1) && "Too few arguments for OverloadExpr!"); + + Expr **Args = reinterpret_cast(args); + // The first argument is required to be a constant expression. It tells us + // the number of arguments to pass to each of the functions to be overloaded. + Expr *NParamsExpr = Args[0]; + llvm::APSInt constEval(32); + SourceLocation ExpLoc; + if (!NParamsExpr->isIntegerConstantExpr(constEval, Context, &ExpLoc)) + return Diag(ExpLoc, diag::err_overload_expr_requires_non_zero_constant, + NParamsExpr->getSourceRange()); + + // Verify that the number of parameters is > 0 + unsigned NumParams = constEval.getZExtValue(); + if (NumParams == 0) + return Diag(ExpLoc, diag::err_overload_expr_requires_non_zero_constant, + NParamsExpr->getSourceRange()); + // Verify that we have at least 1 + NumParams arguments to the builtin. + if ((NumParams + 1) > NumArgs) + return Diag(RParenLoc, diag::err_typecheck_call_too_few_args, + SourceRange(BuiltinLoc, RParenLoc)); + + // Figure out the return type, by matching the args to one of the functions + // listed after the paramters. + for (unsigned i = NumParams + 1; i < NumArgs; ++i) { + // UsualUnaryConversions will convert the function DeclRefExpr into a + // pointer to function. + Expr *Fn = UsualUnaryConversions(Args[i]); + FunctionTypeProto *FnType = 0; + if (const PointerType *PT = Fn->getType()->getAsPointerType()) + FnType = dyn_cast(PT->getPointeeType()); + + // The Expr type must be FunctionTypeProto, since FunctionTypeProto has no + // parameters, and the number of parameters must match the value passed to + // the builtin. + if (!FnType || (FnType->getNumArgs() != NumParams)) + continue; + + // Scan the parameter list for the FunctionType, checking the QualType of + // each paramter against the QualTypes of the arguments to the builtin. + // If they match, return a new OverloadExpr. + if (ExprsCompatibleWithFnType(Args+1, FnType)) + return new OverloadExpr(Args, NumArgs, i, FnType->getResultType(), + BuiltinLoc, RParenLoc); + } + + // If we didn't find a matching function Expr in the __builtin_overload list + // the return an error. + std::string typeNames; + for (unsigned i = 0; i != NumParams; ++i) + typeNames += Args[i+1]->getType().getAsString() + " "; + + return Diag(BuiltinLoc, diag::err_overload_no_match, typeNames, + SourceRange(BuiltinLoc, RParenLoc)); +} + Sema::ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc, ExprTy *expr, TypeTy *type, SourceLocation RPLoc) { diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 0c2a3d085c..3771c00281 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -1124,6 +1124,57 @@ public: virtual child_iterator child_end(); }; +/// OverloadExpr - Clang builtin-in function __builtin_overload. +/// This AST node provides a way to overload functions in C +/// i.e. float Z = __builtin_overload(2, X, Y, modf, mod, modl); +/// would pick whichever of the functions modf, mod, and modl that took two +/// arguments of the same type as X and Y. +class OverloadExpr : public Expr { + Expr **SubExprs; + unsigned NumArgs; + unsigned FnIndex; + SourceLocation BuiltinLoc; + SourceLocation RParenLoc; +public: + OverloadExpr(Expr **args, unsigned narg, unsigned idx, QualType t, + SourceLocation bloc, SourceLocation rploc) + : Expr(OverloadExprClass, t), NumArgs(narg), FnIndex(idx), BuiltinLoc(bloc), + RParenLoc(rploc) { + SubExprs = new Expr*[narg]; + for (unsigned i = 0; i != narg; ++i) + SubExprs[i] = args[i]; + } + ~OverloadExpr() { + delete [] SubExprs; + } + + typedef Expr * const *arg_const_iterator; + arg_const_iterator arg_begin() const { return SubExprs+1; } + + /// getNumArgs - Return the number of actual arguments to this call. + /// + unsigned getNumArgs() const { return NumArgs; } + + /// getArg - Return the specified argument. + Expr *getArg(unsigned Arg) { + assert(Arg < NumArgs && "Arg access out of range!"); + return SubExprs[Arg]; + } + Expr *getFn() { return SubExprs[FnIndex]; } + + virtual SourceRange getSourceRange() const { + return SourceRange(BuiltinLoc, RParenLoc); + } + static bool classof(const Stmt *T) { + return T->getStmtClass() == OverloadExprClass; + } + static bool classof(const OverloadExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); +}; + /// VAArgExpr, used for the builtin function __builtin_va_start. class VAArgExpr : public Expr { Expr *Val; diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index 99262a71a0..89953b1315 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -98,7 +98,10 @@ STMT(73, ObjCSelectorExpr , Expr) STMT(74, ObjCProtocolExpr , Expr) STMT(75, ObjCIvarRefExpr , Expr) -LAST_EXPR(75) +// Clang Extensions. +STMT(76, OverloadExpr , Expr) + +LAST_EXPR(76) #undef STMT #undef FIRST_STMT diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 50815ec216..519af246ef 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -834,6 +834,10 @@ DIAG(err_invalid_conversion_between_vector_and_integer, ERROR, "of different size") DIAG(err_invalid_conversion_between_vector_and_scalar, ERROR, "invalid conversion between vector type '%0' and scalar type '%1'") +DIAG(err_overload_expr_requires_non_zero_constant, ERROR, + "overload requires a non-zero constant expression as first argument") +DIAG(err_overload_no_match, ERROR, + "no matching overload found for arguments of type '%0'") // CHECK: printf format string errors DIAG(warn_printf_not_string_constant, WARNING, diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index fcdf8c80a7..31d6bc74bb 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -289,6 +289,7 @@ KEYWORD(__null , NOTC90|NOTC99|EXTCPP|EXTCPP0x) // C++-only KEYWORD(__alignof , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__attribute , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__builtin_choose_expr , EXTC90|EXTC99|EXTCPP|EXTCPP0x) +KEYWORD(__builtin_overload , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__builtin_offsetof , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__builtin_types_compatible_p, EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__builtin_va_arg , EXTC90|EXTC99|EXTCPP|EXTCPP0x) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 0e5ae2a79b..d948b94b86 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -476,6 +476,14 @@ public: SourceLocation RPLoc) { return 0; } + // __builtin_overload(...) + virtual ExprResult ActOnOverloadExpr(ExprTy **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + return 0; + } + // __builtin_va_arg(expr, type) virtual ExprResult ActOnVAArg(SourceLocation BuiltinLoc,