[cfe-dev] aligned 'new'
Anton Yartsev
anton.yartsev at gmail.com
Mon Oct 10 04:06:06 PDT 2011
On 20.09.2011 22:37, Anton Yartsev wrote:
> Here is an updated patch with lots of changes. Tried to make it of
> higher quality. Please review.
>
> aligned_new_test.cpp - new test from clang\test\CodeGenCXX\
> aligned_new.h - header with new declarations from clang\lib\Headers\
> HelperLibs.zip - files for creating library. Placed it to clang\lib\
>
>>> Putting this functions to clang header makes the feature independent
>>> of library used
>> Yes, but it has several unfortunate side effects:
>>
>> (1) We'll end up with copies of these functions in every .o file
>> that uses new or delete, which is likely to cause some serious code
>> bloat
>> (2) Unlike with the default new/new[]/delete/delete[] operators
>> provided by the C++ implementation, these 'aligned' versions are not
>> replaceable by the user because their definitions are internal to
>> each .o file
>>
>> There are other ways to achieve library independence. For example,
>> the definitions could be in a separate library that we link against
>> when -fstrict-aligned is provided, and the declarations themselves
>> could be synthesized by the compiler when needed (e.g., as a library
>> built-in).
> Made standard non-placement declarations synthesized by the compiler
> as it is made for standard non-placement new/new[]/delete/delete[].
> Moved the definitions to separate library
>>
>>>> @@ -1282,10 +1354,54 @@
>>>>
>>>> // Didn't find a member overload. Look for a global one.
>>>> DeclareGlobalNewDelete();
>>>> DeclContext *TUDecl = Context.getTranslationUnitDecl();
>>>> +
>>>> if (FindAllocationOverload(StartLoc, Range,
>>>> NewName,&AllocArgs[0],
>>>> AllocArgs.size(), TUDecl,
>>>> /*AllowMissing=*/false,
>>>> OperatorNew))
>>>> return true;
>>>> +
>>>> + // replace calls to allocation functions defined in the<new>
>>>> header
>>>> + // with calls to its aligned analogs
>>>> + if (getLangOptions().StrictAligned&&
>>>> + isDefaultLibraryAllocationFunction(OperatorNew)) {
>>>> +
>>>> + OperatorNew = 0;
>>>> +
>>>> + DeclarationName AlignedNewName(
>>>> +&Context.Idents.get(IsArray ? "__aligned_new_arr" :
>>>> "__aligned_new"));
>>>> +
>>>> + DeclarationName AlignedDeleteName(
>>>> +&Context.Idents.get(IsArray ? "__aligned_delete_arr"
>>>> + : "__aligned_delete"));
>>>> +
>>>> + // construct additional parameter - alignment
>>>> + IntegerLiteral AlignmentArg(Context, llvm::APInt::getNullValue(
>>>> + Context.Target.getPointerWidth(0)),
>>>> + Context.getSizeType(),
>>>> + SourceLocation());
>>>> +
>>>> + llvm::SmallVector<Expr*, 8> ArgsPlusAlign(AllocArgs);
>>>> +
>>>> + // insert alignment parameter just after the Size parameter
>>>> + ArgsPlusAlign.insert(ArgsPlusAlign.begin()+1,&AlignmentArg);
>>>> +
>>>> + if (FindAllocationOverload(StartLoc, Range, AlignedNewName,
>>>> +&ArgsPlusAlign[0], ArgsPlusAlign.size(), TUDecl,
>>>> + /*AllowMissing=*/false,
>>>> OperatorNew))
>>>> + return true;
>>>>
>>>> There's no fallback here, which seems to imply that if the call to
>>>> __aligned_new/__aligned_new_arr/etc. fails, we won't end up trying
>>>> to the standard-mandated operator new/operator new[]/etc. Is that
>>>> really the intended behavior? It seems like fairly significant
>>>> breakage to me.
>>> If the call to __aligned_... fails we end up with an error ('no
>>> matching function for call to ...'). This behavior seem correct to
>>> me as specifying -fstrict-aligned option we expect our data to be
>>> aligned properly.
>> Does this actually break existing C++ code that overloads operators
>> new/new[]/delete/delete[], or am I missing something? If it does
>> break code, is that "by design" or "by accident"?
> No, calls to standard new/new[]/delete/delete[], either replaced or
> not, will be substituted, calls to overloaded
> new/new[]/delete/delete[] will remain unchanged. Calls to
> new/new[]/delete/delete[] declared in the class scope remain unchanged
> regardless of the signature.
>
>>> That is how the feature is designed in ppu-lv2-gcc:
>>> giving -fstrict-align option to ppu-lv2-gcc does the following:
>>> 1) It cause ppu-lv2-gcc to define the built-in macro
>>> "__STRICT_ALIGNED".
>>> 2) The PS3 header "new" has some new declarations for new and delete
>>> operators (standard, nothrow and placement, both simple and array
>>> versions) that take an additional size_t parameter for alignment.
>>> These are conditionalized on the __STRICT_ALIGNED flag.
>> If the PS3 header<new> has the declarations of the aligned
>> new/delete operators now, why is your patch adding these operators to
>> Clang's mm_alloc.h? Isn't the precedent already there to make
>> sure<new> has these declarations, which is most likely the better
>> answer?
> I want the declarations live in the <new> header, but I want the
> feature be independent of the library. Adding <new> header to clang
> leads to ambiguity. Moved the declarations temporarily to separate
> header aligned_new.h. It will not be included often as standard
> non-placement declarations are now synthesized by clang. I can make
> the rest of the declarations also be synthesized and then there will
> be no need to include aligned_new.h at all
>>> 3) If this option is set, when ppu-lv2-gcc sees a new or delete of a
>>> type which either has an inherent alignment, or has an explicit
>>> alignment specified by __attribute__((__aligned__(x))), it will
>>> transform the standard new or delete call to a call to the aligned
>>> version of new or delete.
>> Every type has alignment, so you're saying that every "standard"
>> new/delete/new[]/delete[] expression calls into the aligned version
>> of new/delete/new[]/delete[]. And you're only talking about the
>> global operators new/delete/new[]/delete[] that have one of the forms
>> described in<new>; this replacement doesn't apply for operators with
>> different signatures from those in<new> or for those operators when
>> they're defined at class scope. Is the omission of support for
>> allocation/deallocation functions at class scope intentional? If so,
>> the compiler should emit an error if someone does write one of these
>> operators at class scope, because they're useless (and accepting them
>> is misleading). If not, then we're missing code to support that case.
> Calls to new/new[]/delete/delete[] declared in the class scope are all
> left unchanged. No errors or warnings are generated. That is how
> ppu-lv2-gcc does. Do you want me to change the behavior?
>>
>> Some more detailed comments to follow, but I'm having a lot of
>> trouble with this feature. I understand that the design of the
>> feature is not really up for debate, because we're implementing an
>> extension that was defined by another compiler, and I can live with
>> that. That said, we need to have a specification of this feature that
>> users and compiler writers alike can use to understand how this
>> feature is supposed to work, because we don't all have access to a
>> copy of ppu-lv2-gcc to act as an oracle for how this feature behaves.
>> The specification needs to go into Clang's language extensions page,
>> so people know it is there and usable:
>>
>> http://clang.llvm.org/docs/LanguageExtensions.html
> Updated LanguageExtensions.html
>>
>> Now, for the minor comments:
>>
>> Index: include/clang/Basic/LangOptions.h
>> ===================================================================
>> --- include/clang/Basic/LangOptions.h (revision 138994)
>> +++ include/clang/Basic/LangOptions.h (working copy)
>> @@ -140,6 +140,8 @@
>>
>> unsigned MRTD : 1; // -mrtd calling convention
>> unsigned DelayedTemplateParsing : 1; // Delayed template parsing
>>
>> + unsigned StrictAligned : 1; // Enforce aligned attribute with
>> 'operator new'
>> +
>> private:
>> // We declare multibit enums as unsigned because MSVC insists on
>> making enums
>> // signed. Set/Query these values using accessors.
>>
>> Every LangOptions flag needs to be serialized/de-serialized and
>> checked by the ASTWriter and ASTReader.
>>
>> Also, StrictAligned needs to be initialized.
> Done
>>
>> Index: include/clang/Driver/CC1Options.td
>> ===================================================================
>> --- include/clang/Driver/CC1Options.td (revision 138994)
>> +++ include/clang/Driver/CC1Options.td (working copy)
>> @@ -433,6 +433,8 @@
>>
>> HelpText<"Disable implicit builtin knowledge of functions">;
>> def faltivec : Flag<"-faltivec">,
>> HelpText<"Enable AltiVec vector initializer syntax">;
>> +def fstrict_aligned : Flag<"-fstrict-aligned">,
>> + HelpText<"Enforce aligned attribute with 'operator new'">;
>> def fno_access_control : Flag<"-fno-access-control">,
>> HelpText<"Disable C++ access control">;
>> def fno_assume_sane_operator_new :
>> Flag<"-fno-assume-sane-operator-new">,
>>
>> This is a -cc1-level flag. I assume that you also want it to work in
>> the Clang driver? You'll need to update the driver, too.
> Done
>>
>> Index: lib/AST/Decl.cpp
>> ===================================================================
>> --- lib/AST/Decl.cpp (revision 138994)
>> +++ lib/AST/Decl.cpp (working copy)
>> @@ -1533,7 +1533,31 @@
>>
>> getIdentifier()->isStr("main");
>> }
>>
>> +bool FunctionDecl::isAlignedSubstitutionOfNewDelete(bool CheckNew,
>> + bool CheckNewArr,
>> + bool CheckDelete,
>> + bool
>> CheckDeleteArr) const {
>> +
>> + if (!getASTContext().getLangOptions().StrictAligned)
>> + return false;
>> +
>> + IdentifierInfo *II = getDeclName().getAsIdentifierInfo();
>> +
>> + return (II&& ((CheckNew&& II->isStr("__aligned_new")) ||
>> + (CheckNewArr&& II->isStr("__aligned_new_arr")) ||
>> + (CheckDelete&& II->isStr("__aligned_delete")) ||
>> + (CheckDeleteArr&&
>> II->isStr("__aligned_delete_arr"))));
>> +}
>>
>> isStr() is inefficient. You'll need to cache the IdentifierInfos for
>> these four strings and perform pointer comparisons against II.
>>
>> I also find this API to be a bit strangeā¦ why not call this
>> classifyAlignedSubstitutionOfNewDelete() and return a value of
>> enumeration type that tells us which one it is?
> Done
>>
>> bool FunctionDecl::isReservedGlobalPlacementOperator() const {
>> +
>> + // calls to new/delete were substituted by calls to it's aligned
>> analogs
>> + if (isAlignedSubstitutionOfNewDelete(
>> + /*new*/true, /*new[]*/true, /*delete*/true, /*delete[]*/true))
>> + return false;
>> + //FIXME: maybe add logic for detecting aligned analogs of reserved
>> global
>> + // placement operators
>>
>> Why is this isAlignedSubstitutionOfNewDelete check even necessary? If
>> you're changing the contract of
>> FunctionDecl::isReservedGlobalPlacementOperator() to allow functions
>> whose name isn't a C++ new/delete/new[]/delete[] operator, that's
>> fine, but just return false if
> Done
>>
>> getDeclName().getNameKind() != DeclarationName::CXXOperatorName
>>
>> @@ -1836,6 +1839,9 @@
>> else
>> Opts.ObjCXXARCStandardLibrary =
>> (ObjCXXARCStandardLibraryKind)Library;
>> }
>> +
>> + if(Args.hasArg(OPT_fstrict_aligned))
>> + Opts.Includes.push_back("mm_malloc.h");
>> }
>>
>> Architecturally, I'm strongly opposed to auto-including headers like
>> this. Either the user should have to include a header manually
>> (e.g.,<new>) or the compiler should synthesize the declarations it
>> needs on demand.
> Done
>
ping
--
Anton
More information about the cfe-dev
mailing list