From 9e1289b63cf75be792a5deb73fd5cdb397623bce Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Fri, 7 Nov 2025 13:10:38 -0800 Subject: [PATCH 01/17] hctdb.py: Add dxil op table for experimental opcode tables --- include/dxc/DXIL/DxilConstants.h | 69 +++++++++++--- include/dxc/DXIL/DxilOperations.h | 29 +++++- lib/DXIL/DxilOperations.cpp | 141 ++++++++++++++++++++++------ utils/hct/hctdb.py | 149 +++++++++++++++++++++++++----- utils/hct/hctdb_instrhelp.py | 81 +++++++++++----- 5 files changed, 379 insertions(+), 90 deletions(-) diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index d66f14a1e9..8b0a01dd2f 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -490,10 +490,44 @@ inline bool IsFeedbackTexture(DXIL::ResourceKind ResourceKind) { ResourceKind == DXIL::ResourceKind::FeedbackTexture2DArray; } +// clang-format off +// Python lines need to be not formatted. +/* hctdb_instrhelp.get_enum_decl("OpCodeTableID")*/ +// clang-format on +// OPCODETABLE-ENUM:BEGIN +// Enumeration for DXIL opcode tables +enum class OpCodeTableID : unsigned { + Core = 0, // Core DXIL operations + ExperimentalCommon = 1, // Common Experimental DXIL operations + + NumOpCodeTables = 2, // exclusive last value of enumeration +}; +// OPCODETABLE-ENUM:END + +// clang-format off +// Python lines need to be not formatted. +/* hctdb_instrhelp.get_extended_table_opcode_enum_decls()*/ +// clang-format on +// EXTOPCODES-ENUM:BEGIN +namespace ExperimentalCommon { +static const OpCodeTableID TableID = OpCodeTableID::ExperimentalCommon; +// Enumeration for ExperimentalCommon DXIL operations +enum class OpCode : unsigned { + ExperimentalNop = 0, // nop does nothing + + NumOpCodes = 1, // exclusive last value of enumeration +}; +} // namespace ExperimentalCommon +// EXTOPCODES-ENUM:END + +#define EXP_OPCODE(feature, opcode) \ + opcode = \ + (((unsigned)feature::TableID << 16) | (unsigned)feature::OpCode::opcode) + // TODO: change opcodes. /* hctdb_instrhelp.get_enum_decl("OpCode")*/ // OPCODE-ENUM:BEGIN -// Enumeration for operations specified by DXIL +// Enumeration for Core DXIL operations enum class OpCode : unsigned { // Reserved0 = 226, // reserved @@ -1089,9 +1123,24 @@ enum class OpCode : unsigned { NumOpCodes_Dxil_1_8 = 258, NumOpCodes_Dxil_1_9 = 312, - NumOpCodes = 312 // exclusive last value of enumeration + NumOpCodes = 312, // exclusive last value of enumeration + Invalid = 0xFFFFFFFF, // stable invalid OpCode value + + // OpCodes for extended tables follow. + + // OpCodeTableID = 1 + // ExperimentalCommon + EXP_OPCODE(ExperimentalCommon, ExperimentalNop), // nop does nothing + }; // OPCODE-ENUM:END +#undef EXP_OPCODE + +// Create Core namespace for consistency with other opcode groups +namespace Core { +static const OpCodeTableID TableID = OpCodeTableID::Core; +using OpCode = hlsl::DXIL::OpCode; +} // namespace Core // clang-format off // Python lines need to be not formatted. @@ -1244,6 +1293,9 @@ enum class OpCodeClass : unsigned { StorePrimitiveOutput, StoreVertexOutput, + // No-op + Nop, + // Other CycleCounterLegacy, @@ -1414,18 +1466,7 @@ enum class OpCodeClass : unsigned { NodeOutputIsValid, OutputComplete, - NumOpClasses_Dxil_1_0 = 93, - NumOpClasses_Dxil_1_1 = 95, - NumOpClasses_Dxil_1_2 = 97, - NumOpClasses_Dxil_1_3 = 118, - NumOpClasses_Dxil_1_4 = 120, - NumOpClasses_Dxil_1_5 = 143, - NumOpClasses_Dxil_1_6 = 149, - NumOpClasses_Dxil_1_7 = 153, - NumOpClasses_Dxil_1_8 = 174, - NumOpClasses_Dxil_1_9 = 196, - - NumOpClasses = 196 // exclusive last value of enumeration + NumOpClasses = 197, // exclusive last value of enumeration }; // OPCODECLASS-ENUM:END diff --git a/include/dxc/DXIL/DxilOperations.h b/include/dxc/DXIL/DxilOperations.h index c8b6762b3f..adf0aa0537 100644 --- a/include/dxc/DXIL/DxilOperations.h +++ b/include/dxc/DXIL/DxilOperations.h @@ -37,6 +37,7 @@ class OP { public: using OpCode = DXIL::OpCode; using OpCodeClass = DXIL::OpCodeClass; + using OpCodeTableID = DXIL::OpCodeTableID; public: OP() = delete; @@ -131,6 +132,7 @@ class OP { llvm::Constant *GetFloatConst(float v); llvm::Constant *GetDoubleConst(double v); + static OP::OpCode getOpCode(unsigned OpCode); static OP::OpCode getOpCode(const llvm::Instruction *I); static llvm::Type *GetOverloadType(OpCode OpCode, llvm::Function *F); static OpCode GetDxilOpFuncCallInst(const llvm::Instruction *I); @@ -226,8 +228,7 @@ class OP { std::unordered_map m_FunctionToOpClass; void UpdateCache(OpCodeClass opClass, llvm::Type *Ty, llvm::Function *F); -private: - // Static properties. +public: struct OverloadMask { // mask of type slot bits as (1 << TypeSlot) uint16_t SlotMask; @@ -255,7 +256,29 @@ class OP { // AllowedOverloads[n][TS_Vector] is true. OverloadMask AllowedVectorElements[DXIL::kDxilMaxOloadDims]; }; - static const OpCodeProperty m_OpCodeProps[(unsigned)OpCode::NumOpCodes]; + struct OpCodeTable { + OpCodeTableID ID; + const OpCodeProperty *Table; + unsigned Count; + }; + + // Look up table using high 16-bits as table ID, low 16-bits as OpCode. + // Return true if valid. + // unsigned versions are for use with whatever value was in a DXIL Op + // instruction. + // LocalOpCode is the low 16-bits OpCode for local table lookup. + static bool DecodeOpCode(unsigned EncodedOpCode, OpCodeTableID &TableID, + unsigned &LocalOpCode); + static bool DecodeOpCode(OpCode EncodedOpCode, OpCodeTableID &TableID, + unsigned &LocalOpCode); + static bool IsValidOpCode(unsigned EncodedOpCode); + static bool IsValidOpCode(OpCode EncodedOpCode); + +private: + // Static properties. + static OpCodeTable g_OpCodeTables[(unsigned)OpCodeTableID::NumOpCodeTables]; + static const OpCodeProperty &GetOpCodeProps(unsigned opCode); + static const OpCodeProperty &GetOpCodeProps(OpCode opCode); static const char *m_OverloadTypeName[TS_BasicCount]; static const char *m_NamePrefix; diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index 9902ef8d14..9864368ba3 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -39,7 +39,7 @@ import hctdb_instrhelp */ /* hctdb_instrhelp.get_oloads_props()*/ // OPCODE-OLOADS:BEGIN -const OP::OpCodeProperty OP::m_OpCodeProps[(unsigned)OP::OpCode::NumOpCodes] = { +static const OP::OpCodeProperty Core_OpCodeProps[] = { // Temporary, indexable, input, output registers {OC::TempRegLoad, "TempRegLoad", @@ -2715,6 +2715,32 @@ const OP::OpCodeProperty OP::m_OpCodeProps[(unsigned)OP::OpCode::NumOpCodes] = { {{0x400}}, {{0x3}}}, // Overloads: > 16); + if (TID >= (unsigned)OP::OpCodeTableID::NumOpCodeTables) + return false; + unsigned Op = (EncodedOpCode & 0xFFFF); + if (Op >= OP::g_OpCodeTables[TID].Count) + return false; + TableID = (OP::OpCodeTableID)TID; + LocalOpCode = Op; + return true; +} +bool OP::DecodeOpCode(OpCode EncodedOpCode, OP::OpCodeTableID &TableID, + unsigned &LocalOpCode) { + return DecodeOpCode((unsigned)EncodedOpCode, TableID, LocalOpCode); +} +bool OP::IsValidOpCode(unsigned EncodedOpCode) { + if (EncodedOpCode == (unsigned)OP::OpCode::Invalid) + return false; + OP::OpCodeTableID TID; + unsigned LocalOpCode; + return DecodeOpCode(EncodedOpCode, TID, LocalOpCode); +} +bool OP::IsValidOpCode(OP::OpCode EncodedOpCode) { + return IsValidOpCode((unsigned)EncodedOpCode); +} +const OP::OpCodeProperty &OP::GetOpCodeProps(unsigned OriginalOpCode) { + OP::OpCodeTableID TID = OP::OpCodeTableID::Core; + unsigned Op = 0; + bool Success = DecodeOpCode(OriginalOpCode, TID, Op); + DXASSERT(Success, "otherwise invalid OpCode"); + const OP::OpCodeTable &Table = OP::g_OpCodeTables[(unsigned)TID]; + return Table.Table[Op]; +} +const OP::OpCodeProperty &OP::GetOpCodeProps(OP::OpCode OriginalOpCode) { + return GetOpCodeProps((unsigned)OriginalOpCode); +} + unsigned OP::GetTypeSlot(Type *pType) { Type::TypeID T = pType->getTypeID(); switch (T) { @@ -2842,7 +2909,7 @@ StringRef OP::ConstructOverloadName(Type *Ty, DXIL::OpCode opCode, } const char *OP::GetOpCodeName(OpCode opCode) { - return m_OpCodeProps[(unsigned)opCode].pOpCodeName; + return GetOpCodeProps(opCode).pOpCodeName; } const char *OP::GetAtomicOpName(DXIL::AtomicBinOpCode OpCode) { @@ -2854,24 +2921,23 @@ const char *OP::GetAtomicOpName(DXIL::AtomicBinOpCode OpCode) { } OP::OpCodeClass OP::GetOpCodeClass(OpCode opCode) { - return m_OpCodeProps[(unsigned)opCode].opCodeClass; + return GetOpCodeProps(opCode).opCodeClass; } const char *OP::GetOpCodeClassName(OpCode opCode) { - return m_OpCodeProps[(unsigned)opCode].pOpCodeClassName; + return GetOpCodeProps(opCode).pOpCodeClassName; } llvm::Attribute::AttrKind OP::GetMemAccessAttr(OpCode opCode) { - return m_OpCodeProps[(unsigned)opCode].FuncAttr; + return GetOpCodeProps(opCode).FuncAttr; } bool OP::IsOverloadLegal(OpCode opCode, Type *pType) { - if (static_cast(opCode) >= - static_cast(OpCode::NumOpCodes)) - return false; if (!pType) return false; - auto &OpProps = m_OpCodeProps[static_cast(opCode)]; + if (!IsValidOpCode(opCode)) + return false; + auto &OpProps = GetOpCodeProps(opCode); if (OpProps.NumOverloadDims == 0) return pType->isVoidTy(); @@ -2904,9 +2970,18 @@ bool OP::IsOverloadLegal(OpCode opCode, Type *pType) { } bool OP::CheckOpCodeTable() { - for (unsigned i = 0; i < (unsigned)OpCode::NumOpCodes; i++) { - if ((unsigned)m_OpCodeProps[i].opCode != i) - return false; + for (unsigned t = 0; t < (unsigned)OP::OpCodeTableID::NumOpCodeTables; t++) { + const OP::OpCodeTable &Table = OP::g_OpCodeTables[t]; + for (unsigned Op = 0; Op < Table.Count; Op++) { + const OP::OpCodeProperty &Prop = Table.Table[Op]; + OP::OpCodeTableID DecodedTID; + unsigned DecodedLocalOp; + bool Success = OP::DecodeOpCode(Prop.opCode, DecodedTID, DecodedLocalOp); + if (!Success) + return false; + if (DecodedTID != Table.ID || DecodedLocalOp != Op) + return false; + } } return true; @@ -2937,14 +3012,19 @@ bool OP::IsDxilOpFuncCallInst(const llvm::Instruction *I, OpCode opcode) { return (unsigned)getOpCode(I) == (unsigned)opcode; } +OP::OpCode OP::getOpCode(unsigned OpCode) { + if (!IsValidOpCode(OpCode)) + return OP::OpCode::Invalid; + return static_cast(OpCode); +} OP::OpCode OP::getOpCode(const llvm::Instruction *I) { auto *OpConst = llvm::dyn_cast(I->getOperand(0)); if (!OpConst) - return OpCode::NumOpCodes; + return OpCode::Invalid; uint64_t OpCodeVal = OpConst->getZExtValue(); - if (OpCodeVal >= static_cast(OP::OpCode::NumOpCodes)) - return OP::OpCode::NumOpCodes; - return static_cast(OpCodeVal); + if (OpCodeVal >= static_cast(OP::OpCode::Invalid)) + return OP::OpCode::Invalid; + return getOpCode(static_cast(OpCodeVal)); } OP::OpCode OP::GetDxilOpFuncCallInst(const llvm::Instruction *I) { @@ -3014,9 +3094,9 @@ bool OP::IsDxilOpBarrier(OpCode C) { } bool OP::IsDxilOpExtendedOverload(OpCode C) { - if (C >= OpCode::NumOpCodes) + if (!IsValidOpCode(C)) return false; - return m_OpCodeProps[static_cast(C)].NumOverloadDims > 1; + return GetOpCodeProps(C).NumOverloadDims > 1; } static unsigned MaskMemoryTypeFlagsIfAllowed(unsigned memoryTypeFlags, @@ -3645,8 +3725,6 @@ OP::OP(LLVMContext &Ctx, Module *pModule) memset(m_pResRetType, 0, sizeof(m_pResRetType)); memset(m_pCBufferRetType, 0, sizeof(m_pCBufferRetType)); memset(m_OpCodeClassCache, 0, sizeof(m_OpCodeClassCache)); - static_assert(_countof(OP::m_OpCodeProps) == (size_t)OP::OpCode::NumOpCodes, - "forgot to update OP::m_OpCodeProps"); m_pHandleType = GetOrCreateStructType(m_Ctx, Type::getInt8PtrTy(m_Ctx), "dx.types.Handle", pModule); @@ -3750,10 +3828,10 @@ void OP::UpdateCache(OpCodeClass opClass, Type *Ty, llvm::Function *F) { } bool OP::MayHaveNonCanonicalOverload(OpCode OC) { - if (OC >= OpCode::NumOpCodes) + if (!IsValidOpCode(OC)) return false; const unsigned CheckMask = (1 << TS_UDT) | (1 << TS_Object); - auto &OpProps = m_OpCodeProps[static_cast(OC)]; + auto &OpProps = GetOpCodeProps(OC); for (unsigned I = 0; I < OpProps.NumOverloadDims; ++I) if ((CheckMask & OpProps.AllowedOverloads[I].SlotMask) != 0) return true; @@ -3761,10 +3839,9 @@ bool OP::MayHaveNonCanonicalOverload(OpCode OC) { } Function *OP::GetOpFunc(OpCode OC, ArrayRef OverloadTypes) { - if (OC >= OpCode::NumOpCodes) + if (!IsValidOpCode(OC)) return nullptr; - if (OverloadTypes.size() != - m_OpCodeProps[static_cast(OC)].NumOverloadDims) { + if (OverloadTypes.size() != GetOpCodeProps(OC).NumOverloadDims) { llvm_unreachable("incorrect overload dimensions"); return nullptr; } @@ -3777,12 +3854,12 @@ Function *OP::GetOpFunc(OpCode OC, ArrayRef OverloadTypes) { } Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { - if (opCode >= OpCode::NumOpCodes) + if (!IsValidOpCode(opCode)) return nullptr; if (!pOverloadType) return nullptr; - auto &OpProps = m_OpCodeProps[static_cast(opCode)]; + auto &OpProps = GetOpCodeProps(opCode); if (IsDxilOpExtendedOverload(opCode)) { // Make sure pOverloadType is well formed for an extended overload. StructType *ST = dyn_cast(pOverloadType); @@ -6044,6 +6121,12 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pETy); A(pETy); break; + + // No-op + case OpCode::ExperimentalNop: + A(pV); + A(pI32); + break; // OPCODE-OLOAD-FUNCS:END default: DXASSERT(false, "otherwise unhandled case"); @@ -6086,8 +6169,7 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { const SmallMapVector & OP::GetOpFuncList(OpCode opCode) const { - return m_OpCodeClassCache[(unsigned)m_OpCodeProps[(unsigned)opCode] - .opCodeClass] + return m_OpCodeClassCache[(unsigned)GetOpCodeProps(opCode).opCodeClass] .pOverloads; } @@ -6335,6 +6417,7 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::ReservedC7: case OpCode::ReservedC8: case OpCode::ReservedC9: + case OpCode::ExperimentalNop: return Type::getVoidTy(Ctx); case OpCode::CheckAccessFullyMapped: case OpCode::SampleIndex: diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 3639f49c76..e36342e223 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -82,6 +82,9 @@ def __init__(self, name, doc, valNameDocTuples=()): db_dxil_enum_value(n, v, d) for v, n, d in valNameDocTuples ] # Note transmutation self.is_internal = False # whether this is never serialized + self.last_value_name = None # optional last value name for dense enums + self.dxil_version_info = {} # version info for this enum + self.postfix_lines = [] # optional postfix to include inside enum declaration def value_names(self): return [i.name for i in self.values] @@ -94,6 +97,7 @@ def __init__(self, name, **kwargs): self.name = name # short, unique name self.llvm_id = 0 # ID of LLVM instruction self.llvm_name = "" # name of LLVM instruction type + self.dxil_table = "Core" # name of the DXIL operation table self.is_dxil_op = False # whether this is a call into a built-in DXIL function self.dxil_op = "" # name of DXIL operation @@ -117,13 +121,15 @@ def __init__(self, name, **kwargs): self.shader_model = 6, 0 # minimum shader model required self.inst_helper_prefix = None self.fully_qualified_name_prefix = "hlsl::OP::OpCode" + self.shader_model_translated = () # minimum shader model required with translation by linker + self.props = {} # extra properties + self.num_oloads = 0 # number of overloads for this instruction + for k, v in list(kwargs.items()): setattr(self, k, v) + self.is_dxil_op = self.dxil_op != "" # whether this is a DXIL operation self.is_reserved = self.dxil_class == "Reserved" - self.shader_model_translated = () # minimum shader model required with translation by linker - self.props = {} # extra properties - self.num_oloads = 0 # number of overloads for this instruction if self.is_dxil_op: self.process_oload_types() @@ -333,26 +339,62 @@ def __str__(self): return self.name +# DXIL operations are grouped into tables to support experimental and extended +# features. +class db_dxil_op_table(object): + "Table definition for a set of DXIL operations" + + def __init__(self, id, name, doc): + self.id = id + self.name = name + self.doc = doc + self.instr = [] # DXIL instructions + self.op_count = 0 + self.op_enum = db_dxil_enum( + "OpCode", + f"Enumeration for {self.name} DXIL operations" + ) + self.op_enum.last_value_name = "NumOpCodes" + + def next_id(self): + new_id = self.op_count + self.op_count += 1 + return new_id + + def get_count(self): + return self.op_count + + def set_op_count_for_version(self, major, minor): + op_count = self.get_count() + self.op_enum.dxil_version_info[(major, minor)] = op_count + return op_count + class db_dxil(object): "A database of DXIL instruction data" def __init__(self): - self.instr = [] # DXIL instructions self.enums = [] # enumeration types self.val_rules = [] # validation rules self.metadata = [] # named metadata (db_dxil_metadata) self.passes = [] # inventory of available passes (db_dxil_pass) self.name_idx = {} # DXIL instructions by name self.enum_idx = {} # enumerations by name - self.dxil_version_info = {} # list of counters for instructions and dxil ops, # starting with extra ones specified here self.counters = extra_counters - self.next_dxil_op_id = 0 # next available DXIL op ID + self.dxil_op_tables = [ + db_dxil_op_table(0, "Core", "Core DXIL operations"), + db_dxil_op_table(1, "ExperimentalCommon", "Common Experimental DXIL operations"), + ] + self.dxil_op_tables_by_name = dict([(t.name, t) for t in self.dxil_op_tables]) + # Setting the current table sets self.instr. + self.set_dxil_op_table() self.populate_llvm_instructions() self.call_instr = self.get_instr_by_llvm_name("CallInst") self.populate_dxil_operations() + self.populate_experimental_ops() + self.finalize_dxil_operations() self.build_indices() self.populate_extended_docs() self.populate_categories_and_models() @@ -365,15 +407,18 @@ def __init__(self): self.build_indices() self.populate_counters() + def set_dxil_op_table(self, table_name = "Core"): + "Set the current DXIL operation table, defaulting to Core." + self.cur_table = self.dxil_op_tables_by_name[table_name] + self.instr = self.cur_table.instr + + def get_dxil_op_table(self, table_name = "Core"): + "Get the specified DXIL operation table." + return self.dxil_op_tables_by_name[table_name] + def __str__(self): return "\n".join(str(i) for i in self.instr) - def next_id(self): - "Returns the next available DXIL op ID and increments the counter" - val = self.next_dxil_op_id - self.next_dxil_op_id += 1 - return val - def add_enum_type(self, name, doc, valNameDocTuples): "Adds a new enumeration type with name/value/doc tuples" self.enums.append(db_dxil_enum(name, doc, valNameDocTuples)) @@ -381,17 +426,16 @@ def add_enum_type(self, name, doc, valNameDocTuples): def build_indices(self): "Build a name_idx dictionary with instructions and an enum_idx dictionary with enumeration types" self.name_idx = {} - for i in self.instr: - self.name_idx[i.name] = i + for table in self.dxil_op_tables: + for i in table.instr: + self.name_idx[i.name] = i self.enum_idx = {} for i in self.enums: self.enum_idx[i.name] = i def build_opcode_enum(self): # Build enumeration from instructions - OpCodeEnum = db_dxil_enum( - "OpCode", "Enumeration for operations specified by DXIL" - ) + OpCodeEnum = self.get_dxil_op_table().op_enum class_dict = {} class_dict["LlvmInst"] = "LLVM Instructions" for i in self.instr: @@ -400,17 +444,48 @@ def build_opcode_enum(self): v.category = i.category class_dict[i.dxil_class] = i.category OpCodeEnum.values.append(v) + postfix = OpCodeEnum.postfix_lines or [] + # Add OpCode::Invalid + postfix.append("Invalid = 0xFFFFFFFF, // stable invalid OpCode value") + # Generate extended opcode enums into OpCodeEnum.postfix_lines: + OpCodeTableID = db_dxil_enum( + "OpCodeTableID", "Enumeration for DXIL opcode tables", + [(0, "Core", "Core DXIL operations")] + ) + OpCodeTableID.last_value_name = "NumOpCodeTables" + postfix.append("") + postfix.append("// OpCodes for extended tables follow.\n") + for table in self.dxil_op_tables[1:]: # Skip Core table + OpCodeTableID.values.append( + db_dxil_enum_value(table.name, table.id, table.doc) + ) + if not table.get_count(): + continue + postfix.append(f"// OpCodeTableID = {table.id}") + postfix.append(f"// {table.name}") + for i in table.instr: + class_dict[i.dxil_class] = i.category + postfix.append( + f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}" + ) + table.op_enum.values.append( + db_dxil_enum_value(i.dxil_op, i.dxil_opid, i.doc) + ) + postfix.append("") + OpCodeEnum.postfix_lines = postfix self.enums.append(OpCodeEnum) OpCodeClass = db_dxil_enum( "OpCodeClass", "Groups for DXIL operations with equivalent function templates", ) OpCodeClass.is_internal = True + OpCodeClass.last_value_name = "NumOpClasses" for k, v in iter(class_dict.items()): ev = db_dxil_enum_value(k, 0, None) ev.category = v OpCodeClass.values.append(ev) self.enums.append(OpCodeClass) + self.enums.append(OpCodeTableID) def mark_disallowed_operations(self): # Disallow indirect branching, unreachable instructions and support for exception unwinding. @@ -436,10 +511,7 @@ def verify_dense(self, it, pred, name_proj): val = i_val def set_op_count_for_version(self, major, minor): - info = self.dxil_version_info.setdefault((major, minor), dict()) - info["NumOpCodes"] = self.next_dxil_op_id - info["NumOpClasses"] = len(set([op.dxil_class for op in self.instr])) - return self.next_dxil_op_id + return self.cur_table.set_op_count_for_version(major, minor) def populate_categories_and_models(self): "Populate the category and shader_stages member of instructions." @@ -886,6 +958,13 @@ def populate_categories_and_models(self): ).split(","): self.name_idx[i].category = "Linear Algebra Operations" self.name_idx[i].shader_model = 6, 10 + # End of core DXIL ops + self.populate_categories_and_models_ExperimentalCommon() + + def populate_categories_and_models_ExperimentalCommon(self): + for i in "ExperimentalNop".split(","): + self.name_idx[i].category = "No-op" + self.name_idx[i].shader_model = 6, 10 def populate_llvm_instructions(self): # Add instructions that map to LLVM instructions. @@ -5889,6 +5968,27 @@ def UFI(name, **mappings): % op_count ) + def populate_experimental_ops(self): + "Populate experimental DXIL operations." + self.set_dxil_op_table("ExperimentalCommon") + self.populate_ExperimentalCommon_ops() + self.set_dxil_op_table() + + def populate_ExperimentalCommon_ops(self): + "Populate experimental DXIL operations for ExperimentalCommon." + # Add Nop to test experimental table infrastructure. + self.add_dxil_op( + "ExperimentalNop", + "Nop", + "nop does nothing", + "v", + "rn", + [ + db_dxil_param(0, "v", "", "no result"), + ], + ) + + def finalize_dxil_operations(self): # Set interesting properties. self.build_indices() for ( @@ -8520,6 +8620,7 @@ def add_valrule_msg(self, name, desc, err_msg): def add_llvm_instr( self, kind, llvm_id, name, llvm_name, doc, oload_types, op_params, **props ): + assert self.cur_table.name == "Core", "LLVM instructions can only be added to the Core table" i = db_dxil_inst( name, llvm_id=llvm_id, @@ -8541,7 +8642,8 @@ def add_dxil_op( llvm_id=self.call_instr.llvm_id, llvm_name=self.call_instr.llvm_name, dxil_op=name, - dxil_opid=self.next_id(), + dxil_opid=self.cur_table.next_id(), + dxil_table=self.cur_table.name, doc=doc, ops=op_params, dxil_class=code_class, @@ -8559,7 +8661,8 @@ def add_dxil_op_reserved(self, name): llvm_id=self.call_instr.llvm_id, llvm_name=self.call_instr.llvm_name, dxil_op=name, - dxil_opid=self.next_id(), + dxil_opid=self.cur_table.next_id(), + dxil_table=self.cur_table.name, doc="reserved", ops=op_params, dxil_class="Reserved", diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 9debd6e07f..7c93ec7e8f 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -423,8 +423,6 @@ class db_enumhelp_gen: def __init__(self, db): self.db = db - # Some enums should get a last enum marker. - self.lastEnumNames = {"OpCode": "NumOpCodes", "OpCodeClass": "NumOpClasses"} def print_enum(self, e, **kwargs): print("// %s" % e.doc) @@ -451,12 +449,11 @@ def print_enum(self, e, **kwargs): if v.doc: line_format += " // {doc}" print(line_format.format(name=v.name, value=v.value, doc=v.doc)) - if e.name in self.lastEnumNames: - lastName = self.lastEnumNames[e.name] + if e.last_value_name: + lastName = e.last_value_name versioned = [ - "%s_Dxil_%d_%d = %d," % (lastName, major, minor, info[lastName]) - for (major, minor), info in sorted(self.db.dxil_version_info.items()) - if lastName in info + "%s_Dxil_%d_%d = %d," % (lastName, major, minor, count) + for (major, minor), count in sorted(e.dxil_version_info.items()) ] if versioned: print("") @@ -468,8 +465,11 @@ def print_enum(self, e, **kwargs): + lastName + " = " + str(len(sorted_values)) - + " // exclusive last value of enumeration" + + ", // exclusive last value of enumeration" ) + if e.postfix_lines: + for line in e.postfix_lines: + print(" " + line) print("};") def print_rdat_enum(self, e, **kwargs): @@ -484,6 +484,13 @@ def print_rdat_enum(self, e, **kwargs): line_format += " // {doc}" print(line_format.format(name=v.name, value=v.value, doc=v.doc)) + def print_extended_table_opcode_enums(self): + for table in self.db.dxil_op_tables[1:]: # Skip Core table + print(f"namespace {table.name} {{") + print(f"static const OpCodeTableID TableID = OpCodeTableID::{table.name};") + self.print_enum(table.op_enum) + print(f"}} // namespace {table.name}") + def print_content(self): for e in sorted(self.db.enums, key=lambda e: e.name): self.print_enum(e) @@ -494,13 +501,14 @@ class db_oload_gen: def __init__(self, db): self.db = db - instrs = [i for i in self.db.instr if i.is_dxil_op] - self.instrs = sorted(instrs, key=lambda i: i.dxil_opid) - - # Allow these to be overridden by external scripts. - self.OP = "OP" - self.OC = "OC" - self.OCC = "OCC" + self.instrs = [] + for table in self.db.dxil_op_tables: + self.instrs.extend( + sorted( + [i for i in table.instr if i.is_dxil_op], + key=lambda i: i.dxil_opid + ) + ) def print_content(self): self.print_opfunc_props() @@ -508,10 +516,31 @@ def print_content(self): self.print_opfunc_table() def print_opfunc_props(self): + # Print all the tables for OP::m_OpCodeProps + for table in self.db.dxil_op_tables: + self.print_opfunc_props_for_table(table) + print() + # Print the overall table of tables + print("// Table of DXIL OpCode Property tables") print( - "const {OP}::OpCodeProperty {OP}::m_OpCodeProps[(unsigned){OP}::OpCode::NumOpCodes] = {{".format( - OP=self.OP + f"OP::OpCodeTable OP::g_OpCodeTables[(unsigned)" + + f"OP::OpCodeTableID::NumOpCodeTables] = {{" + ) + for table in self.db.dxil_op_tables: + print( + f" {{ OP::OpCodeTableID::{table.name}, " + + f"{table.name}_OpCodeProps, " + + f"(unsigned)DXIL::{table.name}::OpCode::NumOpCodes }}," ) + print("};") + + def print_opfunc_props_for_table(self, table): + print( + f"static const OP::OpCodeProperty {table.name}_OpCodeProps[] = {{" + ) + instrs = sorted( + [i for i in table.instr if i.is_dxil_op], + key=lambda i: i.dxil_opid ) last_category = None @@ -539,7 +568,7 @@ def print_opfunc_props(self): oloads_fn = lambda oloads: ( "{" + ",".join(["{0x%x}" % m for m in oloads]) + "}" ) - for i in self.instrs: + for i in instrs: if last_category != i.category: if last_category != None: print("") @@ -559,7 +588,7 @@ def print_opfunc_props(self): vector_masks.append(0) print( ( - " {{ {OC}::{name:24} {quotName:27} {OCC}::{className:25} " + " {{ OC::{name:24} {quotName:27} OCC::{className:25} " + "{classNameQuot:28} {attr:20}, {num_oloads}, " + "{scalar_masks:16}, {vector_masks:16} }}, " + "// Overloads: {oloads}" @@ -573,11 +602,14 @@ def print_opfunc_props(self): scalar_masks=oloads_fn(scalar_masks), vector_masks=oloads_fn(vector_masks), oloads=i.oload_types, - OC=self.OC, - OCC=self.OCC, ) ) print("};") + print( + f"static_assert(_countof({table.name}_OpCodeProps) == " + + f"(size_t)DXIL::{table.name}::OpCode::NumOpCodes, " + + f'"mismatch in opcode count for {table.name} OpCodeProps");' + ) def print_opfunc_table(self): # Print the table for OP::GetOpFunc @@ -1644,6 +1676,13 @@ def get_interpretation_table(): return run_with_stdout(lambda: gen.print_interpretation_table()) +def get_extended_table_opcode_enum_decls(): + db = get_db_dxil() + gen = db_enumhelp_gen(db) + return run_with_stdout( + lambda: gen.print_extended_table_opcode_enums() + ) + # highest minor is different than highest released minor, # since there can be pre-release versions that are higher # than the last released version From dd001f811557cbf5149e1d5b761082f8fc6dc166 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Mon, 17 Nov 2025 18:44:30 -0800 Subject: [PATCH 02/17] hctdb.py: Core->CoreOps, ExperimentalCommon->ExperimentalOps --- include/dxc/DXIL/DxilConstants.h | 24 ++++++++++++------------ lib/DXIL/DxilOperations.cpp | 26 +++++++++++++------------- utils/hct/hctdb.py | 30 +++++++++++++++--------------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index 8b0a01dd2f..267a0bf752 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -497,8 +497,8 @@ inline bool IsFeedbackTexture(DXIL::ResourceKind ResourceKind) { // OPCODETABLE-ENUM:BEGIN // Enumeration for DXIL opcode tables enum class OpCodeTableID : unsigned { - Core = 0, // Core DXIL operations - ExperimentalCommon = 1, // Common Experimental DXIL operations + CoreOps = 0, // Core DXIL operations + ExperimentalOps = 1, // Experimental DXIL operations NumOpCodeTables = 2, // exclusive last value of enumeration }; @@ -509,15 +509,15 @@ enum class OpCodeTableID : unsigned { /* hctdb_instrhelp.get_extended_table_opcode_enum_decls()*/ // clang-format on // EXTOPCODES-ENUM:BEGIN -namespace ExperimentalCommon { -static const OpCodeTableID TableID = OpCodeTableID::ExperimentalCommon; -// Enumeration for ExperimentalCommon DXIL operations +namespace ExperimentalOps { +static const OpCodeTableID TableID = OpCodeTableID::ExperimentalOps; +// Enumeration for ExperimentalOps DXIL operations enum class OpCode : unsigned { ExperimentalNop = 0, // nop does nothing NumOpCodes = 1, // exclusive last value of enumeration }; -} // namespace ExperimentalCommon +} // namespace ExperimentalOps // EXTOPCODES-ENUM:END #define EXP_OPCODE(feature, opcode) \ @@ -527,7 +527,7 @@ enum class OpCode : unsigned { // TODO: change opcodes. /* hctdb_instrhelp.get_enum_decl("OpCode")*/ // OPCODE-ENUM:BEGIN -// Enumeration for Core DXIL operations +// Enumeration for CoreOps DXIL operations enum class OpCode : unsigned { // Reserved0 = 226, // reserved @@ -1129,18 +1129,18 @@ enum class OpCode : unsigned { // OpCodes for extended tables follow. // OpCodeTableID = 1 - // ExperimentalCommon - EXP_OPCODE(ExperimentalCommon, ExperimentalNop), // nop does nothing + // ExperimentalOps + EXP_OPCODE(ExperimentalOps, ExperimentalNop), // nop does nothing }; // OPCODE-ENUM:END #undef EXP_OPCODE // Create Core namespace for consistency with other opcode groups -namespace Core { -static const OpCodeTableID TableID = OpCodeTableID::Core; +namespace CoreOps { +static const OpCodeTableID TableID = OpCodeTableID::CoreOps; using OpCode = hlsl::DXIL::OpCode; -} // namespace Core +} // namespace CoreOps // clang-format off // Python lines need to be not formatted. diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index 9864368ba3..c7170a4a30 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -39,7 +39,7 @@ import hctdb_instrhelp */ /* hctdb_instrhelp.get_oloads_props()*/ // OPCODE-OLOADS:BEGIN -static const OP::OpCodeProperty Core_OpCodeProps[] = { +static const OP::OpCodeProperty CoreOps_OpCodeProps[] = { // Temporary, indexable, input, output registers {OC::TempRegLoad, "TempRegLoad", @@ -2715,10 +2715,10 @@ static const OP::OpCodeProperty Core_OpCodeProps[] = { {{0x400}}, {{0x3}}}, // Overloads: Date: Thu, 20 Nov 2025 17:27:17 -0800 Subject: [PATCH 03/17] Support arbitrary opcode table IDs, set experimental ID 0x8000 --- include/dxc/DXIL/DxilConstants.h | 6 ++-- include/dxc/DXIL/DxilOperations.h | 8 +++-- lib/DXIL/DxilOperations.cpp | 58 +++++++++++++++++++++---------- utils/hct/hctdb.py | 2 +- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index 267a0bf752..faae653256 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -497,8 +497,8 @@ inline bool IsFeedbackTexture(DXIL::ResourceKind ResourceKind) { // OPCODETABLE-ENUM:BEGIN // Enumeration for DXIL opcode tables enum class OpCodeTableID : unsigned { - CoreOps = 0, // Core DXIL operations - ExperimentalOps = 1, // Experimental DXIL operations + CoreOps = 0, // Core DXIL operations + ExperimentalOps = 32768, // Experimental DXIL operations NumOpCodeTables = 2, // exclusive last value of enumeration }; @@ -1128,7 +1128,7 @@ enum class OpCode : unsigned { // OpCodes for extended tables follow. - // OpCodeTableID = 1 + // OpCodeTableID = 32768 // ExperimentalOps EXP_OPCODE(ExperimentalOps, ExperimentalNop), // nop does nothing diff --git a/include/dxc/DXIL/DxilOperations.h b/include/dxc/DXIL/DxilOperations.h index adf0aa0537..6bc672699b 100644 --- a/include/dxc/DXIL/DxilOperations.h +++ b/include/dxc/DXIL/DxilOperations.h @@ -266,11 +266,13 @@ class OP { // Return true if valid. // unsigned versions are for use with whatever value was in a DXIL Op // instruction. - // LocalOpCode is the low 16-bits OpCode for local table lookup. + // OpIndex is the low 16-bits, for index lookup within the table. static bool DecodeOpCode(unsigned EncodedOpCode, OpCodeTableID &TableID, - unsigned &LocalOpCode); + unsigned &OpIndex, + unsigned *OptTableIndex = nullptr); static bool DecodeOpCode(OpCode EncodedOpCode, OpCodeTableID &TableID, - unsigned &LocalOpCode); + unsigned &OpIndex, + unsigned *OptTableIndex = nullptr); static bool IsValidOpCode(unsigned EncodedOpCode); static bool IsValidOpCode(OpCode EncodedOpCode); diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index c7170a4a30..3aa766cf39 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2757,31 +2757,47 @@ static const char *AtomicBinOpCodeName[] = { "AtomicInvalid" // Must be last. }; +static unsigned GetOpCodeTableIndex(OP::OpCodeTableID TableID) { + static_assert((unsigned)OP::OpCodeTableID::NumOpCodeTables == 2, + "Otherwise, update GetOpCodeTableIndex to be generated."); + switch (TableID) { + case OP::OpCodeTableID::CoreOps: + return 0; + case OP::OpCodeTableID::ExperimentalOps: + return 1; + default: + return UINT_MAX; + } +} + // Safe opcode decoder bool OP::DecodeOpCode(unsigned EncodedOpCode, OP::OpCodeTableID &TableID, - unsigned &LocalOpCode) { + unsigned &OpIndex, unsigned *OptTableIndex) { if (EncodedOpCode == (unsigned)OP::OpCode::Invalid) return false; - unsigned TID = (EncodedOpCode >> 16); - if (TID >= (unsigned)OP::OpCodeTableID::NumOpCodeTables) + OP::OpCodeTableID TID = (OP::OpCodeTableID)(EncodedOpCode >> 16); + unsigned TableIndex = GetOpCodeTableIndex(TID); + if (TableIndex >= (unsigned)OP::OpCodeTableID::NumOpCodeTables) return false; unsigned Op = (EncodedOpCode & 0xFFFF); - if (Op >= OP::g_OpCodeTables[TID].Count) + if (Op >= OP::g_OpCodeTables[TableIndex].Count) return false; TableID = (OP::OpCodeTableID)TID; - LocalOpCode = Op; + OpIndex = Op; + if (OptTableIndex) + *OptTableIndex = TableIndex; return true; } bool OP::DecodeOpCode(OpCode EncodedOpCode, OP::OpCodeTableID &TableID, - unsigned &LocalOpCode) { - return DecodeOpCode((unsigned)EncodedOpCode, TableID, LocalOpCode); + unsigned &OpIndex, unsigned *OptTableIndex) { + return DecodeOpCode((unsigned)EncodedOpCode, TableID, OpIndex, OptTableIndex); } bool OP::IsValidOpCode(unsigned EncodedOpCode) { if (EncodedOpCode == (unsigned)OP::OpCode::Invalid) return false; OP::OpCodeTableID TID; - unsigned LocalOpCode; - return DecodeOpCode(EncodedOpCode, TID, LocalOpCode); + unsigned OpIndex; + return DecodeOpCode(EncodedOpCode, TID, OpIndex); } bool OP::IsValidOpCode(OP::OpCode EncodedOpCode) { return IsValidOpCode((unsigned)EncodedOpCode); @@ -2789,9 +2805,10 @@ bool OP::IsValidOpCode(OP::OpCode EncodedOpCode) { const OP::OpCodeProperty &OP::GetOpCodeProps(unsigned OriginalOpCode) { OP::OpCodeTableID TID = OP::OpCodeTableID::CoreOps; unsigned Op = 0; - bool Success = DecodeOpCode(OriginalOpCode, TID, Op); + unsigned TableIndex = 0; + bool Success = DecodeOpCode(OriginalOpCode, TID, Op, &TableIndex); DXASSERT(Success, "otherwise invalid OpCode"); - const OP::OpCodeTable &Table = OP::g_OpCodeTables[(unsigned)TID]; + const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; return Table.Table[Op]; } const OP::OpCodeProperty &OP::GetOpCodeProps(OP::OpCode OriginalOpCode) { @@ -2970,16 +2987,21 @@ bool OP::IsOverloadLegal(OpCode opCode, Type *pType) { } bool OP::CheckOpCodeTable() { - for (unsigned t = 0; t < (unsigned)OP::OpCodeTableID::NumOpCodeTables; t++) { - const OP::OpCodeTable &Table = OP::g_OpCodeTables[t]; - for (unsigned Op = 0; Op < Table.Count; Op++) { - const OP::OpCodeProperty &Prop = Table.Table[Op]; + for (unsigned TableIndex = 0; + TableIndex < (unsigned)OP::OpCodeTableID::NumOpCodeTables; + TableIndex++) { + const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; + for (unsigned OpIndex = 0; OpIndex < Table.Count; OpIndex++) { + const OP::OpCodeProperty &Prop = Table.Table[OpIndex]; OP::OpCodeTableID DecodedTID; - unsigned DecodedLocalOp; - bool Success = OP::DecodeOpCode(Prop.opCode, DecodedTID, DecodedLocalOp); + unsigned DecodedOpIndex; + unsigned DecodedTableIndex; + bool Success = OP::DecodeOpCode(Prop.opCode, DecodedTID, DecodedOpIndex, + &DecodedTableIndex); if (!Success) return false; - if (DecodedTID != Table.ID || DecodedLocalOp != Op) + if (DecodedTID != Table.ID || DecodedOpIndex != OpIndex || + DecodedTableIndex != TableIndex) return false; } } diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 67e38d127f..eb0aa6f465 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -384,7 +384,7 @@ def __init__(self): self.counters = extra_counters self.dxil_op_tables = [ db_dxil_op_table(0, "CoreOps", "Core DXIL operations"), - db_dxil_op_table(1, "ExperimentalOps", "Experimental DXIL operations"), + db_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations"), ] self.dxil_op_tables_by_name = dict([(t.name, t) for t in self.dxil_op_tables]) # Setting the current table sets self.instr. From 7d34cad0e469dad24095e71447f67bbc0fcaa77f Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Mon, 24 Nov 2025 16:03:12 -0800 Subject: [PATCH 04/17] Update validation for experimental ops --- docs/DXIL.rst | 3 ++- include/dxc/DXIL/DxilShaderModel.h | 3 +++ lib/DXIL/DxilShaderModel.cpp | 8 ++++++++ lib/DxilValidation/DxilValidation.cpp | 18 ++++++++++++++++-- utils/hct/hctdb.py | 14 +++++++++++++- 5 files changed, 42 insertions(+), 4 deletions(-) diff --git a/docs/DXIL.rst b/docs/DXIL.rst index 31878959eb..ba1df0036b 100644 --- a/docs/DXIL.rst +++ b/docs/DXIL.rst @@ -3134,10 +3134,11 @@ INSTR.CREATEHANDLEIMMRANGEID Local resource mus INSTR.DXILSTRUCTUSER Dxil struct types should only be used by ExtractValue. INSTR.DXILSTRUCTUSEROUTOFBOUND Index out of bound when extract value from dxil struct types. INSTR.EVALINTERPOLATIONMODE Interpolation mode on %0 used with eval_* instruction must be linear, linear_centroid, linear_noperspective, linear_noperspective_centroid, linear_sample or linear_noperspective_sample. +INSTR.EXPDXILOPCODEREQUIRESEXPSM Use of experimental DXILOpCode requires an experimental shader model. INSTR.EXTRACTVALUE ExtractValue should only be used on dxil struct types and cmpxchg. INSTR.FAILTORESLOVETGSMPOINTER TGSM pointers must originate from an unambiguous TGSM global variable. INSTR.HANDLENOTFROMCREATEHANDLE Resource handle should returned by createHandle. -INSTR.ILLEGALDXILOPCODE DXILOpCode must be [0..%0]. %1 specified. +INSTR.ILLEGALDXILOPCODE DXILOpCode must be valid or a supported experimental opcode. INSTR.ILLEGALDXILOPFUNCTION '%0' is not a DXILOpFuncition for DXILOpcode '%1'. INSTR.IMMBIASFORSAMPLEB bias amount for sample_b must be in the range [%0,%1], but %2 was specified as an immediate. INSTR.INBOUNDSACCESS Access to out-of-bounds memory is disallowed. diff --git a/include/dxc/DXIL/DxilShaderModel.h b/include/dxc/DXIL/DxilShaderModel.h index c61903ea8f..2d10ec63f4 100644 --- a/include/dxc/DXIL/DxilShaderModel.h +++ b/include/dxc/DXIL/DxilShaderModel.h @@ -66,6 +66,9 @@ class ShaderModel { bool IsSMAtLeast(unsigned Major, unsigned Minor) const { return m_Major > Major || (m_Major == Major && m_Minor >= Minor); } + bool IsPreReleaseShaderModel() const { + return IsPreReleaseShaderModel(m_Major, m_Minor); + } bool IsSM50Plus() const { return IsSMAtLeast(5, 0); } bool IsSM51Plus() const { return IsSMAtLeast(5, 1); } bool AllowDerivatives(DXIL::ShaderKind sk) const; diff --git a/lib/DXIL/DxilShaderModel.cpp b/lib/DXIL/DxilShaderModel.cpp index e6f4fdc211..894ee528cb 100644 --- a/lib/DXIL/DxilShaderModel.cpp +++ b/lib/DXIL/DxilShaderModel.cpp @@ -214,6 +214,14 @@ bool ShaderModel::IsPreReleaseShaderModel(int major, int minor) { kHighestReleasedMinor) <= 0) return false; + // FIXME: Why not just return true here? If the version is higher than we + // recognize, we don't want to consider it a released shader model. + // This is used by the validator to check the program part to determine which + // hash to apply (PREVIEW pattern or normal hash). This scenario should not be + // possible, since the program part version will be checked against the shader + // model in the llvm module. Still, it seems very odd to check if the version + // is higher than recognized and return false if so. Returning false isn't a + // failure, it implies that the version is a normal released shader model. // now compare against highest recognized if (DXIL::CompareVersions(major, minor, kHighestMajor, kHighestMinor) <= 0) return true; diff --git a/lib/DxilValidation/DxilValidation.cpp b/lib/DxilValidation/DxilValidation.cpp index 2ea6701581..8c962aca23 100644 --- a/lib/DxilValidation/DxilValidation.cpp +++ b/lib/DxilValidation/DxilValidation.cpp @@ -2520,7 +2520,11 @@ static void ValidateExternalFunction(Function *F, ValidationContext &ValCtx) { } unsigned Opcode = ConstOpcode->getLimitedValue(); - if (Opcode >= (unsigned)DXIL::OpCode::NumOpCodes) { + OP::OpCodeTableID TableID; + unsigned OpIndex; + if (!OP::DecodeOpCode(Opcode, TableID, OpIndex) || + (TableID != OP::OpCodeTableID::CoreOps && + !pSM->IsPreReleaseShaderModel())) { // invalid Opcode; function body will validate this error. continue; } @@ -3205,6 +3209,8 @@ static void ValidateFunctionBody(Function *F, ValidationContext &ValCtx) { ValCtx.DxilMod.GetGlobalFlags() & DXIL::kEnableMinPrecision; bool SupportsLifetimeIntrinsics = ValCtx.DxilMod.GetShaderModel()->IsSM66Plus(); + bool ExperimentalShaderModel = + ValCtx.DxilMod.GetShaderModel()->IsPreReleaseShaderModel(); SmallVector GradientOps; SmallVector Barriers; CallInst *SetMeshOutputCounts = nullptr; @@ -3262,13 +3268,21 @@ static void ValidateFunctionBody(Function *F, ValidationContext &ValCtx) { } unsigned Opcode = OpcodeConst->getLimitedValue(); - if (Opcode >= static_cast(DXIL::OpCode::NumOpCodes)) { + OP::OpCodeTableID TableID; + unsigned OpIndex; + if (!OP::DecodeOpCode(Opcode, TableID, OpIndex)) { ValCtx.EmitInstrFormatError( &I, ValidationRule::InstrIllegalDXILOpCode, {std::to_string((unsigned)DXIL::OpCode::NumOpCodes), std::to_string(Opcode)}); continue; } + if (TableID != OP::OpCodeTableID::CoreOps && + !ExperimentalShaderModel) { + ValCtx.EmitInstrError( + &I, ValidationRule::InstrExpDXILOpCodeRequiresExpSM); + continue; + } DXIL::OpCode DxilOpcode = (DXIL::OpCode)Opcode; bool IllegalOpFunc = true; diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index eb0aa6f465..a4d00db404 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -962,6 +962,9 @@ def populate_categories_and_models(self): self.populate_categories_and_models_ExperimentalOps() def populate_categories_and_models_ExperimentalOps(self): + # Note: Experimental ops must be set to a shader model higher than the + # most recent release until infrastructure is in place to opt-in to + # experimental ops and the validator can force use of the PREVIEW hash. for i in "ExperimentalNop".split(","): self.name_idx[i].category = "No-op" self.name_idx[i].shader_model = 6, 10 @@ -7656,8 +7659,17 @@ def build_valrules(self): "Instr.ImmBiasForSampleB", "bias amount for sample_b must be in the range [%0,%1], but %2 was specified as an immediate.", ) + self.add_valrule_msg( + "Instr.IllegalDXILOpCode", + "DXILOpCode must be valid or a supported experimental opcode.", + "DXILOpCode must be [0..%0] or a supported experimental opcode. %1 specified.", + ) + # In the future, if experimental opcodes are allowed in non-experimental + # shader models, the following rule will need to change to one requiring + # a flag to support experimental opcodes. self.add_valrule( - "Instr.IllegalDXILOpCode", "DXILOpCode must be [0..%0]. %1 specified." + "Instr.ExpDXILOpCodeRequiresExpSM", + "Use of experimental DXILOpCode requires an experimental shader model.", ) self.add_valrule( "Instr.IllegalDXILOpFunction", From ac36ac7d86f102a29162ed803aa00957f59b06e5 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Mon, 24 Nov 2025 16:25:40 -0800 Subject: [PATCH 05/17] Rework db.inst to contain all opcodes, and dxil_opid to include table id And fix disassembler. --- docs/DXIL.rst | 14 +++++ include/dxc/DXIL/DxilInstructions.h | 20 +++++++ lib/DXIL/DxilOperations.cpp | 4 +- .../tools/dxcompiler/dxcdisassembler.cpp | 7 ++- utils/hct/hctdb.py | 54 +++++++++++++------ utils/hct/hctdb_instrhelp.py | 37 +++++++++---- 6 files changed, 104 insertions(+), 32 deletions(-) diff --git a/docs/DXIL.rst b/docs/DXIL.rst index ba1df0036b..96f4f11288 100644 --- a/docs/DXIL.rst +++ b/docs/DXIL.rst @@ -2111,6 +2111,8 @@ Opcodes are defined on a dense range and will be provided as enum in a header fi .. hctdb_instrhelp.get_opcodes_rst() .. OPCODES-RST:BEGIN +Opcode Table CoreOps, id=0: Core DXIL operations + === ===================================================== ======================================================================================================================================================================================================================= ID Name Description === ===================================================== ======================================================================================================================================================================================================================= @@ -3055,6 +3057,18 @@ Given width, offset: ushr dest, src2, offset } + + + +Opcode Table ExperimentalOps, id=32768: Experimental DXIL operations + +========== =============== ================ +ID Name Description +========== =============== ================ +2147483648 ExperimentalNop nop does nothing +========== =============== ================ + + .. OPCODES-RST:END diff --git a/include/dxc/DXIL/DxilInstructions.h b/include/dxc/DXIL/DxilInstructions.h index 726d9615ac..52a3efaaac 100644 --- a/include/dxc/DXIL/DxilInstructions.h +++ b/include/dxc/DXIL/DxilInstructions.h @@ -10231,5 +10231,25 @@ struct DxilInst_FDot { llvm::Value *get_b() const { return Instr->getOperand(2); } void set_b(llvm::Value *val) { Instr->setOperand(2, val); } }; + +/// This instruction nop does nothing +struct DxilInst_ExperimentalNop { + llvm::Instruction *Instr; + // Construction and identification + DxilInst_ExperimentalNop(llvm::Instruction *pInstr) : Instr(pInstr) {} + operator bool() const { + return hlsl::OP::IsDxilOpFuncCallInst(Instr, + hlsl::OP::OpCode::ExperimentalNop); + } + // Validation support + bool isAllowed() const { return true; } + bool isArgumentListValid() const { + if (1 != llvm::dyn_cast(Instr)->getNumArgOperands()) + return false; + return true; + } + // Metadata + bool requiresUniformInputs() const { return false; } +}; // INSTR-HELPER:END } // namespace hlsl diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index 3aa766cf39..e14b25396e 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -3638,8 +3638,8 @@ void OP::GetMinShaderModelAndMask(OpCode C, bool bWithTranslation, return; } // Instructions: MatVecMul=305, MatVecMulAdd=306, OuterProductAccumulate=307, - // VectorAccumulate=308 - if ((305 <= op && op <= 308)) { + // VectorAccumulate=308, ExperimentalNop=2147483648 + if ((305 <= op && op <= 308) || op == 2147483648) { major = 6; minor = 10; return; diff --git a/tools/clang/tools/dxcompiler/dxcdisassembler.cpp b/tools/clang/tools/dxcompiler/dxcdisassembler.cpp index 16d8b1dadd..1ad8474e50 100644 --- a/tools/clang/tools/dxcompiler/dxcdisassembler.cpp +++ b/tools/clang/tools/dxcompiler/dxcdisassembler.cpp @@ -1312,7 +1312,10 @@ class DxcAssemblyAnnotationWriter : public llvm::AssemblyAnnotationWriter { } unsigned opcodeVal = CInt->getZExtValue(); - if (opcodeVal >= (unsigned)DXIL::OpCode::NumOpCodes) { + OP::OpCodeTableID TableID; + unsigned OpIndex; + unsigned TableIndex; + if (!hlsl::OP::DecodeOpCode(opcodeVal, TableID, OpIndex, &TableIndex)) { OS << " ; invalid DXIL opcode #" << opcodeVal; return; } @@ -1321,7 +1324,7 @@ class DxcAssemblyAnnotationWriter : public llvm::AssemblyAnnotationWriter { // name/binding DXIL::OpCode opcode = (DXIL::OpCode)opcodeVal; OS << " ; " << hlsl::OP::GetOpCodeName(opcode) - << OpCodeSignatures[opcodeVal]; + << OpCodeSignatures[TableIndex][OpIndex]; // Add extra decoding for certain ops switch (opcode) { diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index a4d00db404..e2dcca3914 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -136,6 +136,14 @@ def __init__(self, name, **kwargs): def __str__(self): return self.name + def dxil_op_index(self): + "Get the index of this DXIL op in its table." + return self.dxil_opid & 0xFFFF + + def table_id(self): + "Get the table ID of this DXIL op." + return (self.dxil_opid >> 16) & 0xFFFF + def fully_qualified_name(self): return "{}::{}".format(self.fully_qualified_name_prefix, self.name) @@ -345,6 +353,7 @@ class db_dxil_op_table(object): "Table definition for a set of DXIL operations" def __init__(self, id, name, doc): + assert id & ~0xFFFF == 0, "DXIL op table ID must fit in high 16 bits" self.id = id self.name = name self.doc = doc @@ -359,7 +368,7 @@ def __init__(self, id, name, doc): def next_id(self): new_id = self.op_count self.op_count += 1 - return new_id + return ((self.id << 16) | new_id) def get_count(self): return self.op_count @@ -373,6 +382,7 @@ class db_dxil(object): "A database of DXIL instruction data" def __init__(self): + self.instr = [] # all LLVM instructions and DXIL operations self.enums = [] # enumeration types self.val_rules = [] # validation rules self.metadata = [] # named metadata (db_dxil_metadata) @@ -387,7 +397,7 @@ def __init__(self): db_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations"), ] self.dxil_op_tables_by_name = dict([(t.name, t) for t in self.dxil_op_tables]) - # Setting the current table sets self.instr. + # Set cur_table. self.set_dxil_op_table() self.populate_llvm_instructions() @@ -410,7 +420,6 @@ def __init__(self): def set_dxil_op_table(self, table_name = "CoreOps"): "Set the current DXIL operation table, defaulting to CoreOps." self.cur_table = self.dxil_op_tables_by_name[table_name] - self.instr = self.cur_table.instr def get_dxil_op_table(self, table_name = "CoreOps"): "Get the specified DXIL operation table." @@ -426,9 +435,8 @@ def add_enum_type(self, name, doc, valNameDocTuples): def build_indices(self): "Build a name_idx dictionary with instructions and an enum_idx dictionary with enumeration types" self.name_idx = {} - for table in self.dxil_op_tables: - for i in table.instr: - self.name_idx[i.name] = i + for i in self.instr: + self.name_idx[i.name] = i self.enum_idx = {} for i in self.enums: self.enum_idx[i.name] = i @@ -438,7 +446,7 @@ def build_opcode_enum(self): OpCodeEnum = self.get_dxil_op_table().op_enum class_dict = {} class_dict["LlvmInst"] = "LLVM Instructions" - for i in self.instr: + for i in self.get_dxil_op_table().instr: if i.is_dxil_op: v = db_dxil_enum_value(i.dxil_op, i.dxil_opid, i.doc) v.category = i.category @@ -469,7 +477,7 @@ def build_opcode_enum(self): f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}" ) table.op_enum.values.append( - db_dxil_enum_value(i.dxil_op, i.dxil_opid, i.doc) + db_dxil_enum_value(i.dxil_op, i.dxil_op_index(), i.doc) ) postfix.append("") OpCodeEnum.postfix_lines = postfix @@ -6008,11 +6016,18 @@ def finalize_dxil_operations(self): # TODO - some arguments are required to be immediate constants in DXIL, eg resource kinds; add this information # consider - report instructions that are overloaded on a single type, then turn them into non-overloaded version of that type - self.verify_dense( - self.get_dxil_insts(), lambda x: x.dxil_opid, lambda x: x.name - ) - for i in self.instr: - self.verify_dense(i.ops, lambda x: x.pos, lambda x: i.name) + for table in self.dxil_op_tables: + insts = [i for i in table.instr if i.is_dxil_op] + self.verify_dense( + insts, lambda x: x.dxil_opid, lambda x: x.name + ) + for i in insts: + assert i.table_id() == table.id, ( + "dxil op %s has table id %d inconsistent with containing table id %d" % ( + i.name, i.table_id(), table.id + ) + ) + self.verify_dense(i.ops, lambda x: x.pos, lambda x: i.name) # Verify that all operations in each class have the same signature. import itertools @@ -8610,7 +8625,7 @@ def populate_counters(self): self.dxil_op_counters = set() for i in self.instr: counters = getattr(i, "props", {}).get("counters", ()) - if i.dxil_opid: + if i.is_dxil_op: self.dxil_op_counters.update(counters) else: self.llvm_op_counters.update(counters) @@ -8629,6 +8644,11 @@ def add_valrule_msg(self, name, desc, err_msg): db_dxil_valrule(name, len(self.val_rules), err_msg=err_msg, doc=desc) ) + def add_inst(self, i): + assert i.table_id() == self.cur_table.id, "Instruction table mismatch" + self.cur_table.instr.append(i) + self.instr.append(i) + def add_llvm_instr( self, kind, llvm_id, name, llvm_name, doc, oload_types, op_params, **props ): @@ -8642,7 +8662,7 @@ def add_llvm_instr( oload_types=oload_types, ) i.props = props - self.instr.append(i) + self.add_inst(i) def add_dxil_op( self, name, code_class, doc, oload_types, fn_attr, op_params, **props @@ -8663,7 +8683,7 @@ def add_dxil_op( fn_attr=fn_attr, ) i.props = props - self.instr.append(i) + self.add_inst(i) def add_dxil_op_reserved(self, name): # The return value is parameter 0, insert the opcode as 1. @@ -8681,7 +8701,7 @@ def add_dxil_op_reserved(self, name): oload_types="v", fn_attr="", ) - self.instr.append(i) + self.add_inst(i) def reserve_dxil_op_range(self, group_name, count, start_reserved_id=0): "Reserve a range of dxil opcodes for future use; returns next id" diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 7c93ec7e8f..f3c459d936 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -501,14 +501,10 @@ class db_oload_gen: def __init__(self, db): self.db = db - self.instrs = [] - for table in self.db.dxil_op_tables: - self.instrs.extend( - sorted( - [i for i in table.instr if i.is_dxil_op], - key=lambda i: i.dxil_opid - ) - ) + self.instrs = sorted( + [i for i in db.instr if i.is_dxil_op], + key=lambda i: i.dxil_opid + ) def print_content(self): self.print_opfunc_props() @@ -1410,9 +1406,17 @@ def get_is_pass_option_name(): def get_opcodes_rst(): - "Create an rst table of opcodes" + "Create an rst table for each opcode table" db = get_db_dxil() - instrs = [i for i in db.instr if i.is_allowed and i.is_dxil_op] + result = "" + for table in db.dxil_op_tables: + result += f"\n\nOpcode Table {table.name}, id={table.id}: {table.doc}" + result += get_opcodes_rst_for_table(table) + return result + +def get_opcodes_rst_for_table(table): + "Create an rst table of opcodes for given opcode table" + instrs = [i for i in table.instr if i.is_allowed and i.is_dxil_op] instrs = sorted(instrs, key=lambda v: v.dxil_opid) rows = [] rows.append(["ID", "Name", "Description"]) @@ -1445,13 +1449,24 @@ def get_valrules_rst(): def get_opsigs(): + db = get_db_dxil() + result = "" + for table in db.dxil_op_tables: + result += f"\n\n// Opcode Signatures for Table {table.name}, id={table.id}\n" + result += get_opsigs_for_table(table) + result += "static const char **OpCodeSignatures[] = {\n" + for table in db.dxil_op_tables: + result += " OpCodeSignatures_%s,\n" % table.name + result += "};\n" + return result +def get_opsigs_for_table(table): # Create a list of DXIL operation signatures, sorted by ID. db = get_db_dxil() instrs = [i for i in db.instr if i.is_dxil_op] instrs = sorted(instrs, key=lambda v: v.dxil_opid) # db_dxil already asserts that the numbering is dense. # Create the code to write out. - code = "static const char *OpCodeSignatures[] = {\n" + code = f"static const char *OpCodeSignatures_{table.name}[] = {{\n" for inst_idx, i in enumerate(instrs): code += ' "(' for operand in i.ops: From bebc04a68d8c7ca6bb661b1fbbdfa4720c8e230b Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Mon, 24 Nov 2025 17:43:49 -0800 Subject: [PATCH 06/17] Add tests --- .../test/DXC/experimental-dxil-6-10-op.ll | 44 +++++++++++++++++++ ...nvalid-experimental-dxil-6-10-op-on-6-8.ll | 35 +++++++++++++++ ...nvalid-experimental-dxil-6-10-op-on-6-9.ll | 35 +++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 tools/clang/test/DXC/experimental-dxil-6-10-op.ll create mode 100644 tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-8.ll create mode 100644 tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-9.ll diff --git a/tools/clang/test/DXC/experimental-dxil-6-10-op.ll b/tools/clang/test/DXC/experimental-dxil-6-10-op.ll new file mode 100644 index 0000000000..4868648d2c --- /dev/null +++ b/tools/clang/test/DXC/experimental-dxil-6-10-op.ll @@ -0,0 +1,44 @@ +; REQUIRES: dxil-1-10 +; RUN: %dxa %s -o %t.dxil | FileCheck %s -check-prefix=DXA +; RUN: %dxc -dumpbin %t.dxil | FileCheck %s -check-prefix=DXIL +; RUN: %dxv %t.dxil -o %t.hash.dxil 2>&1 | FileCheck %s -check-prefix=VAL +; RUN: %dxa %t.hash.dxil -dumphash | FileCheck %s -check-prefix=HASH + +; DXA: Assembly succeeded. + +; DXIL: call void @dx.op.nop(i32 -2147483648) ; ExperimentalNop(index) +; DXIL: declare void @dx.op.nop(i32) #[[ATTR:[0-9]+]] +; DXIL: attributes #[[ATTR]] = { nounwind readnone } + +; VAL: Validation succeeded. + +; Make sure it's the PREVIEW hash. +; HASH: Validation hash: 0x02020202020202020202020202020202 + +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +define void @main() { + call void @dx.op.nop(i32 -2147483648) + ret void +} + +; Function Attrs: nounwind readnone +declare void @dx.op.nop(i32) #0 + +attributes #0 = { nounwind readnone } + +!llvm.ident = !{!0} +!dx.version = !{!1} +!dx.valver = !{!1} +!dx.shaderModel = !{!2} +!dx.resources = !{!3} +!dx.entryPoints = !{!4} + +!0 = !{!"custom IR"} +!1 = !{i32 1, i32 10} +!2 = !{!"cs", i32 6, i32 10} +!3 = !{null, null, null, null} +!4 = !{void ()* @main, !"main", null, !3, !5} +!5 = !{i32 0, i64 0, i32 4, !6} +!6 = !{i32 4, i32 1, i32 1} diff --git a/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-8.ll b/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-8.ll new file mode 100644 index 0000000000..438cb03c2c --- /dev/null +++ b/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-8.ll @@ -0,0 +1,35 @@ +; REQUIRES: dxil-1-10 +; RUN: not %dxv %s 2>&1 | FileCheck %s +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +; CHECK: Function: main: error: Use of experimental DXILOpCode requires an experimental shader model. +; CHECK-NEXT: note: at 'call void @dx.op.nop(i32 -2147483648)' in block '#0' of function 'main'. +; CHECK-NEXT: Function: main: error: Entry function performs some operation that is incompatible with the shader stage or other entry properties. See other errors for details. +; CHECK-NEXT: Function: main: error: Function uses features incompatible with the shader model. +; CHECK-NEXT: Validation failed. + +define void @main() { + call void @dx.op.nop(i32 -2147483648) + ret void +} + +; Function Attrs: nounwind readnone +declare void @dx.op.nop(i32) #0 + +attributes #0 = { nounwind readnone } + +!llvm.ident = !{!0} +!dx.version = !{!1} +!dx.valver = !{!1} +!dx.shaderModel = !{!2} +!dx.resources = !{!3} +!dx.entryPoints = !{!6} + +!0 = !{!"custom IR"} +!1 = !{i32 1, i32 8} +!2 = !{!"cs", i32 6, i32 8} +!3 = !{null, null, null, null} +!6 = !{void ()* @main, !"main", null, !3, !7} +!7 = !{i32 0, i64 0, i32 4, !8} +!8 = !{i32 4, i32 1, i32 1} diff --git a/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-9.ll b/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-9.ll new file mode 100644 index 0000000000..93434cc584 --- /dev/null +++ b/tools/clang/test/LitDXILValidation/invalid-experimental-dxil-6-10-op-on-6-9.ll @@ -0,0 +1,35 @@ +; REQUIRES: dxil-1-10 +; RUN: not %dxv %s 2>&1 | FileCheck %s +target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +; CHECK: Function: main: error: Opcode ExperimentalNop not valid in shader model cs_6_9. +; CHECK-NEXT: note: at 'call void @dx.op.nop(i32 -2147483648)' in block '#0' of function 'main'. +; CHECK-NEXT: Function: main: error: Entry function performs some operation that is incompatible with the shader stage or other entry properties. See other errors for details. +; CHECK-NEXT: Function: main: error: Function uses features incompatible with the shader model. +; CHECK-NEXT: Validation failed. + +define void @main() { + call void @dx.op.nop(i32 -2147483648) + ret void +} + +; Function Attrs: nounwind readnone +declare void @dx.op.nop(i32) #0 + +attributes #0 = { nounwind readnone } + +!llvm.ident = !{!0} +!dx.version = !{!1} +!dx.valver = !{!1} +!dx.shaderModel = !{!2} +!dx.resources = !{!3} +!dx.entryPoints = !{!6} + +!0 = !{!"custom IR"} +!1 = !{i32 1, i32 9} +!2 = !{!"cs", i32 6, i32 9} +!3 = !{null, null, null, null} +!6 = !{void ()* @main, !"main", null, !3, !7} +!7 = !{i32 0, i64 0, i32 4, !8} +!8 = !{i32 4, i32 1, i32 1} From 97c6ff83948ab958a9bccb38e375d5dc8fdb4afc Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Mon, 24 Nov 2025 18:03:15 -0800 Subject: [PATCH 07/17] Fix test for change in validation error wording and invalid opcode table --- tools/clang/test/LitDXILValidation/illegalDXILOp.ll | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/clang/test/LitDXILValidation/illegalDXILOp.ll b/tools/clang/test/LitDXILValidation/illegalDXILOp.ll index 1ff27428ef..3997a1cecc 100644 --- a/tools/clang/test/LitDXILValidation/illegalDXILOp.ll +++ b/tools/clang/test/LitDXILValidation/illegalDXILOp.ll @@ -39,10 +39,16 @@ define void @main() { %6 = call %dx.types.Handle @dx.op.annotateHandle2(i32 216, %dx.types.Handle %2, %dx.types.ResourceProperties { i32 32782, i32 0 }) ; AnnotateHandle(res,props) resource: SamplerComparisonState -; CHECK: error: DXILOpCode must be [0..{{[0-9]+}}]. 1999981 specified. -; CHECK: note: at '%7 = call float @dx.op.calculateLOD.f32(i32 1999981, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true)' in block '#0' of function 'main'. +; CHECK: error: DXILOpCode must be [0..{{[0-9]+}}] or a supported experimental opcode. 12345 specified. +; CHECK: note: at '%7 = call float @dx.op.calculateLOD.f32(i32 12345, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true)' in block '#0' of function 'main'. - %7 = call float @dx.op.calculateLOD.f32(i32 1999981, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true) ; CalculateLOD(handle,sampler,coord0,coord1,coord2,clamped) + %7 = call float @dx.op.calculateLOD.f32(i32 12345, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true) ; CalculateLOD(handle,sampler,coord0,coord1,coord2,clamped) + +; Try Opcode with invalid table ID (1) +; CHECK: error: DXILOpCode must be [0..{{[0-9]+}}] or a supported experimental opcode. 65536 specified. +; CHECK: note: at '%invalid_optable = call float @dx.op.calculateLOD.f32(i32 65536, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true)' in block '#0' of function 'main'. + + %invalid_optable = call float @dx.op.calculateLOD.f32(i32 65536, %dx.types.Handle %5, %dx.types.Handle %6, float %3, float %4, float undef, i1 true) ; CalculateLOD(handle,sampler,coord0,coord1,coord2,clamped) %I = call i32 @dx.op.loadInput.i32(i32 4, i32 0, i32 0, i8 0, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) From 9c2789d2434a2b4fb7a1075f474d6336b38a01c9 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 08:59:15 -0800 Subject: [PATCH 08/17] formatting --- utils/hct/hctdb.py | 36 +++++++++++++++++------------------- utils/hct/hctdb_instrhelp.py | 18 ++++++++---------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index e2dcca3914..98556cad04 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -360,15 +360,14 @@ def __init__(self, id, name, doc): self.instr = [] # DXIL instructions self.op_count = 0 self.op_enum = db_dxil_enum( - "OpCode", - f"Enumeration for {self.name} DXIL operations" - ) + "OpCode", f"Enumeration for {self.name} DXIL operations" + ) self.op_enum.last_value_name = "NumOpCodes" def next_id(self): new_id = self.op_count self.op_count += 1 - return ((self.id << 16) | new_id) + return (self.id << 16) | new_id def get_count(self): return self.op_count @@ -378,6 +377,7 @@ def set_op_count_for_version(self, major, minor): self.op_enum.dxil_version_info[(major, minor)] = op_count return op_count + class db_dxil(object): "A database of DXIL instruction data" @@ -417,11 +417,11 @@ def __init__(self): self.build_indices() self.populate_counters() - def set_dxil_op_table(self, table_name = "CoreOps"): + def set_dxil_op_table(self, table_name="CoreOps"): "Set the current DXIL operation table, defaulting to CoreOps." self.cur_table = self.dxil_op_tables_by_name[table_name] - def get_dxil_op_table(self, table_name = "CoreOps"): + def get_dxil_op_table(self, table_name="CoreOps"): "Get the specified DXIL operation table." return self.dxil_op_tables_by_name[table_name] @@ -457,13 +457,14 @@ def build_opcode_enum(self): postfix.append("Invalid = 0xFFFFFFFF, // stable invalid OpCode value") # Generate extended opcode enums into OpCodeEnum.postfix_lines: OpCodeTableID = db_dxil_enum( - "OpCodeTableID", "Enumeration for DXIL opcode tables", - [(0, "CoreOps", "Core DXIL operations")] + "OpCodeTableID", + "Enumeration for DXIL opcode tables", + [(0, "CoreOps", "Core DXIL operations")], ) OpCodeTableID.last_value_name = "NumOpCodeTables" postfix.append("") postfix.append("// OpCodes for extended tables follow.\n") - for table in self.dxil_op_tables[1:]: # Skip CoreOps table + for table in self.dxil_op_tables[1:]: # Skip CoreOps table OpCodeTableID.values.append( db_dxil_enum_value(table.name, table.id, table.doc) ) @@ -473,9 +474,7 @@ def build_opcode_enum(self): postfix.append(f"// {table.name}") for i in table.instr: class_dict[i.dxil_class] = i.category - postfix.append( - f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}" - ) + postfix.append(f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}") table.op_enum.values.append( db_dxil_enum_value(i.dxil_op, i.dxil_op_index(), i.doc) ) @@ -6018,14 +6017,11 @@ def finalize_dxil_operations(self): # consider - report instructions that are overloaded on a single type, then turn them into non-overloaded version of that type for table in self.dxil_op_tables: insts = [i for i in table.instr if i.is_dxil_op] - self.verify_dense( - insts, lambda x: x.dxil_opid, lambda x: x.name - ) + self.verify_dense(insts, lambda x: x.dxil_opid, lambda x: x.name) for i in insts: assert i.table_id() == table.id, ( - "dxil op %s has table id %d inconsistent with containing table id %d" % ( - i.name, i.table_id(), table.id - ) + "dxil op %s has table id %d inconsistent with containing table id %d" + % (i.name, i.table_id(), table.id) ) self.verify_dense(i.ops, lambda x: x.pos, lambda x: i.name) @@ -8652,7 +8648,9 @@ def add_inst(self, i): def add_llvm_instr( self, kind, llvm_id, name, llvm_name, doc, oload_types, op_params, **props ): - assert self.cur_table.name == "CoreOps", "LLVM instructions can only be added to the CoreOps table" + assert ( + self.cur_table.name == "CoreOps" + ), "LLVM instructions can only be added to the CoreOps table" i = db_dxil_inst( name, llvm_id=llvm_id, diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index f3c459d936..faec3d3808 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -502,8 +502,7 @@ class db_oload_gen: def __init__(self, db): self.db = db self.instrs = sorted( - [i for i in db.instr if i.is_dxil_op], - key=lambda i: i.dxil_opid + [i for i in db.instr if i.is_dxil_op], key=lambda i: i.dxil_opid ) def print_content(self): @@ -531,12 +530,9 @@ def print_opfunc_props(self): print("};") def print_opfunc_props_for_table(self, table): - print( - f"static const OP::OpCodeProperty {table.name}_OpCodeProps[] = {{" - ) + print(f"static const OP::OpCodeProperty {table.name}_OpCodeProps[] = {{") instrs = sorted( - [i for i in table.instr if i.is_dxil_op], - key=lambda i: i.dxil_opid + [i for i in table.instr if i.is_dxil_op], key=lambda i: i.dxil_opid ) last_category = None @@ -1414,6 +1410,7 @@ def get_opcodes_rst(): result += get_opcodes_rst_for_table(table) return result + def get_opcodes_rst_for_table(table): "Create an rst table of opcodes for given opcode table" instrs = [i for i in table.instr if i.is_allowed and i.is_dxil_op] @@ -1459,6 +1456,8 @@ def get_opsigs(): result += " OpCodeSignatures_%s,\n" % table.name result += "};\n" return result + + def get_opsigs_for_table(table): # Create a list of DXIL operation signatures, sorted by ID. db = get_db_dxil() @@ -1694,9 +1693,8 @@ def get_interpretation_table(): def get_extended_table_opcode_enum_decls(): db = get_db_dxil() gen = db_enumhelp_gen(db) - return run_with_stdout( - lambda: gen.print_extended_table_opcode_enums() - ) + return run_with_stdout(lambda: gen.print_extended_table_opcode_enums()) + # highest minor is different than highest released minor, # since there can be pre-release versions that are higher From b8aae2a30d5f4bfc2607a60b7953612a1e250dae Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 09:15:59 -0800 Subject: [PATCH 09/17] Fix build break on Release --- lib/DXIL/DxilOperations.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index e14b25396e..cd449dbb1c 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2808,6 +2808,8 @@ const OP::OpCodeProperty &OP::GetOpCodeProps(unsigned OriginalOpCode) { unsigned TableIndex = 0; bool Success = DecodeOpCode(OriginalOpCode, TID, Op, &TableIndex); DXASSERT(Success, "otherwise invalid OpCode"); + if (!Success) + return *(OP::OpCodeProperty*)nullptr; // For release build safety. const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; return Table.Table[Op]; } From d47be8862e5eb04ac2c9b810fba1861d2627f8ef Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 09:37:14 -0800 Subject: [PATCH 10/17] formatting --- lib/DXIL/DxilOperations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index cd449dbb1c..b7d71ef37d 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2809,7 +2809,7 @@ const OP::OpCodeProperty &OP::GetOpCodeProps(unsigned OriginalOpCode) { bool Success = DecodeOpCode(OriginalOpCode, TID, Op, &TableIndex); DXASSERT(Success, "otherwise invalid OpCode"); if (!Success) - return *(OP::OpCodeProperty*)nullptr; // For release build safety. + return *(OP::OpCodeProperty *)nullptr; // For release build safety. const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; return Table.Table[Op]; } From 3b73899d913456c0d69b4e182429bda295167bd1 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 09:54:33 -0800 Subject: [PATCH 11/17] Switch to DXASSERT_LOCALVAR for unused local. --- lib/DXIL/DxilOperations.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index b7d71ef37d..0a542bfb25 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2807,9 +2807,7 @@ const OP::OpCodeProperty &OP::GetOpCodeProps(unsigned OriginalOpCode) { unsigned Op = 0; unsigned TableIndex = 0; bool Success = DecodeOpCode(OriginalOpCode, TID, Op, &TableIndex); - DXASSERT(Success, "otherwise invalid OpCode"); - if (!Success) - return *(OP::OpCodeProperty *)nullptr; // For release build safety. + DXASSERT_LOCALVAR(Success, Success, "otherwise invalid OpCode"); const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; return Table.Table[Op]; } From e70c6cdab8f80ddbb46416c878b62f714f35aa27 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 13:56:50 -0800 Subject: [PATCH 12/17] hctdb_instrhelp: clean upunnecessary sorting by dxil_opid This table is already required to be sorted this way. --- utils/hct/hctdb.py | 6 ++++++ utils/hct/hctdb_instrhelp.py | 10 ++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 98556cad04..5eeac4038f 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -397,6 +397,12 @@ def __init__(self): db_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations"), ] self.dxil_op_tables_by_name = dict([(t.name, t) for t in self.dxil_op_tables]) + # Table id should be strictly increasing. + last_table_id = None + for t in self.dxil_op_tables: + if last_table_id is not None: + assert t.id > last_table_id, "DXIL op table IDs must be strictly increasing" + last_table_id = t.id # Set cur_table. self.set_dxil_op_table() diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index faec3d3808..e587bec906 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -501,9 +501,7 @@ class db_oload_gen: def __init__(self, db): self.db = db - self.instrs = sorted( - [i for i in db.instr if i.is_dxil_op], key=lambda i: i.dxil_opid - ) + self.instrs = [i for i in self.db.instr if i.is_dxil_op] def print_content(self): self.print_opfunc_props() @@ -531,9 +529,7 @@ def print_opfunc_props(self): def print_opfunc_props_for_table(self, table): print(f"static const OP::OpCodeProperty {table.name}_OpCodeProps[] = {{") - instrs = sorted( - [i for i in table.instr if i.is_dxil_op], key=lambda i: i.dxil_opid - ) + instrs = [i for i in table.instr if i.is_dxil_op] last_category = None lower_exceptions = { @@ -1414,7 +1410,6 @@ def get_opcodes_rst(): def get_opcodes_rst_for_table(table): "Create an rst table of opcodes for given opcode table" instrs = [i for i in table.instr if i.is_allowed and i.is_dxil_op] - instrs = sorted(instrs, key=lambda v: v.dxil_opid) rows = [] rows.append(["ID", "Name", "Description"]) for i in instrs: @@ -1462,7 +1457,6 @@ def get_opsigs_for_table(table): # Create a list of DXIL operation signatures, sorted by ID. db = get_db_dxil() instrs = [i for i in db.instr if i.is_dxil_op] - instrs = sorted(instrs, key=lambda v: v.dxil_opid) # db_dxil already asserts that the numbering is dense. # Create the code to write out. code = f"static const char *OpCodeSignatures_{table.name}[] = {{\n" From c99d4ce70ee5e493ba7f906efe49c0c310382807 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 13:59:23 -0800 Subject: [PATCH 13/17] Remove added FIXME comment in ShaderModel::IsPreReleaseShaderModel --- lib/DXIL/DxilShaderModel.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/DXIL/DxilShaderModel.cpp b/lib/DXIL/DxilShaderModel.cpp index 894ee528cb..e6f4fdc211 100644 --- a/lib/DXIL/DxilShaderModel.cpp +++ b/lib/DXIL/DxilShaderModel.cpp @@ -214,14 +214,6 @@ bool ShaderModel::IsPreReleaseShaderModel(int major, int minor) { kHighestReleasedMinor) <= 0) return false; - // FIXME: Why not just return true here? If the version is higher than we - // recognize, we don't want to consider it a released shader model. - // This is used by the validator to check the program part to determine which - // hash to apply (PREVIEW pattern or normal hash). This scenario should not be - // possible, since the program part version will be checked against the shader - // model in the llvm module. Still, it seems very odd to check if the version - // is higher than recognized and return false if so. Returning false isn't a - // failure, it implies that the version is a normal released shader model. // now compare against highest recognized if (DXIL::CompareVersions(major, minor, kHighestMajor, kHighestMinor) <= 0) return true; From 0c73adedb4f081af38ac28ea409bd5c269b117ed Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 25 Nov 2025 14:21:29 -0800 Subject: [PATCH 14/17] formatting --- utils/hct/hctdb.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 5eeac4038f..2f4c24a877 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -401,7 +401,9 @@ def __init__(self): last_table_id = None for t in self.dxil_op_tables: if last_table_id is not None: - assert t.id > last_table_id, "DXIL op table IDs must be strictly increasing" + assert ( + t.id > last_table_id + ), "DXIL op table IDs must be strictly increasing" last_table_id = t.id # Set cur_table. self.set_dxil_op_table() From 307e94ca51ad2518378c504ab95e2a4dcd59b030 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Tue, 2 Dec 2025 15:08:36 -0800 Subject: [PATCH 15/17] Move NumOpCodeTables definition out of OpCodeTableID --- include/dxc/DXIL/DxilConstants.h | 3 +-- include/dxc/DXIL/DxilOperations.h | 2 +- lib/DXIL/DxilOperations.cpp | 18 ++++++++---------- utils/hct/hctdb.py | 1 - utils/hct/hctdb_instrhelp.py | 4 ++-- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index faae653256..d12dd3def4 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -499,8 +499,6 @@ inline bool IsFeedbackTexture(DXIL::ResourceKind ResourceKind) { enum class OpCodeTableID : unsigned { CoreOps = 0, // Core DXIL operations ExperimentalOps = 32768, // Experimental DXIL operations - - NumOpCodeTables = 2, // exclusive last value of enumeration }; // OPCODETABLE-ENUM:END @@ -518,6 +516,7 @@ enum class OpCode : unsigned { NumOpCodes = 1, // exclusive last value of enumeration }; } // namespace ExperimentalOps +static const unsigned NumOpCodeTables = 2; // EXTOPCODES-ENUM:END #define EXP_OPCODE(feature, opcode) \ diff --git a/include/dxc/DXIL/DxilOperations.h b/include/dxc/DXIL/DxilOperations.h index 6bc672699b..fabf07ee14 100644 --- a/include/dxc/DXIL/DxilOperations.h +++ b/include/dxc/DXIL/DxilOperations.h @@ -278,7 +278,7 @@ class OP { private: // Static properties. - static OpCodeTable g_OpCodeTables[(unsigned)OpCodeTableID::NumOpCodeTables]; + static OpCodeTable g_OpCodeTables[DXIL::NumOpCodeTables]; static const OpCodeProperty &GetOpCodeProps(unsigned opCode); static const OpCodeProperty &GetOpCodeProps(OpCode opCode); diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index 0a542bfb25..e03682959a 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2734,12 +2734,11 @@ static_assert(_countof(ExperimentalOps_OpCodeProps) == "mismatch in opcode count for ExperimentalOps OpCodeProps"); // Table of DXIL OpCode Property tables -OP::OpCodeTable - OP::g_OpCodeTables[(unsigned)OP::OpCodeTableID::NumOpCodeTables] = { - {OP::OpCodeTableID::CoreOps, CoreOps_OpCodeProps, - (unsigned)DXIL::CoreOps::OpCode::NumOpCodes}, - {OP::OpCodeTableID::ExperimentalOps, ExperimentalOps_OpCodeProps, - (unsigned)DXIL::ExperimentalOps::OpCode::NumOpCodes}, +OP::OpCodeTable OP::g_OpCodeTables[DXIL::NumOpCodeTables] = { + {OP::OpCodeTableID::CoreOps, CoreOps_OpCodeProps, + (unsigned)DXIL::CoreOps::OpCode::NumOpCodes}, + {OP::OpCodeTableID::ExperimentalOps, ExperimentalOps_OpCodeProps, + (unsigned)DXIL::ExperimentalOps::OpCode::NumOpCodes}, }; // OPCODE-OLOADS:END @@ -2758,7 +2757,7 @@ static const char *AtomicBinOpCodeName[] = { }; static unsigned GetOpCodeTableIndex(OP::OpCodeTableID TableID) { - static_assert((unsigned)OP::OpCodeTableID::NumOpCodeTables == 2, + static_assert(DXIL::NumOpCodeTables == 2, "Otherwise, update GetOpCodeTableIndex to be generated."); switch (TableID) { case OP::OpCodeTableID::CoreOps: @@ -2777,7 +2776,7 @@ bool OP::DecodeOpCode(unsigned EncodedOpCode, OP::OpCodeTableID &TableID, return false; OP::OpCodeTableID TID = (OP::OpCodeTableID)(EncodedOpCode >> 16); unsigned TableIndex = GetOpCodeTableIndex(TID); - if (TableIndex >= (unsigned)OP::OpCodeTableID::NumOpCodeTables) + if (TableIndex >= DXIL::NumOpCodeTables) return false; unsigned Op = (EncodedOpCode & 0xFFFF); if (Op >= OP::g_OpCodeTables[TableIndex].Count) @@ -2987,8 +2986,7 @@ bool OP::IsOverloadLegal(OpCode opCode, Type *pType) { } bool OP::CheckOpCodeTable() { - for (unsigned TableIndex = 0; - TableIndex < (unsigned)OP::OpCodeTableID::NumOpCodeTables; + for (unsigned TableIndex = 0; TableIndex < DXIL::NumOpCodeTables; TableIndex++) { const OP::OpCodeTable &Table = OP::g_OpCodeTables[TableIndex]; for (unsigned OpIndex = 0; OpIndex < Table.Count; OpIndex++) { diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 2f4c24a877..32460e9094 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -469,7 +469,6 @@ def build_opcode_enum(self): "Enumeration for DXIL opcode tables", [(0, "CoreOps", "Core DXIL operations")], ) - OpCodeTableID.last_value_name = "NumOpCodeTables" postfix.append("") postfix.append("// OpCodes for extended tables follow.\n") for table in self.dxil_op_tables[1:]: # Skip CoreOps table diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index e587bec906..d2427f429d 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -490,6 +490,7 @@ def print_extended_table_opcode_enums(self): print(f"static const OpCodeTableID TableID = OpCodeTableID::{table.name};") self.print_enum(table.op_enum) print(f"}} // namespace {table.name}") + print(f"static const unsigned NumOpCodeTables = {len(self.db.dxil_op_tables)};") def print_content(self): for e in sorted(self.db.enums, key=lambda e: e.name): @@ -516,8 +517,7 @@ def print_opfunc_props(self): # Print the overall table of tables print("// Table of DXIL OpCode Property tables") print( - f"OP::OpCodeTable OP::g_OpCodeTables[(unsigned)" - + f"OP::OpCodeTableID::NumOpCodeTables] = {{" + f"OP::OpCodeTable OP::g_OpCodeTables[DXIL::NumOpCodeTables] = {{" ) for table in self.db.dxil_op_tables: print( From ac73bb0809e19bc9ce122741b2bc0f99178a42ec Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Wed, 3 Dec 2025 16:32:45 -0800 Subject: [PATCH 16/17] Remove cur_table state and refactor/clean things up - db_dxil_op_table manages dxil op lists, build ops using table now. - split off llvm instruction list, use generators for supersets, remove unused accessors (get_instr_by_llvm_name, get_dxil_insts), cleaned references. - Move OpCodeClass verification to verify_dxil_op_classes, with a few additions. Enable with exception for known conflict that's fortunately not serious. Fixed conflicts in RayQuery and HitObject accessors, which fortunately will cause no DXIL differences. - consistency improvements for enums - add to indexes when adding insts or enums --- include/dxc/DXIL/DxilConstants.h | 2 +- lib/DXIL/DxilOperations.cpp | 129 ++-- utils/hct/hctdb.py | 1053 +++++++++++++++--------------- utils/hct/hctdb_instrhelp.py | 46 +- 4 files changed, 604 insertions(+), 626 deletions(-) diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index d12dd3def4..6f1f1d66ea 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -511,6 +511,7 @@ namespace ExperimentalOps { static const OpCodeTableID TableID = OpCodeTableID::ExperimentalOps; // Enumeration for ExperimentalOps DXIL operations enum class OpCode : unsigned { + // No-op ExperimentalNop = 0, // nop does nothing NumOpCodes = 1, // exclusive last value of enumeration @@ -1130,7 +1131,6 @@ enum class OpCode : unsigned { // OpCodeTableID = 32768 // ExperimentalOps EXP_OPCODE(ExperimentalOps, ExperimentalNop), // nop does nothing - }; // OPCODE-ENUM:END #undef EXP_OPCODE diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index e03682959a..378c33495c 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -5310,162 +5310,162 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pF32); break; case OpCode::RayQuery_CommittedStatus: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateType: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateObjectToWorld3x4: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CandidateWorldToObject3x4: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CommittedObjectToWorld3x4: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CommittedWorldToObject3x4: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CandidateProceduralPrimitiveNonOpaque: - A(pI1); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateTriangleFrontFace: - A(pI1); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedTriangleFrontFace: - A(pI1); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateTriangleBarycentrics: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CommittedTriangleBarycentrics: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_RayFlags: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_WorldRayOrigin: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_WorldRayDirection: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_RayTMin: - A(pF32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateTriangleRayT: - A(pF32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedRayT: - A(pF32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateInstanceIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateInstanceID: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateGeometryIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidatePrimitiveIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CandidateObjectRayOrigin: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CandidateObjectRayDirection: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CommittedInstanceIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedInstanceID: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedGeometryIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedPrimitiveIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedObjectRayOrigin: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); break; case OpCode::RayQuery_CommittedObjectRayDirection: - A(pF32); + A(pETy); A(pI32); A(pI32); A(pI8); @@ -5479,12 +5479,12 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { // Inline Ray Query case OpCode::RayQuery_CandidateInstanceContributionToHitGroupIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; case OpCode::RayQuery_CommittedInstanceContributionToHitGroupIndex: - A(pI32); + A(pETy); A(pI32); A(pI32); break; @@ -5878,32 +5878,32 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pI32); break; case OpCode::HitObject_IsMiss: - A(pI1); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_IsHit: - A(pI1); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_IsNop: - A(pI1); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_RayFlags: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_RayTMin: - A(pF32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_RayTCurrent: - A(pF32); + A(pETy); A(pI32); A(pHit); break; @@ -5946,32 +5946,32 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pI32); break; case OpCode::HitObject_GeometryIndex: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_InstanceIndex: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_InstanceID: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_PrimitiveIndex: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_HitKind: - A(pI32); + A(pETy); A(pI32); A(pHit); break; case OpCode::HitObject_ShaderTableIndex: - A(pI32); + A(pETy); A(pI32); A(pHit); break; @@ -6460,29 +6460,9 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::PrimitiveIndex: case OpCode::Dot4AddI8Packed: case OpCode::Dot4AddU8Packed: - case OpCode::RayQuery_CommittedStatus: - case OpCode::RayQuery_CandidateType: - case OpCode::RayQuery_RayFlags: - case OpCode::RayQuery_CandidateInstanceIndex: - case OpCode::RayQuery_CandidateInstanceID: - case OpCode::RayQuery_CandidateGeometryIndex: - case OpCode::RayQuery_CandidatePrimitiveIndex: - case OpCode::RayQuery_CommittedInstanceIndex: - case OpCode::RayQuery_CommittedInstanceID: - case OpCode::RayQuery_CommittedGeometryIndex: - case OpCode::RayQuery_CommittedPrimitiveIndex: case OpCode::GeometryIndex: - case OpCode::RayQuery_CandidateInstanceContributionToHitGroupIndex: - case OpCode::RayQuery_CommittedInstanceContributionToHitGroupIndex: case OpCode::StartVertexLocation: case OpCode::StartInstanceLocation: - case OpCode::HitObject_RayFlags: - case OpCode::HitObject_GeometryIndex: - case OpCode::HitObject_InstanceIndex: - case OpCode::HitObject_InstanceID: - case OpCode::HitObject_PrimitiveIndex: - case OpCode::HitObject_HitKind: - case OpCode::HitObject_ShaderTableIndex: return IntegerType::get(Ctx, 32); case OpCode::CalculateLOD: case OpCode::DomainLocation: @@ -6494,23 +6474,6 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::WorldToObject: case OpCode::RayTMin: case OpCode::RayTCurrent: - case OpCode::RayQuery_CandidateObjectToWorld3x4: - case OpCode::RayQuery_CandidateWorldToObject3x4: - case OpCode::RayQuery_CommittedObjectToWorld3x4: - case OpCode::RayQuery_CommittedWorldToObject3x4: - case OpCode::RayQuery_CandidateTriangleBarycentrics: - case OpCode::RayQuery_CommittedTriangleBarycentrics: - case OpCode::RayQuery_WorldRayOrigin: - case OpCode::RayQuery_WorldRayDirection: - case OpCode::RayQuery_RayTMin: - case OpCode::RayQuery_CandidateTriangleRayT: - case OpCode::RayQuery_CommittedRayT: - case OpCode::RayQuery_CandidateObjectRayOrigin: - case OpCode::RayQuery_CandidateObjectRayDirection: - case OpCode::RayQuery_CommittedObjectRayOrigin: - case OpCode::RayQuery_CommittedObjectRayDirection: - case OpCode::HitObject_RayTMin: - case OpCode::HitObject_RayTCurrent: case OpCode::HitObject_WorldRayOrigin: case OpCode::HitObject_WorldRayDirection: case OpCode::HitObject_ObjectRayOrigin: @@ -6522,14 +6485,8 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::SplitDouble: return Type::getDoubleTy(Ctx); case OpCode::RayQuery_Proceed: - case OpCode::RayQuery_CandidateProceduralPrimitiveNonOpaque: - case OpCode::RayQuery_CandidateTriangleFrontFace: - case OpCode::RayQuery_CommittedTriangleFrontFace: case OpCode::IsHelperLane: case OpCode::QuadVote: - case OpCode::HitObject_IsMiss: - case OpCode::HitObject_IsHit: - case OpCode::HitObject_IsNop: return IntegerType::get(Ctx, 1); case OpCode::CBufferLoadLegacy: case OpCode::Sample: diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 32460e9094..910944448c 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -65,9 +65,9 @@ class db_dxil_enum_value(object): "A representation for a value in an enumeration type" - def __init__(self, name, value, doc): - self.name = name # Name (identifier) + def __init__(self, value, name, doc): self.value = value # Numeric value + self.name = name # Name (identifier) self.doc = doc # Documentation string self.category = None @@ -79,8 +79,8 @@ def __init__(self, name, doc, valNameDocTuples=()): self.name = name self.doc = doc self.values = [ - db_dxil_enum_value(n, v, d) for v, n, d in valNameDocTuples - ] # Note transmutation + db_dxil_enum_value(*args) for args in valNameDocTuples + ] self.is_internal = False # whether this is never serialized self.last_value_name = None # optional last value name for dense enums self.dxil_version_info = {} # version info for this enum @@ -89,6 +89,11 @@ def __init__(self, name, doc, valNameDocTuples=()): def value_names(self): return [i.name for i in self.values] + def add_value(self, *args): + v = db_dxil_enum_value(*args) + self.values.append(v) + return v + class db_dxil_inst(object): "A representation for a DXIL instruction" @@ -99,7 +104,6 @@ def __init__(self, name, **kwargs): self.llvm_name = "" # name of LLVM instruction type self.dxil_table = "CoreOps" # name of the DXIL operation table - self.is_dxil_op = False # whether this is a call into a built-in DXIL function self.dxil_op = "" # name of DXIL operation self.dxil_opid = 0 # ID of DXIL operation self.dxil_class = "" # name of the opcode class @@ -142,7 +146,9 @@ def dxil_op_index(self): def table_id(self): "Get the table ID of this DXIL op." - return (self.dxil_opid >> 16) & 0xFFFF + if self.is_dxil_op: + return (self.dxil_opid >> 16) & 0xFFFF + return 0 # LLVM ops are in table 0 (CoreOps) def fully_qualified_name(self): return "{}::{}".format(self.fully_qualified_name_prefix, self.name) @@ -352,37 +358,96 @@ def __str__(self): class db_dxil_op_table(object): "Table definition for a set of DXIL operations" - def __init__(self, id, name, doc): + def __init__(self, db, id, name, doc): assert id & ~0xFFFF == 0, "DXIL op table ID must fit in high 16 bits" self.id = id self.name = name self.doc = doc - self.instr = [] # DXIL instructions - self.op_count = 0 + self.ops = [] # DXIL operations self.op_enum = db_dxil_enum( "OpCode", f"Enumeration for {self.name} DXIL operations" ) self.op_enum.last_value_name = "NumOpCodes" + self.call_instr = db.call_instr + self.opcode_param = db_dxil_param(1, "i32", "opcode", "DXIL opcode") + + def _next_id(self): + return (self.id << 16) | len(self.ops) - def next_id(self): - new_id = self.op_count - self.op_count += 1 - return (self.id << 16) | new_id + def __len__(self): + return len(self.ops) - def get_count(self): - return self.op_count + def __getitem__(self, idx): + return self.ops[idx] + + def __iter__(self): + return iter(self.ops) def set_op_count_for_version(self, major, minor): - op_count = self.get_count() + op_count = len(self.ops) self.op_enum.dxil_version_info[(major, minor)] = op_count return op_count + def add(self, i): + assert i.table_id() == self.id, "Instruction table mismatch" + assert i.dxil_op_index() == len(self.ops), "Instruction index mismatch" + self.ops.append(i) + return i + + def add_dxil_op( + self, name, code_class, doc, oload_types, fn_attr, op_params, **props + ): + # The return value is parameter 0, insert the opcode as 1. + op_params.insert(1, self.opcode_param) + i = db_dxil_inst( + name, + llvm_id=self.call_instr.llvm_id, + llvm_name=self.call_instr.llvm_name, + dxil_op=name, + dxil_opid=self._next_id(), + dxil_table=self.name, + doc=doc, + ops=op_params, + dxil_class=code_class, + oload_types=oload_types, + fn_attr=fn_attr, + ) + i.props = props + return self.add(i) + + def add_dxil_op_reserved(self, name): + # The return value is parameter 0, insert the opcode as 1. + op_params = [db_dxil_param(0, "v", "", "reserved"), self.opcode_param] + i = db_dxil_inst( + name, + llvm_id=self.call_instr.llvm_id, + llvm_name=self.call_instr.llvm_name, + dxil_op=name, + dxil_opid=self._next_id(), + dxil_table=self.name, + doc="reserved", + ops=op_params, + dxil_class="Reserved", + oload_types="v", + fn_attr="", + ) + return self.add(i) + + def reserve_dxil_op_range(self, group_name, count, start_reserved_id=0): + "Reserve a range of dxil opcodes for future use; returns next id" + return [ + self.add_dxil_op_reserved( + "{0}{1}".format(group_name, start_reserved_id + i) + ) + for i in range(0, count) + ] + class db_dxil(object): "A database of DXIL instruction data" def __init__(self): - self.instr = [] # all LLVM instructions and DXIL operations + self._llvm_insts = [] # LLVM instructions self.enums = [] # enumeration types self.val_rules = [] # validation rules self.metadata = [] # named metadata (db_dxil_metadata) @@ -392,28 +457,23 @@ def __init__(self): # list of counters for instructions and dxil ops, # starting with extra ones specified here self.counters = extra_counters - self.dxil_op_tables = [ - db_dxil_op_table(0, "CoreOps", "Core DXIL operations"), - db_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations"), - ] - self.dxil_op_tables_by_name = dict([(t.name, t) for t in self.dxil_op_tables]) - # Table id should be strictly increasing. - last_table_id = None - for t in self.dxil_op_tables: - if last_table_id is not None: - assert ( - t.id > last_table_id - ), "DXIL op table IDs must be strictly increasing" - last_table_id = t.id - # Set cur_table. - self.set_dxil_op_table() + # Add core LLVM instructions, and set call_instr for DXIL ops self.populate_llvm_instructions() - self.call_instr = self.get_instr_by_llvm_name("CallInst") - self.populate_dxil_operations() - self.populate_experimental_ops() + + # OpCode tables + self.op_table_enum = self.add_enum_type( + "OpCodeTableID", "Enumeration for DXIL opcode tables" + ) + self.op_tables = [] + self.op_table_idx = {} + + # Add DXIL operations for each table + self.populate_CoreOps() + self.populate_ExperimentalOps() + + # Finalize dxil operations and populate additional data self.finalize_dxil_operations() - self.build_indices() self.populate_extended_docs() self.populate_categories_and_models() self.build_opcode_enum() @@ -422,84 +482,93 @@ def __init__(self): self.populate_passes() self.build_valrules() self.build_semantics() - self.build_indices() self.populate_counters() - def set_dxil_op_table(self, table_name="CoreOps"): - "Set the current DXIL operation table, defaulting to CoreOps." - self.cur_table = self.dxil_op_tables_by_name[table_name] + def get_llvm_insts(self): + "Get all LLVM instructions." + for i in self._llvm_insts: + yield i + + def get_dxil_ops(self): + "Get all DXIL operations." + for table in self.op_tables: + for i in table: + yield i + + def get_all_insts(self): + "Get all instructions, including LLVM and DXIL operations." + for i in self._llvm_insts: + yield i + for table in self.op_tables: + for i in table: + yield i + + def add_dxil_op_table(self, id, name, doc): + "Add a new DXIL operation table." + assert name not in self.op_table_idx, ( + f"DXIL op table '{name}' already exists" + ) + assert id & ~0xFFFF == 0, "DXIL op table ID must fit in high 16 bits" + assert len(self.op_tables) < 2, "Only two DXIL op tables are currently supported" + self.op_table_enum.add_value(id, name, doc) + table = db_dxil_op_table(self, id, name, doc) + self.op_tables.append(table) + self.op_table_idx[table.name] = table + return table def get_dxil_op_table(self, table_name="CoreOps"): "Get the specified DXIL operation table." - return self.dxil_op_tables_by_name[table_name] + return self.op_table_idx[table_name] def __str__(self): - return "\n".join(str(i) for i in self.instr) + return "\n".join(str(i) for i in self.get_all_insts()) - def add_enum_type(self, name, doc, valNameDocTuples): - "Adds a new enumeration type with name/value/doc tuples" - self.enums.append(db_dxil_enum(name, doc, valNameDocTuples)) - - def build_indices(self): - "Build a name_idx dictionary with instructions and an enum_idx dictionary with enumeration types" - self.name_idx = {} - for i in self.instr: - self.name_idx[i.name] = i - self.enum_idx = {} - for i in self.enums: - self.enum_idx[i.name] = i + def add_enum_type(self, name, doc, valNameDocTuples=()): + "Adds a new enumeration type with optional name/value/doc tuples" + assert name not in self.enum_idx, "Enumeration type %s already exists" % (name) + enum = db_dxil_enum(name, doc, valNameDocTuples) + self.enum_idx[enum.name] = enum + self.enums.append(enum) + return enum def build_opcode_enum(self): - # Build enumeration from instructions - OpCodeEnum = self.get_dxil_op_table().op_enum + # Core table enum is exposed globally as "OpCode" + self.enum_idx[self.core_table.op_enum.name] = self.core_table.op_enum + self.enums.append(self.core_table.op_enum) + + # Use postfix for OpCode::Invalid and extended table opcodes + postfix = self.core_table.op_enum.postfix_lines + postfix.append("Invalid = 0xFFFFFFFF, // stable invalid OpCode value\n") + postfix.append("// OpCodes for extended tables follow.") + + # Keep track of last seen class/category pairs for OpCodeClass class_dict = {} class_dict["LlvmInst"] = "LLVM Instructions" - for i in self.get_dxil_op_table().instr: - if i.is_dxil_op: - v = db_dxil_enum_value(i.dxil_op, i.dxil_opid, i.doc) + + # Build table enumerations from instructions + for table in self.op_tables: + if table != self.core_table: + postfix.append("") + postfix.append(f"// OpCodeTableID = {table.id}") + postfix.append(f"// {table.name}") + + for i in table: + v = table.op_enum.add_value(i.dxil_op_index(), i.dxil_op, i.doc) v.category = i.category class_dict[i.dxil_class] = i.category - OpCodeEnum.values.append(v) - postfix = OpCodeEnum.postfix_lines or [] - # Add OpCode::Invalid - postfix.append("Invalid = 0xFFFFFFFF, // stable invalid OpCode value") - # Generate extended opcode enums into OpCodeEnum.postfix_lines: - OpCodeTableID = db_dxil_enum( - "OpCodeTableID", - "Enumeration for DXIL opcode tables", - [(0, "CoreOps", "Core DXIL operations")], - ) - postfix.append("") - postfix.append("// OpCodes for extended tables follow.\n") - for table in self.dxil_op_tables[1:]: # Skip CoreOps table - OpCodeTableID.values.append( - db_dxil_enum_value(table.name, table.id, table.doc) - ) - if not table.get_count(): - continue - postfix.append(f"// OpCodeTableID = {table.id}") - postfix.append(f"// {table.name}") - for i in table.instr: - class_dict[i.dxil_class] = i.category - postfix.append(f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}") - table.op_enum.values.append( - db_dxil_enum_value(i.dxil_op, i.dxil_op_index(), i.doc) - ) - postfix.append("") - OpCodeEnum.postfix_lines = postfix - self.enums.append(OpCodeEnum) - OpCodeClass = db_dxil_enum( + if table != self.core_table: + postfix.append(f"EXP_OPCODE({table.name}, {i.dxil_op}), // {i.doc}") + + # Build OpCodeClass enum + OpCodeClass = self.add_enum_type( "OpCodeClass", "Groups for DXIL operations with equivalent function templates", ) OpCodeClass.is_internal = True OpCodeClass.last_value_name = "NumOpClasses" for k, v in iter(class_dict.items()): - ev = db_dxil_enum_value(k, 0, None) + ev = OpCodeClass.add_value(0, k, None) ev.category = v - OpCodeClass.values.append(ev) - self.enums.append(OpCodeClass) - self.enums.append(OpCodeTableID) def mark_disallowed_operations(self): # Disallow indirect branching, unreachable instructions and support for exception unwinding. @@ -524,9 +593,6 @@ def verify_dense(self, it, pred, name_proj): ) val = i_val - def set_op_count_for_version(self, major, minor): - return self.cur_table.set_op_count_for_version(major, minor) - def populate_categories_and_models(self): "Populate the category and shader_stages member of instructions." for ( @@ -660,7 +726,7 @@ def populate_categories_and_models(self): self.name_idx[i].category = "Other" for i in "LegacyF32ToF16,LegacyF16ToF32".split(","): self.name_idx[i].category = "Legacy floating-point" - for i in self.instr: + for i in self.get_dxil_ops(): if i.name.startswith("Wave"): i.category = "Wave" i.is_wave = True @@ -1439,7 +1505,16 @@ def populate_llvm_instructions(self): self.add_llvm_instr( "OTHER", 48, "PHI", "PHINode", "is a PHI node instruction", "", [] ) - self.add_llvm_instr("OTHER", 49, "Call", "CallInst", "calls a function", "", []) + # keep track of CallInst used by all DXIL ops + self.call_instr = self.add_llvm_instr( + "OTHER", + 49, + "Call", + "CallInst", + "calls a function", + "", + [] + ) self.add_llvm_instr( "OTHER", 50, "Select", "SelectInst", "selects an instruction", "", [] ) @@ -1508,15 +1583,18 @@ def populate_llvm_instructions(self): [], ) - def populate_dxil_operations(self): + def populate_CoreOps(self): + # Set up core DXIL operations. + self.core_table = self.add_dxil_op_table(0, "CoreOps", "Core DXIL operations") + op_table = self.core_table + # $o in a parameter type means the overload type # $r in a parameter type means the resource type # $cb in a parameter type means cbuffer legacy load return type # $o_{component} in a return type means the overload template shape with the specified component type # overload types are a string of (v)oid, (h)alf, (f)loat, (d)ouble, (1)-bit, (8)-bit, (w)ord, (i)nt, (l)ong - self.opcode_param = db_dxil_param(1, "i32", "opcode", "DXIL opcode") retvoid_param = db_dxil_param(0, "v", "", "no return value") - self.add_dxil_op( + op_table.add_dxil_op( "TempRegLoad", "TempRegLoad", "helper load operation", @@ -1527,7 +1605,7 @@ def populate_dxil_operations(self): db_dxil_param(2, "u32", "index", "linearized register index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "TempRegStore", "TempRegStore", "helper store operation", @@ -1539,7 +1617,7 @@ def populate_dxil_operations(self): db_dxil_param(3, "$o", "value", "value to store"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "MinPrecXRegLoad", "MinPrecXRegLoad", "helper load operation for minprecision", @@ -1552,7 +1630,7 @@ def populate_dxil_operations(self): db_dxil_param(4, "u8", "component", "component"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "MinPrecXRegStore", "MinPrecXRegStore", "helper store operation for minprecision", @@ -1566,7 +1644,7 @@ def populate_dxil_operations(self): db_dxil_param(5, "$o", "value", "value to store"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LoadInput", "LoadInput", "loads the value from shader input", @@ -1581,7 +1659,7 @@ def populate_dxil_operations(self): ], counters=("sig_ld",), ) - self.add_dxil_op( + op_table.add_dxil_op( "StoreOutput", "StoreOutput", "stores the value to shader output", @@ -1611,7 +1689,7 @@ def UFI(name, **mappings): # Unary float operations are regular. for i in "FAbs,Saturate".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Unary", "returns the " + i, @@ -1624,7 +1702,7 @@ def UFI(name, **mappings): counters=("floats",), ) for i in "IsNaN,IsInf,IsFinite,IsNormal".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "IsSpecialFloat", "returns the " + i, @@ -1641,7 +1719,7 @@ def UFI(name, **mappings): ) in "Cos,Sin,Tan,Acos,Asin,Atan,Hcos,Hsin,Htan,Exp,Frc,Log,Sqrt,Rsqrt,Round_ne,Round_ni,Round_pi,Round_z".split( "," ): - self.add_dxil_op( + op_table.add_dxil_op( i, "Unary", "returns the " + i, @@ -1656,7 +1734,7 @@ def UFI(name, **mappings): # Unary int operations are regular. for i in "Bfrev".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Unary", "returns the reverse bit pattern of the input value", @@ -1669,7 +1747,7 @@ def UFI(name, **mappings): counters=("uints",), ) for i in "Countbits,FirstbitLo".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "UnaryBits", "returns the " + i, @@ -1682,7 +1760,7 @@ def UFI(name, **mappings): counters=("uints",), ) for i in "FirstbitHi,FirstbitSHi".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "UnaryBits", "returns src != 0? (BitWidth-1 - " + i + ") : -1", @@ -1697,7 +1775,7 @@ def UFI(name, **mappings): # Binary float operations for i in "FMax,FMin".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Binary", "returns the " + i + " of the input values", @@ -1713,7 +1791,7 @@ def UFI(name, **mappings): # Binary int operations for i in "IMax,IMin,UMax,UMin".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Binary", "returns the " + i + " of the input values", @@ -1729,7 +1807,7 @@ def UFI(name, **mappings): # Binary int operations with two outputs for i in "IMul,UMul,UDiv".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "BinaryWithTwoOuts", "returns the " + i + " of the input values", @@ -1745,7 +1823,7 @@ def UFI(name, **mappings): # Binary int operations with carry for i in "UAddc,USubb".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "BinaryWithCarryOrBorrow", "returns the " + i + " of the input values", @@ -1762,7 +1840,7 @@ def UFI(name, **mappings): ) # Tertiary float. - self.add_dxil_op( + op_table.add_dxil_op( "FMad", "Tertiary", "performs a fused multiply add (FMA) of the form a * b + c", @@ -1777,7 +1855,7 @@ def UFI(name, **mappings): db_dxil_param(4, "$o", "c", "third value for FMA, the addend"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "Fma", "Tertiary", "performs a fused multiply add (FMA) of the form a * b + c", @@ -1799,7 +1877,7 @@ def UFI(name, **mappings): # Tertiary int. for i in "IMad,UMad".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Tertiary", "performs an integral " + i, @@ -1818,7 +1896,7 @@ def UFI(name, **mappings): counters=(UFI(i),), ) for i in "Msad,Ibfe,Ubfe".split(","): - self.add_dxil_op( + op_table.add_dxil_op( i, "Tertiary", "performs an integral " + i, @@ -1838,7 +1916,7 @@ def UFI(name, **mappings): ) # Quaternary - self.add_dxil_op( + op_table.add_dxil_op( "Bfi", "Quaternary", "given a bit range from the LSB of a number, places that number of bits in another number at any offset", @@ -1861,7 +1939,7 @@ def UFI(name, **mappings): ) # Dot - self.add_dxil_op( + op_table.add_dxil_op( "Dot2", "Dot2", "two-dimensional vector dot-product", @@ -1882,7 +1960,7 @@ def UFI(name, **mappings): ], counters=("floats",), ) - self.add_dxil_op( + op_table.add_dxil_op( "Dot3", "Dot3", "three-dimensional vector dot-product", @@ -1907,7 +1985,7 @@ def UFI(name, **mappings): ], counters=("floats",), ) - self.add_dxil_op( + op_table.add_dxil_op( "Dot4", "Dot4", "four-dimensional vector dot-product", @@ -1940,7 +2018,7 @@ def UFI(name, **mappings): ) # Resources. - self.add_dxil_op( + op_table.add_dxil_op( "CreateHandle", "CreateHandle", "creates the handle to a resource", @@ -1968,7 +2046,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CBufferLoad", "CBufferLoad", "loads a value from a constant buffer resource", @@ -1985,7 +2063,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CBufferLoadLegacy", "CBufferLoadLegacy", "loads a value from a constant buffer resource", @@ -2001,7 +2079,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "Sample", "Sample", "samples a texture", @@ -2041,7 +2119,7 @@ def UFI(name, **mappings): ], counters=("tex_norm",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleBias", "SampleBias", "samples a texture after applying the input bias to the mipmap level", @@ -2082,7 +2160,7 @@ def UFI(name, **mappings): ], counters=("tex_bias",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleLevel", "SampleLevel", "samples a texture using a mipmap-level offset", @@ -2127,7 +2205,7 @@ def UFI(name, **mappings): ], counters=("tex_norm",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleGrad", "SampleGrad", "samples a texture using a gradient to influence the way the sample location is calculated", @@ -2203,7 +2281,7 @@ def UFI(name, **mappings): ], counters=("tex_grad",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleCmp", "SampleCmp", "samples a texture and compares a single component against the specified comparison value", @@ -2246,7 +2324,7 @@ def UFI(name, **mappings): ], counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleCmpLevelZero", "SampleCmpLevelZero", "samples a texture and compares a single component against the specified comparison value", @@ -2288,7 +2366,7 @@ def UFI(name, **mappings): ], counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureLoad", "TextureLoad", "reads texel data without any filtering or sampling", @@ -2312,7 +2390,7 @@ def UFI(name, **mappings): ], counters=("tex_load",), ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureStore", "TextureStore", "reads texel data without any filtering or sampling", @@ -2332,7 +2410,7 @@ def UFI(name, **mappings): ], counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "BufferLoad", "BufferLoad", "reads from a TypedBuffer", @@ -2346,7 +2424,7 @@ def UFI(name, **mappings): ], counters=("tex_load",), ) - self.add_dxil_op( + op_table.add_dxil_op( "BufferStore", "BufferStore", "writes to a RWTypedBuffer", @@ -2365,7 +2443,7 @@ def UFI(name, **mappings): ], counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "BufferUpdateCounter", "BufferUpdateCounter", "atomically increments/decrements the hidden 32-bit counter stored with a Count or Append UAV", @@ -2383,7 +2461,7 @@ def UFI(name, **mappings): ], counters=("atomic",), ) - self.add_dxil_op( + op_table.add_dxil_op( "CheckAccessFullyMapped", "CheckAccessFullyMapped", "determines whether all values from a Sample, Gather, or Load operation accessed mapped tiles in a tiled resource", @@ -2404,7 +2482,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GetDimensions", "GetDimensions", "gets texture size information", @@ -2416,7 +2494,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "mipLevel", "mip level to query"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureGather", "TextureGather", "gathers the four texels that would be used in a bi-linear filtering operation", @@ -2453,7 +2531,7 @@ def UFI(name, **mappings): ], counters=("tex_norm",), ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureGatherCmp", "TextureGatherCmp", "same as TextureGather, except this instrution performs comparison on texels, similar to SampleCmp", @@ -2492,7 +2570,7 @@ def UFI(name, **mappings): counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "Texture2DMSGetSamplePosition", "Texture2DMSGetSamplePosition", "gets the position of the specified sample", @@ -2504,7 +2582,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "index", "zero-based sample index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RenderTargetGetSamplePosition", "RenderTargetGetSamplePosition", "gets the position of the specified sample", @@ -2515,7 +2593,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "index", "zero-based sample index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RenderTargetGetSampleCount", "RenderTargetGetSampleCount", "gets the number of samples for a render target", @@ -2529,7 +2607,7 @@ def UFI(name, **mappings): ) # Atomics. Note that on TGSM, atomics are performed with LLVM instructions. - self.add_dxil_op( + op_table.add_dxil_op( "AtomicBinOp", "AtomicBinOp", "performs an atomic operation on two operands", @@ -2553,7 +2631,7 @@ def UFI(name, **mappings): ], counters=("atomic",), ) - self.add_dxil_op( + op_table.add_dxil_op( "AtomicCompareExchange", "AtomicCompareExchange", "atomic compare and exchange to memory", @@ -2574,7 +2652,7 @@ def UFI(name, **mappings): ) # Synchronization. - self.add_dxil_op( + op_table.add_dxil_op( "Barrier", "Barrier", "inserts a memory barrier in the shader", @@ -2594,7 +2672,7 @@ def UFI(name, **mappings): ) # Pixel shader - self.add_dxil_op( + op_table.add_dxil_op( "CalculateLOD", "CalculateLOD", "calculates the level of detail", @@ -2615,7 +2693,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "Discard", "Discard", "discard the current pixel", @@ -2628,7 +2706,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "DerivCoarseX", "Unary", "computes the rate of change of components per stamp", @@ -2644,7 +2722,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "input to rate of change"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "DerivCoarseY", "Unary", "computes the rate of change of components per stamp", @@ -2660,7 +2738,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "input to rate of change"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "DerivFineX", "Unary", "computes the rate of change of components per pixel", @@ -2676,7 +2754,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "input to rate of change"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "DerivFineY", "Unary", "computes the rate of change of components per pixel", @@ -2692,7 +2770,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "input to rate of change"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "EvalSnapped", "EvalSnapped", "evaluates an input attribute at pixel center with an offset", @@ -2721,7 +2799,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "EvalSampleIndex", "EvalSampleIndex", "evaluates an input attribute at a sample location", @@ -2739,7 +2817,7 @@ def UFI(name, **mappings): db_dxil_param(5, "i32", "sampleIndex", "sample location"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "EvalCentroid", "EvalCentroid", "evaluates an input attribute at pixel center", @@ -2756,7 +2834,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleIndex", "SampleIndex", "returns the sample index in a sample-frequency pixel shader", @@ -2764,7 +2842,7 @@ def UFI(name, **mappings): "rn", [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "Coverage", "Coverage", "returns the coverage mask input in a pixel shader", @@ -2772,7 +2850,7 @@ def UFI(name, **mappings): "rn", [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "InnerCoverage", "InnerCoverage", "returns underestimated coverage input from conservative rasterization in a pixel shader", @@ -2782,7 +2860,7 @@ def UFI(name, **mappings): ) # Compute shader. - self.add_dxil_op( + op_table.add_dxil_op( "ThreadId", "ThreadId", "reads the thread ID", @@ -2793,7 +2871,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "component", "component to read (x,y,z)"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GroupId", "GroupId", "reads the group ID (SV_GroupID)", @@ -2804,7 +2882,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "component", "component to read"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ThreadIdInGroup", "ThreadIdInGroup", "reads the thread ID within the group (SV_GroupThreadID)", @@ -2815,7 +2893,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "component", "component to read (x,y,z)"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "FlattenedThreadIdInGroup", "FlattenedThreadIdInGroup", "provides a flattened index for a given thread within a given group (SV_GroupIndex)", @@ -2825,7 +2903,7 @@ def UFI(name, **mappings): ) # Geometry shader - self.add_dxil_op( + op_table.add_dxil_op( "EmitStream", "EmitStream", "emits a vertex to a given stream", @@ -2837,7 +2915,7 @@ def UFI(name, **mappings): ], counters=("gs_emit",), ) - self.add_dxil_op( + op_table.add_dxil_op( "CutStream", "CutStream", "completes the current primitive topology at the specified stream", @@ -2849,7 +2927,7 @@ def UFI(name, **mappings): ], counters=("gs_cut",), ) - self.add_dxil_op( + op_table.add_dxil_op( "EmitThenCutStream", "EmitThenCutStream", "equivalent to an EmitStream followed by a CutStream", @@ -2861,7 +2939,7 @@ def UFI(name, **mappings): ], counters=("gs_emit", "gs_cut"), ) - self.add_dxil_op( + op_table.add_dxil_op( "GSInstanceID", "GSInstanceID", "GSInstanceID", @@ -2871,7 +2949,7 @@ def UFI(name, **mappings): ) # Double precision - self.add_dxil_op( + op_table.add_dxil_op( "MakeDouble", "MakeDouble", "creates a double value", @@ -2883,7 +2961,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "hi", "high part of double"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "SplitDouble", "SplitDouble", "splits a double into low and high parts", @@ -2896,7 +2974,7 @@ def UFI(name, **mappings): ) # Domain & Hull shader. - self.add_dxil_op( + op_table.add_dxil_op( "LoadOutputControlPoint", "LoadOutputControlPoint", "LoadOutputControlPoint", @@ -2911,7 +2989,7 @@ def UFI(name, **mappings): ], counters=("sig_ld",), ) - self.add_dxil_op( + op_table.add_dxil_op( "LoadPatchConstant", "LoadPatchConstant", "LoadPatchConstant", @@ -2927,7 +3005,7 @@ def UFI(name, **mappings): ) # Domain shader. - self.add_dxil_op( + op_table.add_dxil_op( "DomainLocation", "DomainLocation", "DomainLocation", @@ -2940,7 +3018,7 @@ def UFI(name, **mappings): ) # Hull shader. - self.add_dxil_op( + op_table.add_dxil_op( "StorePatchConstant", "StorePatchConstant", "StorePatchConstant", @@ -2955,7 +3033,7 @@ def UFI(name, **mappings): ], counters=("sig_st",), ) - self.add_dxil_op( + op_table.add_dxil_op( "OutputControlPointID", "OutputControlPointID", "OutputControlPointID", @@ -2963,7 +3041,7 @@ def UFI(name, **mappings): "rn", [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "PrimitiveID", "PrimitiveID", "PrimitiveID", @@ -2972,7 +3050,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "CycleCounterLegacy", "CycleCounterLegacy", "CycleCounterLegacy", @@ -2982,7 +3060,7 @@ def UFI(name, **mappings): ) # Add wave intrinsics. - self.add_dxil_op( + op_table.add_dxil_op( "WaveIsFirstLane", "WaveIsFirstLane", "returns 1 for the first lane in the wave", @@ -2990,7 +3068,7 @@ def UFI(name, **mappings): "", [db_dxil_param(0, "i1", "", "operation result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveGetLaneIndex", "WaveGetLaneIndex", "returns the index of the current lane in the wave", @@ -2998,7 +3076,7 @@ def UFI(name, **mappings): "ro", [db_dxil_param(0, "i32", "", "operation result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveGetLaneCount", "WaveGetLaneCount", "returns the number of lanes in the wave", @@ -3006,7 +3084,7 @@ def UFI(name, **mappings): "rn", [db_dxil_param(0, "i32", "", "operation result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveAnyTrue", "WaveAnyTrue", "returns 1 if any of the lane evaluates the value to true", @@ -3017,7 +3095,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i1", "cond", "condition to test"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveAllTrue", "WaveAllTrue", "returns 1 if all the lanes evaluate the value to true", @@ -3028,7 +3106,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i1", "cond", "condition to test"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveActiveAllEqual", "WaveActiveAllEqual", "returns 1 if all the lanes have the same value", @@ -3039,7 +3117,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "value to compare"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveActiveBallot", "WaveActiveBallot", "returns a struct with a bit set for each lane where the condition is true", @@ -3050,7 +3128,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i1", "cond", "condition to ballot on"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveReadLaneAt", "WaveReadLaneAt", "returns the value from the specified lane", @@ -3062,7 +3140,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "lane", "lane index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveReadLaneFirst", "WaveReadLaneFirst", "returns the value from the first lane", @@ -3073,7 +3151,7 @@ def UFI(name, **mappings): db_dxil_param(2, "$o", "value", "value to read"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveActiveOp", "WaveActiveOp", "returns the result the operation across waves", @@ -3118,7 +3196,7 @@ def UFI(name, **mappings): (3, "Max", "maximum value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveActiveBit", "WaveActiveBit", "returns the result of the operation across all lanes", @@ -3146,7 +3224,7 @@ def UFI(name, **mappings): (2, "Xor", "bitwise xor of values"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WavePrefixOp", "WavePrefixOp", "returns the result of the operation on prior lanes", @@ -3173,7 +3251,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "QuadReadLaneAt", "QuadReadLaneAt", "reads from a lane in the quad", @@ -3208,7 +3286,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "QuadOp", "QuadOp", "returns the result of a quad-level operation", @@ -3224,7 +3302,7 @@ def UFI(name, **mappings): ) # Add bitcasts - self.add_dxil_op( + op_table.add_dxil_op( "BitcastI16toF16", "BitcastI16toF16", "bitcast between different sizes", @@ -3235,7 +3313,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i16", "value", "input value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BitcastF16toI16", "BitcastF16toI16", "bitcast between different sizes", @@ -3246,7 +3324,7 @@ def UFI(name, **mappings): db_dxil_param(2, "h", "value", "input value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BitcastI32toF32", "BitcastI32toF32", "bitcast between different sizes", @@ -3257,7 +3335,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "value", "input value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BitcastF32toI32", "BitcastF32toI32", "bitcast between different sizes", @@ -3268,7 +3346,7 @@ def UFI(name, **mappings): db_dxil_param(2, "f", "value", "input value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BitcastI64toF64", "BitcastI64toF64", "bitcast between different sizes", @@ -3279,7 +3357,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i64", "value", "input value"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BitcastF64toI64", "BitcastF64toI64", "bitcast between different sizes", @@ -3291,7 +3369,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LegacyF32ToF16", "LegacyF32ToF16", "legacy fuction to convert float (f32) to half (f16) (this is not related to min-precision)", @@ -3305,7 +3383,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LegacyF16ToF32", "LegacyF16ToF32", "legacy fuction to convert half (f16) to float (f32) (this is not related to min-precision)", @@ -3317,7 +3395,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LegacyDoubleToFloat", "LegacyDoubleToFloat", "legacy fuction to convert double to float", @@ -3329,7 +3407,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LegacyDoubleToSInt32", "LegacyDoubleToSInt32", "legacy fuction to convert double to int32", @@ -3341,7 +3419,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "LegacyDoubleToUInt32", "LegacyDoubleToUInt32", "legacy fuction to convert double to uint32", @@ -3353,7 +3431,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveAllBitCount", "WaveAllOp", "returns the count of bits set to 1 across the wave", @@ -3367,7 +3445,7 @@ def UFI(name, **mappings): # WavePrefixBitCount has different signature compare to WavePrefixOp, set its opclass to WavePrefixOp is not correct. # It works now because WavePrefixOp and WavePrefixBitCount don't interfere on overload types. # Keep it unchanged for back-compat. - self.add_dxil_op( + op_table.add_dxil_op( "WavePrefixBitCount", "WavePrefixOp", "returns the count of bits set to 1 on prior lanes", @@ -3380,9 +3458,9 @@ def UFI(name, **mappings): ) # End of DXIL 1.0 opcodes. - self.set_op_count_for_version(1, 0) + op_table.set_op_count_for_version(1, 0) - self.add_dxil_op( + op_table.add_dxil_op( "AttributeAtVertex", "AttributeAtVertex", "returns the values of the attributes at the vertex.", @@ -3400,7 +3478,7 @@ def UFI(name, **mappings): db_dxil_param(5, "i8", "VertexID", "Vertex Index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ViewID", "ViewID", "returns the view index", @@ -3410,9 +3488,9 @@ def UFI(name, **mappings): ) # End of DXIL 1.1 opcodes. - self.set_op_count_for_version(1, 1) + op_table.set_op_count_for_version(1, 1) - self.add_dxil_op( + op_table.add_dxil_op( "RawBufferLoad", "RawBufferLoad", "reads from a raw buffer and structured buffer", @@ -3445,7 +3523,7 @@ def UFI(name, **mappings): counters=("tex_load",), ) - self.add_dxil_op( + op_table.add_dxil_op( "RawBufferStore", "RawBufferStore", "writes to a RWByteAddressBuffer or RWStructuredBuffer", @@ -3489,13 +3567,13 @@ def UFI(name, **mappings): ) # End of DXIL 1.2 opcodes. - op_count = self.set_op_count_for_version(1, 2) + op_count = op_table.set_op_count_for_version(1, 2) assert op_count == 141, ( "next operation index is %d rather than 141 and thus opcodes are broken" % op_count ) - self.add_dxil_op( + op_table.add_dxil_op( "InstanceID", "InstanceID", "The user-provided InstanceID on the bottom-level acceleration structure instance within the top-level structure", @@ -3504,7 +3582,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "InstanceIndex", "InstanceIndex", "The autogenerated index of the current instance in the top-level structure", @@ -3513,7 +3591,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitKind", "HitKind", "Returns the value passed as HitKind in ReportIntersection(). If intersection was reported by fixed-function triangle intersection, HitKind will be one of HIT_KIND_TRIANGLE_FRONT_FACE or HIT_KIND_TRIANGLE_BACK_FACE.", @@ -3522,7 +3600,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayFlags", "RayFlags", "uint containing the current ray flags.", @@ -3531,7 +3609,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "DispatchRaysIndex", "DispatchRaysIndex", "The current x and y location within the Width and Height", @@ -3543,7 +3621,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "DispatchRaysDimensions", "DispatchRaysDimensions", "The Width and Height values from the D3D12_DISPATCH_RAYS_DESC structure provided to the originating DispatchRays() call.", @@ -3555,7 +3633,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WorldRayOrigin", "WorldRayOrigin", "The world-space origin for the current ray.", @@ -3567,7 +3645,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WorldRayDirection", "WorldRayDirection", "The world-space direction for the current ray.", @@ -3579,7 +3657,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ObjectRayOrigin", "ObjectRayOrigin", "Object-space origin for the current ray.", @@ -3591,7 +3669,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ObjectRayDirection", "ObjectRayDirection", "Object-space direction for the current ray.", @@ -3603,7 +3681,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ObjectToWorld", "ObjectToWorld", "Matrix for transforming from object-space to world-space.", @@ -3616,7 +3694,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WorldToObject", "WorldToObject", "Matrix for transforming from world-space to object-space.", @@ -3629,7 +3707,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayTMin", "RayTMin", "float representing the parametric starting point for the ray.", @@ -3638,7 +3716,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "f", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayTCurrent", "RayTCurrent", "float representing the current parametric ending point for the ray", @@ -3647,7 +3725,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "f", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "IgnoreHit", "IgnoreHit", "Used in an any hit shader to reject an intersection and terminate the shader", @@ -3656,7 +3734,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "v", "", "")], ) - self.add_dxil_op( + op_table.add_dxil_op( "AcceptHitAndEndSearch", "AcceptHitAndEndSearch", "Used in an any hit shader to abort the ray query and the intersection shader (if any). The current hit is committed and execution passes to the closest hit shader with the closest hit recorded so far", @@ -3665,7 +3743,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "v", "", "")], ) - self.add_dxil_op( + op_table.add_dxil_op( "TraceRay", "TraceRay", "initiates raytrace", @@ -3721,7 +3799,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "ReportHit", "ReportHit", "returns true if hit was accepted", @@ -3747,7 +3825,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CallShader", "CallShader", "Call a shader in the callable shader table supplied through the DispatchRays() API", @@ -3770,7 +3848,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CreateHandleForLib", "CreateHandleForLib", "create resource handle from resource struct for library", @@ -3783,7 +3861,7 @@ def UFI(name, **mappings): ) # Maps to PrimitiveIndex() intrinsics for raytracing (same meaning as PrimitiveID) - self.add_dxil_op( + op_table.add_dxil_op( "PrimitiveIndex", "PrimitiveIndex", "PrimitiveIndex for raytracing shaders", @@ -3793,13 +3871,13 @@ def UFI(name, **mappings): ) # End of DXIL 1.3 opcodes. - op_count = self.set_op_count_for_version(1, 3) + op_count = op_table.set_op_count_for_version(1, 3) assert op_count == 162, ( "next operation index is %d rather than 162 and thus opcodes are broken" % op_count ) - self.add_dxil_op( + op_table.add_dxil_op( "Dot2AddHalf", "Dot2AddHalf", "2D half dot product with accumulate to float", @@ -3818,7 +3896,7 @@ def UFI(name, **mappings): counters=("floats",), ) - self.add_dxil_op( + op_table.add_dxil_op( "Dot4AddI8Packed", "Dot4AddPacked", "signed dot product of 4 x i8 vectors packed into i32, with accumulate to i32", @@ -3833,7 +3911,7 @@ def UFI(name, **mappings): counters=("ints",), ) - self.add_dxil_op( + op_table.add_dxil_op( "Dot4AddU8Packed", "Dot4AddPacked", "unsigned dot product of 4 x u8 vectors packed into i32, with accumulate to i32", @@ -3849,13 +3927,13 @@ def UFI(name, **mappings): ) # End of DXIL 1.4 opcodes. - op_count = self.set_op_count_for_version(1, 4) + op_count = op_table.set_op_count_for_version(1, 4) assert op_count == 165, ( "next operation index is %d rather than 165 and thus opcodes are broken" % op_count ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveMatch", "WaveMatch", "returns the bitmask of active lanes that have the same value", @@ -3867,7 +3945,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveMultiPrefixOp", "WaveMultiPrefixOp", "returns the result of the operation on groups of lanes identified by a bitmask", @@ -3910,7 +3988,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "WaveMultiPrefixBitCount", "WaveMultiPrefixBitCount", "returns the count of bits set to 1 on groups of lanes identified by a bitmask", @@ -3927,7 +4005,7 @@ def UFI(name, **mappings): ) # Mesh Shader - self.add_dxil_op( + op_table.add_dxil_op( "SetMeshOutputCounts", "SetMeshOutputCounts", "Mesh shader intrinsic SetMeshOutputCounts", @@ -3939,7 +4017,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "numPrimitives", "number of output primitives"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "EmitIndices", "EmitIndices", "emit a primitive's vertex indices in a mesh shader", @@ -3959,7 +4037,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GetMeshPayload", "GetMeshPayload", "get the mesh payload which is from amplification shader", @@ -3967,7 +4045,7 @@ def UFI(name, **mappings): "ro", [db_dxil_param(0, "$o", "", "mesh payload result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "StoreVertexOutput", "StoreVertexOutput", "stores the value to mesh shader vertex output", @@ -3985,7 +4063,7 @@ def UFI(name, **mappings): ], counters=("sig_st",), ) - self.add_dxil_op( + op_table.add_dxil_op( "StorePrimitiveOutput", "StorePrimitiveOutput", "stores the value to mesh shader primitive output", @@ -4005,7 +4083,7 @@ def UFI(name, **mappings): ) # Amplification Shader - self.add_dxil_op( + op_table.add_dxil_op( "DispatchMesh", "DispatchMesh", "Amplification shader intrinsic DispatchMesh", @@ -4021,7 +4099,7 @@ def UFI(name, **mappings): ) # Sampler feedback - self.add_dxil_op( + op_table.add_dxil_op( "WriteSamplerFeedback", "WriteSamplerFeedback", "updates a feedback texture for a sampling operation", @@ -4042,7 +4120,7 @@ def UFI(name, **mappings): ], counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "WriteSamplerFeedbackBias", "WriteSamplerFeedbackBias", "updates a feedback texture for a sampling operation with a bias on the mipmap level", @@ -4064,7 +4142,7 @@ def UFI(name, **mappings): ], counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "WriteSamplerFeedbackLevel", "WriteSamplerFeedbackLevel", "updates a feedback texture for a sampling operation with a mipmap-level offset", @@ -4085,7 +4163,7 @@ def UFI(name, **mappings): ], counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "WriteSamplerFeedbackGrad", "WriteSamplerFeedbackGrad", "updates a feedback texture for a sampling operation with explicit gradients", @@ -4141,7 +4219,7 @@ def UFI(name, **mappings): ) # RayQuery - self.add_dxil_op( + op_table.add_dxil_op( "AllocateRayQuery", "AllocateRayQuery", "allocates space for RayQuery and return handle", @@ -4159,7 +4237,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_TraceRayInline", "RayQuery_TraceRayInline", "initializes RayQuery for raytrace", @@ -4197,7 +4275,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_Proceed", "RayQuery_Proceed", "advances a ray query", @@ -4209,7 +4287,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_Abort", "RayQuery_Abort", "aborts a ray query", @@ -4221,7 +4299,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommitNonOpaqueTriangleHit", "RayQuery_CommitNonOpaqueTriangleHit", "commits a non opaque triangle hit", @@ -4233,7 +4311,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommitProceduralPrimitiveHit", "RayQuery_CommitProceduralPrimitiveHit", "commits a procedural primitive hit", @@ -4248,371 +4326,371 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedStatus", "RayQuery_StateScalar", "returns uint status (COMMITTED_STATUS) of the committed hit in a ray query", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateType", "RayQuery_StateScalar", "returns uint candidate type (CANDIDATE_TYPE) of the current hit candidate in a ray query, after Proceed() has returned true", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateObjectToWorld3x4", "RayQuery_StateMatrix", "returns matrix for transforming from object-space to world-space for a candidate hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i32", "row", "row [0..2], relative to the element"), db_dxil_param(4, "i8", "col", "column [0..3], relative to the element"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateWorldToObject3x4", "RayQuery_StateMatrix", "returns matrix for transforming from world-space to object-space for a candidate hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i32", "row", "row [0..2], relative to the element"), db_dxil_param(4, "i8", "col", "column [0..3], relative to the element"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedObjectToWorld3x4", "RayQuery_StateMatrix", "returns matrix for transforming from object-space to world-space for a Committed hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i32", "row", "row [0..2], relative to the element"), db_dxil_param(4, "i8", "col", "column [0..3], relative to the element"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedWorldToObject3x4", "RayQuery_StateMatrix", "returns matrix for transforming from world-space to object-space for a Committed hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i32", "row", "row [0..2], relative to the element"), db_dxil_param(4, "i8", "col", "column [0..3], relative to the element"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateProceduralPrimitiveNonOpaque", "RayQuery_StateScalar", "returns if current candidate procedural primitive is non opaque", "1", "ro", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateTriangleFrontFace", "RayQuery_StateScalar", "returns if current candidate triangle is front facing", "1", "ro", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedTriangleFrontFace", "RayQuery_StateScalar", "returns if current committed triangle is front facing", "1", "ro", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateTriangleBarycentrics", "RayQuery_StateVector", "returns candidate triangle hit barycentrics", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedTriangleBarycentrics", "RayQuery_StateVector", "returns committed triangle hit barycentrics", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_RayFlags", "RayQuery_StateScalar", "returns ray flags", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_WorldRayOrigin", "RayQuery_StateVector", "returns world ray origin", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_WorldRayDirection", "RayQuery_StateVector", "returns world ray direction", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_RayTMin", "RayQuery_StateScalar", "returns float representing the parametric starting point for the ray.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateTriangleRayT", "RayQuery_StateScalar", "returns float representing the parametric point on the ray for the current candidate triangle hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedRayT", "RayQuery_StateScalar", "returns float representing the parametric point on the ray for the current committed hit.", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateInstanceIndex", "RayQuery_StateScalar", "returns candidate hit instance index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateInstanceID", "RayQuery_StateScalar", "returns candidate hit instance ID", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateGeometryIndex", "RayQuery_StateScalar", "returns candidate hit geometry index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidatePrimitiveIndex", "RayQuery_StateScalar", "returns candidate hit geometry index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateObjectRayOrigin", "RayQuery_StateVector", "returns candidate hit object ray origin", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateObjectRayDirection", "RayQuery_StateVector", "returns candidate object ray direction", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedInstanceIndex", "RayQuery_StateScalar", "returns committed hit instance index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedInstanceID", "RayQuery_StateScalar", "returns committed hit instance ID", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedGeometryIndex", "RayQuery_StateScalar", "returns committed hit geometry index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedPrimitiveIndex", "RayQuery_StateScalar", "returns committed hit geometry index", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedObjectRayOrigin", "RayQuery_StateVector", "returns committed hit object ray origin", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedObjectRayDirection", "RayQuery_StateVector", "returns committed object ray direction", "f", "ro", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), db_dxil_param(3, "i8", "component", "component [0..2]", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GeometryIndex", "GeometryIndex", "The autogenerated index of the current geometry in the bottom-level structure", @@ -4621,38 +4699,38 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CandidateInstanceContributionToHitGroupIndex", "RayQuery_StateScalar", "returns candidate hit InstanceContributionToHitGroupIndex", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "RayQuery_CommittedInstanceContributionToHitGroupIndex", "RayQuery_StateScalar", "returns committed hit InstanceContributionToHitGroupIndex", "i", "ro", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "i32", "rayQueryHandle", "RayQuery handle"), ], ) # End of DXIL 1.5 opcodes. - op_count = self.set_op_count_for_version(1, 5) + op_count = op_table.set_op_count_for_version(1, 5) assert op_count == 216, ( "216 is expected next operation index but encountered %d and thus opcodes are broken" % op_count ) - self.add_dxil_op( + op_table.add_dxil_op( "AnnotateHandle", "AnnotateHandle", "annotate handle with resource properties", @@ -4671,7 +4749,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CreateHandleFromBinding", "CreateHandleFromBinding", "create resource handle from binding", @@ -4693,7 +4771,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CreateHandleFromHeap", "CreateHandleFromHeap", "create resource handle from heap", @@ -4719,7 +4797,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "Unpack4x8", "Unpack4x8", "unpacks 4 8-bit signed or unsigned values into int32 or int16 vector", @@ -4732,7 +4810,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "Pack4x8", "Pack4x8", "packs vector of 4 signed or unsigned values into a packed datatype, drops or clamps unused bits", @@ -4748,7 +4826,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "IsHelperLane", "IsHelperLane", "returns true on helper lanes in pixel shaders", @@ -4758,7 +4836,7 @@ def UFI(name, **mappings): ) # End of DXIL 1.6 opcodes. - op_count = self.set_op_count_for_version(1, 6) + op_count = op_table.set_op_count_for_version(1, 6) assert op_count == 222, ( "222 is expected next operation index but encountered %d and thus opcodes are broken" % op_count @@ -4772,7 +4850,7 @@ def UFI(name, **mappings): (1, "All", "true if all conditions are true in this quad"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "QuadVote", "QuadVote", "compares boolean accross a quad", @@ -4792,7 +4870,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureGatherRaw", "TextureGatherRaw", "Gather raw elements from 4 texels with no type conversions (SRV type is constrained)", @@ -4831,7 +4909,7 @@ def UFI(name, **mappings): counters=("tex_norm",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleCmpLevel", "SampleCmpLevel", "samples a texture and compares a single component against the specified comparison value", @@ -4878,7 +4956,7 @@ def UFI(name, **mappings): counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "TextureStoreSample", "TextureStoreSample", "stores texel data at specified sample index", @@ -4903,17 +4981,17 @@ def UFI(name, **mappings): ) # End of DXIL 1.7 opcodes. - op_count = self.set_op_count_for_version(1, 7) + op_count = op_table.set_op_count_for_version(1, 7) assert op_count == 226, ( "226 is expected next operation index but encountered %d and thus opcodes are broken" % op_count ) # Reserved ops - self.reserve_dxil_op_range("Reserved", 12) + op_table.reserve_dxil_op_range("Reserved", 12) # Work Graph - self.add_dxil_op( + op_table.add_dxil_op( "AllocateNodeOutputRecords", "AllocateNodeOutputRecords", "returns a handle for the output records", @@ -4927,7 +5005,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GetNodeRecordPtr", "GetNodeRecordPtr", "retrieve node input/output record pointer in address space 6", @@ -4941,7 +5019,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "arrayIndex", "array index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "IncrementOutputCount", "IncrementOutputCount", "Select the next logical output count for an EmptyNodeOutput for the whole group or per thread.", @@ -4956,7 +5034,7 @@ def UFI(name, **mappings): db_dxil_param(4, "i1", "perThread", "perThread flag", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "OutputComplete", "OutputComplete", "indicates all outputs for a given records are complete", @@ -4967,7 +5045,7 @@ def UFI(name, **mappings): db_dxil_param(2, "noderecordhandle", "output", "handle of record"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GetInputRecordCount", "GetInputRecordCount", "returns the number of records that have been coalesced into the current thread group", @@ -4978,7 +5056,7 @@ def UFI(name, **mappings): db_dxil_param(2, "noderecordhandle", "input", "handle of input record"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "FinishedCrossGroupSharing", "FinishedCrossGroupSharing", "returns true if the current thread group is the last to access the input", @@ -4994,7 +5072,7 @@ def UFI(name, **mappings): db_dxil_param(2, "noderecordhandle", "input", "handle of input record"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "BarrierByMemoryType", "BarrierByMemoryType", "Request a barrier for a set of memory types and/or thread group execution sync", @@ -5011,7 +5089,7 @@ def UFI(name, **mappings): ], counters=("barrier",), ) - self.add_dxil_op( + op_table.add_dxil_op( "BarrierByMemoryHandle", "BarrierByMemoryHandle", "Request a barrier for just the memory used by the specified object", @@ -5026,7 +5104,7 @@ def UFI(name, **mappings): ], counters=("barrier",), ) - self.add_dxil_op( + op_table.add_dxil_op( "BarrierByNodeRecordHandle", "BarrierByNodeRecordHandle", "Request a barrier for just the memory used by the node record", @@ -5041,7 +5119,7 @@ def UFI(name, **mappings): ], counters=("barrier",), ) - self.add_dxil_op( + op_table.add_dxil_op( "CreateNodeOutputHandle", "createNodeOutputHandle", "Creates a handle to a NodeOutput", @@ -5052,7 +5130,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "MetadataIdx", "metadata index", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "IndexNodeHandle", "IndexNodeHandle", "returns the handle for the location in the output node array at the indicated index", @@ -5069,7 +5147,7 @@ def UFI(name, **mappings): db_dxil_param(3, "i32", "ArrayIndex", "array index"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "AnnotateNodeHandle", "AnnotateNodeHandle", "annotate handle with node properties", @@ -5087,7 +5165,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "CreateNodeInputRecordHandle", "CreateNodeInputRecordHandle", "create a handle for an InputRecord", @@ -5098,7 +5176,7 @@ def UFI(name, **mappings): db_dxil_param(2, "i32", "MetadataIdx", "metadata index", is_const=True), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "AnnotateNodeRecordHandle", "AnnotateNodeRecordHandle", "annotate handle with node record properties", @@ -5120,7 +5198,7 @@ def UFI(name, **mappings): ), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "NodeOutputIsValid", "NodeOutputIsValid", "returns true if the specified output node is present in the work graph", @@ -5131,7 +5209,7 @@ def UFI(name, **mappings): db_dxil_param(2, "nodehandle", "output", "handle of output node"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "GetRemainingRecursionLevels", "GetRemainingRecursionLevels", "returns how many levels of recursion remain", @@ -5141,7 +5219,7 @@ def UFI(name, **mappings): ) # Comparison Sampling - self.add_dxil_op( + op_table.add_dxil_op( "SampleCmpGrad", "SampleCmpGrad", "samples a texture using a gradient and compares a single component against the specified comparison value", @@ -5219,7 +5297,7 @@ def UFI(name, **mappings): counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "SampleCmpBias", "SampleCmpBias", "samples a texture after applying the input bias to the mipmap level and compares a single component against the specified comparison value", @@ -5262,7 +5340,7 @@ def UFI(name, **mappings): counters=("tex_cmp",), ) - self.add_dxil_op( + op_table.add_dxil_op( "StartVertexLocation", "StartVertexLocation", "returns the BaseVertexLocation from DrawIndexedInstanced or StartVertexLocation from DrawInstanced", @@ -5271,7 +5349,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "i32", "", "result")], ) - self.add_dxil_op( + op_table.add_dxil_op( "StartInstanceLocation", "StartInstanceLocation", "returns the StartInstanceLocation from Draw*Instanced", @@ -5281,14 +5359,14 @@ def UFI(name, **mappings): ) # End of DXIL 1.8 opcodes. - op_count = self.set_op_count_for_version(1, 8) + op_count = op_table.set_op_count_for_version(1, 8) assert op_count == 258, ( "258 is expected next operation index but encountered %d and thus opcodes are broken" % op_count ) # RayQuery - self.add_dxil_op( + op_table.add_dxil_op( "AllocateRayQuery2", "AllocateRayQuery2", "allocates space for RayQuery and return handle", @@ -5314,10 +5392,10 @@ def UFI(name, **mappings): ) # Reserved block A - self.reserve_dxil_op_range("ReservedA", 3) + op_table.reserve_dxil_op_range("ReservedA", 3) # Shader Execution Reordering - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_TraceRay", "HitObject_TraceRay", "Analogous to TraceRay but without invoking CH/MS and returns the intermediate state as a HitObject", @@ -5378,7 +5456,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_FromRayQuery", "HitObject_FromRayQuery", "Creates a new HitObject representing a committed hit from a RayQuery", @@ -5392,7 +5470,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_FromRayQueryWithAttrs", "HitObject_FromRayQueryWithAttrs", "Creates a new HitObject representing a committed hit from a RayQuery and committed attributes", @@ -5413,7 +5491,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_MakeMiss", "HitObject_MakeMiss", "Creates a new HitObject representing a miss", @@ -5434,7 +5512,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_MakeNop", "HitObject_MakeNop", "Creates an empty nop HitObject", @@ -5443,7 +5521,7 @@ def UFI(name, **mappings): [db_dxil_param(0, "hit_object", "", "Empty nop HitObject")], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_Invoke", "HitObject_Invoke", "Represents the invocation of the CH/MS shader represented by the HitObject", @@ -5461,7 +5539,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "MaybeReorderThread", "MaybeReorderThread", "Reorders the current thread", @@ -5480,79 +5558,79 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_IsMiss", "HitObject_StateScalar", "Returns `true` if the HitObject represents a miss", "1", "rn", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_IsHit", "HitObject_StateScalar", "Returns `true` if the HitObject is a NOP-HitObject", "1", "rn", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_IsNop", "HitObject_StateScalar", "Returns `true` if the HitObject represents a nop", "1", "rn", [ - db_dxil_param(0, "i1", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_RayFlags", "HitObject_StateScalar", "Returns the ray flags set in the HitObject", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_RayTMin", "HitObject_StateScalar", "Returns the TMin value set in the HitObject", "f", "rn", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_RayTCurrent", "HitObject_StateScalar", "Returns the current T value set in the HitObject", "f", "rn", [ - db_dxil_param(0, "f", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_WorldRayOrigin", "HitObject_StateVector", "Returns the ray origin in world space", @@ -5565,7 +5643,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_WorldRayDirection", "HitObject_StateVector", "Returns the ray direction in world space", @@ -5578,7 +5656,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_ObjectRayOrigin", "HitObject_StateVector", "Returns the ray origin in object space", @@ -5591,7 +5669,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_ObjectRayDirection", "HitObject_StateVector", "Returns the ray direction in object space", @@ -5604,7 +5682,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_ObjectToWorld3x4", "HitObject_StateMatrix", "Returns the object to world space transformation matrix in 3x4 form", @@ -5630,7 +5708,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_WorldToObject3x4", "HitObject_StateMatrix", "Returns the world to object space transformation matrix in 3x4 form", @@ -5656,79 +5734,79 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_GeometryIndex", "HitObject_StateScalar", "Returns the geometry index committed on hit", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_InstanceIndex", "HitObject_StateScalar", "Returns the instance index committed on hit", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_InstanceID", "HitObject_StateScalar", "Returns the instance id committed on hit", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_PrimitiveIndex", "HitObject_StateScalar", "Returns the primitive index committed on hit", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_HitKind", "HitObject_StateScalar", "Returns the HitKind of the hit", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_ShaderTableIndex", "HitObject_StateScalar", "Returns the shader table index set for this HitObject", "i", "rn", [ - db_dxil_param(0, "i32", "", "operation result"), + db_dxil_param(0, "$o", "", "operation result"), db_dxil_param(2, "hit_object", "hitObject", "hit"), ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_SetShaderTableIndex", "HitObject_SetShaderTableIndex", "Returns a HitObject with updated shader table index", @@ -5743,7 +5821,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_LoadLocalRootTableConstant", "HitObject_LoadLocalRootTableConstant", "Returns the root table constant for this HitObject and offset", @@ -5756,7 +5834,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "HitObject_Attributes", "HitObject_Attributes", "Returns the attributes set for this HitObject", @@ -5771,13 +5849,13 @@ def UFI(name, **mappings): ], ) - self.reserve_dxil_op_range("ReservedB", 3, 28) + op_table.reserve_dxil_op_range("ReservedB", 3, 28) # Reserved block C - self.reserve_dxil_op_range("ReservedC", 10) + op_table.reserve_dxil_op_range("ReservedC", 10) # Long Vectors - self.add_dxil_op( + op_table.add_dxil_op( "RawBufferVectorLoad", "RawBufferVectorLoad", "reads from a raw buffer and structured buffer", @@ -5809,7 +5887,7 @@ def UFI(name, **mappings): counters=("tex_load",), ) - self.add_dxil_op( + op_table.add_dxil_op( "RawBufferVectorStore", "RawBufferVectorStore", "writes to a RWByteAddressBuffer or RWStructuredBuffer", @@ -5842,7 +5920,7 @@ def UFI(name, **mappings): counters=("tex_store",), ) - self.add_dxil_op( + op_table.add_dxil_op( "MatVecMul", "MatVecMul", "Multiplies a MxK dimension matrix and a K sized input vector", @@ -5865,7 +5943,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "MatVecMulAdd", "MatVecMulAdd", "multiplies a MxK dimension matrix and a K sized input vector and adds an M-sized bias vector", @@ -5893,7 +5971,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "OuterProductAccumulate", "OuterProductAccumulate", "Computes the outer product between column vectors and an MxN matrix is accumulated component-wise atomically (with device scope) in memory", @@ -5917,7 +5995,7 @@ def UFI(name, **mappings): ], ) - self.add_dxil_op( + op_table.add_dxil_op( "VectorAccumulate", "VectorAccumulate", "Accumulates the components of a vector component-wise atomically (with device scope) to the corresponding elements of an array in memory", @@ -5932,7 +6010,7 @@ def UFI(name, **mappings): ) # Long Vector Reduction - self.add_dxil_op( + op_table.add_dxil_op( "VectorReduceAnd", "VectorReduce", "Bitwise AND reduction of the vector returning a scalar", @@ -5947,7 +6025,7 @@ def UFI(name, **mappings): "uints", ), ) - self.add_dxil_op( + op_table.add_dxil_op( "VectorReduceOr", "VectorReduce", "Bitwise OR reduction of the vector returning a scalar", @@ -5964,7 +6042,7 @@ def UFI(name, **mappings): ) # Long Vector Dot - self.add_dxil_op( + op_table.add_dxil_op( "FDot", "Dot", "computes the n-dimensional vector dot-product", @@ -5979,22 +6057,17 @@ def UFI(name, **mappings): ) # End of DXIL 1.9 opcodes. - op_count = self.set_op_count_for_version(1, 9) + op_count = op_table.set_op_count_for_version(1, 9) assert op_count == 312, ( "312 is expected next operation index but encountered %d and thus opcodes are broken" % op_count ) - def populate_experimental_ops(self): - "Populate experimental DXIL operations." - self.set_dxil_op_table("ExperimentalOps") - self.populate_ExperimentalOps_ops() - self.set_dxil_op_table() - - def populate_ExperimentalOps_ops(self): - "Populate experimental DXIL operations for ExperimentalOps." + def populate_ExperimentalOps(self): + "Populate DXIL operations for ExperimentalOps." + op_table = self.add_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations") # Add Nop to test experimental table infrastructure. - self.add_dxil_op( + op_table.add_dxil_op( "ExperimentalNop", "Nop", "nop does nothing", @@ -6006,8 +6079,17 @@ def populate_ExperimentalOps_ops(self): ) def finalize_dxil_operations(self): + "Finalize DXIL operations by setting properties and verifying consistency." + + # Sort tables by ID + self.op_tables.sort(key=lambda t: t.id) + + # Add all ops from tables to combined instruction list + for table in self.op_tables: + for op in table: + self.add_inst(op) + # Set interesting properties. - self.build_indices() for ( i ) in "CalculateLOD,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY,Sample,SampleBias,SampleCmp,SampleCmpBias".split( @@ -6022,43 +6104,48 @@ def finalize_dxil_operations(self): # TODO - some arguments are required to be immediate constants in DXIL, eg resource kinds; add this information # consider - report instructions that are overloaded on a single type, then turn them into non-overloaded version of that type - for table in self.dxil_op_tables: - insts = [i for i in table.instr if i.is_dxil_op] - self.verify_dense(insts, lambda x: x.dxil_opid, lambda x: x.name) - for i in insts: + for table in self.op_tables: + self.verify_dense(table, lambda x: x.dxil_opid, lambda x: x.name) + for i in table: assert i.table_id() == table.id, ( "dxil op %s has table id %d inconsistent with containing table id %d" % (i.name, i.table_id(), table.id) ) self.verify_dense(i.ops, lambda x: x.pos, lambda x: i.name) - # Verify that all operations in each class have the same signature. + self.verify_dxil_op_classes() + + def verify_dxil_op_classes(self): + "Verify that all DXIL operations in each class have the same signature." import itertools - class_sort_func = lambda x, y: x < y class_key_func = lambda x: x.dxil_class - instr_ordered_by_class = sorted( - [i for i in self.instr if i.is_dxil_op], key=class_key_func - ) + instr_ordered_by_class = sorted(self.get_dxil_ops(), key=class_key_func) instr_grouped_by_class = itertools.groupby( instr_ordered_by_class, key=class_key_func ) def calc_oload_sig(inst): - result = "" - for o in inst.ops: - result += o.llvm_type + # if function class is ever overloaded, no "v" oload should be used + oload = inst.oload_types == "v" and "void" or "overloaded" + result = f"{inst.fn_attr}|{oload}(" + result += ",".join([o.llvm_type for o in inst.ops]) + ")" return result for k, g in instr_grouped_by_class: group = list(g) if len(group) > 1: first = group[0] - first_group = calc_oload_sig(first) + first_sig = calc_oload_sig(first) for other in group[1:]: - other_group = calc_oload_sig(other) - # TODO: uncomment assert when opcodes are fixed - # assert first_group == other_group, "overload signature %s for instruction %s differs from %s in %s" % (first.name, first_group, other.name, other_group) + if other.name == "WavePrefixBitCount": + # known exception - this op is overloaded but has different signature for "v" oload + continue + other_sig = calc_oload_sig(other) + assert first_sig == other_sig, ( + "overload signature '%s' for DXIL op %s differs from '%s' in %s" % + (first_sig, first.name, other_sig, other.name) + ) def populate_extended_docs(self): "Update the documentation with text from external files." @@ -7145,7 +7232,7 @@ def add_pass(name, type_name, doc, opts): self.pass_idx_args.add(anarg.name) def build_semantics(self): - SemanticKind = db_dxil_enum( + SemanticKind = self.add_enum_type( "SemanticKind", "Semantic kind; Arbitrary or specific system value.", [ @@ -7185,8 +7272,7 @@ def build_semantics(self): (33, "Invalid", ""), ], ) - self.enums.append(SemanticKind) - SigPointKind = db_dxil_enum( + SigPointKind = self.add_enum_type( "SigPointKind", "Signature Point is more specific than shader stage or signature as it is unique in both stage and item dimensionality or frequency.", [ @@ -7233,8 +7319,7 @@ def build_semantics(self): (21, "Invalid", ""), ], ) - self.enums.append(SigPointKind) - PackingKind = db_dxil_enum( + self.add_enum_type( "PackingKind", "Kind of signature point", [ @@ -7247,7 +7332,7 @@ def build_semantics(self): ], ) - Float32DenormMode = db_dxil_enum( + self.add_enum_type( "Float32DenormMode", "float32 denorm behavior", [ @@ -7261,7 +7346,6 @@ def build_semantics(self): (7, "Reserve7", "Reserved Value. Not used for now"), ], ) - self.enums.append(Float32DenormMode) SigPointCSV = """ SigPoint, Related, ShaderKind, PackingKind, SignatureKind @@ -7299,8 +7383,7 @@ def build_semantics(self): assert False and "SigPointKind does not align with SigPointCSV row labels" self.sigpoint_table = table - self.enums.append(PackingKind) - SemanticInterpretationKind = db_dxil_enum( + self.add_enum_type( "SemanticInterpretationKind", "Defines how a semantic is interpreted at a particular SignaturePoint", [ @@ -7329,7 +7412,6 @@ def build_semantics(self): (9, "Invalid", ""), ], ) - self.enums.append(SemanticInterpretationKind) # The following has SampleIndex, Coverage, and InnerCoverage as loaded with instructions rather than from the signature SemanticInterpretationCSV = """ @@ -8613,20 +8695,19 @@ def build_valrules(self): "UNI": "Uniform analysis", "DECL": "Declaration", } - valrule_enum = db_dxil_enum("ValidationRule", "Known validation rules") + valrule_enum = self.add_enum_type("ValidationRule", "Known validation rules") valrule_enum.is_internal = True for vr in self.val_rules: vr.category = cat_names[vr.group_name] - vrval = db_dxil_enum_value(vr.enum_name, vr.rule_id, vr.doc) + vrval = valrule_enum.add_value(vr.rule_id, vr.enum_name, vr.doc) vrval.category = vr.category vrval.err_msg = vr.err_msg - valrule_enum.values.append(vrval) self.enums.append(valrule_enum) def populate_counters(self): self.llvm_op_counters = set() self.dxil_op_counters = set() - for i in self.instr: + for i in self.get_all_insts(): counters = getattr(i, "props", {}).get("counters", ()) if i.is_dxil_op: self.dxil_op_counters.update(counters) @@ -8648,16 +8729,17 @@ def add_valrule_msg(self, name, desc, err_msg): ) def add_inst(self, i): - assert i.table_id() == self.cur_table.id, "Instruction table mismatch" - self.cur_table.instr.append(i) - self.instr.append(i) + if i.name != "UDiv": + # These should not overlap, but UDiv is a known collision. + assert i.name not in self.name_idx, f"Duplicate instruction name: {i.name}" + self.name_idx[i.name] = i + if not i.is_dxil_op: + self._llvm_insts.append(i) + return i def add_llvm_instr( self, kind, llvm_id, name, llvm_name, doc, oload_types, op_params, **props ): - assert ( - self.cur_table.name == "CoreOps" - ), "LLVM instructions can only be added to the CoreOps table" i = db_dxil_inst( name, llvm_id=llvm_id, @@ -8667,73 +8749,18 @@ def add_llvm_instr( oload_types=oload_types, ) i.props = props - self.add_inst(i) - - def add_dxil_op( - self, name, code_class, doc, oload_types, fn_attr, op_params, **props - ): - # The return value is parameter 0, insert the opcode as 1. - op_params.insert(1, self.opcode_param) - i = db_dxil_inst( - name, - llvm_id=self.call_instr.llvm_id, - llvm_name=self.call_instr.llvm_name, - dxil_op=name, - dxil_opid=self.cur_table.next_id(), - dxil_table=self.cur_table.name, - doc=doc, - ops=op_params, - dxil_class=code_class, - oload_types=oload_types, - fn_attr=fn_attr, - ) - i.props = props - self.add_inst(i) - - def add_dxil_op_reserved(self, name): - # The return value is parameter 0, insert the opcode as 1. - op_params = [db_dxil_param(0, "v", "", "reserved"), self.opcode_param] - i = db_dxil_inst( - name, - llvm_id=self.call_instr.llvm_id, - llvm_name=self.call_instr.llvm_name, - dxil_op=name, - dxil_opid=self.cur_table.next_id(), - dxil_table=self.cur_table.name, - doc="reserved", - ops=op_params, - dxil_class="Reserved", - oload_types="v", - fn_attr="", - ) - self.add_inst(i) - - def reserve_dxil_op_range(self, group_name, count, start_reserved_id=0): - "Reserve a range of dxil opcodes for future use; returns next id" - for i in range(0, count): - self.add_dxil_op_reserved( - "{0}{1}".format(group_name, start_reserved_id + i) - ) - - def get_instr_by_llvm_name(self, llvm_name): - "Return the instruction with the given LLVM name" - return next(i for i in self.instr if i.llvm_name == llvm_name) - - def get_dxil_insts(self): - for i in self.instr: - if i.dxil_op != "": - yield i + return self.add_inst(i) def print_stats(self): "Print some basic statistics on the instruction database." - print("Instruction count: %d" % len(self.instr)) + print("Instruction count: %d" % len(self.get_all_insts())) print( "Max parameter count in instruction: %d" - % max(len(i.ops) - 1 for i in self.instr) + % max(len(i.ops) - 1 for i in self.get_all_insts()) ) print( "Parameter count: %d" - % sum(len(i.ops) - 1 for i in self.instr) + % sum(len(i.ops) - 1 for i in self.get_all_insts()) ) diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index d2427f429d..34cee13186 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -151,12 +151,10 @@ class db_docsref_gen: def __init__(self, db): self.db = db - instrs = [i for i in self.db.instr if i.is_dxil_op] - instrs = sorted( - instrs, + self.instrs = sorted( + self.db.get_dxil_ops(), key=lambda v: ("" if v.category == None else v.category) + "." + v.name, ) - self.instrs = instrs val_rules = sorted( db.val_rules, key=lambda v: ("" if v.category == None else v.category) + "." + v.name, @@ -328,7 +326,7 @@ def op_set_const_expr(self, o): ) def print_body(self): - for i in self.db.instr: + for i in self.db.get_all_insts(): if i.is_reserved: continue if i.inst_helper_prefix: @@ -485,12 +483,12 @@ def print_rdat_enum(self, e, **kwargs): print(line_format.format(name=v.name, value=v.value, doc=v.doc)) def print_extended_table_opcode_enums(self): - for table in self.db.dxil_op_tables[1:]: # Skip Core table + for table in self.db.op_tables[1:]: # Skip Core table print(f"namespace {table.name} {{") print(f"static const OpCodeTableID TableID = OpCodeTableID::{table.name};") self.print_enum(table.op_enum) print(f"}} // namespace {table.name}") - print(f"static const unsigned NumOpCodeTables = {len(self.db.dxil_op_tables)};") + print(f"static const unsigned NumOpCodeTables = {len(self.db.op_tables)};") def print_content(self): for e in sorted(self.db.enums, key=lambda e: e.name): @@ -502,7 +500,6 @@ class db_oload_gen: def __init__(self, db): self.db = db - self.instrs = [i for i in self.db.instr if i.is_dxil_op] def print_content(self): self.print_opfunc_props() @@ -511,7 +508,7 @@ def print_content(self): def print_opfunc_props(self): # Print all the tables for OP::m_OpCodeProps - for table in self.db.dxil_op_tables: + for table in self.db.op_tables: self.print_opfunc_props_for_table(table) print() # Print the overall table of tables @@ -519,7 +516,7 @@ def print_opfunc_props(self): print( f"OP::OpCodeTable OP::g_OpCodeTables[DXIL::NumOpCodeTables] = {{" ) - for table in self.db.dxil_op_tables: + for table in self.db.op_tables: print( f" {{ OP::OpCodeTableID::{table.name}, " + f"{table.name}_OpCodeProps, " @@ -529,7 +526,6 @@ def print_opfunc_props(self): def print_opfunc_props_for_table(self, table): print(f"static const OP::OpCodeProperty {table.name}_OpCodeProps[] = {{") - instrs = [i for i in table.instr if i.is_dxil_op] last_category = None lower_exceptions = { @@ -556,7 +552,7 @@ def print_opfunc_props_for_table(self, table): oloads_fn = lambda oloads: ( "{" + ",".join(["{0x%x}" % m for m in oloads]) + "}" ) - for i in instrs: + for i in table: if last_category != i.category: if last_category != None: print("") @@ -656,7 +652,7 @@ def print_opfunc_table(self): "$x1": "EXT(1);", } last_category = None - for i in self.instrs: + for i in self.db.get_dxil_ops(): if last_category != i.category: if last_category != None: print("") @@ -696,7 +692,7 @@ def print_opfunc_oload_type(self): struct_list = [] extended_list = [] - for instr in self.instrs: + for instr in self.db.get_dxil_ops(): if instr.num_oloads > 1: # Process extended overloads separately. extended_list.append(instr) @@ -930,7 +926,7 @@ def op_const_expr(self, o): ) def print_body(self): - llvm_instrs = [i for i in self.db.instr if i.is_allowed and not i.is_dxil_op] + llvm_instrs = [i for i in self.db.get_llvm_insts() if i.is_allowed] print("static bool IsLLVMInstructionAllowed(llvm::Instruction &I) {") self.print_comment( " // ", @@ -1276,7 +1272,7 @@ def get_instrs_pred(varname, pred, attr_name="dxil_opid"): pred_fn = lambda i: getattr(i, pred) else: pred_fn = pred - llvm_instrs = [i for i in db.instr if pred_fn(i)] + llvm_instrs = [i for i in db.get_all_insts() if pred_fn(i)] result = format_comment( "// ", "Instructions: %s" @@ -1319,7 +1315,7 @@ def get_dxil_op_counters(): def get_instrs_rst(): "Create an rst table of allowed LLVM instructions." db = get_db_dxil() - instrs = [i for i in db.instr if i.is_allowed and not i.is_dxil_op] + instrs = [i for i in db.get_llvm_insts() if i.is_allowed] instrs = sorted(instrs, key=lambda v: v.llvm_id) rows = [] rows.append(["Instruction", "Action", "Operand overloads"]) @@ -1401,7 +1397,7 @@ def get_opcodes_rst(): "Create an rst table for each opcode table" db = get_db_dxil() result = "" - for table in db.dxil_op_tables: + for table in db.op_tables: result += f"\n\nOpcode Table {table.name}, id={table.id}: {table.doc}" result += get_opcodes_rst_for_table(table) return result @@ -1409,7 +1405,7 @@ def get_opcodes_rst(): def get_opcodes_rst_for_table(table): "Create an rst table of opcodes for given opcode table" - instrs = [i for i in table.instr if i.is_allowed and i.is_dxil_op] + instrs = [i for i in table] rows = [] rows.append(["ID", "Name", "Description"]) for i in instrs: @@ -1443,11 +1439,11 @@ def get_valrules_rst(): def get_opsigs(): db = get_db_dxil() result = "" - for table in db.dxil_op_tables: + for table in db.op_tables: result += f"\n\n// Opcode Signatures for Table {table.name}, id={table.id}\n" result += get_opsigs_for_table(table) result += "static const char **OpCodeSignatures[] = {\n" - for table in db.dxil_op_tables: + for table in db.op_tables: result += " OpCodeSignatures_%s,\n" % table.name result += "};\n" return result @@ -1456,7 +1452,7 @@ def get_opsigs(): def get_opsigs_for_table(table): # Create a list of DXIL operation signatures, sorted by ID. db = get_db_dxil() - instrs = [i for i in db.instr if i.is_dxil_op] + instrs = [i for i in db.get_dxil_ops()] # db_dxil already asserts that the numbering is dense. # Create the code to write out. code = f"static const char *OpCodeSignatures_{table.name}[] = {{\n" @@ -1498,9 +1494,8 @@ def get_opsigs_for_table(table): def get_min_sm_and_mask_text(): db = get_db_dxil() - instrs = [i for i in db.instr if i.is_dxil_op] instrs = sorted( - instrs, + db.get_dxil_ops(), key=lambda v: ( v.shader_model, v.shader_model_translated, @@ -1589,9 +1584,8 @@ def flush_instrs(grouped_instrs, last_model, last_model_translated, last_stage): def get_valopcode_sm_text(): db = get_db_dxil() - instrs = [i for i in db.instr if i.is_dxil_op] instrs = sorted( - instrs, key=lambda v: (v.shader_model, v.shader_stages, v.dxil_opid) + db.get_dxil_ops(), key=lambda v: (v.shader_model, v.shader_stages, v.dxil_opid) ) last_model = None last_stage = None From 80c18131833fbcc28aecd7e2afdad2186e4338a4 Mon Sep 17 00:00:00 2001 From: Tex Riddell Date: Wed, 3 Dec 2025 16:54:36 -0800 Subject: [PATCH 17/17] formatting --- utils/hct/hctdb.py | 28 +++++++++++----------------- utils/hct/hctdb_instrhelp.py | 4 +--- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 910944448c..14439c70fc 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -78,9 +78,7 @@ class db_dxil_enum(object): def __init__(self, name, doc, valNameDocTuples=()): self.name = name self.doc = doc - self.values = [ - db_dxil_enum_value(*args) for args in valNameDocTuples - ] + self.values = [db_dxil_enum_value(*args) for args in valNameDocTuples] self.is_internal = False # whether this is never serialized self.last_value_name = None # optional last value name for dense enums self.dxil_version_info = {} # version info for this enum @@ -505,11 +503,11 @@ def get_all_insts(self): def add_dxil_op_table(self, id, name, doc): "Add a new DXIL operation table." - assert name not in self.op_table_idx, ( - f"DXIL op table '{name}' already exists" - ) + assert name not in self.op_table_idx, f"DXIL op table '{name}' already exists" assert id & ~0xFFFF == 0, "DXIL op table ID must fit in high 16 bits" - assert len(self.op_tables) < 2, "Only two DXIL op tables are currently supported" + assert ( + len(self.op_tables) < 2 + ), "Only two DXIL op tables are currently supported" self.op_table_enum.add_value(id, name, doc) table = db_dxil_op_table(self, id, name, doc) self.op_tables.append(table) @@ -1507,13 +1505,7 @@ def populate_llvm_instructions(self): ) # keep track of CallInst used by all DXIL ops self.call_instr = self.add_llvm_instr( - "OTHER", - 49, - "Call", - "CallInst", - "calls a function", - "", - [] + "OTHER", 49, "Call", "CallInst", "calls a function", "", [] ) self.add_llvm_instr( "OTHER", 50, "Select", "SelectInst", "selects an instruction", "", [] @@ -6065,7 +6057,9 @@ def UFI(name, **mappings): def populate_ExperimentalOps(self): "Populate DXIL operations for ExperimentalOps." - op_table = self.add_dxil_op_table(0x8000, "ExperimentalOps", "Experimental DXIL operations") + op_table = self.add_dxil_op_table( + 0x8000, "ExperimentalOps", "Experimental DXIL operations" + ) # Add Nop to test experimental table infrastructure. op_table.add_dxil_op( "ExperimentalNop", @@ -6143,8 +6137,8 @@ def calc_oload_sig(inst): continue other_sig = calc_oload_sig(other) assert first_sig == other_sig, ( - "overload signature '%s' for DXIL op %s differs from '%s' in %s" % - (first_sig, first.name, other_sig, other.name) + "overload signature '%s' for DXIL op %s differs from '%s' in %s" + % (first_sig, first.name, other_sig, other.name) ) def populate_extended_docs(self): diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 34cee13186..cdf422cef3 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -513,9 +513,7 @@ def print_opfunc_props(self): print() # Print the overall table of tables print("// Table of DXIL OpCode Property tables") - print( - f"OP::OpCodeTable OP::g_OpCodeTables[DXIL::NumOpCodeTables] = {{" - ) + print(f"OP::OpCodeTable OP::g_OpCodeTables[DXIL::NumOpCodeTables] = {{") for table in self.db.op_tables: print( f" {{ OP::OpCodeTableID::{table.name}, "