//===----------------------------------------------------------------------===// // // 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 CLANG_LIB_CIR_CIRGENVALUE_H #define CLANG_LIB_CIR_CIRGENVALUE_H #include "Address.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" #include "CIRGenRecordLayout.h" #include "mlir/IR/Value.h" #include "clang/CIR/MissingFeatures.h" namespace clang::CIRGen { /// 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 }; union { mlir::Value value; // Stores aggregate address. Address aggregateAddr; }; unsigned isVolatile : 1; unsigned flavor : 2; public: RValue() : value(nullptr), flavor(Scalar) {} bool isScalar() const { return flavor == Scalar; } bool isComplex() const { return flavor == Complex; } bool isAggregate() const { return flavor == Aggregate; } bool isVolatileQualified() const { return isVolatile; } /// Return the value of this scalar value. mlir::Value getValue() const { assert(isScalar() && "Not a scalar!"); return value; } /// Return the value of this complex value. mlir::Value getComplexValue() const { assert(isComplex() && "Not a complex!"); return value; } /// Return the value of the address of the aggregate. Address getAggregateAddress() const { assert(isAggregate() && "Not an aggregate!"); return aggregateAddr; } mlir::Value getAggregatePointer(QualType pointeeType) const { return getAggregateAddress().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.value = v; er.flavor = Scalar; er.isVolatile = false; return er; } static RValue getComplex(mlir::Value v) { RValue er; er.value = v; er.flavor = Complex; er.isVolatile = false; return er; } // volatile or not. Remove default to find all places that probably get this // wrong. /// Convert an Address to an RValue. If the Address is not /// signed, create an RValue using the unsigned address. Otherwise, resign the /// address using the provided type. static RValue getAggregate(Address addr, bool isVolatile = false) { RValue er; er.aggregateAddr = addr; er.flavor = Aggregate; er.isVolatile = 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; // 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::Value vectorIdx; // Index for vector subscript mlir::Type elementType; LValueBaseInfo baseInfo; const CIRGenBitFieldInfo *bitFieldInfo{nullptr}; void initialize(clang::QualType type, clang::Qualifiers quals, clang::CharUnits alignment, LValueBaseInfo baseInfo) { assert((!alignment.isZero() || type->isIncompleteType()) && "initializing l-value with zero alignment!"); this->type = type; this->quals = quals; 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; } public: bool isSimple() const { return lvType == Simple; } bool isVectorElt() const { return lvType == VectorElt; } bool isBitField() const { return lvType == BitField; } bool isVolatile() const { return quals.hasVolatile(); } bool isVolatileQualified() const { return quals.hasVolatile(); } unsigned getVRQualifiers() const { return quals.getCVRQualifiers() & ~clang::Qualifiers::Const; } 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()); } const clang::Qualifiers &getQuals() const { return quals; } clang::Qualifiers &getQuals() { return quals; } LValueBaseInfo getBaseInfo() const { return baseInfo; } void setBaseInfo(LValueBaseInfo info) { baseInfo = info; } static LValue makeAddr(Address address, clang::QualType t, LValueBaseInfo baseInfo) { // Classic codegen sets the objc gc qualifier here. That requires an // ASTContext, which is passed in from CIRGenFunction::makeAddrLValue. assert(!cir::MissingFeatures::objCGC()); LValue r; r.lvType = Simple; r.v = address.getPointer(); r.elementType = address.getElementType(); r.initialize(t, t.getQualifiers(), address.getAlignment(), baseInfo); return r; } Address getVectorAddress() const { return Address(getVectorPointer(), elementType, getAlignment()); } mlir::Value getVectorPointer() const { assert(isVectorElt()); return v; } mlir::Value getVectorIdx() const { assert(isVectorElt()); return vectorIdx; } static LValue makeVectorElt(Address vecAddress, mlir::Value index, clang::QualType t, LValueBaseInfo baseInfo) { LValue r; r.lvType = VectorElt; r.v = vecAddress.getPointer(); r.elementType = vecAddress.getElementType(); r.vectorIdx = index; r.initialize(t, t.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 { Address addr; 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. LLVM_PREFERRED_TYPE(bool) LLVM_ATTRIBUTE_UNUSED unsigned destructedFlag : 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. LLVM_PREFERRED_TYPE(bool) unsigned 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. LLVM_PREFERRED_TYPE(bool) LLVM_ATTRIBUTE_UNUSED unsigned 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. LLVM_PREFERRED_TYPE(bool) LLVM_ATTRIBUTE_UNUSED unsigned overlapFlag : 1; public: enum IsDestructed_t { IsNotDestructed, IsDestructed }; enum IsZeroed_t { IsNotZeroed, IsZeroed }; enum IsAliased_t { IsNotAliased, IsAliased }; enum Overlap_t { MayOverlap, DoesNotOverlap }; /// Returns an aggregate value slot indicating that the aggregate /// value is being ignored. static AggValueSlot ignored() { return forAddr(Address::invalid(), clang::Qualifiers(), IsNotDestructed, IsNotAliased, DoesNotOverlap); } AggValueSlot(Address addr, clang::Qualifiers quals, bool destructedFlag, bool zeroedFlag, bool aliasedFlag, bool overlapFlag) : addr(addr), quals(quals), destructedFlag(destructedFlag), zeroedFlag(zeroedFlag), aliasedFlag(aliasedFlag), overlapFlag(overlapFlag) {} static AggValueSlot forAddr(Address addr, clang::Qualifiers quals, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed) { return AggValueSlot(addr, quals, isDestructed, isZeroed, isAliased, mayOverlap); } static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed) { return forAddr(LV.getAddress(), LV.getQuals(), isDestructed, isAliased, mayOverlap, isZeroed); } clang::Qualifiers getQualifiers() const { return quals; } Address getAddress() const { return addr; } bool isIgnored() const { return !addr.isValid(); } mlir::Value getPointer() const { return addr.getPointer(); } IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); } RValue asRValue() const { if (isIgnored()) return RValue::getIgnored(); assert(!cir::MissingFeatures::aggValueSlot()); return RValue::getAggregate(getAddress()); } }; } // namespace clang::CIRGen #endif // CLANG_LIB_CIR_CIRGENVALUE_H