/******************************************************************************* * * MIT License * * Copyright 2021-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 namespace rocRoller { struct Instruction { enum { MaxDstRegisters = 2, MaxSrcRegisters = 5, MaxExtraRegisters = 5, MaxModifiers = 10, MaxAllocations = 4, }; Instruction(std::string const& opcode, std::initializer_list dst, std::initializer_list src, std::initializer_list modifiers, std::string const& comment); Instruction(); static Generator EscapeComment(std::string comment, int indent = 0); static Instruction Allocate(Register::ValuePtr reg); static Instruction Allocate(std::shared_ptr reg); static Instruction Allocate(std::initializer_list> regs); static Instruction Directive(std::string const& directive); static Instruction Directive(std::string const& directive, std::string const& comment); static Instruction Comment(std::string const& comment); static Instruction Warning(std::string const& warning); static Instruction Nop(); static Instruction Nop(int nopCount); static Instruction Nop(std::string const& comment); static Instruction Nop(int nopCount, std::string const& comment); static Instruction Label(std::string const& name); static Instruction Label(std::string&& name); static Instruction Label(std::string const& name, std::string const& comment); static Instruction Label(std::string&& name, std::string const& comment); static Instruction Label(Register::ValuePtr label); static Instruction Label(Register::ValuePtr label, std::string const& comment); static Instruction InoutInstruction(std::string const& opcode, std::initializer_list inout, std::initializer_list modifiers, std::string const& comment); static Instruction Wait(WaitCount const& wait); static Instruction Wait(WaitCount&& wait); static Instruction Lock(Scheduling::Dependency dependency, std::string comment = ""); static Instruction Unlock(Scheduling::Dependency dependency, std::string comment = ""); static Instruction Unlock(std::string comment = ""); /** * This instruction will be considered to write `reg` even though it * will not be included in the actual dst operands in the string * rendering. This will include it in the wait queue for the * instruction if it's a memory instruction. */ Instruction& addExtraDst(Register::ValuePtr reg); /** * This instruction will implicitly add a waitcnt for any pending * load/stores on `reg`, without including it in the input operands for * the string rendering of this instruction. */ Instruction& addExtraSrc(Register::ValuePtr reg); /** * Returns all the destination operands that will appear in the instruction. */ auto const& getDsts() const; /** * Returns all the source operands that will appear in the instruction. */ auto const& getSrcs() const; /** * Returns all the 'extra' destination operands; i.e. ones that will * not appear in the instruction. */ auto const& getExtraDsts() const; /** * Returns all the 'extra' source operands; i.e. ones that will * not appear in the instruction. */ auto const& getExtraSrcs() const; /** * Yields all the destination operands, including ones that will not * appear in the instruction. */ Generator getAllDsts() const; /** * Yields all the source operands, including ones that will not * appear in the instruction. */ Generator getAllSrcs() const; /** * Yields all destination and source operands, including ones that will * not appear in the instruction. */ Generator getAllOperands() const; bool hasRegisters() const; constexpr bool readsSpecialRegisters() const; WaitCount getWaitCount() const; /** * @brief Returns |currentSrc n previousDest| > 0 or |currentDest n previousDest| > 0 * * @param previousDest Destination registers of previous instruction. * @return Whether this instructions registers intersect with a past * instruction's destination registers. */ template T> bool isAfterWriteDependency(T const& previousDest) const; void toStream(std::ostream&, LogLevel level) const; std::string toString(LogLevel level) const; constexpr Scheduling::LockOperation getLockValue() const; constexpr Scheduling::Dependency getDependency() const; std::string const& getOpCode() const; std::array getModifiers() const; CoexecCategory getCategory() const; constexpr int getNopCount() const { return m_nopCount; } Instruction& lock(Scheduling::Dependency dependency, std::string comment = ""); Instruction& unlock(Scheduling::Dependency dependency, std::string comment = ""); Instruction& unlock(std::string comment = ""); void addControlOp(int id); std::vector const& controlOps() const; std::optional innerControlOp() const; std::string const& referencedArg() const; void setReferencedArg(std::string arg); void addAllocation(std::shared_ptr alloc); void addWaitCount(WaitCount const& wait); void addComment(std::string const& comment); void addWarning(std::string const& warning); void addNop(); void addNop(int count); void setNopMin(int count); std::vector const& comments() const; constexpr int nopCount() const; constexpr bool isCommentOnly() const; constexpr bool isLabel() const; std::string getLabel() const; /** * Get the status that was peeked by the Observers */ Scheduling::InstructionStatus const& peekedStatus() const; /** * Set the status that was peeked by the Observers */ void setPeekedStatus(Scheduling::InstructionStatus status); /** * How many instructions actually executed by the GPU are included? * This includes the main instruction, as well as any s_nop or s_waitcnt * instructions attached to it. */ int numExecutedInstructions() const; void allocateNow(); using AllocationArray = std::array, MaxAllocations>; AllocationArray allocations() const; private: /** * toString = preamble + functional + coda */ void preambleString(std::ostream& oss, LogLevel level) const; void functionalString(std::ostream& oss, LogLevel level) const; void codaString(std::ostream& oss, LogLevel level) const; /** * Attempts to find a pointer to the context by looking at all the operands. */ ContextPtr findContextFromOperands() const; /** * A comment detailing allocations that happened when scheduling this instruction. */ void allocationString(std::ostream& oss, LogLevel level) const; /** * Assembler directive(s), if this instruction contains any. */ void directiveString(std::ostream& oss, LogLevel level) const; /** * Just the main instruction. */ void coreInstructionString(std::ostream& oss) const; bool requiresVnopForHazard() const; /** * When this instruction is scheduled, perform this register allocation. */ AllocationArray m_allocations; int m_nopCount = 0; std::string m_directive; std::vector m_warnings; std::vector m_comments; std::vector m_controlOps; std::string m_label; WaitCount m_waitCount; std::string m_opcode; std::string m_referencedArg; Scheduling::LockOperation m_lockOp = Scheduling::LockOperation::None; Scheduling::Dependency m_dependency = Scheduling::Dependency::None; std::array m_inoutDsts; std::array m_dst; std::array m_src; std::array m_extraDsts; std::array m_extraSrcs; std::array m_modifiers; bool m_operandsAreInout = false; Scheduling::InstructionStatus m_peekedStatus; }; } #include