//===-- irtypeaggr.cpp ----------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "ir/irtypeaggr.h" #include "dmd/aggregate.h" #include "dmd/errors.h" #include "dmd/init.h" #include "gen/irstate.h" #include "gen/logger.h" #include "gen/llvmhelpers.h" #include "llvm/IR/DerivedTypes.h" using namespace dmd; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // FIXME A similar function is in ir/iraggr.cpp and RTTIBuilder::push(). static inline size_t add_zeros(std::vector &defaultTypes, size_t startOffset, size_t endOffset) { assert(startOffset <= endOffset); const size_t paddingSize = endOffset - startOffset; if (paddingSize) { llvm::ArrayType *pad = llvm::ArrayType::get( llvm::Type::getInt8Ty(gIR->context()), paddingSize); defaultTypes.push_back(pad); } return paddingSize ? 1 : 0; } AggrTypeBuilder::AggrTypeBuilder(unsigned offset) : m_offset(offset) { m_defaultTypes.reserve(32); } void AggrTypeBuilder::addType(llvm::Type *type, unsigned size) { const unsigned fieldAlignment = getABITypeAlign(type); assert(fieldAlignment); // If the field offset does not have natural alignment, mark the aggregate as // packed for IR. if ((m_offset & (fieldAlignment - 1)) != 0) { m_packed = true; } m_defaultTypes.push_back(type); m_offset += size; m_fieldIndex++; m_maxFieldIRAlignment = std::max(m_maxFieldIRAlignment, fieldAlignment); } void AggrTypeBuilder::addAggregate(AggregateDeclaration *ad) { addAggregate(ad, nullptr, Aliases::AddToVarGEPIndices); } void AggrTypeBuilder::addAggregate( AggregateDeclaration *ad, const AggrTypeBuilder::VarInitMap *explicitInits, AggrTypeBuilder::Aliases aliases) { const size_t n = ad->fields.length; if (n == 0) return; // Objective-C instance variables are laid out at runtime. // as such, we should not generate the aggregate body. if (auto klass = ad->isClassDeclaration()) { if (klass->classKind == ClassKind::objc) { this->addType(getOpaquePtrType(), getPointerSize()); return; } } // Unions may lead to overlapping fields, and we need to flatten them for LLVM // IR. We usually take the first field (in declaration order) of an // overlapping set, but a literal with an explicit initializer for a dominated // field might require us to select that field. struct Data { VarDeclaration *field; LLType *llType; uint64_t size; }; LLSmallVector actualFields; // list of pairs: alias => actual field (same offset, same LL type (not // checked for bit fields)) LLSmallVector, 16> aliasPairs; // Bit fields additionally complicate matters. E.g.: // struct S { // unsigned char a:7; // byte offset 0, bit offset 0, bit width 7 // _Bool b:1; // byte offset 0, bit offset 7, bit width 1 // _Bool c:1; // byte offset 1, bit offset 0, bit width 1 // unsigned d:22; // byte offset 1, bit offset 1, bit width 22 // _Bool e:1; // byte offset 3, bit offset 7, bit width 1 // }; // => group 1: byte offset 0, size 1 (`a`, with alias `b`) // group 2: byte offset 1, size 3 (`c`, with alias `d` and extra member `e` // (with greater byte offset)) // list of pairs: extra bit field member (greater byte offset) => first member // of bit field group LLSmallVector, 8> extraBitFieldMembers; // Iterate over all fields in declaration order, in 1 or 2 passes. for (int pass = explicitInits ? 0 : 1; pass < 2; ++pass) { for (size_t i = 0; i < ad->fields.length; ++i) { const auto field = ad->fields[i]; bool haveExplicitInit = explicitInits && explicitInits->find(field) != explicitInits->end(); uint64_t fieldSize = size(field->type); const bool isBitField = field->isBitFieldDeclaration() != nullptr; if (isBitField) { const auto group = BitFieldGroup::startingFrom( i, ad->fields.length, [ad](size_t i) { return ad->fields[i]; }); if (!haveExplicitInit && explicitInits) { haveExplicitInit = llvm::any_of( group.bitFields, [explicitInits](BitFieldDeclaration *bf) { return explicitInits->find(bf) != explicitInits->end(); }); } fieldSize = group.sizeInBytes; // final pass: create unconditional aliases/extra members for the other // bit fields if (pass == 1) { for (size_t j = 1; j < group.bitFields.size(); ++j) { auto bf = group.bitFields[j]; if (bf->offset == group.byteOffset) { aliasPairs.push_back({bf, field}); } else { extraBitFieldMembers.push_back( {bf, field->isBitFieldDeclaration()}); } } } // skip the other bit fields in this pass i += group.bitFields.size() - 1; } // 1st pass: only for fields with explicit initializer if (pass == 0 && !haveExplicitInit) continue; // final pass: only for fields without explicit initializer if (pass == 1 && haveExplicitInit) continue; // skip empty fields if (fieldSize == 0) continue; const uint64_t f_begin = field->offset; const uint64_t f_end = f_begin + fieldSize; // use an i8 array for bit field groups const auto llType = isBitField ? llvm::ArrayType::get(getI8Type(), fieldSize) : DtoMemType(field->type); // check for overlap with existing fields (on a byte level, not bits) bool overlaps = false; if (isBitField || field->overlapped()) { for (const auto &existing : actualFields) { const uint64_t e_begin = existing.field->offset; const uint64_t e_end = e_begin + existing.size; if (e_begin < f_end && e_end > f_begin) { overlaps = true; if (aliases == Aliases::AddToVarGEPIndices && e_begin == f_begin && existing.llType == llType) { aliasPairs.push_back(std::make_pair(field, existing.field)); } break; } } } if (!overlaps) actualFields.push_back({field, llType, fieldSize}); } } // Now we can build a list of LLVM types for the actual LL fields. // Make sure to zero out any padding and set the GEP indices for the directly // indexable variables. // first we sort the list by offset std::sort(actualFields.begin(), actualFields.end(), [](const Data &l, const Data &r) { return l.field->offset < r.field->offset; }); for (const auto &af : actualFields) { const auto vd = af.field; const auto llType = af.llType; if (vd->offset < m_offset) { error(vd->loc, "%s `%s` @ %u overlaps previous field @ %u. This is an ICE, please file an " "LDC issue.", vd->kind(), vd->toPrettyChars(), vd->offset, m_offset); fatal(); } // Add an explicit field for any padding so we can zero it, as per TDPL // §7.1.1. if (m_offset < vd->offset) { m_fieldIndex += add_zeros(m_defaultTypes, m_offset, vd->offset); m_offset = vd->offset; } // add default type m_defaultTypes.push_back(llType); if (!llType->isSized()) { error(vd->loc, "unexpected IR type forward declaration for aggregate member of " "type `%s`. This is an ICE, please file an LDC issue.", vd->type->toPrettyChars()); fatal(); } const unsigned fieldAlignment = getABITypeAlign(llType); const unsigned fieldSize = getTypeAllocSize(llType); assert(fieldSize <= af.size); // advance offset to right past this field if (!m_packed) { assert(fieldAlignment); m_packed = ((m_offset & (fieldAlignment - 1)) != 0); } m_offset += fieldSize; // set the field index m_varGEPIndices[vd] = m_fieldIndex; // let any aliases reuse this field/GEP index for (const auto &pair : aliasPairs) { if (pair.second == vd) m_varGEPIndices[pair.first] = m_fieldIndex; } // store extra bit field members of this group for (const auto &pair : extraBitFieldMembers) { if (pair.second == vd) m_extraBitFieldMembers.push_back(pair); } ++m_fieldIndex; m_maxFieldIRAlignment = std::max(m_maxFieldIRAlignment, fieldAlignment); } } void AggrTypeBuilder::alignCurrentOffset(unsigned alignment) { unsigned aligned = (m_offset + alignment - 1) & ~(alignment - 1); if (m_offset < aligned) { m_fieldIndex += add_zeros(m_defaultTypes, m_offset, aligned); m_offset = aligned; } } void AggrTypeBuilder::addTailPadding(unsigned aggregateSize) { assert(m_offset <= aggregateSize && "IR aggregate type is larger than the corresponding D type"); if (m_offset < aggregateSize) add_zeros(m_defaultTypes, m_offset, aggregateSize); // check if the aggregate size makes it packed in IR terms if (!m_packed && (aggregateSize & (m_maxFieldIRAlignment - 1))) m_packed = true; } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// IrTypeAggr::IrTypeAggr(AggregateDeclaration *ad) : IrType(ad->type, LLStructType::create(gIR->context(), ad->toPrettyChars())), aggr(ad) {} unsigned IrTypeAggr::getMemberLocation(VarDeclaration *var, bool& isFieldIdx) { // Note: The interface is a bit more general than what we actually return. // Specifically, the frontend offset information we use for overlapping // fields is always based at the object start. const auto &varGEPIndices = getVarGEPIndices(); auto it = varGEPIndices.find(var); if (it != varGEPIndices.end()) { isFieldIdx = true; return it->second; } else { isFieldIdx = false; return var->offset; } } ////////////////////////////////////////////////////////////////////////////// BitFieldGroup BitFieldGroup::startingFrom( size_t startFieldIndex, size_t numTotalFields, std::function getFieldFn) { BitFieldGroup group; for (size_t i = startFieldIndex; i < numTotalFields; ++i) { auto bf = getFieldFn(i)->isBitFieldDeclaration(); if (!bf) break; unsigned bitOffset = bf->bitOffset; if (i == startFieldIndex) { group.byteOffset = bf->offset; } else if (bf->offset >= group.byteOffset + group.sizeInBytes || bf->offset < group.byteOffset) { // unions // starts a new bit field group break; } else { // the byte offset might not match the group's bitOffset += (bf->offset - group.byteOffset) * 8; } const auto sizeInBytes = (bitOffset + bf->fieldWidth + 7) / 8; group.sizeInBytes = std::max(group.sizeInBytes, sizeInBytes); group.bitFields.push_back(bf); } return group; } unsigned BitFieldGroup::getBitOffset(BitFieldDeclaration *member) const { assert(member->offset >= byteOffset); return (member->offset - byteOffset) * 8 + member->bitOffset; }