| //===-- CIRGenValue.h - CIRGen wrappers for mlir::Value ---------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // These classes implement wrappers around mlir::Value in order to fully |
| // represent the range of values for C L- and R- values. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_LIB_CIR_CIRGENVALUE_H |
| #define LLVM_CLANG_LIB_CIR_CIRGENVALUE_H |
| |
| #include "Address.h" |
| #include "CIRGenRecordLayout.h" |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/CharUnits.h" |
| #include "clang/AST/Type.h" |
| #include "clang/CIR/Dialect/IR/CIRTypes.h" |
| |
| #include "llvm/ADT/PointerIntPair.h" |
| |
| #include "mlir/IR/Value.h" |
| |
| namespace cir { |
| |
| /// This trivial value class is used to represent the result of an |
| /// expression that is evaluated. It can be one of three things: either a |
| /// simple MLIR SSA value, a pair of SSA values for complex numbers, or the |
| /// address of an aggregate value in memory. |
| class RValue { |
| enum Flavor { Scalar, Complex, Aggregate }; |
| |
| // The shift to make to an aggregate's alignment to make it look |
| // like a pointer. |
| enum { AggAlignShift = 4 }; |
| |
| // Stores first value and flavor. |
| llvm::PointerIntPair<mlir::Value, 2, Flavor> V1; |
| // Stores second value and volatility. |
| llvm::PointerIntPair<llvm::PointerUnion<mlir::Value, int *>, 1, bool> V2; |
| // Stores element type for aggregate values. |
| mlir::Type ElementType; |
| |
| public: |
| bool isScalar() const { return V1.getInt() == Scalar; } |
| bool isComplex() const { return V1.getInt() == Complex; } |
| bool isAggregate() const { return V1.getInt() == Aggregate; } |
| bool isIgnored() const { return isScalar() && !getScalarVal(); } |
| |
| bool isVolatileQualified() const { return V2.getInt(); } |
| |
| /// Return the mlir::Value of this scalar value. |
| mlir::Value getScalarVal() const { |
| assert(isScalar() && "Not a scalar!"); |
| return V1.getPointer(); |
| } |
| |
| /// Return the real/imag components of this complex value. |
| mlir::Value getComplexVal() const { |
| assert(isComplex() && "Not a complex!"); |
| return V1.getPointer(); |
| } |
| |
| /// Return the mlir::Value of the address of the aggregate. |
| Address getAggregateAddress() const { |
| assert(isAggregate() && "Not an aggregate!"); |
| auto align = reinterpret_cast<uintptr_t>(V2.getPointer().get<int *>()) >> |
| AggAlignShift; |
| return Address(V1.getPointer(), ElementType, |
| clang::CharUnits::fromQuantity(align)); |
| } |
| |
| mlir::Value getAggregatePointer() const { |
| assert(isAggregate() && "Not an aggregate!"); |
| return V1.getPointer(); |
| } |
| |
| static RValue getIgnored() { |
| // FIXME: should we make this a more explicit state? |
| return get(nullptr); |
| } |
| |
| static RValue get(mlir::Value V) { |
| RValue ER; |
| ER.V1.setPointer(V); |
| ER.V1.setInt(Scalar); |
| ER.V2.setInt(false); |
| return ER; |
| } |
| static RValue getComplex(mlir::Value V) { |
| RValue ER; |
| ER.V1.setPointer(V); |
| ER.V1.setInt(Complex); |
| ER.V2.setInt(false); |
| return ER; |
| } |
| // FIXME: Aggregate rvalues need to retain information about whether they are |
| // volatile or not. Remove default to find all places that probably get this |
| // wrong. |
| static RValue getAggregate(Address addr, bool isVolatile = false) { |
| RValue ER; |
| ER.V1.setPointer(addr.getPointer()); |
| ER.V1.setInt(Aggregate); |
| ER.ElementType = addr.getElementType(); |
| |
| auto align = static_cast<uintptr_t>(addr.getAlignment().getQuantity()); |
| ER.V2.setPointer(reinterpret_cast<int *>(align << AggAlignShift)); |
| ER.V2.setInt(isVolatile); |
| return ER; |
| } |
| }; |
| |
| /// The source of the alignment of an l-value; an expression of |
| /// confidence in the alignment actually matching the estimate. |
| enum class AlignmentSource { |
| /// The l-value was an access to a declared entity or something |
| /// equivalently strong, like the address of an array allocated by a |
| /// language runtime. |
| Decl, |
| |
| /// The l-value was considered opaque, so the alignment was |
| /// determined from a type, but that type was an explicitly-aligned |
| /// typedef. |
| AttributedType, |
| |
| /// The l-value was considered opaque, so the alignment was |
| /// determined from a type. |
| Type |
| }; |
| |
| /// Given that the base address has the given alignment source, what's |
| /// our confidence in the alignment of the field? |
| static inline AlignmentSource getFieldAlignmentSource(AlignmentSource Source) { |
| // For now, we don't distinguish fields of opaque pointers from |
| // top-level declarations, but maybe we should. |
| return AlignmentSource::Decl; |
| } |
| |
| class LValueBaseInfo { |
| AlignmentSource AlignSource; |
| |
| public: |
| explicit LValueBaseInfo(AlignmentSource Source = AlignmentSource::Type) |
| : AlignSource(Source) {} |
| AlignmentSource getAlignmentSource() const { return AlignSource; } |
| void setAlignmentSource(AlignmentSource Source) { AlignSource = Source; } |
| |
| void mergeForCast(const LValueBaseInfo &Info) { |
| setAlignmentSource(Info.getAlignmentSource()); |
| } |
| }; |
| |
| class LValue { |
| enum { |
| Simple, // This is a normal l-value, use getAddress(). |
| VectorElt, // This is a vector element l-value (V[i]), use getVector* |
| BitField, // This is a bitfield l-value, use getBitfield*. |
| ExtVectorElt, // This is an extended vector subset, use getExtVectorComp |
| GlobalReg, // This is a register l-value, use getGlobalReg() |
| MatrixElt // This is a matrix element, use getVector* |
| } LVType; |
| clang::QualType Type; |
| clang::Qualifiers Quals; |
| |
| // LValue is non-gc'able for any reason, including being a parameter or local |
| // variable. |
| bool NonGC : 1; |
| |
| // This flag shows if a nontemporal load/stores should be used when accessing |
| // this lvalue. |
| bool Nontemporal : 1; |
| |
| private: |
| void Initialize(clang::QualType Type, clang::Qualifiers Quals, |
| clang::CharUnits Alignment, LValueBaseInfo BaseInfo) { |
| assert((!Alignment.isZero() || Type->isIncompleteType()) && |
| "initializing l-value with zero alignment!"); |
| if (isGlobalReg()) |
| assert(ElementType == nullptr && "Global reg does not store elem type"); |
| |
| this->Type = Type; |
| this->Quals = Quals; |
| // This flag shows if a nontemporal load/stores should be used when |
| // accessing this lvalue. |
| const unsigned MaxAlign = 1U << 31; |
| this->Alignment = Alignment.getQuantity() <= MaxAlign |
| ? Alignment.getQuantity() |
| : MaxAlign; |
| assert(this->Alignment == Alignment.getQuantity() && |
| "Alignment exceeds allowed max!"); |
| this->BaseInfo = BaseInfo; |
| |
| // TODO: ObjC flags |
| // Initialize Objective-C flags. |
| this->NonGC = false; |
| this->Nontemporal = false; |
| } |
| |
| // The alignment to use when accessing this lvalue. (For vector elements, |
| // this is the alignment of the whole vector) |
| unsigned Alignment; |
| mlir::Value V; |
| mlir::Type ElementType; |
| mlir::Value VectorIdx; // Index for vector subscript |
| mlir::Attribute VectorElts; // ExtVector element subset: V.xyx |
| LValueBaseInfo BaseInfo; |
| const CIRGenBitFieldInfo *BitFieldInfo{0}; |
| |
| public: |
| bool isSimple() const { return LVType == Simple; } |
| bool isVectorElt() const { return LVType == VectorElt; } |
| bool isBitField() const { return LVType == BitField; } |
| bool isExtVectorElt() const { return LVType == ExtVectorElt; } |
| bool isGlobalReg() const { return LVType == GlobalReg; } |
| bool isMatrixElt() const { return LVType == MatrixElt; } |
| |
| bool isVolatileQualified() const { return Quals.hasVolatile(); } |
| |
| unsigned getVRQualifiers() const { |
| return Quals.getCVRQualifiers() & ~clang::Qualifiers::Const; |
| } |
| |
| bool isNonGC() const { return NonGC; } |
| void setNonGC(bool Value) { NonGC = Value; } |
| |
| bool isNontemporal() const { return Nontemporal; } |
| |
| bool isObjCWeak() const { |
| return Quals.getObjCGCAttr() == clang::Qualifiers::Weak; |
| } |
| bool isObjCStrong() const { |
| return Quals.getObjCGCAttr() == clang::Qualifiers::Strong; |
| } |
| |
| bool isVolatile() const { return Quals.hasVolatile(); } |
| |
| clang::QualType getType() const { return Type; } |
| |
| mlir::Value getPointer() const { return V; } |
| |
| clang::CharUnits getAlignment() const { |
| return clang::CharUnits::fromQuantity(Alignment); |
| } |
| void setAlignment(clang::CharUnits A) { Alignment = A.getQuantity(); } |
| |
| Address getAddress() const { |
| return Address(getPointer(), ElementType, getAlignment()); |
| } |
| |
| void setAddress(Address address) { |
| assert(isSimple()); |
| V = address.getPointer(); |
| ElementType = address.getElementType(); |
| Alignment = address.getAlignment().getQuantity(); |
| // TODO(cir): IsKnownNonNull = address.isKnownNonNull(); |
| } |
| |
| LValueBaseInfo getBaseInfo() const { return BaseInfo; } |
| void setBaseInfo(LValueBaseInfo Info) { BaseInfo = Info; } |
| |
| static LValue makeAddr(Address address, clang::QualType T, |
| AlignmentSource Source = AlignmentSource::Type) { |
| LValue R; |
| R.LVType = Simple; |
| R.V = address.getPointer(); |
| R.ElementType = address.getElementType(); |
| R.Initialize(T, T.getQualifiers(), address.getAlignment(), |
| LValueBaseInfo(Source)); |
| return R; |
| } |
| |
| // FIXME: only have one of these static methods. |
| static LValue makeAddr(Address address, clang::QualType T, |
| LValueBaseInfo LBI) { |
| LValue R; |
| R.LVType = Simple; |
| R.V = address.getPointer(); |
| R.ElementType = address.getElementType(); |
| R.Initialize(T, T.getQualifiers(), address.getAlignment(), LBI); |
| return R; |
| } |
| |
| static LValue makeAddr(Address address, clang::QualType type, |
| clang::ASTContext &Context, LValueBaseInfo BaseInfo) { |
| clang::Qualifiers qs = type.getQualifiers(); |
| qs.setObjCGCAttr(Context.getObjCGCAttrKind(type)); |
| |
| LValue R; |
| R.LVType = Simple; |
| assert(mlir::cast<mlir::cir::PointerType>(address.getPointer().getType())); |
| R.V = address.getPointer(); |
| R.ElementType = address.getElementType(); |
| R.Initialize(type, qs, address.getAlignment(), |
| BaseInfo); // TODO: TBAAInfo); |
| return R; |
| } |
| |
| const clang::Qualifiers &getQuals() const { return Quals; } |
| clang::Qualifiers &getQuals() { return Quals; } |
| |
| // vector element lvalue |
| Address getVectorAddress() const { |
| return Address(getVectorPointer(), ElementType, getAlignment()); |
| } |
| mlir::Value getVectorPointer() const { |
| assert(isVectorElt()); |
| return V; |
| } |
| mlir::Value getVectorIdx() const { |
| assert(isVectorElt()); |
| return VectorIdx; |
| } |
| |
| // extended vector elements. |
| Address getExtVectorAddress() const { |
| assert(isExtVectorElt()); |
| return Address(getExtVectorPointer(), ElementType, getAlignment()); |
| } |
| mlir::Value getExtVectorPointer() const { |
| assert(isExtVectorElt()); |
| return V; |
| } |
| mlir::ArrayAttr getExtVectorElts() const { |
| assert(isExtVectorElt()); |
| return mlir::cast<mlir::ArrayAttr>(VectorElts); |
| } |
| |
| static LValue MakeVectorElt(Address vecAddress, mlir::Value Index, |
| clang::QualType type, LValueBaseInfo BaseInfo) { |
| LValue R; |
| R.LVType = VectorElt; |
| R.V = vecAddress.getPointer(); |
| R.ElementType = vecAddress.getElementType(); |
| R.VectorIdx = Index; |
| R.Initialize(type, type.getQualifiers(), vecAddress.getAlignment(), |
| BaseInfo); |
| return R; |
| } |
| |
| static LValue MakeExtVectorElt(Address vecAddress, mlir::ArrayAttr elts, |
| clang::QualType type, |
| LValueBaseInfo baseInfo) { |
| LValue R; |
| R.LVType = ExtVectorElt; |
| R.V = vecAddress.getPointer(); |
| R.ElementType = vecAddress.getElementType(); |
| R.VectorElts = elts; |
| R.Initialize(type, type.getQualifiers(), vecAddress.getAlignment(), |
| baseInfo); |
| return R; |
| } |
| |
| // bitfield lvalue |
| Address getBitFieldAddress() const { |
| return Address(getBitFieldPointer(), ElementType, getAlignment()); |
| } |
| |
| mlir::Value getBitFieldPointer() const { |
| assert(isBitField()); |
| return V; |
| } |
| |
| const CIRGenBitFieldInfo &getBitFieldInfo() const { |
| assert(isBitField()); |
| return *BitFieldInfo; |
| } |
| |
| /// Create a new object to represent a bit-field access. |
| /// |
| /// \param Addr - The base address of the bit-field sequence this |
| /// bit-field refers to. |
| /// \param Info - The information describing how to perform the bit-field |
| /// access. |
| static LValue MakeBitfield(Address Addr, const CIRGenBitFieldInfo &Info, |
| clang::QualType type, LValueBaseInfo BaseInfo) { |
| LValue R; |
| R.LVType = BitField; |
| R.V = Addr.getPointer(); |
| R.ElementType = Addr.getElementType(); |
| R.BitFieldInfo = &Info; |
| R.Initialize(type, type.getQualifiers(), Addr.getAlignment(), BaseInfo); |
| return R; |
| } |
| }; |
| |
| /// An aggregate value slot. |
| class AggValueSlot { |
| /// The address. |
| Address Addr; |
| |
| // Qualifiers |
| clang::Qualifiers Quals; |
| |
| /// This is set to true if some external code is responsible for setting up a |
| /// destructor for the slot. Otherwise the code which constructs it should |
| /// push the appropriate cleanup. |
| bool DestructedFlag : 1; |
| |
| /// This is set to true if writing to the memory in the slot might require |
| /// calling an appropriate Objective-C GC barrier. The exact interaction here |
| /// is unnecessarily mysterious. |
| bool ObjCGCFlag : 1; |
| |
| /// This is set to true if the memory in the slot is known to be zero before |
| /// the assignment into it. This means that zero fields don't need to be set. |
| bool ZeroedFlag : 1; |
| |
| /// This is set to true if the slot might be aliased and it's not undefined |
| /// behavior to access it through such an alias. Note that it's always |
| /// undefined behavior to access a C++ object that's under construction |
| /// through an alias derived from outside the construction process. |
| /// |
| /// This flag controls whether calls that produce the aggregate |
| /// value may be evaluated directly into the slot, or whether they |
| /// must be evaluated into an unaliased temporary and then memcpy'ed |
| /// over. Since it's invalid in general to memcpy a non-POD C++ |
| /// object, it's important that this flag never be set when |
| /// evaluating an expression which constructs such an object. |
| bool AliasedFlag : 1; |
| |
| /// This is set to true if the tail padding of this slot might overlap |
| /// another object that may have already been initialized (and whose |
| /// value must be preserved by this initialization). If so, we may only |
| /// store up to the dsize of the type. Otherwise we can widen stores to |
| /// the size of the type. |
| bool OverlapFlag : 1; |
| |
| /// If is set to true, sanitizer checks are already generated for this address |
| /// or not required. For instance, if this address represents an object |
| /// created in 'new' expression, sanitizer checks for memory is made as a part |
| /// of 'operator new' emission and object constructor should not generate |
| /// them. |
| bool SanitizerCheckedFlag : 1; |
| |
| AggValueSlot(Address Addr, clang::Qualifiers Quals, bool DestructedFlag, |
| bool ObjCGCFlag, bool ZeroedFlag, bool AliasedFlag, |
| bool OverlapFlag, bool SanitizerCheckedFlag) |
| : Addr(Addr), Quals(Quals), DestructedFlag(DestructedFlag), |
| ObjCGCFlag(ObjCGCFlag), ZeroedFlag(ZeroedFlag), |
| AliasedFlag(AliasedFlag), OverlapFlag(OverlapFlag), |
| SanitizerCheckedFlag(SanitizerCheckedFlag) {} |
| |
| public: |
| enum IsAliased_t { IsNotAliased, IsAliased }; |
| enum IsDestructed_t { IsNotDestructed, IsDestructed }; |
| enum IsZeroed_t { IsNotZeroed, IsZeroed }; |
| enum Overlap_t { DoesNotOverlap, MayOverlap }; |
| enum NeedsGCBarriers_t { DoesNotNeedGCBarriers, NeedsGCBarriers }; |
| enum IsSanitizerChecked_t { IsNotSanitizerChecked, IsSanitizerChecked }; |
| |
| /// ignored - Returns an aggregate value slot indicating that the aggregate |
| /// value is being ignored. |
| static AggValueSlot ignored() { |
| return forAddr(Address::invalid(), clang::Qualifiers(), IsNotDestructed, |
| DoesNotNeedGCBarriers, IsNotAliased, DoesNotOverlap); |
| } |
| |
| /// forAddr - Make a slot for an aggregate value. |
| /// |
| /// \param quals - The qualifiers that dictate how the slot should be |
| /// initialized. Only 'volatile' and the Objective-C lifetime qualifiers |
| /// matter. |
| /// |
| /// \param isDestructed - true if something else is responsible for calling |
| /// destructors on this object |
| /// \param needsGC - true fi the slot is potentially located somewhere that |
| /// ObjC GC calls should be emitted for |
| static AggValueSlot |
| forAddr(Address addr, clang::Qualifiers quals, IsDestructed_t isDestructed, |
| NeedsGCBarriers_t needsGC, IsAliased_t isAliased, |
| Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed, |
| IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) { |
| return AggValueSlot(addr, quals, isDestructed, needsGC, isZeroed, isAliased, |
| mayOverlap, isChecked); |
| } |
| |
| static AggValueSlot |
| forLValue(const LValue &LV, IsDestructed_t isDestructed, |
| NeedsGCBarriers_t needsGC, IsAliased_t isAliased, |
| Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed, |
| IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) { |
| return forAddr(LV.getAddress(), LV.getQuals(), isDestructed, needsGC, |
| isAliased, mayOverlap, isZeroed, isChecked); |
| } |
| |
| IsDestructed_t isExternallyDestructed() const { |
| return IsDestructed_t(DestructedFlag); |
| } |
| void setExternallyDestructed(bool destructed = true) { |
| DestructedFlag = destructed; |
| } |
| |
| clang::Qualifiers getQualifiers() const { return Quals; } |
| |
| bool isVolatile() const { return Quals.hasVolatile(); } |
| |
| Address getAddress() const { return Addr; } |
| |
| bool isIgnored() const { return !Addr.isValid(); } |
| |
| mlir::Value getPointer() const { return Addr.getPointer(); } |
| |
| Overlap_t mayOverlap() const { return Overlap_t(OverlapFlag); } |
| |
| bool isSanitizerChecked() const { return SanitizerCheckedFlag; } |
| |
| IsZeroed_t isZeroed() const { return IsZeroed_t(ZeroedFlag); } |
| void setZeroed(bool V = true) { ZeroedFlag = V; } |
| |
| NeedsGCBarriers_t requiresGCollection() const { |
| return NeedsGCBarriers_t(ObjCGCFlag); |
| } |
| |
| IsAliased_t isPotentiallyAliased() const { return IsAliased_t(AliasedFlag); } |
| |
| RValue asRValue() const { |
| if (isIgnored()) { |
| return RValue::getIgnored(); |
| } else { |
| return RValue::getAggregate(getAddress(), isVolatile()); |
| } |
| } |
| |
| /// Get the preferred size to use when storing a value to this slot. This |
| /// is the type size unless that might overlap another object, in which |
| /// case it's the dsize. |
| clang::CharUnits getPreferredSize(clang::ASTContext &Ctx, |
| clang::QualType Type) { |
| return mayOverlap() ? Ctx.getTypeInfoDataSizeInChars(Type).Width |
| : Ctx.getTypeSizeInChars(Type); |
| } |
| }; |
| |
| } // namespace cir |
| |
| #endif |