[cfe-commits] [PATCH] Encoding calling conventions in FunctionTypes
Douglas Gregor
dgregor at apple.com
Tue Dec 22 10:53:55 PST 2009
On Dec 21, 2009, at 7:01 PM, Charles Davis wrote:
> Douglas Gregor wrote:
>> On Dec 16, 2009, at 1:12 PM, Charles Davis wrote:
>>
>>> Hi,
>>>
>>> This patch encodes calling conventions into FunctionType objects. Right
>>> now, only cdecl, stdcall, and fastcall are supported. No real users yet,
>>> but I went through and fixed all the users of
>>> ASTContext::getFunctionType() and friends to respect the calling
>>> convention.
>>>
>>> Next up: applying calling convention attributes to the function types.
>>
>> Comments below.
> How's the attached patch? (Responses to particular comments below.)
Very nice! A few little issues below, then I'd like to put this into Clang.
>>> Index: include/clang/AST/ASTContext.h
>>> ===================================================================
>>> --- include/clang/AST/ASTContext.h (revision 91549)
>>> +++ include/clang/AST/ASTContext.h (working copy)
>>> @@ -399,6 +399,11 @@
>>> /// BlockPointer.
>>> QualType getNoReturnType(QualType T, bool AddNoReturn = true);
>>>
>>> + /// getCallConvType - Adds the specified calling convention
>>> attribute to
>>> + /// the given type, which must be a FunctionType or a pointer to an
>>> + /// allowable type.
>>> + QualType getCallConvType(QualType T, unsigned CallConv);
>>> +
>>
>> The calling convention needs to be an enumeration (defined in Type.h)
>> rather than just an "unsigned". We're only likely to support a few
>> calling conventions, and adding new ones will be a non-trivial undertaking.
> Done.
>>
>>> /// getComplexType - Return the uniqued reference to the type for a
>>> complex
>>> /// number with the specified element type.
>>> QualType getComplexType(QualType T);
>>> @@ -515,7 +520,8 @@
>>>
>>> /// getFunctionNoProtoType - Return a K&R style C function type like
>>> 'int()'.
>>> ///
>>> - QualType getFunctionNoProtoType(QualType ResultTy, bool NoReturn =
>>> false);
>>> + QualType getFunctionNoProtoType(QualType ResultTy, bool NoReturn =
>>> false,
>>> + unsigned CallConv = 0);
>>>
>>> /// getFunctionType - Return a normal function type with a typed
>>> argument
>>> /// list. isVariadic indicates whether the argument list includes
>>> '...'.
>>> @@ -524,7 +530,7 @@
>>> unsigned TypeQuals, bool hasExceptionSpec =
>>> false,
>>> bool hasAnyExceptionSpec = false,
>>> unsigned NumExs = 0, const QualType
>>> *ExArray = 0,
>>> - bool NoReturn = false);
>>> + bool NoReturn = false, unsigned CallConv =
>>> 0);
>>>
>>> /// getTypeDeclType - Return the unique reference to the type for
>>> /// the specified type declaration.
>>> Index: include/clang/AST/Type.h
>>> ===================================================================
>>> --- include/clang/AST/Type.h (revision 91549)
>>> +++ include/clang/AST/Type.h (working copy)
>>> @@ -1708,21 +1708,26 @@
>>> /// NoReturn - Indicates if the function type is attribute noreturn.
>>> unsigned NoReturn : 1;
>>>
>>> + /// CallConv - The calling convention used by the function. Is 0
>>> + /// for default, 1 for C, 2 for stdcall, and 3 for fastcall.
>>> + unsigned CallConv : 2;
>>> +
>>> // The type returned by the function.
>>> QualType ResultType;
>>> protected:
>>> FunctionType(TypeClass tc, QualType res, bool SubclassInfo,
>>> unsigned typeQuals, QualType Canonical, bool Dependent,
>>> - bool noReturn = false)
>>> + bool noReturn = false, unsigned callConv = 0)
>>> : Type(tc, Canonical, Dependent),
>>> SubClassData(SubclassInfo), TypeQuals(typeQuals),
>>> NoReturn(noReturn),
>>> - ResultType(res) {}
>>> + CallConv(callConv), ResultType(res) {}
>>> bool getSubClassData() const { return SubClassData; }
>>> unsigned getTypeQuals() const { return TypeQuals; }
>>> public:
>>>
>>> QualType getResultType() const { return ResultType; }
>>> bool getNoReturnAttr() const { return NoReturn; }
>>> + unsigned getCallConv() const { return CallConv; }
>>
>> Any change to one of the ASTs requires a (de-)serialization code for
>> Clang's precompiled headers. PCHWriter.cpp and PCHReader.cpp will need
>> to be updated to include the calling convention.
> Are you sure? I looked at PCHWriter.cpp, and I didn't see any special
> handling for the noreturn attribute, even when it's stored as part of
> the type. It looks like PCHWriter just stores the noreturn attribute
> itself into the PCH.
Oops, that was a bug; fixed in r91911. The calling convention and noreturn both need to be serialized to PCH.
>>> +QualType ASTContext::getCallConvType(QualType T, unsigned CallConv) {
>>> + QualType ResultType;
>>> + if (const PointerType *Pointer = T->getAs<PointerType>()) {
>>> + QualType Pointee = Pointer->getPointeeType();
>>> + ResultType = getCallConvType(Pointee, CallConv);
>>> + if (ResultType == Pointee)
>>> + return T;
>>> +
>>> + ResultType = getPointerType(ResultType);
>>> + } else if (const FunctionType *F = T->getAs<FunctionType>()) {
>>> + if (F->getCallConv() == CallConv)
>>> + return T;
>>> +
>>> + if (const FunctionNoProtoType *FNPT =
>>> dyn_cast<FunctionNoProtoType>(F)) {
>>> + ResultType = getFunctionNoProtoType(FNPT->getResultType(),
>>> + FNPT->getNoReturn(),
>>> CallConv);
>>> + } else {
>>> + const FunctionProtoType *FPT = cast<FunctionProtoType>(F);
>>> + ResultType
>>> + = getFunctionType(FPT->getResultType(), FPT->arg_type_begin(),
>>> + FPT->getNumArgs(), FPT->isVariadic(),
>>> + FPT->getTypeQuals(),
>>> + FPT->hasExceptionSpec(),
>>> FPT->hasAnyExceptionSpec(),
>>> + FPT->getNumExceptions(),
>>> FPT->exception_begin(),
>>> + FPT->getNoReturn(), CallConv);
>>> + }
>>> + } else
>>> + return T;
>>> +
>>> + return getQualifiedType(ResultType, T.getLocalQualifiers());
>>> +}
>>
>> It's really unfortunate that this code is nearly a copy of
>> getNoReturnType. Is there a way to consolidate the two?
> Took me a while, but I found one. The problem was that, when I called
> the common function from getNoReturnType() (or its brother
> getCallConvType()), I needed a default value for the attribute I wasn't
> setting. The obvious place to get it from would be from the type itself,
> but then there's the case where the get*Type() methods get called on
> something that isn't a function type. So I added a method to QualType
> that does the check and grabs the calling convention from the FunctionType.
Okay, sounds good.
> Index: include/clang/AST/ASTContext.h
> ===================================================================
> --- include/clang/AST/ASTContext.h (revision 91861)
> +++ include/clang/AST/ASTContext.h (working copy)
> @@ -400,6 +400,11 @@
> /// BlockPointer.
> QualType getNoReturnType(QualType T, bool AddNoReturn = true);
>
> + /// getCallConvType - Adds the specified calling convention attribute to
> + /// the given type, which must be a FunctionType or a pointer to an
> + /// allowable type.
> + QualType getCallConvType(QualType T, CallingConv CallConv);
> +
> /// getComplexType - Return the uniqued reference to the type for a complex
> /// number with the specified element type.
> QualType getComplexType(QualType T);
> @@ -516,7 +521,8 @@
>
> /// getFunctionNoProtoType - Return a K&R style C function type like 'int()'.
> ///
> - QualType getFunctionNoProtoType(QualType ResultTy, bool NoReturn = false);
> + QualType getFunctionNoProtoType(QualType ResultTy, bool NoReturn = false,
> + CallingConv CallConv = CC_Default);
>
> /// getFunctionType - Return a normal function type with a typed argument
> /// list. isVariadic indicates whether the argument list includes '...'.
> @@ -525,7 +531,8 @@
> unsigned TypeQuals, bool hasExceptionSpec = false,
> bool hasAnyExceptionSpec = false,
> unsigned NumExs = 0, const QualType *ExArray = 0,
> - bool NoReturn = false);
> + bool NoReturn = false,
> + CallingConv CallConv = CC_Default);
>
> /// getTypeDeclType - Return the unique reference to the type for
> /// the specified type declaration.
> Index: include/clang/AST/Type.h
> ===================================================================
> --- include/clang/AST/Type.h (revision 91861)
> +++ include/clang/AST/Type.h (working copy)
> @@ -385,7 +385,15 @@
> }
> };
>
> +/// CallingConv - Specifies the calling convention that a function uses.
> +enum CallingConv {
> + CC_Default,
> + CC_C, // __attribute__((cdecl))
> + CC_X86StdCall, // __attribute__((stdcall))
> + CC_X86FastCall // __attribute__((fastcall))
> +};
>
> +
> /// QualType - For efficiency, we don't store CV-qualified types as nodes on
> /// their own: instead each reference to a type stores the qualifiers. This
> /// greatly reduces the number of nodes we need to allocate for types (for
> @@ -664,6 +672,10 @@
> /// false otherwise.
> bool getNoReturnAttr() const;
>
> + /// getCallConv - Returns the calling convention of the type if the type
> + /// is a function type, CC_Default otherwise.
> + CallingConv getCallConv() const;
> +
> private:
> // These methods are implemented in a separate translation unit;
> // "static"-ize them to avoid creating temporary QualTypes in the
> @@ -1708,21 +1720,25 @@
> /// NoReturn - Indicates if the function type is attribute noreturn.
> unsigned NoReturn : 1;
>
> + /// CallConv - The calling convention used by the function.
> + CallingConv CallConv : 2;
> +
MSVC has an annoying bug w.r.t. bit-fields of enum type, so this bit-field needs to be declared as
unsigned CallConv : 2;
> +/// getCallConv - Returns the calling convention of the type if the type
> +/// is a function type, CC_Default otherwise.
> +inline CallingConv QualType::getCallConv() const {
> + if (const PointerType *PT = getTypePtr()->getAs<PointerType>())
> + return PT->getPointeeType().getCallConv();
> + else if (const BlockPointerType *BPT =
> + getTypePtr()->getAs<BlockPointerType>()) {
> + if (const FunctionType *FT = BPT->getPointeeType()->getAs<FunctionType>())
> + return FT->getCallConv();
> + } else if (const FunctionType *FT = getTypePtr()->getAs<FunctionType>())
> + return FT->getCallConv();
> +
> + return CC_Default;
> +}
It seems like this should cope with ReferenceType and MemberPointerType nodes as well since, IIRC, you can have references and member pointers to functions with specific calling conventions. That's not specifically your problem, though, so it won't hold up your patch.
>
> Index: lib/AST/ASTContext.cpp
> ===================================================================
> --- lib/AST/ASTContext.cpp (revision 91861)
> +++ lib/AST/ASTContext.cpp (working copy)
> @@ -1200,45 +1200,60 @@
> return getExtQualType(TypeNode, Quals);
> }
>
> -QualType ASTContext::getNoReturnType(QualType T, bool AddNoReturn) {
> +static QualType getNoReturnCallConvType(ASTContext& Context, QualType T,
> + bool AddNoReturn,
> + CallingConv CallConv) {
> QualType ResultType;
> if (const PointerType *Pointer = T->getAs<PointerType>()) {
> QualType Pointee = Pointer->getPointeeType();
> - ResultType = getNoReturnType(Pointee, AddNoReturn);
> + ResultType = getNoReturnCallConvType(Context, Pointee, AddNoReturn,
> + CallConv);
> if (ResultType == Pointee)
> return T;
> -
> - ResultType = getPointerType(ResultType);
> +
> + ResultType = Context.getPointerType(ResultType);
> } else if (const BlockPointerType *BlockPointer
> = T->getAs<BlockPointerType>()) {
> QualType Pointee = BlockPointer->getPointeeType();
> - ResultType = getNoReturnType(Pointee, AddNoReturn);
> + ResultType = getNoReturnCallConvType(Context, Pointee, AddNoReturn,
> + CallConv);
> if (ResultType == Pointee)
> return T;
> -
> - ResultType = getBlockPointerType(ResultType);
> - } else if (const FunctionType *F = T->getAs<FunctionType>()) {
> - if (F->getNoReturnAttr() == AddNoReturn)
> +
> + ResultType = Context.getBlockPointerType(ResultType);
> + } else if (const FunctionType *F = T->getAs<FunctionType>()) {
> + if (F->getNoReturnAttr() == AddNoReturn && F->getCallConv() == CallConv)
> return T;
> -
> +
> if (const FunctionNoProtoType *FNPT = dyn_cast<FunctionNoProtoType>(F)) {
> - ResultType = getFunctionNoProtoType(FNPT->getResultType(), AddNoReturn);
> + ResultType = Context.getFunctionNoProtoType(FNPT->getResultType(),
> + AddNoReturn, CallConv);
> } else {
> const FunctionProtoType *FPT = cast<FunctionProtoType>(F);
> ResultType
> - = getFunctionType(FPT->getResultType(), FPT->arg_type_begin(),
> - FPT->getNumArgs(), FPT->isVariadic(),
> - FPT->getTypeQuals(),
> - FPT->hasExceptionSpec(), FPT->hasAnyExceptionSpec(),
> - FPT->getNumExceptions(), FPT->exception_begin(),
> - AddNoReturn);
> + = Context.getFunctionType(FPT->getResultType(), FPT->arg_type_begin(),
> + FPT->getNumArgs(), FPT->isVariadic(),
> + FPT->getTypeQuals(),
> + FPT->hasExceptionSpec(),
> + FPT->hasAnyExceptionSpec(),
> + FPT->getNumExceptions(),
> + FPT->exception_begin(),
> + AddNoReturn, CallConv);
> }
> } else
> return T;
> -
> - return getQualifiedType(ResultType, T.getLocalQualifiers());
> +
> + return Context.getQualifiedType(ResultType, T.getLocalQualifiers());
> }
Great, thanks!
@@ -4213,6 +4231,11 @@
allLTypes = false;
if (NoReturn != rbase->getNoReturnAttr())
allRTypes = false;
+ CallingConv lcc = lbase->getCallConv();
+ CallingConv rcc = rbase->getCallConv();
+ // Compatible functions must have the same calling convention
+ if((lcc == 0 || rcc != 0) && lcc != rcc)
+ return QualType();
I find this logic a bit hard to follow... could you abstract out an "is same calling convention" predicate, so that we can turn this into just
if (!isSameCallingConvention(lcc, rcc))
return QualType();
It'll be very helpful for all of the other places where we'll need to check for equivalence of calling conventions.
- Doug
More information about the cfe-commits
mailing list