[cfe-commits] [PATCH] Add _Atomic specifier for atomic types
Jeffrey Yasskin
jyasskin at google.com
Tue Oct 4 11:47:21 PDT 2011
[cc Cary because of the dwarf question.]
On Mon, Oct 3, 2011 at 7:53 PM, Eli Friedman <eli.friedman at gmail.com> wrote:
> Per subject; after some discussions with John, I decided that we
> really want to be prepared for C1x atomics, and being able to use
> _Atomic in the implementation of <atomic> will make the implementation
> of <atomic> more flexible. This patch follows the syntax from the C1x
> draft which will hopefully stick: _Atomic(int*) specifies an atomic
> pointer to an integer, etc.
The C1x draft makes _Atomic(type) a type specifier and plain _Atomic a
type qualifier. It looks like your patch only implements the type
specifier. Was that intentional?
> After this patch is committed, I plan to update my other patch (for
> the __atomic_* builtins) to use these atomic types, which will make
> the implementation of <atomic> completely straightforward (although
> the implementation might want to use a bit of trickery to make sure
> errors mentioning _Atomic don't leak out). I'm still not quite sure
> what the runtime support is going to look like, so the initial
> implementation will probably error out on non-lock-free atomics.
Sounds good to me. Comments inline.
> I don't plan on working on <stdatomic.h> at the moment, but it should
> be relatively easy to implement.
>
> Any comments would be appreciated.
> Index: test/Sema/atomic-type.c
> ===================================================================
> --- test/Sema/atomic-type.c (revision 0)
> +++ test/Sema/atomic-type.c (revision 0)
> @@ -0,0 +1,12 @@
> +// RUN: %clang_cc1 %s -verify -fsyntax-only
> +
> +// Basic parsing/Sema tests for _Atomic
> +// (No operations are actually supported on objects of this type yet.)
> +_Atomic(int) t1;
> +_Atomic(int) *t2 = &t1;
> +void testf(void*);
> +void f(void) {
> + _Atomic(_Atomic(int)*) t3;
> + _Atomic(_Atomic(int)*) *t4[2] = { &t3, 0 };
If you meant to implement the type qualifier, you should add tests for it too.
> + testf(t4);
> +}
> \ No newline at end of file
^ Newlines!
> Index: include/clang/AST/Type.h
> ===================================================================
> --- include/clang/AST/Type.h (revision 141016)
> +++ include/clang/AST/Type.h (working copy)
> @@ -4352,6 +4353,37 @@
> static bool classof(const ObjCObjectPointerType *) { return true; }
> };
>
> +class AtomicType : public Type, public llvm::FoldingSetNode {
> + QualType ValueType;
> +
> + AtomicType(QualType ValTy, QualType Canonical)
> + : Type(Atomic, Canonical, ValTy->isDependentType(),
> + ValTy->isInstantiationDependentType(),
> + ValTy->isVariablyModifiedType(),
> + ValTy->containsUnexpandedParameterPack()),
> + ValueType(ValTy) {}
> + friend class ASTContext; // ASTContext creates these.
> +
> + public:
> + /// getValueType - Gets the type contained by this atomic type, i.e.
> + /// the type returned by performing an atomic load of this atomic type.
> + QualType getValueType() const { return ValueType; }
> +
> + bool isSugared() const { return false; }
> + QualType desugar() const { return QualType(this, 0); }
> +
> + void Profile(llvm::FoldingSetNodeID &ID) {
> + Profile(ID, getValueType());
Could be getValueType().Profile(ID) to eliminate the need for the
static function.
> + }
> + static void Profile(llvm::FoldingSetNodeID &ID, QualType T) {
> + ID.AddPointer(T.getAsOpaquePtr());
> + }
> + static bool classof(const Type *T) {
> + return T->getTypeClass() == Atomic;
> + }
> + static bool classof(const AtomicType *) { return true; }
> +};
> +
> /// A qualifier set is used to build a set of qualifiers.
> class QualifierCollector : public Qualifiers {
> public:
> Index: lib/Sema/TreeTransform.h
> ===================================================================
> --- lib/Sema/TreeTransform.h (revision 141016)
> +++ lib/Sema/TreeTransform.h (working copy)
> @@ -905,6 +905,12 @@
> NumExpansions);
> }
>
> + /// \brief Build a new atomic type given its value type.
> + ///
> + /// By default, performs semantic analysis when building the pointer type.
Mis-copied comment.
> + /// Subclasses may override this routine to provide different behavior.
> + QualType RebuildAtomicType(QualType ValueType, SourceLocation KWLoc);
> +
> /// \brief Build a new template name given a nested name specifier, a flag
> /// indicating whether the "template" keyword was provided, and the template
> /// that the template name refers to.
> Index: lib/Sema/SemaTemplateDeduction.cpp
> ===================================================================
> --- lib/Sema/SemaTemplateDeduction.cpp (revision 141016)
> +++ lib/Sema/SemaTemplateDeduction.cpp (working copy)
> @@ -1116,7 +1116,17 @@
> Info, Deduced, TDF);
>
> return Sema::TDK_NonDeducedMismatch;
> -
> +
> + // _Atomic T [extension]
Is this _Atomic T or _Atomic(T) or both?
> + case Type::Atomic:
> + if (const AtomicType *AtomicArg = Arg->getAs<AtomicType>())
> + return DeduceTemplateArguments(S, TemplateParams,
> + cast<AtomicType>(Param)->getValueType(),
> + AtomicArg->getValueType(),
> + Info, Deduced, TDF);
> +
> + return Sema::TDK_NonDeducedMismatch;
> +
> // T *
> case Type::Pointer: {
> QualType PointeeType;
> Index: lib/Sema/SemaType.cpp
> ===================================================================
> --- lib/Sema/SemaType.cpp (revision 141016)
> +++ lib/Sema/SemaType.cpp (working copy)
> @@ -2872,6 +2882,10 @@
> void VisitTagTypeLoc(TagTypeLoc TL) {
> TL.setNameLoc(DS.getTypeSpecTypeNameLoc());
> }
> + void VisitAtomicTypeLoc(AtomicTypeLoc TL) {
> + TL.setKWLoc(DS.getTypeSpecTypeLoc());
> + TL.setParensRange(DS.getTypeofParensRange());
Is getTypeofParensRange() named like that because at one point the
only thing that had a parens range was typeof()? It may be time to
change the name.
> + }
>
> void VisitTypeLoc(TypeLoc TL) {
> // FIXME: add other typespec types and change this to an assert.
> @@ -4183,3 +4197,16 @@
> }
> llvm_unreachable("unknown unary transform type");
> }
> +
> +QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
> + if (!T->isDependentType() &&
> + (T->isIncompleteType() || !T.isTriviallyCopyableType(Context))) {
> + Diag(Loc, diag::err_atomic_specifier_bad_type) << T;
I'd appreciate a better message here. "cannot be specified" doesn't
tell the user what they did wrong. Instead, split it into two
messages, for incomplete vs non-trivially copyable types, and say
something like "_Atomic specifier cannot apply to incomplete type" and
"atomic types must be trivially copyable". I suspect the library will
use static_assert(is_trivially_copyable<T>::value, "…") so that C++
users never see this particular diagnostic, but we still shouldn't
confuse C users.
Also, C1x says that the _Atomic specifier can't be applied to "an
array type, a function type, an atomic type, or a qualified type.",
while the _Atomic qualifier can't be applied to "an array type or a
function type." Are those covered by the trivially-copyable
restriction? This probably deserves a comment.
> + return QualType();
> + }
> +
> + // FIXME: Do we need any handling for ARC here?
> +
> + // Build the pointer type.
> + return Context.getAtomicType(T);
> +}
> Index: lib/Sema/SemaExpr.cpp
> ===================================================================
> --- lib/Sema/SemaExpr.cpp (revision 141016)
> +++ lib/Sema/SemaExpr.cpp (working copy)
> @@ -342,6 +342,10 @@
> QualType T = E->getType();
> assert(!T.isNull() && "r-value conversion on typeless expression?");
>
> + // We can't do lvalue-to-rvalue on atomics yet.
According to the C1x draft I have, it's not "yet". The only way to get
an rvalue from an atomic lvalue is to call atomic_load().
> + if (T->getAs<AtomicType>())
> + return Owned(E);
> +
> // Create a load out of an ObjCProperty l-value, if necessary.
> if (E->getObjectKind() == OK_ObjCProperty) {
> ExprResult Res = ConvertPropertyForRValue(E);
> @@ -5388,6 +5392,10 @@
> LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType();
> RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType();
>
> + // We can't do assignment from/to atomics yet.
Again, the only way to assign an atomic is with atomic_store().
> + if (LHSType->isAtomicType())
> + return Incompatible;
> +
> // Common case: no conversion required.
> if (LHSType == RHSType) {
> Kind = CK_NoOp;
> Index: lib/AST/TypePrinter.cpp
> ===================================================================
> --- lib/AST/TypePrinter.cpp (revision 141016)
> +++ lib/AST/TypePrinter.cpp (working copy)
> @@ -581,6 +582,16 @@
> }
> }
>
> +void TypePrinter::printAtomic(const AtomicType *T, std::string &S) {
> + if (!S.empty())
> + S = ' ' + S;
> + std::string Str;
> + IncludeStrongLifetimeRAII Strong(Policy);
> + print(T->getValueType(), Str);
> +
> + S = "_Atomic(" + Str + ")" + S;
Wow, this is inefficient. I wouldn't comment, but printAtomic takes
an out parameter for the result, and the only reason to do that is to
avoid copies, but this code copies out the wazoo.
> +}
> +
> /// Appends the given scope to the end of a string.
> void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) {
> if (DC->isTranslationUnit()) return;
> Index: lib/AST/ItaniumMangle.cpp
> ===================================================================
> --- lib/AST/ItaniumMangle.cpp (revision 141016)
> +++ lib/AST/ItaniumMangle.cpp (working copy)
> @@ -2111,6 +2112,11 @@
> mangleType(D);
> }
>
> +void CXXNameMangler::mangleType(const AtomicType *T) {
The other mangleType definitions have a grammar fragment in a comment.
It looks like the official ABI doesn't mention atomics yet, so can
you describe where you got the v17 prefix?
> + Out << "v17_Atomic";
> + mangleType(T->getValueType());
> +}
> +
> void CXXNameMangler::mangleIntegerLiteral(QualType T,
> const llvm::APSInt &Value) {
> // <expr-primary> ::= L <type> <value number> E # integer literal
> Index: lib/AST/ASTContext.cpp
> ===================================================================
> --- lib/AST/ASTContext.cpp (revision 141016)
> +++ lib/AST/ASTContext.cpp (working copy)
> @@ -1063,8 +1063,13 @@
> return getTypeInfo(getCanonicalType(T));
> }
>
> + case Type::Atomic: {
> + // FIXME: The alignment needs to be "fixed".
What do you mean "fixed"? Do you mean rounded up to the next power of
2 over the size, or changed to match the target platform?
> + return getTypeInfo(cast<AtomicType>(T)->getValueType());
> }
>
> + }
> +
> assert(Align && (Align & (Align-1)) == 0 && "Alignment must be power of 2");
> return std::make_pair(Width, Align);
> }
> @@ -1707,6 +1712,10 @@
> break;
> }
>
> + case Type::Atomic: {
> + // FIXME: Implement!
> + }
Does "The type name in an atomic type specifier shall not refer to an
array type" from C1x 6.7.2.4 imply this should go in the "type should
never be variably-modified" section?
> +
> case Type::ConstantArray: {
> const ConstantArrayType *cat = cast<ConstantArrayType>(ty);
> result = getConstantArrayType(
> @@ -2904,6 +2913,34 @@
> return QualType(AT, 0);
> }
>
> +/// getAtomicType - Return the uniqued reference to the atomic type for
> +/// the given value type.
> +QualType ASTContext::getAtomicType(QualType T) const {
> + // Unique pointers, to guarantee there is only one pointer of a particular
> + // structure.
> + llvm::FoldingSetNodeID ID;
> + AtomicType::Profile(ID, T);
> +
> + void *InsertPos = 0;
> + if (AtomicType *AT = AtomicTypes.FindNodeOrInsertPos(ID, InsertPos))
> + return QualType(AT, 0);
> +
> + // If the pointee type isn't canonical, this won't be a canonical type either,
s/pointee/atomic/
> + // so fill in the canonical type field.
> + QualType Canonical;
> + if (!T.isCanonical()) {
> + Canonical = getAtomicType(getCanonicalType(T));
> +
> + // Get the new insert position for the node we care about.
> + AtomicType *NewIP = AtomicTypes.FindNodeOrInsertPos(ID, InsertPos);
> + assert(NewIP == 0 && "Shouldn't be in the map!"); (void)NewIP;
> + }
> + AtomicType *New = new (*this, TypeAlignment) AtomicType(T, Canonical);
> + Types.push_back(New);
> + AtomicTypes.InsertNode(New, InsertPos);
> + return QualType(New, 0);
> +}
> +
> /// getAutoDeductType - Get type pattern for deducing against 'auto'.
> QualType ASTContext::getAutoDeductType() const {
> if (AutoDeductTy.isNull())
> @@ -5802,6 +5839,24 @@
> return RHS;
> return getBlockPointerType(ResultType);
> }
> + case Type::Atomic:
> + {
> + // Merge two pointer types, while trying to preserve typedef info
> + QualType LHSValue = LHS->getAs<AtomicType>()->getValueType();
> + QualType RHSValue = RHS->getAs<AtomicType>()->getValueType();
> + if (Unqualified) {
> + LHSValue = LHSValue.getUnqualifiedType();
> + RHSValue = RHSValue.getUnqualifiedType();
> + }
> + QualType ResultType = mergeTypes(LHSValue, RHSValue, false,
> + Unqualified);
> + if (ResultType.isNull()) return QualType();
> + if (getCanonicalType(LHSValue) == getCanonicalType(ResultType))
> + return LHS;
> + if (getCanonicalType(RHSValue) == getCanonicalType(ResultType))
> + return RHS;
> + return getPointerType(ResultType);
You mean getAtomicType? If getPointerType is wrong here, please add a
test that fails because of it.
> + }
> case Type::ConstantArray:
> {
> const ConstantArrayType* LCAT = getAsConstantArrayType(LHS);
> Index: lib/CodeGen/CodeGenFunction.cpp
> ===================================================================
> --- lib/CodeGen/CodeGenFunction.cpp (revision 141016)
> +++ lib/CodeGen/CodeGenFunction.cpp (working copy)
> @@ -86,6 +86,10 @@
> case Type::ObjCObject:
> case Type::ObjCInterface:
> return true;
> +
> + // In IRGen, atomic types are just the underlying type
Does the extra alignment that's sometimes required imply wrapping an
aggregate around some atomics' value types?
> + case Type::Atomic:
> + return hasAggregateLLVMType(type->getAs<AtomicType>()->getValueType());
> }
> llvm_unreachable("unknown type kind!");
> }
> @@ -978,6 +982,10 @@
> case Type::FunctionNoProto:
> type = cast<FunctionType>(ty)->getResultType();
> break;
> +
> + case Type::Atomic:
> + type = cast<AtomicType>(ty)->getValueType();
Similarly here, it's possible atomics are never variably modified.
> + break;
> }
> } while (type->isVariablyModifiedType());
> }
> Index: lib/CodeGen/CGRTTI.cpp
> ===================================================================
> --- lib/CodeGen/CGRTTI.cpp (revision 141016)
> +++ lib/CodeGen/CGRTTI.cpp (working copy)
> @@ -474,6 +474,10 @@
> // abi::__pointer_to_member_type_info.
> VTableName = "_ZTVN10__cxxabiv129__pointer_to_member_type_infoE";
> break;
> +
> + case Type::Atomic:
> + // Pretend this is a class; not sure what else we can do here.
FIXME: ask the C++ ABI folks? Complex and vector get to be
fundamental types, so maybe atomic should be too?
> + VTableName = ClassTypeInfo;
> }
>
> llvm::Constant *VTable =
> Index: lib/CodeGen/CGDebugInfo.cpp
> ===================================================================
> --- lib/CodeGen/CGDebugInfo.cpp (revision 141016)
> +++ lib/CodeGen/CGDebugInfo.cpp (working copy)
> @@ -1418,6 +1418,13 @@
> 0, 0, Elements);
> }
>
> +llvm::DIType CGDebugInfo::CreateType(const AtomicType *Ty,
> + llvm::DIFile U) {
> + // Ignore the atomic wrapping
> + // FIXME: What is the correct representation?
Cary Coutant suggests adding a DW_TAG_atomic_type node with a child
node pointing at the value type, similar to DW_TAG_const_type, etc.
Then extra alignment, size, etc. information can be attached to the
DW_TAG_atomic_type node. He's going to bring this up with the Dwarf
committee and let us know what they say.
> + return getOrCreateType(Ty->getValueType(), U);
> +}
> +
> /// CreateEnumType - get enumeration type.
> llvm::DIType CGDebugInfo::CreateEnumType(const EnumDecl *ED) {
> llvm::DIFile Unit = getOrCreateFile(ED->getLocation());
More information about the cfe-commits
mailing list