/******************************************************************************* * * MIT License * * Copyright 2024-2025 AMD ROCm(TM) Software * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace rocRoller { namespace Register { uint constexpr bitsPerRegister = 32; inline std::string RegisterId::toString() const { if(IsRegister(regType)) { return concatenate(TypePrefix(regType), regIndex); } else { return concatenate(regType, regIndex); } } inline std::string toString(RegisterId const& regId) { return regId.toString(); } inline std::ostream& operator<<(std::ostream& stream, RegisterId const& regId) { return stream << toString(regId); } inline std::string TypePrefix(Type t) { switch(t) { case Type::Scalar: return "s"; case Type::Vector: return "v"; case Type::Accumulator: return "a"; case Type::Literal: case Type::LocalData: case Type::Label: case Type::NullLiteral: case Type::SCC: case Type::M0: case Type::VCC: case Type::VCC_LO: case Type::VCC_HI: case Type::EXEC: case Type::EXEC_LO: case Type::EXEC_HI: case Type::TTMP7: case Type::TTMP9: case Type::Constant: case Type::Count: Throw("No prefix available for ", toString(t), " values"); } return ""; } constexpr inline bool IsRegister(Type t) { switch(t) { case Type::Scalar: case Type::Vector: case Type::Accumulator: return true; default: return false; } } constexpr inline bool IsSpecial(Type t) { switch(t) { case Type::M0: case Type::SCC: case Type::VCC: case Type::VCC_LO: case Type::VCC_HI: case Type::EXEC: case Type::EXEC_LO: case Type::EXEC_HI: case Type::TTMP7: case Type::TTMP9: return true; default: return false; } } constexpr inline bool IsWriteableSpecial(Type t) { switch(t) { case Type::M0: case Type::SCC: case Type::VCC: case Type::VCC_LO: case Type::VCC_HI: case Type::EXEC: case Type::EXEC_LO: case Type::EXEC_HI: return true; case Type::TTMP7: case Type::TTMP9: default: return false; } } constexpr inline bool IsTTMP(Type t) { switch(t) { case Type::TTMP7: case Type::TTMP9: return true; default: return false; } } constexpr inline Type PromoteType(Type lhs, Type rhs) { if(lhs == rhs) return lhs; if(IsRegister(lhs) && !IsRegister(rhs)) return lhs; if(!IsRegister(lhs) && IsRegister(rhs)) return rhs; if((lhs == Type::M0 && rhs == Type::Literal) || (lhs == Type::Literal && rhs == Type::M0)) return Type::M0; if(lhs == Type::Count || rhs == Type::Count) { return Type::Count; } if((IsTTMP(lhs) && rhs == Type::Literal) || (lhs == Type::Literal && IsTTMP(rhs))) return Type::Scalar; if(!IsRegister(lhs) && !IsRegister(rhs)) { AssertFatal(lhs == rhs, "Can't promote between two non-register types.", ShowValue(lhs), ShowValue(rhs)); return rhs; } if(lhs == Type::Accumulator || rhs == Type::Accumulator) return Type::Accumulator; if(lhs == Type::Vector || rhs == Type::Vector) return Type::Vector; Throw("Invalid Register::Type combo: ", ShowValue(lhs), ShowValue(rhs)); } constexpr inline Type MapSPRTypeToGPRType(Type t) { switch(t) { case Type::M0: case Type::SCC: case Type::EXEC: case Type::EXEC_LO: case Type::EXEC_HI: case Type::TTMP7: case Type::TTMP9: case Type::VCC: case Type::VCC_LO: case Type::VCC_HI: return Type::Scalar; default: AssertFatal(!IsSpecial(t), "Unmapped Special type", ShowValue(t)); return t; } } inline std::string toString(Type t) { switch(t) { case Type::Literal: return "Literal"; case Type::Label: return "Label"; case Type::NullLiteral: return "NullLiteral"; case Type::Scalar: return "SGPR"; case Type::Vector: return "VGPR"; case Type::Accumulator: return "ACCVGPR"; case Type::LocalData: return "LDS"; case Type::Count: return "Count"; case Type::M0: return "M0"; case Type::SCC: return "SCC"; case Type::VCC: return "VCC"; case Type::VCC_LO: return "VCC_LO"; case Type::VCC_HI: return "VCC_HI"; case Type::EXEC: return "EXEC"; case Type::EXEC_LO: return "EXEC_LO"; case Type::EXEC_HI: return "EXEC_HI"; case Type::TTMP7: return "TTMP7"; case Type::TTMP9: return "TTMP9"; case Type::Constant: return "Constant"; } Throw("Invalid register type!"); } inline std::ostream& operator<<(std::ostream& stream, Type t) { return stream << toString(t); } inline std::string toString(AllocationState state) { switch(state) { case AllocationState::Unallocated: return "Unallocated"; case AllocationState::Allocated: return "Allocated"; case AllocationState::Freed: return "Freed"; case AllocationState::NoAllocation: return "NoAllocation"; case AllocationState::Error: return "Error"; case AllocationState::Count: return "Count"; default: throw std::runtime_error("Invalid allocation state!"); } } inline std::ostream& operator<<(std::ostream& stream, AllocationState state) { return stream << toString(state); } inline Value::Value() = default; inline Value::~Value() = default; inline Value::Value(ContextPtr ctx, Type regType, VariableType variableType, int count, AllocationOptions options) : m_context(ctx) , m_regType(regType) , m_varType(variableType) { AssertFatal(ctx != nullptr); AssertFatal(regType != Register::Type::Constant); AssertFatal(count > 0, "Invalid register count ", ShowValue(count)); auto const info = DataTypeInfo::Get(variableType); uint numRegisters = count; if(info.elementBits > bitsPerRegister) numRegisters = CeilDivide(count * info.elementBits, bitsPerRegister); m_allocationCoord = std::vector(numRegisters); std::iota(m_allocationCoord.begin(), m_allocationCoord.end(), 0); m_allocation = Allocation::SameAs(*this, m_name, options); } template inline Value::Value(ContextPtr ctx, Type regType, VariableType variableType, T const& coord) : m_context(ctx) , m_regType(regType) , m_varType(variableType) , m_allocationCoord(coord.begin(), coord.end()) { AssertFatal(ctx != nullptr); AssertFatal(regType != Register::Type::Constant); m_allocation = Allocation::SameAs(*this, m_name, {}); } inline Value::Value(ContextPtr ctx, Type regType, VariableType variableType, std::vector&& coord) : m_context(ctx) , m_regType(regType) , m_varType(variableType) , m_allocationCoord(coord) { AssertFatal(ctx != nullptr); AssertFatal(regType != Register::Type::Constant); m_allocation = Allocation::SameAs(*this, m_name, {}); } inline Value::Value(AllocationPtr alloc, Type regType, VariableType variableType, int count) : m_context(alloc->m_context) , m_allocation(alloc) , m_regType(regType) , m_varType(variableType) { AssertFatal(regType != Register::Type::Constant); // auto range = std::ranges::iota_view{0, count}; // m_allocationCoord = std::vector(range.begin(), range.end()); m_allocationCoord = std::vector(count); std::iota(m_allocationCoord.begin(), m_allocationCoord.end(), 0); AssertFatal(m_context.lock() != nullptr); } template inline Value::Value(AllocationPtr alloc, Type regType, VariableType variableType, T& coord) : m_context(alloc->m_context) , m_allocation(alloc) , m_regType(regType) , m_varType(variableType) , m_allocationCoord(coord.begin(), coord.end()) { AssertFatal(regType != Register::Type::Constant); AssertFatal(m_context.lock() != nullptr); } inline Value::Value(AllocationPtr alloc, Type regType, VariableType variableType, std::vector&& coord) : m_context(alloc->m_context) , m_allocation(alloc) , m_regType(regType) , m_varType(variableType) , m_allocationCoord(coord) { AssertFatal(regType != Register::Type::Constant); AssertFatal(m_context.lock() != nullptr); } template inline ValuePtr Value::Literal(T const& value) { return Literal(CommandArgumentValue(value)); } inline ValuePtr Value::Literal(CommandArgumentValue const& value) { auto v = std::make_shared(); v->m_literalValue = value; v->m_varType = rocRoller::variableType(value); v->m_regType = Type::Literal; return v; } inline ValuePtr Value::Label(const std::string& label) { auto v = std::make_shared(); v->m_regType = Type::Label; v->m_label = label; return v; } inline ValuePtr Value::NullLiteral() { auto v = std::make_shared(); v->m_regType = Type::NullLiteral; v->m_label = "null"; return v; } inline ValuePtr Value::AllocateLDS(ContextPtr ctx, VariableType variableType, int count, unsigned int alignment, uint paddingBytes) { auto v = std::make_shared(); v->m_regType = Type::LocalData; v->m_varType = variableType; auto allocator = ctx->ldsAllocator(); auto const info = DataTypeInfo::Get(variableType); auto const elementBytes = CeilDivide(info.elementBits * count, 8u); v->m_ldsAllocation = allocator->allocate(elementBytes + paddingBytes, alignment); return v; } inline ValuePtr Value::Placeholder(ContextPtr ctx, Type regType, VariableType variableType, int count, AllocationOptions allocOptions) { AssertFatal(ctx != nullptr); return std::make_shared(ctx, regType, variableType, count, allocOptions); } inline constexpr AllocationState Value::allocationState() const { if(!IsRegister(m_regType)) return AllocationState::NoAllocation; AssertFatal(m_allocation); return m_allocation->allocationState(); } inline AllocationPtr Value::allocation() const { return m_allocation; } inline std::vector Value::allocationCoord() const { return m_allocationCoord; } inline Instruction Value::allocate() { AssertFatal(allocationState() == AllocationState::Unallocated, "Unneccessary allocation", ShowValue(allocationState())); AssertFatal(m_allocation); return Instruction::Allocate(shared_from_this()); } inline void Value::allocate(Instruction& inst) { if(allocationState() != AllocationState::Unallocated) return; AssertFatal(m_allocation); inst.addAllocation(m_allocation); } inline bool Value::canAllocateNow() const { AssertFatal(m_allocation); return m_allocation->canAllocateNow(); } inline void Value::allocateNow() { AssertFatal(m_allocation); m_allocation->allocateNow(); m_contiguousIndices.reset(); } inline constexpr bool Value::isPlaceholder() const { return allocationState() == Register::AllocationState::Unallocated; } inline bool Value::isZeroLiteral() const { if(m_regType != Type::Literal) return false; return std::visit( [](auto const& val) { using T = std::decay_t; if constexpr(std::is_same_v) return static_cast(val) == 0; else if constexpr(std::is_same_v) return val.desc0 == 0 && val.desc1 == 0 && val.desc2 == 0 && val.desc3 == 0; else return val == 0; }, m_literalValue); } inline constexpr bool Value::isSpecial() const { return IsSpecial(m_regType); } inline constexpr bool Value::isTTMP() const { return IsTTMP(m_regType); } inline constexpr bool Value::isSCC() const { return m_regType == Type::SCC; } inline constexpr bool Value::isExec() const { return m_regType == Type::EXEC; } inline ValuePtr Value::placeholder() const { return Placeholder( m_context.lock(), m_regType, m_varType, valueCount(), m_allocation->options()); } inline ValuePtr Value::placeholder(Type regType, AllocationOptions allocOptions) const { return Placeholder(m_context.lock(), regType, m_varType, valueCount(), allocOptions); } inline Type Value::regType() const { return m_regType; } inline VariableType Value::variableType() const { return m_varType; } inline void Value::setVariableType(VariableType value) { // TODO: Assert we have enough registers. m_varType = value; } inline void Value::updateContiguousIndices() const { AssertFatal(m_allocation); if(m_allocationCoord.empty() || allocationState() == AllocationState::Unallocated) { m_contiguousIndices.reset(); return; } auto iter = m_allocationCoord.begin(); auto prevValue = m_allocation->m_registerIndices[*iter]; iter++; for(; iter != m_allocationCoord.end(); iter++) { auto myValue = m_allocation->m_registerIndices[*iter]; if(myValue != prevValue + 1) { m_contiguousIndices = false; return; } prevValue = myValue; } m_contiguousIndices = true; } inline bool Value::canUseAsOperand() const { if(m_regType == Type::Accumulator || m_regType == Type::Scalar || m_regType == Type::Vector) { return allocationState() == AllocationState::Allocated; } return true; } inline void Value::assertCanUseAsOperand() const { AssertFatal(canUseAsOperand(), "Tried to use unallocated register value!", ShowValue(this->toString())); } inline void Value::specialString(std::ostream& os) const { switch(m_regType) { case Type::M0: os << "m0"; return; case Type::SCC: os << "scc"; return; case Type::VCC: os << "vcc"; return; case Type::VCC_LO: os << "vcc_lo"; return; case Type::VCC_HI: os << "vcc_hi"; return; case Type::EXEC: os << "exec"; return; case Type::EXEC_LO: os << "exec_lo"; return; case Type::EXEC_HI: os << "exec_hi"; return; case Type::TTMP7: os << "ttmp7"; return; case Type::TTMP9: os << "ttmp9"; return; default: break; } Throw("Invalid special register: ", (int)m_regType); } inline void Value::gprString(std::ostream& os) const { AssertFatal(m_regType == Type::Accumulator || m_regType == Type::Scalar || m_regType == Type::Vector, "gprString is only applicable for actual GPRs."); if(!canUseAsOperand()) { os << TypePrefix(m_regType); os << "**UNALLOCATED**"; return; } auto prefix = TypePrefix(m_regType); auto const& regIndices = m_allocation->m_registerIndices; if(m_negate) { AssertFatal(hasContiguousIndices()); os << "neg("; } if(m_allocationCoord.size() == 1) { // one register, use simple syntax, e.g. 'v0' os << concatenate(prefix, regIndices[m_allocationCoord[0]]); } else if(hasContiguousIndices()) { // contiguous range of registers, e.g. v[0:3]. os << concatenate(prefix, "[", regIndices[m_allocationCoord.front()], ":", regIndices[m_allocationCoord.back()], "]"); } else { // non-contiguous registers, e.g. [v0, v2, v6, v7] os << "["; auto coordIter = m_allocationCoord.begin(); os << prefix << regIndices[*coordIter]; coordIter++; for(; coordIter != m_allocationCoord.end(); coordIter++) { os << ", " << prefix << regIndices.at(*coordIter); } os << "]"; } if(m_negate) { os << ")"; } } inline std::string Value::toString() const { std::stringstream oss; toStream(oss); return oss.str(); } inline void Value::toStream(std::ostream& os) const { switch(m_regType) { case Type::Accumulator: case Type::Scalar: case Type::Vector: gprString(os); return; case Type::Label: case Type::NullLiteral: os << m_label; return; case Type::M0: case Type::SCC: case Type::VCC: case Type::VCC_LO: case Type::VCC_HI: case Type::EXEC: case Type::EXEC_LO: case Type::EXEC_HI: case Type::TTMP7: case Type::TTMP9: specialString(os); return; case Type::Literal: os << getLiteral(); return; case Type::LocalData: os << "LDS:" << m_ldsAllocation->toString(); return; case Type::Constant: os << getConstant(); return; case Type::Count: break; } throw std::runtime_error("Bad Register::Type"); } inline std::string Value::description() const { auto rv = concatenate(regType(), " ", variableType(), " x ", valueCount(), ": "); if(allocationState() == AllocationState::Unallocated) rv += "(unallocated)"; else rv += toString(); if(!m_name.empty()) rv = m_name + ": " + rv; return rv; } inline bool Value::hasContiguousIndices() const { if(!m_contiguousIndices.has_value()) updateContiguousIndices(); return *m_contiguousIndices; } inline Generator Value::registerIndices() const { AssertFatal(m_regType != Type::Literal, "Literal values have no register indices."); AssertFatal(m_regType != Type::Label, "Label values have no register indices."); AssertFatal(allocationState() == AllocationState::Allocated, "Can't get indices for unallocated registers.", ShowValue(allocationState())); for(int coord : m_allocationCoord) co_yield m_allocation->m_registerIndices.at(coord); } inline std::string Value::getLiteral() const { if(m_regType != Type::Literal) { return ""; } return rocRoller::toString(m_literalValue); } inline std::string Value::getConstant() const { if(m_regType != Type::Constant) { return ""; } // Constant is a subset of literal return rocRoller::toString(m_literalValue); } inline CommandArgumentValue Value::getLiteralValue() const { AssertFatal(m_regType == Type::Literal, ShowValue(m_regType)); return m_literalValue; } inline std::string Value::getLabel() const { return m_label; } inline std::shared_ptr Value::getLDSAllocation() const { return m_ldsAllocation; } inline ContextPtr Value::context() const { return m_context.lock(); } inline size_t Value::registerCount() const { return m_allocationCoord.size(); } inline size_t Value::valueCount() const { if(DataTypeInfo::Get(m_varType).elementBits < bitsPerRegister) return m_allocationCoord.size(); return m_allocationCoord.size() * bitsPerRegister / DataTypeInfo::Get(m_varType).elementBits; } inline std::string Value::name() const { return m_name; } inline void Value::setName(std::string name) { if(allocationState() != AllocationState::NoAllocation) { AssertFatal(m_allocation); if(m_allocation->name() == "") m_allocation->setName(name); } m_name = std::move(name); } inline void Value::setReadOnly() { AssertFatal(m_allocation); m_allocation->setReadOnly(); } inline bool Value::readOnly() const { if(regType() == Type::Literal) return true; if(IsSpecial(regType()) && !IsWriteableSpecial(regType())) return true; if(!m_allocation) return false; return m_allocation->readOnly(); } inline ValuePtr Value::negate() const { AssertFatal(IsRegister(m_regType) && hasContiguousIndices()); auto r = std::make_shared( allocation(), regType(), variableType(), m_allocationCoord); r->m_negate = true; return r; } template inline ValuePtr Value::subset(T const& indices) const { AssertFatal(allocationState() != AllocationState::NoAllocation, ShowValue(allocationState())); AssertFatal(!m_allocationCoord.empty(), ShowValue(m_allocationCoord.size())); std::vector coords; for(auto i : indices) { AssertFatal(i >= 0 && (size_t)i < m_allocationCoord.size(), "Register subset out of bounds.", ShowValue(m_allocationCoord.size()), ShowValue(i)); coords.push_back(m_allocationCoord.at(i)); } return std::make_shared(allocation(), regType(), DataType::Raw32, coords); } template inline ValuePtr Value::subset(std::initializer_list indices) const { return subset>(indices); } template inline ValuePtr Value::element(T const& indices) const { AssertFatal(!m_allocationCoord.empty(), ShowValue(m_allocationCoord.size())); auto const info = DataTypeInfo::Get(m_varType); auto const registersPerElement = CeilDivide(info.elementBits, bitsPerRegister); std::vector coords; for(auto i : indices) { for(auto j = 0; j < registersPerElement; ++j) { auto idx = i * registersPerElement + j; AssertFatal(idx < m_allocationCoord.size(), ShowValue(i), ShowValue(j), ShowValue(registersPerElement), ShowValue(idx), ShowValue(m_allocationCoord.size())); coords.push_back(m_allocationCoord.at(idx)); } } return std::make_shared(m_allocation, m_regType, m_varType, coords); } template std::enable_if_t && !std::is_same_v, ValuePtr> inline Value::element(std::initializer_list indices) const { return element>(indices); } inline ValuePtr Value::bitfield(int bitOffset, int bitWidth) const { AssertFatal(allocationState() != AllocationState::NoAllocation, ShowValue(allocationState())); AssertFatal(!m_allocationCoord.empty(), ShowValue(m_allocationCoord.size())); // Does it make sense to allow taking a bitfield of another bitfield? AssertFatal(!this->isBitfield()); AssertFatal(bitWidth != 0); AssertFatal( bitWidth < bitsPerRegister, ShowValue(bitWidth), ShowValue(bitsPerRegister)); AssertFatal(bitOffset < registerCount() * bitsPerRegister, "bitOffset is greater than number of bits in this value."); uint registerOfBitOffset = bitOffset / bitsPerRegister; uint registerOfLastBit = (bitOffset + bitWidth - 1) / bitsPerRegister; AssertFatal(registerOfBitOffset == registerOfLastBit, "cannot get bitfield across registers."); std::vector allocationCoord = {m_allocationCoord.at(registerOfBitOffset)}; auto bitfieldValue = std::make_shared(m_allocation, m_regType, m_varType, allocationCoord); bitfieldValue->m_bitOffset = bitOffset - registerOfBitOffset * bitsPerRegister; bitfieldValue->m_bitWidth = bitWidth; return bitfieldValue; } template inline ValuePtr Value::segment(T const& indices) const { auto const info = DataTypeInfo::Get(m_varType); AssertFatal(info.packing > 1, "bitfield access by index is only supported for packed types.", ShowValue(m_varType)); auto isContiguousRange = [](T v) -> bool { return std::adjacent_find( v.begin(), v.end(), [](auto a, auto b) { return (b - a) != 1; }) == v.end(); }; AssertFatal(isContiguousRange(indices), "indices must refer to adjacent elements."); auto first = *std::ranges::begin(indices); auto last = *std::ranges::prev(std::ranges::end(indices)); uint startBitOffset = first * (info.elementBits / info.packing); uint endBitOffset = (last + 1) * (info.elementBits / info.packing); uint bitWidth = endBitOffset - startBitOffset; return bitfield(startBitOffset, bitWidth); } template std::enable_if_t && !std::is_same_v, ValuePtr> inline Value::segment(std::initializer_list indices) const { return segment>(indices); } inline bool Value::isBitfield() const { return this->m_bitOffset.has_value(); } inline int Value::getBitOffset() const { return this->m_bitOffset.value_or(0); } inline int Value::getBitWidth() const { AssertFatal(this->m_bitWidth.has_value()); return this->m_bitWidth.value(); } inline Allocation::Allocation(ContextPtr context, Type regType, VariableType variableType, int count, AllocationOptions options) : m_context(context) , m_regType(regType) , m_variableType(variableType) , m_options(options) , m_valueCount(count) { AssertFatal(context != nullptr); setRegisterCount(); if(options.contiguousChunkWidth == Register::FULLY_CONTIGUOUS) { m_options.contiguousChunkWidth = m_registerCount; } else if(options.contiguousChunkWidth == Register::VALUE_CONTIGUOUS) { m_options.contiguousChunkWidth = CeilDivide(variableType.getElementSize(), 4); } if(options.alignment <= 0) { m_options.alignment = m_variableType.registerAlignment( m_regType, m_options.contiguousChunkWidth, context->targetArchitecture()); } if(options.contiguousChunkWidth != Register::MANUAL) { AssertFatal(m_options.alignment <= m_options.contiguousChunkWidth, ShowValue(m_options), ShowValue(variableType)); } else { m_options.contiguousChunkWidth = 1; } AssertFatal(m_options.contiguousChunkWidth > 0, ShowValue(m_options)); } inline AllocationPtr Allocation::SameAs(Value const& val, std::string name, AllocationOptions const& options) { auto rv = std::make_shared( val.m_context.lock(), val.m_regType, val.m_varType, val.valueCount(), options); rv->setName(std::move(name)); return rv; } // These would need to have the shared_ptr, not just the reference/pointer. inline Instruction Allocation::allocate() { return Instruction::Allocate({shared_from_this()}); } inline bool Allocation::canAllocateNow() const { if(m_allocationState == AllocationState::Allocated) return true; if(!IsRegister(m_regType)) return true; auto allocator = m_context.lock()->allocator(m_regType); return allocator->canAllocate(shared_from_this()); } inline void Allocation::allocateNow() { if(m_allocationState == AllocationState::Allocated) return; if(!IsRegister(m_regType)) return; auto allocator = m_context.lock()->allocator(m_regType); allocator->allocate(shared_from_this()); } inline Type Allocation::regType() const { return m_regType; } inline AllocationState Allocation::allocationState() const { return m_allocationState; } inline ValuePtr Allocation::operator*() { // auto *ptr = new Value(shared_from_this(), // m_regType, m_variableType, // std::ranges::iota_view(0, registerCount())); // return {ptr}; std::vector rge(registerCount()); std::iota(rge.begin(), rge.end(), 0); auto rv = std::make_shared(shared_from_this(), m_regType, m_variableType, //std::ranges::iota_view(0, registerCount())); std::move(rge)); rv->setName(m_name); return rv; } inline AllocationOptions AllocationOptions::FullyContiguous() { return {.contiguousChunkWidth = Register::FULLY_CONTIGUOUS}; } inline std::string toString(AllocationOptions const& opts) { return concatenate("alignment: ", opts.alignment, ", phase: ", opts.alignmentPhase, ", contiguousChunkWidth: ", opts.contiguousChunkWidth); } inline std::ostream& operator<<(std::ostream& stream, AllocationOptions const& opts) { return stream << toString(opts); } inline std::string Allocation::descriptiveComment(std::string const& prefix) const { std::ostringstream msg; // e.g.: // Allocated A: 4 VGPRs (Float): 0, 1, 2, 3 auto regCount = registerCount(); auto typePrefix = TypePrefix(m_regType); msg << prefix << " " << m_name << ": " << regCount << " " << m_regType; if(regCount != 1) msg << 's'; msg << " (" << m_variableType << ")"; if(m_controlOp) { msg << " (op " << *m_controlOp << ")"; } auto iter = m_registerIndices.begin(); if(iter != m_registerIndices.end()) { msg << ": " << typePrefix << *iter; iter++; for(; iter != m_registerIndices.end(); iter++) msg << ", " << typePrefix << *iter; } msg << std::endl; return msg.str(); } inline void Allocation::setReadOnly() { m_readOnly = true; } inline bool Allocation::readOnly() const { return m_readOnly; } inline int Allocation::registerCount() const { return m_registerCount; } inline void Allocation::setRegisterCount() { m_registerCount = m_valueCount * static_cast(DataTypeInfo::Get(m_variableType).registerCount); } inline AllocationOptions Allocation::options() const { return m_options; } inline void Allocation::setAllocation(std::shared_ptr allocator, std::vector const& registers) { auto copy = registers; setAllocation(allocator, std::move(copy)); } inline void Allocation::setAllocation(std::shared_ptr allocator, std::vector&& registers) { AssertFatal(m_allocationState != AllocationState::Allocated, ShowValue(m_allocationState), ShowValue(registers.size()), ShowValue(registerCount())); m_allocator = allocator; m_registerIndices = std::move(registers); m_allocationState = AllocationState::Allocated; } inline std::vector const& Allocation::registerIndices() const { return m_registerIndices; } inline void Allocation::free() { AssertFatal(m_allocationState == AllocationState::Allocated, ShowValue(m_allocationState), ShowValue(AllocationState::Allocated)); AssertFatal(m_allocator != nullptr, ShowValue(m_allocator)); m_allocator->free(m_registerIndices); m_registerIndices.clear(); m_allocationState = AllocationState::Freed; } inline std::string Allocation::name() const { return m_name; } inline void Allocation::setName(std::string name) { m_name = std::move(name); } } }