| # This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. |
| ############################################################################### |
| # DXIL information. # |
| ############################################################################### |
| import os |
| |
| 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) |
| self.value = value # Numeric value |
| self.doc = doc # Documentation string |
| self.category = None |
| |
| class db_dxil_enum(object): |
| "A representation for an enumeration type" |
| 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 |
| self.is_internal = False # whether this is never serialized |
| |
| def value_names(self): |
| return [i.name for i in self.values] |
| |
| class db_dxil_inst(object): |
| "A representation for a DXIL instruction" |
| 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.is_bb_terminator = False # whether this is a basic block terminator |
| self.is_binary = False # whether this is an arithmetic binary/logical operator |
| self.is_memory = False # whether this is a memory manipulator operator |
| self.is_cast = False # whether this is a casting operator |
| 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 |
| self.category = "" # classification for this instruction |
| self.doc = "" # the documentation description of this instruction |
| self.remarks = "" # long-form remarks on this instruction |
| self.ops = [] # the operands that this instruction takes |
| self.is_allowed = True # whether this instruction is allowed in a DXIL program |
| self.oload_types = "" # overload types if applicable |
| self.fn_attr = "" # attribute shorthands: rn=does not access memory,ro=only reads from memory, |
| self.is_deriv = False # whether this is some kind of derivative |
| self.is_gradient = False # whether this requires a gradient calculation |
| self.is_wave = False # whether this requires in-wave, cross-lane functionality |
| self.shader_stages = "*" # shader stages to which this applies, * or one or more of cdghpv |
| self.shader_model = 6,0 # minimum shader model required |
| self.inst_helper_prefix = None |
| self.fully_qualified_name_prefix = "hlsl::OP::OpCode" |
| 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" |
| |
| def __str__(self): |
| return self.name |
| |
| def fully_qualified_name(self): |
| return "{}::{}".format(self.fully_qualified_name_prefix, self.name) |
| |
| class db_dxil_metadata(object): |
| "A representation for a metadata record" |
| def __init__(self, name, doc, **kwargs): |
| self.name = name # named metadata, possibly empty |
| self.doc = doc # the documentation description of this record |
| for k,v in list(kwargs.items()): |
| setattr(self, k, v) |
| |
| class db_dxil_param(object): |
| "The parameter description for a DXIL instruction" |
| def __init__(self, pos, llvm_type, name, doc, **kwargs): |
| self.pos = pos # position in parameter list |
| self.llvm_type = llvm_type # llvm type name, $o for overload, $r for resource type, $cb for legacy cbuffer, $u4 for u4 struct |
| self.name = name # short, unique name |
| self.doc = doc # the documentation description of this parameter |
| self.is_const = False # whether this argument requires a constant value in the IR |
| self.enum_name = "" # the name of the enum type if applicable |
| self.max_value = None # the maximum value for this parameter if applicable |
| for k,v in kwargs.items(): |
| setattr(self, k, v) |
| |
| class db_dxil_pass(object): |
| "The description for a DXIL optimization pass" |
| def __init__(self, name, **kwargs): |
| self.name = name # name for the option, typically the command-line switch name |
| self.args = [] # modifiers for the option |
| self.type_name = "" # name of the class that implements the pass |
| self.doc = "" # documentation for the pass |
| for k,v in kwargs.items(): |
| setattr(self, k, v) |
| |
| class db_dxil_pass_arg(object): |
| "An argument to a DXIL optimization pass" |
| def __init__(self, name, **kwargs): |
| self.name = name # name for the option, typically the command-line switch name |
| self.ident = "" # identifier for a parameter or global switch |
| self.is_ctor_param = False # whether this is a constructor parameter |
| for k,v in kwargs.items(): |
| setattr(self, k, v) |
| if self.is_ctor_param: |
| self.is_ctor_param = True |
| |
| class db_dxil_valrule(object): |
| "The description of a validation rule." |
| def __init__(self, name, id, **kwargs): |
| self.name = name.upper() # short, unique name, eg META.KNOWN |
| self.rule_id = id # unique identifier |
| self.enum_name = name.replace(".", "") # remove period for enum name |
| self.group_name = self.name[:self.name.index(".")] # group name, eg META |
| self.rule_name = self.name[self.name.index(".")+1:] # rule name, eg KNOWN |
| self.definition = "Check" + self.group_name + self.rule_name # function name that defines this constraint |
| self.is_disabled = False # True if the validation rule does not apply |
| self.err_msg = "" # error message associated with rule |
| self.category = "" # classification for this rule |
| self.doc = "" # the documentation description of this rule |
| self.shader_stages = "*" # shader stages to which this applies, * or one or more of cdghpv |
| self.shader_model = 6,0 # minimum shader model required |
| for k,v in list(kwargs.items()): |
| setattr(self, k, v) |
| |
| def __str__(self): |
| return self.name |
| |
| 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 = {} |
| |
| self.populate_llvm_instructions() |
| self.call_instr = self.get_instr_by_llvm_name("CallInst") |
| self.populate_dxil_operations() |
| self.build_indices() |
| self.populate_extended_docs() |
| self.populate_categories_and_models() |
| self.build_opcode_enum() |
| self.mark_disallowed_operations() |
| self.populate_metadata() |
| self.populate_passes() |
| self.build_valrules() |
| self.build_semantics() |
| self.build_indices() |
| |
| def __str__(self): |
| return '\n'.join(str(i) for i in self.instr) |
| |
| 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 build_opcode_enum(self): |
| # Build enumeration from instructions |
| OpCodeEnum = db_dxil_enum("OpCode", "Enumeration for operations specified by DXIL") |
| class_dict = {} |
| class_dict["LlvmInst"] = "LLVM Instructions" |
| for i in self.instr: |
| if i.is_dxil_op: |
| v = db_dxil_enum_value(i.dxil_op, i.dxil_opid, i.doc) |
| v.category = i.category |
| class_dict[i.dxil_class] = i.category |
| OpCodeEnum.values.append(v) |
| self.enums.append(OpCodeEnum); |
| OpCodeClass = db_dxil_enum("OpCodeClass", "Groups for DXIL operations with equivalent function templates") |
| OpCodeClass.is_internal = True |
| 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); |
| |
| def mark_disallowed_operations(self): |
| # Disallow indirect branching, unreachable instructions and support for exception unwinding. |
| for i in "IndirectBr,Invoke,Resume,LandingPad,Unreachable".split(","): |
| self.name_idx[i].is_allowed = False |
| for i in "UserOp1,UserOp2,VAArg".split(","): |
| self.name_idx[i].is_allowed = False |
| # Disallow conversions used for pointer math; GEP is used exclusively in the current model. |
| for i in "PtrToInt,IntToPtr".split(","): |
| self.name_idx[i].is_allowed = False |
| # Barrier supersedes Fence. |
| self.name_idx["Fence"].is_allowed = False |
| |
| def verify_dense(self, it, pred, name_proj): |
| val = None |
| for i in it: |
| i_val = pred(i) |
| if not val is None: |
| assert val + 1 == i_val, "values in predicate are not sequential and dense, %d follows %d for %s" % (i_val, val, name_proj(i)) |
| val = i_val |
| |
| def set_op_count_for_version(self, major, minor, op_count): |
| info = self.dxil_version_info.setdefault((major, minor), dict()) |
| info['NumOpCodes'] = op_count |
| info['NumOpClasses'] = len(set([op.dxil_class for op in self.instr])) |
| |
| def populate_categories_and_models(self): |
| "Populate the category and shader_stages member of instructions." |
| for i in "TempRegLoad,TempRegStore,MinPrecXRegLoad,MinPrecXRegStore,LoadInput,StoreOutput".split(","): |
| self.name_idx[i].category = "Temporary, indexable, input, output registers" |
| for i in "FAbs,Saturate,IsNaN,IsInf,IsFinite,IsNormal,Cos,Sin,Tan,Acos,Asin,Atan,Hcos,Hsin,Htan,Exp,Frc,Log,Sqrt,Rsqrt".split(","): |
| self.name_idx[i].category = "Unary float" |
| for i in "Round_ne,Round_ni,Round_pi,Round_z".split(","): |
| self.name_idx[i].category = "Unary float - rounding" |
| for i in "Bfrev,Countbits,FirstbitLo,FirstbitSHi".split(","): |
| self.name_idx[i].category = "Unary int" |
| for i in "FirstbitHi".split(","): |
| self.name_idx[i].category = "Unary uint" |
| for i in "FMax,FMin".split(","): |
| self.name_idx[i].category = "Binary float" |
| for i in "IMax,IMin,Add,Sub,Mul,SDiv,SRem,And,Or,Xor,AShr,LShr,Shl".split(","): |
| self.name_idx[i].category = "Binary int" |
| for i in "UMax,UMin,UMul,UDiv,URem".split(","): |
| self.name_idx[i].category = "Binary uint" |
| for i in "IMul".split(","): |
| self.name_idx[i].category = "Binary int with two outputs" |
| for i in "UMul,UDiv".split(","): # Rename this UDiv OpCode to UDivMod |
| self.name_idx[i].category = "Binary uint with two outputs" |
| for i in "UAddc,USubb".split(","): |
| self.name_idx[i].category = "Binary uint with carry or borrow" |
| for i in "FMad,Fma".split(","): |
| self.name_idx[i].category = "Tertiary float" |
| for i in "IMad,Msad,Ibfe".split(","): |
| self.name_idx[i].category = "Tertiary int" |
| for i in "UMad,Ubfe".split(","): |
| self.name_idx[i].category = "Tertiary uint" |
| for i in "Bfi".split(","): |
| self.name_idx[i].category = "Quaternary" |
| for i in "Dot2,Dot3,Dot4".split(","): |
| self.name_idx[i].category = "Dot" |
| for i in "CreateHandle,CBufferLoad,CBufferLoadLegacy,TextureLoad,TextureStore,BufferLoad,BufferStore,BufferUpdateCounter,CheckAccessFullyMapped,GetDimensions,RawBufferLoad,RawBufferStore".split(","): |
| self.name_idx[i].category = "Resources" |
| for i in "Sample,SampleBias,SampleLevel,SampleGrad,SampleCmp,SampleCmpLevelZero,Texture2DMSGetSamplePosition,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","): |
| self.name_idx[i].category = "Resources - sample" |
| for i in "Sample,SampleBias,SampleCmp,RenderTargetGetSamplePosition,RenderTargetGetSampleCount".split(","): |
| self.name_idx[i].shader_stages = "p" |
| for i in "TextureGather,TextureGatherCmp".split(","): |
| self.name_idx[i].category = "Resources - gather" |
| for i in "AtomicBinOp,AtomicCompareExchange,Barrier".split(","): |
| self.name_idx[i].category = "Synchronization" |
| for i in "CalculateLOD,Discard,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY,EvalSnapped,EvalSampleIndex,EvalCentroid,SampleIndex,Coverage,InnerCoverage,AttributeAtVertex".split(","): |
| self.name_idx[i].category = "Pixel shader" |
| self.name_idx[i].shader_stages = "p" |
| for i in "ThreadId,GroupId,ThreadIdInGroup,FlattenedThreadIdInGroup".split(","): |
| self.name_idx[i].category = "Compute shader" |
| self.name_idx[i].shader_stages = "c" |
| for i in "EmitStream,CutStream,EmitThenCutStream,GSInstanceID".split(","): |
| self.name_idx[i].category = "Geometry shader" |
| self.name_idx[i].shader_stages = "g" |
| for i in "LoadOutputControlPoint,LoadPatchConstant".split(","): |
| self.name_idx[i].category = "Domain and hull shader" |
| self.name_idx[i].shader_stages = "dh" |
| for i in "DomainLocation".split(","): |
| self.name_idx[i].category = "Domain shader" |
| self.name_idx[i].shader_stages = "d" |
| for i in "StorePatchConstant,OutputControlPointID,PrimitiveID".split(","): |
| self.name_idx[i].category = "Hull shader" |
| self.name_idx[i].shader_stages = "gdhp" if i == "PrimitiveID" else "h" |
| for i in "ViewID".split(","): |
| self.name_idx[i].category = "Graphics shader" |
| self.name_idx[i].shader_stages = "vhdgp" |
| for i in "MakeDouble,SplitDouble,LegacyDoubleToFloat,LegacyDoubleToSInt32,LegacyDoubleToUInt32".split(","): |
| self.name_idx[i].category = "Double precision" |
| for i in "CycleCounterLegacy".split(","): |
| 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: |
| if i.name.startswith("Wave") or i.name.startswith("Quad") or i.name == "GlobalOrderedCountInc": |
| i.category = "Wave" |
| i.is_wave = True |
| elif i.name.startswith("Bitcast"): |
| i.category = "Bitcasts with different sizes" |
| for i in "ViewID,AttributeAtVertex".split(","): |
| self.name_idx[i].shader_model = 6,1 |
| for i in "RawBufferLoad,RawBufferStore".split(","): |
| self.name_idx[i].shader_model = 6,2 |
| |
| def populate_llvm_instructions(self): |
| # Add instructions that map to LLVM instructions. |
| # This is basically include\llvm\IR\Instruction.def |
| # |
| # Some instructions don't have their operands defined here because they are |
| # very specific and expanding generality isn't worth it; for example, |
| # branching refers to basic block arguments. |
| retvoid_param = db_dxil_param(0, "v", "", "no return value") |
| retoload_param = db_dxil_param(0, "$o", "", "no return value") |
| oload_all_arith = "hfd1wil" # note that 8 is missing |
| oload_all_arith_v = "v" + oload_all_arith |
| oload_int_arith = "wil" # note that 8 is missing |
| oload_int_arith_b = "1wil" # note that 8 is missing |
| oload_float_arith = "hfd" |
| oload_cast_params = [retoload_param, db_dxil_param(1, "$o", "value", "Value to cast/convert")] |
| oload_binary_params = [retoload_param, |
| db_dxil_param(1, "$o", "a", "first value"), |
| db_dxil_param(2, "$o", "b", "second value")] |
| self.add_llvm_instr("TERM", 1, "Ret", "ReturnInst", "returns a value (possibly void), from a function.", oload_all_arith_v, [retoload_param]) |
| self.add_llvm_instr("TERM", 2, "Br", "BranchInst", "branches (conditional or unconditional)", "", []) |
| self.add_llvm_instr("TERM", 3, "Switch", "SwitchInst", "performs a multiway switch", "", []) |
| self.add_llvm_instr("TERM", 4, "IndirectBr", "IndirectBrInst", "branches indirectly", "", []) |
| self.add_llvm_instr("TERM", 5, "Invoke", "InvokeInst", "invokes function with normal and exceptional returns", "", []) |
| self.add_llvm_instr("TERM", 6, "Resume", "ResumeInst", "resumes the propagation of an exception", "", []) |
| self.add_llvm_instr("TERM", 7, "Unreachable", "UnreachableInst", "is unreachable", "", []) |
| |
| self.add_llvm_instr("BINARY", 8, "Add" , "BinaryOperator", "returns the sum of its two operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 9, "FAdd" , "BinaryOperator", "returns the sum of its two operands", oload_float_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 10, "Sub" , "BinaryOperator", "returns the difference of its two operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 11, "FSub" , "BinaryOperator", "returns the difference of its two operands", oload_float_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 12, "Mul" , "BinaryOperator", "returns the product of its two operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 13, "FMul" , "BinaryOperator", "returns the product of its two operands", oload_float_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 14, "UDiv" , "BinaryOperator", "returns the quotient of its two unsigned operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 15, "SDiv" , "BinaryOperator", "returns the quotient of its two signed operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 16, "FDiv" , "BinaryOperator", "returns the quotient of its two operands", oload_float_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 17, "URem" , "BinaryOperator", "returns the remainder from the unsigned division of its two operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 18, "SRem" , "BinaryOperator", "returns the remainder from the signed division of its two operands", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 19, "FRem" , "BinaryOperator", "returns the remainder from the division of its two operands", oload_float_arith, oload_binary_params) |
| |
| self.add_llvm_instr("BINARY", 20, "Shl", "BinaryOperator", "shifts left (logical)", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 21, "LShr", "BinaryOperator", "shifts right (logical), with zero bit fill", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 22, "AShr", "BinaryOperator", "shifts right (arithmetic), with 'a' operand sign bit fill", oload_int_arith, oload_binary_params) |
| self.add_llvm_instr("BINARY", 23, "And", "BinaryOperator", "returns a bitwise logical and of its two operands", oload_int_arith_b, oload_binary_params) |
| self.add_llvm_instr("BINARY", 24, "Or", "BinaryOperator", "returns a bitwise logical or of its two operands", oload_int_arith_b, oload_binary_params) |
| self.add_llvm_instr("BINARY", 25, "Xor", "BinaryOperator", "returns a bitwise logical xor of its two operands", oload_int_arith_b, oload_binary_params) |
| |
| self.add_llvm_instr("MEMORY", 26, "Alloca", "AllocaInst", "allocates memory on the stack frame of the currently executing function", "", []) |
| self.add_llvm_instr("MEMORY", 27, "Load", "LoadInst", "reads from memory", "", []) |
| self.add_llvm_instr("MEMORY", 28, "Store", "StoreInst", "writes to memory", "", []) |
| self.add_llvm_instr("MEMORY", 29, "GetElementPtr", "GetElementPtrInst", "gets the address of a subelement of an aggregate value", "", []) |
| self.add_llvm_instr("MEMORY", 30, "Fence", "FenceInst", "introduces happens-before edges between operations", "", []) |
| self.add_llvm_instr("MEMORY", 31, "AtomicCmpXchg", "AtomicCmpXchgInst" , "atomically modifies memory", "", []) |
| self.add_llvm_instr("MEMORY", 32, "AtomicRMW", "AtomicRMWInst", "atomically modifies memory", "", []) |
| |
| self.add_llvm_instr("CAST", 33, "Trunc", "TruncInst", "truncates an integer", oload_int_arith_b, oload_cast_params) |
| self.add_llvm_instr("CAST", 34, "ZExt", "ZExtInst", "zero extends an integer", oload_int_arith_b, oload_cast_params) |
| self.add_llvm_instr("CAST", 35, "SExt", "SExtInst", "sign extends an integer", oload_int_arith_b, oload_cast_params) |
| self.add_llvm_instr("CAST", 36, "FPToUI", "FPToUIInst", "converts a floating point to UInt", oload_all_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 37, "FPToSI", "FPToSIInst", "converts a floating point to SInt", oload_all_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 38, "UIToFP", "UIToFPInst", "converts a UInt to floating point", oload_all_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 39, "SIToFP" , "SIToFPInst", "converts a SInt to floating point", oload_all_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 40, "FPTrunc", "FPTruncInst", "truncates a floating point", oload_float_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 41, "FPExt", "FPExtInst", "extends a floating point", oload_float_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 42, "PtrToInt", "PtrToIntInst", "converts a pointer to integer", "i", oload_cast_params) |
| self.add_llvm_instr("CAST", 43, "IntToPtr", "IntToPtrInst", "converts an integer to Pointer", "i", oload_cast_params) |
| self.add_llvm_instr("CAST", 44, "BitCast", "BitCastInst", "performs a bit-preserving type cast", oload_all_arith, oload_cast_params) |
| self.add_llvm_instr("CAST", 45, "AddrSpaceCast", "AddrSpaceCastInst", "casts a value addrspace", "", oload_cast_params) |
| |
| self.add_llvm_instr("OTHER", 46, "ICmp", "ICmpInst", "compares integers", oload_int_arith_b, oload_binary_params) |
| self.add_llvm_instr("OTHER", 47, "FCmp", "FCmpInst", "compares floating points", oload_float_arith, oload_binary_params) |
| self.add_llvm_instr("OTHER", 48, "PHI", "PHINode", "is a PHI node instruction", "", []) |
| self.add_llvm_instr("OTHER", 49, "Call", "CallInst", "calls a function", "", []) |
| self.add_llvm_instr("OTHER", 50, "Select", "SelectInst", "selects an instruction", "", []) |
| self.add_llvm_instr("OTHER", 51, "UserOp1", "Instruction", "may be used internally in a pass", "", []) |
| self.add_llvm_instr("OTHER", 52, "UserOp2", "Instruction", "internal to passes only", "", []) |
| self.add_llvm_instr("OTHER", 53, "VAArg", "VAArgInst", "vaarg instruction", "", []) |
| self.add_llvm_instr("OTHER", 57, "ExtractValue", "ExtractValueInst", "extracts from aggregate", "", []) |
| self.add_llvm_instr("OTHER", 59, "LandingPad", "LandingPadInst", "represents a landing pad", "", []) |
| |
| def populate_dxil_operations(self): |
| # $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 |
| # 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") |
| next_op_idx = 0 |
| self.add_dxil_op("TempRegLoad", next_op_idx, "TempRegLoad", "helper load operation", "hfwi", "ro", [ |
| db_dxil_param(0, "$o", "", "register value"), |
| db_dxil_param(2, "u32", "index", "linearized register index")]) |
| next_op_idx += 1 |
| self.add_dxil_op("TempRegStore", next_op_idx, "TempRegStore", "helper store operation", "hfwi", "", [ |
| retvoid_param, |
| db_dxil_param(2, "u32", "index", "linearized register index"), |
| db_dxil_param(3, "$o", "value", "value to store")]) |
| next_op_idx += 1 |
| self.add_dxil_op("MinPrecXRegLoad", next_op_idx, "MinPrecXRegLoad", "helper load operation for minprecision", "hw", "ro", [ |
| db_dxil_param(0, "$o", "", "register value"), |
| db_dxil_param(2, "pf32", "regIndex", "pointer to indexable register"), |
| db_dxil_param(3, "i32", "index", "index"), |
| db_dxil_param(4, "u8", "component", "component")]) |
| next_op_idx += 1 |
| self.add_dxil_op("MinPrecXRegStore", next_op_idx, "MinPrecXRegStore", "helper store operation for minprecision", "hw", "", [ |
| retvoid_param, |
| db_dxil_param(2, "pf32", "regIndex", "pointer to indexable register"), |
| db_dxil_param(3, "i32", "index", "index"), |
| db_dxil_param(4, "u8", "component", "component"), |
| db_dxil_param(5, "$o", "value", "value to store")]) |
| next_op_idx += 1 |
| self.add_dxil_op("LoadInput", next_op_idx, "LoadInput", "loads the value from shader input", "hfwi", "rn", [ |
| db_dxil_param(0, "$o", "", "input value"), |
| db_dxil_param(2, "u32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "u32", "rowIndex", "row index relative to element"), |
| db_dxil_param(4, "u8", "colIndex", "column index relative to element"), |
| db_dxil_param(5, "i32", "gsVertexAxis", "gsVertexAxis")]) |
| next_op_idx += 1 |
| self.add_dxil_op("StoreOutput", next_op_idx, "StoreOutput", "stores the value to shader output", "hfwi", "", [ # note, cannot store bit even though load supports it |
| retvoid_param, |
| db_dxil_param(2, "u32", "outputSigId", "output signature element ID"), |
| db_dxil_param(3, "u32", "rowIndex", "row index relative to element"), |
| db_dxil_param(4, "u8", "colIndex", "column index relative to element"), |
| db_dxil_param(5, "$o", "value", "value to store")]) |
| next_op_idx += 1 |
| |
| # Unary float operations are regular. |
| for i in "FAbs,Saturate".split(","): |
| self.add_dxil_op(i, next_op_idx, "Unary", "returns the " + i, "hfd", "rn", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| for i in "IsNaN,IsInf,IsFinite,IsNormal".split(","): |
| self.add_dxil_op(i, next_op_idx, "IsSpecialFloat", "returns the " + i, "hf", "rn", [ |
| db_dxil_param(0, "i1", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| for i 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(i, next_op_idx, "Unary", "returns the " + i, "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| |
| # Unary int operations are regular. |
| for i in "Bfrev".split(","): |
| self.add_dxil_op(i, next_op_idx, "Unary", "returns the reverse bit pattern of the input value", "wil", "rn", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| for i in "Countbits,FirstbitLo".split(","): |
| self.add_dxil_op(i, next_op_idx, "UnaryBits", "returns the " + i, "wil", "rn", [ |
| db_dxil_param(0, "i32", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| for i in "FirstbitHi,FirstbitSHi".split(","): |
| self.add_dxil_op(i, next_op_idx, "UnaryBits", "returns src != 0? (BitWidth-1 - " + i + ") : -1", "wil", "rn", [ |
| db_dxil_param(0, "i32", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value")]) |
| next_op_idx += 1 |
| |
| # Binary float operations |
| for i in "FMax,FMin".split(","): |
| self.add_dxil_op(i, next_op_idx, "Binary", "returns the " + i + " of the input values", "hfd", "rn", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "a", "input value"), |
| db_dxil_param(3, "$o", "b", "input value")]) |
| next_op_idx += 1 |
| |
| # Binary int operations |
| for i in "IMax,IMin,UMax,UMin".split(","): |
| self.add_dxil_op(i, next_op_idx, "Binary", "returns the " + i + " of the input values", "wil", "rn", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "a", "input value"), |
| db_dxil_param(3, "$o", "b", "input value")]) |
| next_op_idx += 1 |
| |
| # Binary int operations with two outputs |
| for i in "IMul,UMul,UDiv".split(","): |
| self.add_dxil_op(i, next_op_idx, "BinaryWithTwoOuts", "returns the " + i + " of the input values", "i", "rn", [ |
| db_dxil_param(0, "twoi32", "", "operation result"), |
| db_dxil_param(2, "$o", "a", "input value"), |
| db_dxil_param(3, "$o", "b", "input value")]) |
| next_op_idx += 1 |
| |
| # Binary int operations with carry |
| for i in "UAddc,USubb".split(","): |
| self.add_dxil_op(i, next_op_idx, "BinaryWithCarryOrBorrow", "returns the " + i + " of the input values", "i", "rn", [ |
| db_dxil_param(0, "i32c", "", "operation result with carry/borrow value"), |
| db_dxil_param(2, "$o", "a", "input value"), |
| db_dxil_param(3, "$o", "b", "input value")]) |
| next_op_idx += 1 |
| |
| # Tertiary float. |
| self.add_dxil_op("FMad", next_op_idx, "Tertiary", "performs a fused multiply add (FMA) of the form a * b + c", "hfd", "rn", [ |
| db_dxil_param(0, "$o", "", "the fused multiply-addition of parameters a * b + c"), |
| db_dxil_param(2, "$o", "a", "first value for FMA, the first factor"), |
| db_dxil_param(3, "$o", "b", "second value for FMA, the second factor"), |
| db_dxil_param(4, "$o", "c", "third value for FMA, the addend")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Fma", next_op_idx, "Tertiary", "performs a fused multiply add (FMA) of the form a * b + c", "d", "rn", [ |
| db_dxil_param(0, "$o", "", "the double-precision fused multiply-addition of parameters a * b + c, accurate to 0.5 units of least precision (ULP)"), |
| db_dxil_param(2, "$o", "a", "first value for FMA, the first factor"), |
| db_dxil_param(3, "$o", "b", "second value for FMA, the second factor"), |
| db_dxil_param(4, "$o", "c", "third value for FMA, the addend")]) |
| next_op_idx += 1 |
| |
| # Tertiary int. |
| for i in "IMad,UMad".split(","): |
| self.add_dxil_op(i, next_op_idx, "Tertiary", "performs an integral " + i, "wil", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "a", "first value for FMA, the first factor"), |
| db_dxil_param(3, "$o", "b", "second value for FMA, the second factor"), |
| db_dxil_param(4, "$o", "c", "third value for FMA, the addend")]) |
| next_op_idx += 1 |
| for i in "Msad,Ibfe,Ubfe".split(","): |
| self.add_dxil_op(i, next_op_idx, "Tertiary", "performs an integral " + i, "il", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "a", "first value for FMA, the first factor"), |
| db_dxil_param(3, "$o", "b", "second value for FMA, the second factor"), |
| db_dxil_param(4, "$o", "c", "third value for FMA, the addend")]) |
| next_op_idx += 1 |
| |
| # Quaternary |
| self.add_dxil_op("Bfi", next_op_idx, "Quaternary", "given a bit range from the LSB of a number, places that number of bits in another number at any offset", "i", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "width", "the bitfield width to take from the value"), |
| db_dxil_param(3, "$o", "offset", "the bitfield offset to replace in the value"), |
| db_dxil_param(4, "$o", "value", "the number the bits are taken from"), |
| db_dxil_param(5, "$o", "replacedValue", "the number with bits to be replaced")]) |
| next_op_idx += 1 |
| |
| # Dot |
| self.add_dxil_op("Dot2", next_op_idx, "Dot2", "two-dimensional vector dot-product", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "ax", "the first component of the first vector"), |
| db_dxil_param(3, "$o", "ay", "the second component of the first vector"), |
| db_dxil_param(4, "$o", "bx", "the first component of the second vector"), |
| db_dxil_param(5, "$o", "by", "the second component of the second vector")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Dot3", next_op_idx, "Dot3", "three-dimensional vector dot-product", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "ax", "the first component of the first vector"), |
| db_dxil_param(3, "$o", "ay", "the second component of the first vector"), |
| db_dxil_param(4, "$o", "az", "the third component of the first vector"), |
| db_dxil_param(5, "$o", "bx", "the first component of the second vector"), |
| db_dxil_param(6, "$o", "by", "the second component of the second vector"), |
| db_dxil_param(7, "$o", "bz", "the third component of the second vector")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Dot4", next_op_idx, "Dot4", "four-dimensional vector dot-product", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "the operation result"), |
| db_dxil_param(2, "$o", "ax", "the first component of the first vector"), |
| db_dxil_param(3, "$o", "ay", "the second component of the first vector"), |
| db_dxil_param(4, "$o", "az", "the third component of the first vector"), |
| db_dxil_param(5, "$o", "aw", "the fourth component of the first vector"), |
| db_dxil_param(6, "$o", "bx", "the first component of the second vector"), |
| db_dxil_param(7, "$o", "by", "the second component of the second vector"), |
| db_dxil_param(8, "$o", "bz", "the third component of the second vector"), |
| db_dxil_param(9, "$o", "bw", "the fourth component of the second vector")]) |
| next_op_idx += 1 |
| |
| # Resources. |
| self.add_dxil_op("CreateHandle", next_op_idx, "CreateHandle", "creates the handle to a resource", "v", "ro", [ |
| db_dxil_param(0, "res", "", "the handle to the resource"), |
| db_dxil_param(2, "i8", "resourceClass", "the class of resource to create (SRV, UAV, CBuffer, Sampler)", is_const=True), # maps to DxilResourceBase::Class |
| db_dxil_param(3, "i32", "rangeId", "range identifier for resource", is_const=True), |
| db_dxil_param(4, "i32", "index", "zero-based index into range"), |
| db_dxil_param(5, "i1", "nonUniformIndex", "non-uniform resource index", is_const=True)]) |
| next_op_idx += 1 |
| self.add_dxil_op("CBufferLoad", next_op_idx, "CBufferLoad", "loads a value from a constant buffer resource", "hfd8wil", "ro", [ |
| db_dxil_param(0, "$o", "", "the value for the constant buffer variable"), |
| db_dxil_param(2, "res", "handle", "cbuffer handle"), |
| db_dxil_param(3, "u32", "byteOffset", "linear byte offset of value"), |
| db_dxil_param(4, "u32", "alignment", "load access alignment", is_const=True)]) |
| next_op_idx += 1 |
| self.add_dxil_op("CBufferLoadLegacy", next_op_idx, "CBufferLoadLegacy", "loads a value from a constant buffer resource", "hfdwil", "ro", [ |
| db_dxil_param(0, "$cb", "", "the value for the constant buffer variable"), |
| db_dxil_param(2, "res", "handle", "cbuffer handle"), |
| db_dxil_param(3, "u32", "regIndex", "0-based index into cbuffer instance")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Sample", next_op_idx, "Sample", "samples a texture", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the sampled value"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "clamp", "clamp value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleBias", next_op_idx, "SampleBias", "samples a texture after applying the input bias to the mipmap level", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the sampled value"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "bias", "bias value"), |
| db_dxil_param(12, "f", "clamp", "clamp value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleLevel", next_op_idx, "SampleLevel", "samples a texture using a mipmap-level offset", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the sampled value"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "LOD", "level of detail, biggest map if less than or equal to zero; fraction used to interpolate across levels")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleGrad", next_op_idx, "SampleGrad", "samples a texture using a gradient to influence the way the sample location is calculated", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the sampled value"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "ddx0", "rate of change of the texture coordinate in the x direction"), |
| db_dxil_param(12, "f", "ddx1", "rate of change of the texture coordinate in the x direction"), |
| db_dxil_param(13, "f", "ddx2", "rate of change of the texture coordinate in the x direction"), |
| db_dxil_param(14, "f", "ddy0", "rate of change of the texture coordinate in the y direction"), |
| db_dxil_param(15, "f", "ddy1", "rate of change of the texture coordinate in the y direction"), |
| db_dxil_param(16, "f", "ddy2", "rate of change of the texture coordinate in the y direction"), |
| db_dxil_param(17, "f", "clamp", "clamp value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleCmp", next_op_idx, "SampleCmp", "samples a texture and compares a single component against the specified comparison value", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the value for the constant buffer variable"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "compareValue", "the value to compare with"), |
| db_dxil_param(12, "f", "clamp", "clamp value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleCmpLevelZero", next_op_idx, "SampleCmpLevelZero", "samples a texture and compares a single component against the specified comparison value", "hf", "ro", [ |
| db_dxil_param(0, "$r", "", "the value for the constant buffer variable"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "offset2", "optional offset, applicable to Texture3D"), |
| db_dxil_param(11, "f", "compareValue", "the value to compare with")]) |
| next_op_idx += 1 |
| self.add_dxil_op("TextureLoad", next_op_idx, "TextureLoad", "reads texel data without any filtering or sampling", "hfwi", "ro", [ |
| db_dxil_param(0, "$r", "", "the loaded value"), |
| db_dxil_param(2, "res", "srv", "handle of SRV or UAV to sample"), |
| db_dxil_param(3, "i32", "mipLevelOrSampleCount", "sample count for Texture2DMS, mip level otherwise"), |
| db_dxil_param(4, "i32", "coord0", "coordinate"), |
| db_dxil_param(5, "i32", "coord1", "coordinate"), |
| db_dxil_param(6, "i32", "coord2", "coordinate"), |
| db_dxil_param(7, "i32", "offset0", "optional offset"), |
| db_dxil_param(8, "i32", "offset1", "optional offset"), |
| db_dxil_param(9, "i32", "offset2", "optional offset")]) |
| next_op_idx += 1 |
| self.add_dxil_op("TextureStore", next_op_idx, "TextureStore", "reads texel data without any filtering or sampling", "hfwi", "", [ |
| db_dxil_param(0, "v", "", ""), |
| db_dxil_param(2, "res", "srv", "handle of UAV to store to"), |
| db_dxil_param(3, "i32", "coord0", "coordinate"), |
| db_dxil_param(4, "i32", "coord1", "coordinate"), |
| db_dxil_param(5, "i32", "coord2", "coordinate"), |
| db_dxil_param(6, "$o", "value0", "value"), |
| db_dxil_param(7, "$o", "value1", "value"), |
| db_dxil_param(8, "$o", "value2", "value"), |
| db_dxil_param(9, "$o", "value3", "value"), |
| db_dxil_param(10,"i8", "mask", "written value mask")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BufferLoad", next_op_idx, "BufferLoad", "reads from a TypedBuffer", "hfwi", "ro", [ |
| db_dxil_param(0, "$r", "", "the loaded value"), |
| db_dxil_param(2, "res", "srv", "handle of TypedBuffer SRV to sample"), |
| db_dxil_param(3, "i32", "index", "element index"), |
| db_dxil_param(4, "i32", "wot", "coordinate")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BufferStore", next_op_idx, "BufferStore", "writes to a RWTypedBuffer", "hfwi", "", [ |
| db_dxil_param(0, "v", "", ""), |
| db_dxil_param(2, "res", "uav", "handle of UAV to store to"), |
| db_dxil_param(3, "i32", "coord0", "coordinate in elements"), |
| db_dxil_param(4, "i32", "coord1", "coordinate (unused?)"), |
| db_dxil_param(5, "$o", "value0", "value"), |
| db_dxil_param(6, "$o", "value1", "value"), |
| db_dxil_param(7, "$o", "value2", "value"), |
| db_dxil_param(8, "$o", "value3", "value"), |
| db_dxil_param(9, "i8", "mask", "written value mask")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BufferUpdateCounter", next_op_idx, "BufferUpdateCounter", "atomically increments/decrements the hidden 32-bit counter stored with a Count or Append UAV", "v", "", [ |
| db_dxil_param(0, "i32", "", "the new value in the buffer"), |
| db_dxil_param(2, "res", "uav", "handle to a structured buffer UAV with the count or append flag"), |
| db_dxil_param(3, "i8", "inc", "1 to increase, 0 to decrease")]) |
| next_op_idx += 1 |
| self.add_dxil_op("CheckAccessFullyMapped", next_op_idx, "CheckAccessFullyMapped", "determines whether all values from a Sample, Gather, or Load operation accessed mapped tiles in a tiled resource", "i", "ro", [ |
| db_dxil_param(0, "i1", "", "nonzero if all values accessed mapped tiles in a tiled resource"), |
| db_dxil_param(2, "u32", "status", "status result from the Sample, Gather or Load operation")]) |
| next_op_idx += 1 |
| self.add_dxil_op("GetDimensions", next_op_idx, "GetDimensions", "gets texture size information", "v", "ro", [ |
| db_dxil_param(0, "dims", "", "dimension information for texture"), |
| db_dxil_param(2, "res", "handle", "resource handle to query"), |
| db_dxil_param(3, "i32", "mipLevel", "mip level to query")]) |
| next_op_idx += 1 |
| self.add_dxil_op("TextureGather", next_op_idx, "TextureGather", "gathers the four texels that would be used in a bi-linear filtering operation", "fi", "ro", [ |
| db_dxil_param(0, "$r", "", "dimension information for texture"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "channel", "channel to sample")]) |
| next_op_idx += 1 |
| self.add_dxil_op("TextureGatherCmp", next_op_idx, "TextureGatherCmp", "same as TextureGather, except this instrution performs comparison on texels, similar to SampleCmp", "fi", "ro", [ |
| db_dxil_param(0, "$r", "", "gathered texels"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "res", "sampler", "handle of sampler to use"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate, undef for Texture1D"), |
| db_dxil_param(6, "f", "coord2", "coordinate, undef for Texture1D, Texture1DArray or Texture2D"), |
| db_dxil_param(7, "f", "coord3", "coordinate, defined only for TextureCubeArray"), |
| db_dxil_param(8, "i32", "offset0", "optional offset, applicable to Texture1D, Texture1DArray, and as part of offset1"), |
| db_dxil_param(9, "i32", "offset1", "optional offset, applicable to Texture2D, Texture2DArray, and as part of offset2"), |
| db_dxil_param(10, "i32", "channel", "channel to sample"), |
| db_dxil_param(11, "f", "compareVale", "value to compare with")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("Texture2DMSGetSamplePosition", next_op_idx, "Texture2DMSGetSamplePosition", "gets the position of the specified sample", "v", "ro", [ |
| db_dxil_param(0, "SamplePos", "", "sample position"), |
| db_dxil_param(2, "res", "srv", "handle of SRV to sample"), |
| db_dxil_param(3, "i32", "index", "zero-based sample index")]) |
| next_op_idx += 1 |
| self.add_dxil_op("RenderTargetGetSamplePosition", next_op_idx, "RenderTargetGetSamplePosition", "gets the position of the specified sample", "v", "ro", [ |
| db_dxil_param(0, "SamplePos", "", "sample position"), |
| db_dxil_param(2, "i32", "index", "zero-based sample index")]) |
| next_op_idx += 1 |
| self.add_dxil_op("RenderTargetGetSampleCount", next_op_idx, "RenderTargetGetSampleCount", "gets the number of samples for a render target", "v", "ro", [ |
| db_dxil_param(0, "u32", "", "number of sampling locations for a render target")]) |
| next_op_idx += 1 |
| |
| # Atomics. Note that on TGSM, atomics are performed with LLVM instructions. |
| self.add_dxil_op("AtomicBinOp", next_op_idx, "AtomicBinOp", "performs an atomic operation on two operands", "i", "", [ |
| db_dxil_param(0, "i32", "", "the original value in the location updated"), |
| db_dxil_param(2, "res", "handle", "typed int or uint UAV handle"), |
| db_dxil_param(3, "i32", "atomicOp", "atomic operation as per DXIL::AtomicBinOpCode"), |
| db_dxil_param(4, "i32", "offset0", "offset in elements"), |
| db_dxil_param(5, "i32", "offset1", "offset"), |
| db_dxil_param(6, "i32", "offset2", "offset"), |
| db_dxil_param(7, "i32", "newValue", "new value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("AtomicCompareExchange", next_op_idx, "AtomicCompareExchange", "atomic compare and exchange to memory", "i", "", [ |
| db_dxil_param(0, "i32", "", "the original value in the location updated"), |
| db_dxil_param(2, "res", "handle", "typed int or uint UAV handle"), |
| db_dxil_param(3, "i32", "offset0", "offset in elements"), |
| db_dxil_param(4, "i32", "offset1", "offset"), |
| db_dxil_param(5, "i32", "offset2", "offset"), |
| db_dxil_param(6, "i32", "compareValue", "value to compare for exchange"), |
| db_dxil_param(7, "i32", "newValue", "new value")]) |
| next_op_idx += 1 |
| |
| # Synchronization. |
| self.add_dxil_op("Barrier", next_op_idx, "Barrier", "inserts a memory barrier in the shader", "v", "nd", [ |
| retvoid_param, |
| db_dxil_param(2, "i32", "barrierMode", "a mask of DXIL::BarrierMode values", is_const=True)]) |
| next_op_idx += 1 |
| |
| # Pixel shader |
| self.add_dxil_op("CalculateLOD", next_op_idx, "CalculateLOD", "calculates the level of detail", "f", "ro", [ |
| db_dxil_param(0, "f", "", "level of detail"), |
| db_dxil_param(2, "res", "handle", "resource handle"), |
| db_dxil_param(3, "res", "sampler", "sampler handle"), |
| db_dxil_param(4, "f", "coord0", "coordinate"), |
| db_dxil_param(5, "f", "coord1", "coordinate"), |
| db_dxil_param(6, "f", "coord2", "coordinate"), |
| db_dxil_param(7, "i1", "clamped", "1 if clampled LOD should be calculated, 0 for unclamped")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Discard", next_op_idx, "Discard", "discard the current pixel", "v", "", [ |
| retvoid_param, |
| db_dxil_param(2, "i1", "condition", "condition for conditional discard")]) |
| next_op_idx += 1 |
| self.add_dxil_op("DerivCoarseX", next_op_idx, "Unary", "computes the rate of change of components per stamp", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "rate of change in value with regards to RenderTarget x direction"), |
| db_dxil_param(2, "$o", "value", "input to rate of change")]) |
| next_op_idx += 1 |
| self.add_dxil_op("DerivCoarseY", next_op_idx, "Unary", "computes the rate of change of components per stamp", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "rate of change in value with regards to RenderTarget y direction"), |
| db_dxil_param(2, "$o", "value", "input to rate of change")]) |
| next_op_idx += 1 |
| self.add_dxil_op("DerivFineX", next_op_idx, "Unary", "computes the rate of change of components per pixel", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "rate of change in value with regards to RenderTarget x direction"), |
| db_dxil_param(2, "$o", "value", "input to rate of change")]) |
| next_op_idx += 1 |
| self.add_dxil_op("DerivFineY", next_op_idx, "Unary", "computes the rate of change of components per pixel", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "rate of change in value with regards to RenderTarget y direction"), |
| db_dxil_param(2, "$o", "value", "input to rate of change")]) |
| next_op_idx += 1 |
| self.add_dxil_op("EvalSnapped", next_op_idx, "EvalSnapped", "evaluates an input attribute at pixel center with an offset", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "inputRowIndex", "row index of an input attribute"), |
| db_dxil_param(4, "i8", "inputColIndex", "column index of an input attribute"), |
| db_dxil_param(5, "i32", "offsetX", "2D offset from the pixel center using a 16x16 grid"), |
| db_dxil_param(6, "i32", "offsetY", "2D offset from the pixel center using a 16x16 grid")]) |
| next_op_idx += 1 |
| self.add_dxil_op("EvalSampleIndex", next_op_idx, "EvalSampleIndex", "evaluates an input attribute at a sample location", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "inputRowIndex", "row index of an input attribute"), |
| db_dxil_param(4, "i8", "inputColIndex", "column index of an input attribute"), |
| db_dxil_param(5, "i32", "sampleIndex", "sample location")]) |
| next_op_idx += 1 |
| self.add_dxil_op("EvalCentroid", next_op_idx, "EvalCentroid", "evaluates an input attribute at pixel center", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "inputRowIndex", "row index of an input attribute"), |
| db_dxil_param(4, "i8", "inputColIndex", "column index of an input attribute")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SampleIndex", next_op_idx, "SampleIndex", "returns the sample index in a sample-frequency pixel shader", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("Coverage", next_op_idx, "Coverage", "returns the coverage mask input in a pixel shader", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("InnerCoverage", next_op_idx, "InnerCoverage", "returns underestimated coverage input from conservative rasterization in a pixel shader", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| |
| # Compute shader. |
| self.add_dxil_op("ThreadId", next_op_idx, "ThreadId", "reads the thread ID", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "thread ID component"), |
| db_dxil_param(2, "i32", "component", "component to read (x,y,z)")]) |
| next_op_idx += 1 |
| self.add_dxil_op("GroupId", next_op_idx, "GroupId", "reads the group ID (SV_GroupID)", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "group ID component"), |
| db_dxil_param(2, "i32", "component", "component to read")]) |
| next_op_idx += 1 |
| self.add_dxil_op("ThreadIdInGroup", next_op_idx, "ThreadIdInGroup", "reads the thread ID within the group (SV_GroupThreadID)", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "thread ID in group component"), |
| db_dxil_param(2, "i32", "component", "component to read (x,y,z)")]) |
| next_op_idx += 1 |
| self.add_dxil_op("FlattenedThreadIdInGroup", next_op_idx, "FlattenedThreadIdInGroup", "provides a flattened index for a given thread within a given group (SV_GroupIndex)", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| |
| # Geometry shader |
| self.add_dxil_op("EmitStream", next_op_idx, "EmitStream", "emits a vertex to a given stream", "v", "", [ |
| retvoid_param, |
| db_dxil_param(2, "i8", "streamId", "target stream ID for operation")]) |
| next_op_idx += 1 |
| self.add_dxil_op("CutStream", next_op_idx, "CutStream", "completes the current primitive topology at the specified stream", "v", "", [ |
| retvoid_param, |
| db_dxil_param(2, "i8", "streamId", "target stream ID for operation")]) |
| next_op_idx += 1 |
| self.add_dxil_op("EmitThenCutStream", next_op_idx, "EmitThenCutStream", "equivalent to an EmitStream followed by a CutStream", "v", "", [ |
| retvoid_param, |
| db_dxil_param(2, "i8", "streamId", "target stream ID for operation")]) |
| next_op_idx += 1 |
| self.add_dxil_op("GSInstanceID", next_op_idx, "GSInstanceID", "GSInstanceID", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| |
| # Double precision |
| self.add_dxil_op("MakeDouble", next_op_idx, "MakeDouble", "creates a double value", "d", "rn", [ |
| db_dxil_param(0, "d", "", "result"), |
| db_dxil_param(2, "i32", "lo", "low part of double"), |
| db_dxil_param(3, "i32", "hi", "high part of double")]) |
| next_op_idx += 1 |
| self.add_dxil_op("SplitDouble", next_op_idx, "SplitDouble", "splits a double into low and high parts", "d", "rn", [ |
| db_dxil_param(0, "splitdouble", "", "result"), |
| db_dxil_param(2, "d", "value", "value to split")]) |
| next_op_idx += 1 |
| |
| # Domain & Hull shader. |
| self.add_dxil_op("LoadOutputControlPoint", next_op_idx, "LoadOutputControlPoint", "LoadOutputControlPoint", "hfwi", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "row", "row, relative to the element"), |
| db_dxil_param(4, "i8", "col", "column, relative to the element"), |
| db_dxil_param(5, "i32", "index", "vertex/point index")]) |
| next_op_idx += 1 |
| self.add_dxil_op("LoadPatchConstant", next_op_idx, "LoadPatchConstant", "LoadPatchConstant", "hfwi", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "row", "row, relative to the element"), |
| db_dxil_param(4, "i8", "col", "column, relative to the element")]) |
| next_op_idx += 1 |
| |
| # Domain shader. |
| self.add_dxil_op("DomainLocation", next_op_idx, "DomainLocation", "DomainLocation", "f", "rn", [ |
| db_dxil_param(0, "f", "", "result"), |
| db_dxil_param(2, "i8", "component", "input", is_const=True)]) |
| next_op_idx += 1 |
| |
| # Hull shader. |
| self.add_dxil_op("StorePatchConstant", next_op_idx, "StorePatchConstant", "StorePatchConstant", "hfwi", "", [ |
| retvoid_param, |
| db_dxil_param(2, "i32", "outputSigID", "output signature element ID"), |
| db_dxil_param(3, "i32", "row", "row, relative to the element"), |
| db_dxil_param(4, "i8", "col", "column, relative to the element"), |
| db_dxil_param(5, "$o", "value", "value to store")]) |
| next_op_idx += 1 |
| self.add_dxil_op("OutputControlPointID", next_op_idx, "OutputControlPointID", "OutputControlPointID", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("PrimitiveID", next_op_idx, "PrimitiveID", "PrimitiveID", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("CycleCounterLegacy", next_op_idx, "CycleCounterLegacy", "CycleCounterLegacy", "v", "", [ |
| db_dxil_param(0, "twoi32", "", "result")]) |
| next_op_idx += 1 |
| |
| # Add wave intrinsics. |
| self.add_dxil_op("WaveIsFirstLane", next_op_idx, "WaveIsFirstLane", "returns 1 for the first lane in the wave", "v", "", [ |
| db_dxil_param(0, "i1", "", "operation result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveGetLaneIndex", next_op_idx, "WaveGetLaneIndex", "returns the index of the current lane in the wave", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "operation result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveGetLaneCount", next_op_idx, "WaveGetLaneCount", "returns the number of lanes in the wave", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "operation result")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveAnyTrue", next_op_idx, "WaveAnyTrue", "returns 1 if any of the lane evaluates the value to true", "v", "", [ |
| db_dxil_param(0, "i1", "", "operation result"), |
| db_dxil_param(2, "i1", "cond", "condition to test")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveAllTrue", next_op_idx, "WaveAllTrue", "returns 1 if all the lanes evaluate the value to true", "v", "", [ |
| db_dxil_param(0, "i1", "", "operation result"), |
| db_dxil_param(2, "i1", "cond", "condition to test")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveActiveAllEqual", next_op_idx, "WaveActiveAllEqual", "returns 1 if all the lanes have the same value", "hfd18wil", "", [ |
| db_dxil_param(0, "i1", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "value to compare")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveActiveBallot", next_op_idx, "WaveActiveBallot", "returns a struct with a bit set for each lane where the condition is true", "v", "", [ |
| db_dxil_param(0, "$u4", "", "operation result"), |
| db_dxil_param(2, "i1", "cond", "condition to ballot on")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveReadLaneAt", next_op_idx, "WaveReadLaneAt", "returns the value from the specified lane", "hfd18wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "value to read"), |
| db_dxil_param(3, "i32", "lane", "lane index")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveReadLaneFirst", next_op_idx, "WaveReadLaneFirst", "returns the value from the first lane", "hf18wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "value to read")]) |
| next_op_idx += 1 |
| self.add_dxil_op("WaveActiveOp", next_op_idx, "WaveActiveOp", "returns the result the operation across waves", "hfd18wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value"), |
| db_dxil_param(3, "i8", "op", "kind of operation to perform", enum_name="WaveOpKind", is_const=True), |
| db_dxil_param(4, "i8", "sop", "sign of operands", enum_name="SignedOpKind", is_const=True)]) |
| next_op_idx += 1 |
| self.add_enum_type("SignedOpKind", "Sign vs. unsigned operands for operation", [ |
| (0, "Signed", "signed integer or floating-point operands"), |
| (1, "Unsigned", "unsigned integer operands")]) |
| self.add_enum_type("WaveOpKind", "Kind of cross-lane operation", [ |
| (0, "Sum", "sum of values"), |
| (1, "Product", "product of values"), |
| (2, "Min", "minimum value"), |
| (3, "Max", "maximum value")]) |
| self.add_dxil_op("WaveActiveBit", next_op_idx, "WaveActiveBit", "returns the result of the operation across all lanes", "8wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value"), |
| db_dxil_param(3, "i8", "op", "kind of operation to perform", enum_name="WaveBitOpKind", is_const=True)]) |
| next_op_idx += 1 |
| self.add_enum_type("WaveBitOpKind", "Kind of bitwise cross-lane operation", [ |
| (0, "And", "bitwise and of values"), |
| (1, "Or", "bitwise or of values"), |
| (2, "Xor", "bitwise xor of values")]) |
| self.add_dxil_op("WavePrefixOp", next_op_idx, "WavePrefixOp", "returns the result of the operation on prior lanes", "hfd8wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "input value"), |
| db_dxil_param(3, "i8", "op", "0=sum,1=product", enum_name="WaveOpKind", is_const=True), |
| db_dxil_param(4, "i8", "sop", "sign of operands", enum_name="SignedOpKind", is_const=True)]) |
| next_op_idx += 1 |
| self.add_dxil_op("QuadReadLaneAt", next_op_idx, "QuadReadLaneAt", "reads from a lane in the quad", "hfd18wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "value to read"), |
| db_dxil_param(3, "u32", "quadLane", "lane to read from (0-4)", max_value = 3, is_const=True)]) |
| next_op_idx += 1 |
| self.add_enum_type("QuadOpKind", "Kind of quad-level operation", [ |
| (0, "ReadAcrossX", "returns the value from the other lane in the quad in the horizontal direction"), |
| (1, "ReadAcrossY", "returns the value from the other lane in the quad in the vertical direction"), |
| (2, "ReadAcrossDiagonal", "returns the value from the lane across the quad in horizontal and vertical direction")]) |
| self.add_dxil_op("QuadOp", next_op_idx, "QuadOp", "returns the result of a quad-level operation", "hfd8wil", "", [ |
| db_dxil_param(0, "$o", "", "operation result"), |
| db_dxil_param(2, "$o", "value", "value for operation"), |
| db_dxil_param(3, "i8", "op", "operation", enum_name = "QuadOpKind", is_const=True)]) |
| next_op_idx += 1 |
| |
| # Add bitcasts |
| self.add_dxil_op("BitcastI16toF16", next_op_idx, "BitcastI16toF16", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "h", "", "operation result"), |
| db_dxil_param(2, "i16", "value", "input value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BitcastF16toI16", next_op_idx, "BitcastF16toI16", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "i16", "", "operation result"), |
| db_dxil_param(2, "h", "value", "input value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BitcastI32toF32", next_op_idx, "BitcastI32toF32", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "f", "", "operation result"), |
| db_dxil_param(2, "i32", "value", "input value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BitcastF32toI32", next_op_idx, "BitcastF32toI32", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "operation result"), |
| db_dxil_param(2, "f", "value", "input value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BitcastI64toF64", next_op_idx, "BitcastI64toF64", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "d", "", "operation result"), |
| db_dxil_param(2, "i64", "value", "input value")]) |
| next_op_idx += 1 |
| self.add_dxil_op("BitcastF64toI64", next_op_idx, "BitcastF64toI64", "bitcast between different sizes", "v", "rn", [ |
| db_dxil_param(0, "i64", "", "operation result"), |
| db_dxil_param(2, "d", "value", "input value")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("LegacyF32ToF16", next_op_idx, "LegacyF32ToF16", "legacy fuction to convert float (f32) to half (f16) (this is not related to min-precision)", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "low 16 bits - half value, high 16 bits - zeroes"), |
| db_dxil_param(2, "f", "value", "float value to convert")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("LegacyF16ToF32", next_op_idx, "LegacyF16ToF32", "legacy fuction to convert half (f16) to float (f32) (this is not related to min-precision)", "v", "rn", [ |
| db_dxil_param(0, "f", "", "converted float value"), |
| db_dxil_param(2, "i32", "value", "half value to convert")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("LegacyDoubleToFloat", next_op_idx, "LegacyDoubleToFloat", "legacy fuction to convert double to float", "v", "rn", [ |
| db_dxil_param(0, "f", "", "float value"), |
| db_dxil_param(2, "d", "value", "double value to convert")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("LegacyDoubleToSInt32", next_op_idx, "LegacyDoubleToSInt32", "legacy fuction to convert double to int32", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "i32 value"), |
| db_dxil_param(2, "d", "value", "double value to convert")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("LegacyDoubleToUInt32", next_op_idx, "LegacyDoubleToUInt32", "legacy fuction to convert double to uint32", "v", "rn", [ |
| db_dxil_param(0, "i32", "", "i32 value"), |
| db_dxil_param(2, "d", "value", "double value to convert")]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("WaveAllBitCount", next_op_idx, "WaveAllOp", "returns the count of bits set to 1 across the wave", "v", "", [ |
| db_dxil_param(0, "i32", "", "operation result"), |
| db_dxil_param(2, "i1", "value", "input value")]) |
| next_op_idx += 1 |
| # 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("WavePrefixBitCount", next_op_idx, "WavePrefixOp", "returns the count of bits set to 1 on prior lanes", "v", "", [ |
| db_dxil_param(0, "i32", "", "operation result"), |
| db_dxil_param(2, "i1", "value", "input value")]) |
| next_op_idx += 1 |
| |
| # End of DXIL 1.0 opcodes. |
| self.set_op_count_for_version(1, 0, next_op_idx) |
| |
| self.add_dxil_op("AttributeAtVertex", next_op_idx, "AttributeAtVertex", "returns the values of the attributes at the vertex.", "hf", "rn", [ |
| db_dxil_param(0, "$o", "", "result"), |
| db_dxil_param(2, "i32", "inputSigId", "input signature element ID"), |
| db_dxil_param(3, "i32", "inputRowIndex", "row index of an input attribute"), |
| db_dxil_param(4, "i8", "inputColIndex", "column index of an input attribute"), |
| db_dxil_param(5, "i8", "VertexID", "Vertex Index") |
| ]) |
| next_op_idx += 1 |
| self.add_dxil_op("ViewID", next_op_idx, "ViewID", "returns the view index", "i", "rn", [ |
| db_dxil_param(0, "i32", "", "result")]) |
| next_op_idx += 1 |
| |
| # End of DXIL 1.1 opcodes. |
| self.set_op_count_for_version(1, 1, next_op_idx) |
| |
| self.add_dxil_op("RawBufferLoad", next_op_idx, "RawBufferLoad", "reads from a raw buffer and structured buffer", "hfwi", "ro", [ |
| db_dxil_param(0, "$r", "", "the loaded value"), |
| db_dxil_param(2, "res", "srv", "handle of TypedBuffer SRV to sample"), |
| db_dxil_param(3, "i32", "index", "element index for StructuredBuffer, or byte offset for ByteAddressBuffer"), |
| db_dxil_param(4, "i32", "elementOffset", "offset into element for StructuredBuffer, or undef for ByteAddressBuffer"), |
| db_dxil_param(5, "i8", "mask", "loading value mask", is_const=True), |
| db_dxil_param(6, "i32", "alignment", "relative load access alignment", is_const=True)]) |
| next_op_idx += 1 |
| |
| self.add_dxil_op("RawBufferStore", next_op_idx, "RawBufferStore", "writes to a RWByteAddressBuffer or RWStructuredBuffer", "hfwi", "", [ |
| db_dxil_param(0, "v", "", ""), |
| db_dxil_param(2, "res", "uav", "handle of UAV to store to"), |
| db_dxil_param(3, "i32", "index", "element index for StructuredBuffer, or byte offset for ByteAddressBuffer"), |
| db_dxil_param(4, "i32", "elementOffset", "offset into element for StructuredBuffer, or undef for ByteAddressBuffer"), |
| db_dxil_param(5, "$o", "value0", "value"), |
| db_dxil_param(6, "$o", "value1", "value"), |
| db_dxil_param(7, "$o", "value2", "value"), |
| db_dxil_param(8, "$o", "value3", "value"), |
| db_dxil_param(9, "i8", "mask", "mask of contiguous components stored starting at first component (valid: 1, 3, 7, 15)", is_const=True), |
| db_dxil_param(10, "i32", "alignment", "relative store access alignment", is_const=True)]) |
| next_op_idx += 1 |
| |
| # End of DXIL 1.2 opcodes. |
| self.set_op_count_for_version(1, 2, next_op_idx) |
| assert next_op_idx == 141, "next operation index is %d rather than 141 and thus opcodes are broken" % next_op_idx |
| |
| # Set interesting properties. |
| self.build_indices() |
| for i in "CalculateLOD,DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY,Sample,SampleBias,SampleCmp,TextureGather,TextureGatherCmp".split(","): |
| self.name_idx[i].is_gradient = True |
| for i in "DerivCoarseX,DerivCoarseY,DerivFineX,DerivFineY".split(","): |
| assert self.name_idx[i].is_gradient == True, "all derivatives are marked as requiring gradients" |
| self.name_idx[i].is_deriv = True |
| |
| # 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 i in self.instr: |
| if i.is_dxil_op: |
| assert i.oload_types != "", "overload for DXIL operation %s should not be empty - use void if n/a" % (i.name) |
| assert i.oload_types == "v" or i.oload_types.find("v") < 0, "void overload should be exclusive to other types (%s)" % i.name |
| assert type(i.oload_types) is str, "overload for %s should be a string - use empty if n/a" % (i.name) |
| |
| # Verify that all 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_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 |
| 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) |
| 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) |
| |
| def populate_extended_docs(self): |
| "Update the documentation with text from external files." |
| inst_starter = "* Inst: " |
| block_starter = "* BLOCK-BEGIN" |
| block_end = "* BLOCK-END" |
| thisdir = os.path.dirname(os.path.realpath(__file__)) |
| with open(os.path.join(thisdir, "hctdb_inst_docs.txt")) as ops_file: |
| inst_name = "" |
| inst_doc = "" |
| inst_remarks = "" |
| is_block = False |
| for idx, line in enumerate(ops_file): |
| if line.startswith("#"): continue |
| if line.startswith(block_starter): |
| assert is_block == False, "unexpected block begin at line %i" % (idx+1) |
| is_block = True |
| continue |
| if line.startswith(block_end): |
| assert is_block == True, "unexpected block end at line %i" % (idx+1) |
| is_block = False |
| continue |
| if line.startswith(inst_starter): |
| if inst_name: |
| # print(inst_name + " - " + inst_remarks.strip()) |
| self.name_idx[inst_name].doc = inst_doc |
| self.name_idx[inst_name].remarks = inst_remarks.strip() |
| inst_remarks = "" |
| line = line[len(inst_starter):] |
| sep_idx = line.find("-") |
| inst_name = line[:sep_idx].strip() |
| inst_doc = line[sep_idx+1:].strip() |
| else: |
| inst_remarks += line if is_block else "\n" + line.strip() |
| if inst_name: |
| self.name_idx[inst_name].remarks = inst_remarks.strip() |
| |
| |
| def populate_metadata(self): |
| # For now, simply describe the allowed named metadata. |
| m = self.metadata |
| m.append(db_dxil_metadata("dx.controlflow.hints", "Provides control flow hints to an instruction.")) |
| m.append(db_dxil_metadata("dx.entryPoints", "Entry point functions.")) |
| m.append(db_dxil_metadata("dx.precise", "Marks an instruction as precise.")) |
| m.append(db_dxil_metadata("dx.resources", "Resources used by the entry point shaders.")) |
| m.append(db_dxil_metadata("dx.shaderModel", "Shader model for the module.")) |
| m.append(db_dxil_metadata("dx.typeAnnotations", "Provides annotations for types.")) |
| m.append(db_dxil_metadata("dx.typevar.*", ".")) |
| m.append(db_dxil_metadata("dx.valver", "Optional validator version.")) |
| m.append(db_dxil_metadata("dx.version", "Optional DXIL version for the module.")) |
| # dx.typevar.* is not the name of metadata, but the prefix for global variables |
| # that will be referenced by structure type annotations. |
| |
| def populate_passes(self): |
| # Populate passes and their options. |
| p = self.passes |
| def add_pass(name, type_name, doc, opts): |
| apass = db_dxil_pass(name, type_name=type_name, doc=doc) |
| for o in opts: |
| assert 'n' in o, "option in %s has no 'n' member" % name |
| apass.args.append(db_dxil_pass_arg(o['n'], ident=o.get('i'), type_name=o.get('t'), is_ctor_param=o.get('c'), doc=o.get('d'))) |
| p.append(apass) |
| # Add discriminators is a DWARF 4 thing, useful for the profiler. |
| # Consider removing lib\Transforms\Utils\AddDiscriminators.cpp altogether |
| add_pass("add-discriminators", "AddDiscriminators", "Add DWARF path discriminators", |
| [{'n':"no-discriminators", 'i':"NoDiscriminators", 't':"bool"}]) |
| # Sample profile is part of the sample profiling infrastructure. |
| # Consider removing lib\Transforms\Scalar\SampleProfile.cpp |
| add_pass("sample-profile", "SampleProfileLoader", "Sample Profile loader", [ |
| {'n':"sample-profile-file", 'i':"SampleProfileFile", 't':"string"}, |
| {'n':"sample-profile-max-propagate-iterations", 'i':"SampleProfileMaxPropagateIterations", 't':"unsigned"}]) |
| # inline and always-inline share a base class - those are the arguments we document for each of them. |
| inliner_args = [ |
| {'n':'InsertLifetime', 't':'bool', 'c':1, 'd':'Insert @llvm.lifetime intrinsics'}, |
| {'n':'InlineThreshold', 't':'unsigned', 'c':1, 'd':'Insert @llvm.lifetime intrinsics'}] |
| add_pass("inline", "SimpleInliner", "Function Integration/Inlining", inliner_args) |
| # {'n':"OptLevel", 't':"unsigned", 'c':1}, |
| # {'n':"SizeOptLevel", 't':'unsigned', 'c':1} |
| add_pass('always-inline', 'AlwaysInliner', 'Inliner for always_inline functions', inliner_args) |
| # {'n':'InsertLifetime', 't':'bool', 'c':1, 'd':'Insert @llvm.lifetime intrinsics'} |
| # Consider a review of the target-specific wrapper. |
| add_pass("tti", "TargetTransformInfoWrapperPass", "Target Transform Information", [ |
| {'n':'TIRA', 't':'TargetIRAnalysis', 'c':1}]) |
| add_pass("verify", "VerifierLegacyPass", "Module Verifier", [ |
| {'n':'FatalErrors', 't':'bool', 'c':1}, |
| {'n':'verify-debug-info', 'i':'VerifyDebugInfo', 't':'bool'}]) |
| add_pass("targetlibinfo", "TargetLibraryInfoWrapperPass", "Target Library Information", [ |
| {'n':'TLIImpl', 't':'TargetLibraryInfoImpl', 'c':1}, |
| {'n':'vector-library', 'i':'ClVectorLibrary', 't':'TargetLibraryInfoImpl::VectorLibrary'}]) |
| add_pass("cfl-aa", "CFLAliasAnalysis", "CFL-Based AA implementation", []) |
| add_pass("tbaa", "TypeBasedAliasAnalysis", "Type-Based Alias Analysis", [ |
| {'n':"enable-tbaa", 'i':'EnableTBAA', 't':'bool', 'd':'Use to disable TBAA functionality'}]) |
| add_pass("scoped-noalias", "ScopedNoAliasAA", "Scoped NoAlias Alias Analysis", [ |
| {'n':"enable-scoped-noalias", 'i':'EnableScopedNoAlias', 't':'bool', 'd':'Use to disable scoped no-alias'}]) |
| add_pass("basicaa", "BasicAliasAnalysis", "Basic Alias Analysis (stateless AA impl)", []) |
| add_pass("reg2mem_hlsl", "RegToMemHlsl", "Demote values with phi-node usage to stack slots", []) |
| add_pass("simplifycfg", "CFGSimplifyPass", "Simplify the CFG", [ |
| {'n':'Threshold', 't':'int', 'c':1}, |
| {'n':'Ftor', 't':'std::function<bool(const Function &)>', 'c':1}, |
| {'n':'bonus-inst-threshold', 'i':'UserBonusInstThreshold', 't':'unsigned', 'd':'Control the number of bonus instructions (default = 1)'}]) |
| # UseNewSROA is used by PassManagerBuilder::populateFunctionPassManager, not a pass per se. |
| add_pass("sroa", "SROA", "Scalar Replacement Of Aggregates", [ |
| {'n':'RequiresDomTree', 't':'bool', 'c':1}, |
| {'n':'force-ssa-updater', 'i':'ForceSSAUpdater', 't':'bool', 'd':'Force the pass to not use DomTree and mem2reg, insteadforming SSA values through the SSAUpdater infrastructure.'}, |
| {'n':'sroa-random-shuffle-slices', 'i':'SROARandomShuffleSlices', 't':'bool', 'd':'Enable randomly shuffling the slices to help uncover instability in their order.'}, |
| {'n':'sroa-strict-inbounds', 'i':'SROAStrictInbounds', 't':'bool', 'd':'Experiment with completely strict handling of inbounds GEPs.'}]) |
| add_pass('scalarrepl', 'SROA_DT', 'Scalar Replacement of Aggregates (DT)', [ |
| {'n':'Threshold', 't':'int', 'c':1}, |
| {'n':'StructMemberThreshold', 't':'int', 'c':1}, |
| {'n':'ArrayElementThreshold', 't':'int', 'c':1}, |
| {'n':'ScalarLoadThreshold', 't':'int', 'c':1}]) |
| add_pass('scalarrepl-ssa', 'SROA_SSAUp', 'Scalar Replacement of Aggregates (SSAUp)', [ |
| {'n':'Threshold', 't':'int', 'c':1}, |
| {'n':'StructMemberThreshold', 't':'int', 'c':1}, |
| {'n':'ArrayElementThreshold', 't':'int', 'c':1}, |
| {'n':'ScalarLoadThreshold', 't':'int', 'c':1}]) |
| add_pass('early-cse', 'EarlyCSELegacyPass', 'Early CSE', []) |
| # More branch weight support. |
| add_pass('lower-expect', 'LowerExpectIntrinsic', "Lower 'expect' Intrinsics", [ |
| {'n':'likely-branch-weight', 'i':'LikelyBranchWeight', 't':'uint32_t', 'd':'Weight of the branch likely to be taken (default = 64)'}, |
| {'n':'unlikely-branch-weight', 'i':'UnlikelyBranchWeight', 't':'uint32_t', 'd':'Weight of the branch unlikely to be taken (default = 4)'}]) |
| # Consider removing lib\Transforms\Utils\SymbolRewriter.cpp |
| add_pass('rewrite-symbols', 'RewriteSymbols', 'Rewrite Symbols', [ |
| {'n':'DL', 't':'SymbolRewriter::RewriteDescriptorList', 'c':1}, |
| {'n':'rewrite-map-file', 'i':'RewriteMapFiles', 't':'string'}]) |
| add_pass('hlsl-hlensure', 'HLEnsureMetadata', 'HLSL High-Level Metadata Ensure', []) |
| add_pass('mergefunc', 'MergeFunctions', 'Merge Functions', [ |
| {'n':'mergefunc-sanity', 'i':'NumFunctionsForSanityCheck', 't':'unsigned', 'd':"How many functions in module could be used for MergeFunctions pass sanity check. '0' disables this check. Works only with '-debug' key."}]) |
| # Consider removing GlobalExtensions globals altogether. |
| add_pass('barrier', 'BarrierNoop', 'A No-Op Barrier Pass', []) |
| add_pass('hlsl-hlemit', 'HLEmitMetadata', 'HLSL High-Level Metadata Emit.', []) |
| add_pass('scalarrepl-param-hlsl', 'SROA_Parameter_HLSL', 'Scalar Replacement of Aggregates HLSL (parameters)', []) |
| add_pass('scalarreplhlsl', 'SROA_DT_HLSL', 'Scalar Replacement of Aggregates HLSL (DT)', []) |
| add_pass('scalarreplhlsl-ssa', 'SROA_SSAUp_HLSL', 'Scalar Replacement of Aggregates HLSL (SSAUp)', []) |
| add_pass('static-global-to-alloca', 'LowerStaticGlobalIntoAlloca', 'Lower static global into Alloca', []) |
| add_pass('hlmatrixlower', 'HLMatrixLowerPass', 'HLSL High-Level Matrix Lower', []) |
| add_pass('dce', 'DCE', 'Dead Code Elimination', []) |
| add_pass('die', 'DeadInstElimination', 'Dead Instruction Elimination', []) |
| add_pass('globaldce', 'GlobalDCE', 'Dead Global Elimination', []) |
| add_pass('dynamic-vector-to-array', 'DynamicIndexingVectorToArray', 'Replace dynamic indexing vector with array', [ |
| {'n':'ReplaceAllVectors','t':'bool','c':1}]) |
| add_pass('hlsl-dxil-legalize-resource-use', 'DxilLegalizeResourceUsePass', 'DXIL legalize resource use', []) |
| add_pass('hlsl-dxil-legalize-static-resource-use', 'DxilLegalizeStaticResourceUsePass', 'DXIL legalize static resource use', []) |
| add_pass('hlsl-dxil-legalize-eval-operations', 'DxilLegalizeEvalOperations', 'DXIL legalize eval operations', []) |
| add_pass('dxilgen', 'DxilGenerationPass', 'HLSL DXIL Generation', [ |
| {'n':'NotOptimized','t':'bool','c':1}]) |
| add_pass('simplify-inst', 'SimplifyInst', 'Simplify Instructions', []) |
| add_pass('mem2reg', 'PromotePass', 'Promote Memory to Register', []) |
| add_pass('hlsl-dxil-precise', 'DxilPrecisePropagatePass', 'DXIL precise attribute propagate', []) |
| add_pass('dxil-legalize-sample-offset', 'DxilLegalizeSampleOffsetPass', 'DXIL legalize sample offset', []) |
| add_pass('scalarizer', 'Scalarizer', 'Scalarize vector operations', []) |
| add_pass('multi-dim-one-dim', 'MultiDimArrayToOneDimArray', 'Flatten multi-dim array into one-dim array', []) |
| add_pass('resource-handle', 'ResourceToHandle', 'Lower resource into handle', []) |
| add_pass('hlsl-passes-nopause', 'NoPausePasses', 'Clears metadata used for pause and resume', []) |
| add_pass('hlsl-passes-pause', 'PausePasses', 'Prepare to pause passes', []) |
| add_pass('hlsl-passes-resume', 'ResumePasses', 'Prepare to resume passes', []) |
| add_pass('hlsl-dxil-condense', 'DxilCondenseResources', 'DXIL Condense Resources', []) |
| add_pass('hlsl-dxil-eliminate-output-dynamic', 'DxilEliminateOutputDynamicIndexing', 'DXIL eliminate ouptut dynamic indexing', []) |
| add_pass('hlsl-dxil-add-pixel-hit-instrmentation', 'DxilAddPixelHitInstrumentation', 'DXIL Count completed PS invocations and costs', [ |
| {'n':'force-early-z','t':'int','c':1}, |
| {'n':'add-pixel-cost','t':'int','c':1}, |
| {'n':'rt-width','t':'int','c':1}, |
| {'n':'sv-position-index','t':'int','c':1}, |
| {'n':'num-pixels','t':'int','c':1}]) |
| add_pass('hlsl-dxil-constantColor', 'DxilOutputColorBecomesConstant', 'DXIL Constant Color Mod', [ |
| {'n':'mod-mode','t':'int','c':1}, |
| {'n':'constant-red','t':'float','c':1}, |
| {'n':'constant-green','t':'float','c':1}, |
| {'n':'constant-blue','t':'float','c':1}, |
| {'n':'constant-alpha','t':'float','c':1}]) |
| add_pass('hlsl-dxil-remove-discards', 'DxilRemoveDiscards', 'HLSL DXIL Remove all discard instructions', []) |
| add_pass('hlsl-dxil-force-early-z', 'DxilForceEarlyZ', 'HLSL DXIL Force the early Z global flag, if shader has no discard calls', []) |
| add_pass('hlsl-dxil-pix-shader-access-instrumentation', 'DxilShaderAccessTracking', 'HLSL DXIL shader access tracking for PIX', [ |
| {'n':'config','t':'int','c':1}, |
| {'n':'checkForDynamicIndexing','t':'bool','c':1}]) |
| add_pass('hlsl-dxil-debug-instrumentation', 'DxilDebugInstrumentation', 'HLSL DXIL debug instrumentation for PIX', [ |
| {'n':'UAVSize','t':'int','c':1}, |
| {'n':'parameter0','t':'int','c':1}, |
| {'n':'parameter1','t':'int','c':1}, |
| {'n':'parameter2','t':'int','c':1}]) |
| add_pass('hlsl-dxil-reduce-msaa-to-single', 'DxilReduceMSAAToSingleSample', 'HLSL DXIL Reduce all MSAA reads to single-sample reads', []) |
| add_pass('hlsl-dxilfinalize', 'DxilFinalizeModule', 'HLSL DXIL Finalize Module', []) |
| add_pass('hlsl-dxilemit', 'DxilEmitMetadata', 'HLSL DXIL Metadata Emit', []) |
| add_pass('hlsl-dxilload', 'DxilLoadMetadata', 'HLSL DXIL Metadata Load', []) |
| add_pass('dxil-dfe', 'DxilDeadFunctionElimination', 'Remove all unused function except entry from DxilModule', []) |
| add_pass('hl-dfe', 'HLDeadFunctionElimination', 'Remove all unused function except entry from HLModule', []) |
| add_pass('hl-preprocess', 'HLPreprocess', 'Preprocess HLModule after inline', []) |
| add_pass('hlsl-dxil-expand-trig', 'DxilExpandTrigIntrinsics', 'DXIL expand trig intrinsics', []) |
| add_pass('hlsl-hca', 'HoistConstantArray', 'HLSL constant array hoisting', []) |
| add_pass('hlsl-dxil-preserve-all-outputs', 'DxilPreserveAllOutputs', 'DXIL write to all outputs in signature', []) |
| add_pass('ipsccp', 'IPSCCP', 'Interprocedural Sparse Conditional Constant Propagation', []) |
| add_pass('globalopt', 'GlobalOpt', 'Global Variable Optimizer', []) |
| add_pass('deadargelim', 'DAE', 'Dead Argument Elimination', []) |
| # Should we get rid of this, or invest in bugpoint support? |
| add_pass('deadarghaX0r', 'DAH', 'Dead Argument Hacking (BUGPOINT USE ONLY; DO NOT USE)', []) |
| add_pass('instcombine', 'InstructionCombiningPass', 'Combine redundant instructions', []) |
| add_pass('prune-eh', 'PruneEH', 'Remove unused exception handling info', []) |
| add_pass('functionattrs', 'FunctionAttrs', 'Deduce function attributes', []) |
| add_pass('argpromotion', 'ArgPromotion', "Promote 'by reference' arguments to scalars", [ |
| {'n':'maxElements', 't':'unsigned', 'c':1}]) |
| add_pass('jump-threading', 'JumpThreading', 'Jump Threading', [ |
| {'n':'Threshold', 't':'int', 'c':1}, |
| {'n':'jump-threading-threshold', 'i':'BBDuplicateThreshold', 't':'unsigned', 'd':'Max block size to duplicate for jump threading'}]) |
| add_pass('correlated-propagation', 'CorrelatedValuePropagation', 'Value Propagation', []) |
| # createTailCallEliminationPass is removed - but is this checked before? |
| add_pass('reassociate', 'Reassociate', 'Reassociate expressions', []) |
| add_pass('loop-rotate', 'LoopRotate', 'Rotate Loops', [ |
| {'n':'MaxHeaderSize', 't':'int', 'c':1}, |
| {'n':'rotation-max-header-size', 'i':'DefaultRotationThreshold', 't':'unsigned', 'd':'The default maximum header size for automatic loop rotation'}]) |
| add_pass('licm', 'LICM', 'Loop Invariant Code Motion', [ |
| {'n':'disable-licm-promotion', 'i':'DisablePromotion', 't':'bool', 'd':'Disable memory promotion in LICM pass'}]) |
| add_pass('loop-unswitch', 'LoopUnswitch', 'Unswitch loops', [ |
| {'n':'Os', 't':'bool', 'c':1, 'd':'Optimize for size'}, |
| {'n':'loop-unswitch-threshold', 'i':'Threshold', 't':'unsigned', 'd':'Max loop size to unswitch'}]) |
| # C:\nobackup\work\HLSLonLLVM\lib\Transforms\IPO\PassManagerBuilder.cpp:353 |
| add_pass('indvars', 'IndVarSimplify', "Induction Variable Simplification", []) |
| add_pass('loop-idiom', 'LoopIdiomRecognize', "Recognize loop idioms", []) |
| add_pass('loop-deletion', 'LoopDeletion', "Delete dead loops", []) |
| add_pass('loop-interchange', 'LoopInterchange', 'Interchanges loops for cache reuse', []) |
| add_pass('loop-unroll', 'LoopUnroll', 'Unroll loops', [ |
| {'n':'Threshold', 't':'int', 'c':1}, |
| {'n':'Count', 't':'int', 'c':1}, |
| {'n':'AllowPartial', 't':'int', 'c':1}, |
| {'n':'Runtime', 't':'int', 'c':1}, |
| {'n':'unroll-threshold', 'i':'UnrollThreshold', 't':'unsigned', 'd':'The baseline cost threshold for loop unrolling'}, |
| {'n':'unroll-percent-dynamic-cost-saved-threshold', 'i':'UnrollPercentDynamicCostSavedThreshold', 't':'unsigned', 'd':'The percentage of estimated dynamic cost which must be saved by unrolling to allow unrolling up to the max threshold.'}, |
| {'n':'unroll-dynamic-cost-savings-discount', 'i':'UnrollDynamicCostSavingsDiscount', 't':'unsigned', 'd':"This is the amount discounted from the total unroll cost when the unrolled form has a high dynamic cost savings (triggered by the '-unroll-perecent-dynamic-cost-saved-threshold' flag)."}, |
| {'n':'unroll-max-iteration-count-to-analyze', 'i':'UnrollMaxIterationsCountToAnalyze', 't':'unsigned', 'd':"Don't allow loop unrolling to simulate more than this number of iterations when checking full unroll profitability"}, |
| {'n':'unroll-count', 'i':'UnrollCount', 't':'unsigned', 'd':'Use this unroll count for all loops including those with unroll_count pragma values, for testing purposes'}, |
| {'n':'unroll-allow-partial', 'i':'UnrollAllowPartial', 't':'bool', 'd':'Allows loops to be partially unrolled until -unroll-threshold loop size is reached.'}, |
| {'n':'unroll-runtime', 'i':'UnrollRuntime', 't':'bool', 'd':'Unroll loops with run-time trip counts'}, |
| {'n':'pragma-unroll-threshold', 'i':'PragmaUnrollThreshold', 't':'unsigned', 'd':'Unrolled size limit for loops with an unroll(full) or unroll_count pragma.'}]) |
| add_pass('mldst-motion', 'MergedLoadStoreMotion', 'MergedLoadStoreMotion', []) |
| add_pass('gvn', 'GVN', 'Global Value Numbering', [ |
| {'n':'noloads', 't':'bool', 'c':1}, |
| {'n':'enable-pre', 'i':'EnablePRE', 't':'bool'}, |
| {'n':'enable-load-pre', 'i':'EnableLoadPRE', 't':'bool'}, |
| {'n':'max-recurse-depth', 'i':'MaxRecurseDepth', 't':'uint32_t', 'd':'Max recurse depth'}]) |
| add_pass('sccp', 'SCCP', 'Sparse Conditional Constant Propagation', []) |
| add_pass('bdce', 'BDCE', 'Bit-Tracking Dead Code Elimination', []) |
| add_pass('dse', 'DSE', 'Dead Store Elimination', []) |
| add_pass('loop-reroll', 'LoopReroll', 'Reroll loops', [ |
| {'n':'max-reroll-increment', 'i':'MaxInc', 't':'unsigned', 'd':'The maximum increment for loop rerolling'}, |
| {'n':'reroll-num-tolerated-failed-matches', 'i':'NumToleratedFailedMatches', 't':'unsigned', 'd':'The maximum number of failures to tolerate during fuzzy matching.'}]) |
| add_pass('load-combine', 'LoadCombine', 'Combine Adjacent Loads', []) |
| add_pass('adce', 'ADCE', 'Aggressive Dead Code Elimination', []) |
| add_pass('float2int', 'Float2Int', 'Float to int', [ |
| {'n':'float2int-max-integer-bw', 'i':'MaxIntegerBW', 't':'unsigned', 'd':'Max integer bitwidth to consider in float2int'}]) |
| add_pass('loop-distribute', 'LoopDistribute', 'Loop Distribition', [ |
| {'n':'loop-distribute-verify', 'i':'LDistVerify', 't':'bool', 'd':'Turn on DominatorTree and LoopInfo verification after Loop Distribution'}, |
| {'n':'loop-distribute-non-if-convertible', 'i':'DistributeNonIfConvertible', 't':'bool', 'd':'Whether to distribute into a loop that may not be if-convertible by the loop vectorizer'}]) |
| add_pass('alignment-from-assumptions', 'AlignmentFromAssumptions', 'Alignment from assumptions', []) |
| add_pass('strip-dead-prototypes', 'StripDeadPrototypesPass', 'Strip Unused Function Prototypes', []) |
| add_pass('elim-avail-extern', 'EliminateAvailableExternally', 'Eliminate Available Externally Globals', []) |
| add_pass('constmerge', 'ConstantMerge', 'Merge Duplicate Global Constants', []) |
| add_pass('lowerbitsets', 'LowerBitSets', 'Lower bitset metadata', [ |
| {'n':'lowerbitsets-avoid-reuse', 'i':'AvoidReuse', 't':'bool', 'd':'Try to avoid reuse of byte array addresses using aliases'}]) |
| add_pass('red', 'ReducibilityAnalysis', 'Reducibility Analysis', []) |
| add_pass('viewid-state', 'ComputeViewIdState', 'Compute information related to ViewID', []) |
| add_pass('hlsl-translate-dxil-opcode-version', 'DxilTranslateRawBuffer', 'Translates one version of dxil to another', []) |
| # TODO: turn STATISTICS macros into ETW events |
| # assert no duplicate names |
| self.pass_idx_args = set() |
| p_names = set() |
| p_ids = set() |
| for ap in p: |
| assert ap.name not in p_names |
| p_names.add(ap.name) |
| for anarg in ap.args: |
| assert anarg.is_ctor_param or anarg.name not in p_ids, "argument %s in %s is not ctor and is duplicate" % (anarg.name, ap.name) |
| if not anarg.is_ctor_param: |
| p_ids.add(anarg.name) |
| self.pass_idx_args.add(anarg.name) |
| |
| def build_semantics(self): |
| SemanticKind = db_dxil_enum("SemanticKind", "Semantic kind; Arbitrary or specific system value.", [ |
| (0, "Arbitrary", ""), |
| (1, "VertexID", ""), |
| (2, "InstanceID", ""), |
| (3, "Position", ""), |
| (4, "RenderTargetArrayIndex", ""), |
| (5, "ViewPortArrayIndex", ""), |
| (6, "ClipDistance", ""), |
| (7, "CullDistance", ""), |
| (8, "OutputControlPointID", ""), |
| (9, "DomainLocation", ""), |
| (10, "PrimitiveID", ""), |
| (11, "GSInstanceID", ""), |
| (12, "SampleIndex", ""), |
| (13, "IsFrontFace", ""), |
| (14, "Coverage", ""), |
| (15, "InnerCoverage", ""), |
| (16, "Target", ""), |
| (17, "Depth", ""), |
| (18, "DepthLessEqual", ""), |
| (19, "DepthGreaterEqual", ""), |
| (20, "StencilRef", ""), |
| (21, "DispatchThreadID", ""), |
| (22, "GroupID", ""), |
| (23, "GroupIndex", ""), |
| (24, "GroupThreadID", ""), |
| (25, "TessFactor", ""), |
| (26, "InsideTessFactor", ""), |
| (27, "ViewID", ""), |
| (28, "Barycentrics", ""), |
| (29, "Invalid", ""), |
| ]) |
| self.enums.append(SemanticKind) |
| SigPointKind = db_dxil_enum("SigPointKind", "Signature Point is more specific than shader stage or signature as it is unique in both stage and item dimensionality or frequency.", [ |
| (0, "VSIn", "Ordinary Vertex Shader input from Input Assembler"), |
| (1, "VSOut", "Ordinary Vertex Shader output that may feed Rasterizer"), |
| (2, "PCIn", "Patch Constant function non-patch inputs"), |
| (3, "HSIn", "Hull Shader function non-patch inputs"), |
| (4, "HSCPIn", "Hull Shader patch inputs - Control Points"), |
| (5, "HSCPOut", "Hull Shader function output - Control Point"), |
| (6, "PCOut", "Patch Constant function output - Patch Constant data passed to Domain Shader"), |
| (7, "DSIn", "Domain Shader regular input - Patch Constant data plus system values"), |
| (8, "DSCPIn", "Domain Shader patch input - Control Points"), |
| (9, "DSOut", "Domain Shader output - vertex data that may feed Rasterizer"), |
| (10, "GSVIn", "Geometry Shader vertex input - qualified with primitive type"), |
| (11, "GSIn", "Geometry Shader non-vertex inputs (system values)"), |
| (12, "GSOut", "Geometry Shader output - vertex data that may feed Rasterizer"), |
| (13, "PSIn", "Pixel Shader input"), |
| (14, "PSOut", "Pixel Shader output"), |
| (15, "CSIn", "Compute Shader input"), |
| (16, "Invalid", ""), |
| ]) |
| self.enums.append(SigPointKind) |
| PackingKind = db_dxil_enum("PackingKind", "Kind of signature point", [ |
| (0, "None", "No packing should be performed"), |
| (1, "InputAssembler", "Vertex Shader input from Input Assembler"), |
| (2, "Vertex", "Vertex that may feed the Rasterizer"), |
| (3, "PatchConstant", "Patch constant signature"), |
| (4, "Target", "Render Target (Pixel Shader Output)"), |
| (5, "Invalid", ""), |
| ]) |
| |
| Float32DenormMode = db_dxil_enum("Float32DenormMode", "float32 denorm behavior", [ |
| (0, "Any", "Undefined behavior for denormal numbers"), |
| (1, "Preserve", "Preserve both input and output"), |
| (2, "FTZ", "Preserve denormal inputs. Flush denorm outputs"), |
| (3, "Reserve3", "Reserved Value. Not used for now"), |
| (4, "Reserve4", "Reserved Value. Not used for now"), |
| (5, "Reserve5", "Reserved Value. Not used for now"), |
| (6, "Reserve6", "Reserved Value. Not used for now"), |
| (7, "Reserve7", "Reserved Value. Not used for now"), |
| ]) |
| self.enums.append(Float32DenormMode) |
| |
| |
| SigPointCSV = """ |
| SigPoint, Related, ShaderKind, PackingKind, SignatureKind |
| VSIn, Invalid, Vertex, InputAssembler, Input |
| VSOut, Invalid, Vertex, Vertex, Output |
| PCIn, HSCPIn, Hull, None, Invalid |
| HSIn, HSCPIn, Hull, None, Invalid |
| HSCPIn, Invalid, Hull, Vertex, Input |
| HSCPOut, Invalid, Hull, Vertex, Output |
| PCOut, Invalid, Hull, PatchConstant, PatchConstant |
| DSIn, Invalid, Domain, PatchConstant, PatchConstant |
| DSCPIn, Invalid, Domain, Vertex, Input |
| DSOut, Invalid, Domain, Vertex, Output |
| GSVIn, Invalid, Geometry, Vertex, Input |
| GSIn, GSVIn, Geometry, None, Invalid |
| GSOut, Invalid, Geometry, Vertex, Output |
| PSIn, Invalid, Pixel, Vertex, Input |
| PSOut, Invalid, Pixel, Target, Output |
| CSIn, Invalid, Compute, None, Invalid |
| Invalid, Invalid, Invalid, Invalid, Invalid |
| """ |
| table = [list(map(str.strip, line.split(','))) for line in SigPointCSV.splitlines() if line.strip()] |
| for row in table[1:]: assert(len(row) == len(table[0])) # Ensure table is rectangular |
| # Make sure labels match enums, otherwise the table isn't aligned or in-sync |
| if not ([row[0] for row in table[1:]] == SigPointKind.value_names()): |
| assert(False and 'SigPointKind does not align with SigPointCSV row labels') |
| self.sigpoint_table = table |
| |
| self.enums.append(PackingKind) |
| SemanticInterpretationKind = db_dxil_enum("SemanticInterpretationKind", "Defines how a semantic is interpreted at a particular SignaturePoint", [ |
| (0, "NA", "Not Available"), |
| (1, "SV", "Normal System Value"), |
| (2, "SGV", "System Generated Value (sorted last)"), |
| (3, "Arb", "Treated as Arbitrary"), |
| (4, "NotInSig", "Not included in signature (intrinsic access)"), |
| (5, "NotPacked", "Included in signature, but does not contribute to packing"), |
| (6, "Target", "Special handling for SV_Target"), |
| (7, "TessFactor", "Special handling for tessellation factors"), |
| (8, "Shadow", "Shadow element must be added to a signature for compatibility"), |
| (9, "Invalid", ""), |
| ]) |
| self.enums.append(SemanticInterpretationKind) |
| |
| # The following has SampleIndex, Coverage, and InnerCoverage as loaded with instructions rather than from the signature |
| SemanticInterpretationCSV = """ |
| Semantic,VSIn,VSOut,PCIn,HSIn,HSCPIn,HSCPOut,PCOut,DSIn,DSCPIn,DSOut,GSVIn,GSIn,GSOut,PSIn,PSOut,CSIn |
| Arbitrary,Arb,Arb,NA,NA,Arb,Arb,Arb,Arb,Arb,Arb,Arb,NA,Arb,Arb,NA,NA |
| VertexID,SV,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA |
| InstanceID,SV,Arb,NA,NA,Arb,Arb,NA,NA,Arb,Arb,Arb,NA,Arb,Arb,NA,NA |
| Position,Arb,SV,NA,NA,SV,SV,Arb,Arb,SV,SV,SV,NA,SV,SV,NA,NA |
| RenderTargetArrayIndex,Arb,SV,NA,NA,SV,SV,Arb,Arb,SV,SV,SV,NA,SV,SV,NA,NA |
| ViewPortArrayIndex,Arb,SV,NA,NA,SV,SV,Arb,Arb,SV,SV,SV,NA,SV,SV,NA,NA |
| ClipDistance,Arb,SV,NA,NA,SV,SV,Arb,Arb,SV,SV,SV,NA,SV,SV,NA,NA |
| CullDistance,Arb,SV,NA,NA,SV,SV,Arb,Arb,SV,SV,SV,NA,SV,SV,NA,NA |
| OutputControlPointID,NA,NA,NA,NotInSig,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA |
| DomainLocation,NA,NA,NA,NA,NA,NA,NA,NotInSig,NA,NA,NA,NA,NA,NA,NA,NA |
| PrimitiveID,NA,NA,NotInSig,NotInSig,NA,NA,NA,NotInSig,NA,NA,NA,Shadow,SGV,SGV,NA,NA |
| GSInstanceID,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig,NA,NA,NA,NA |
| SampleIndex,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,Shadow _41,NA,NA |
| IsFrontFace,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,SGV,SGV,NA,NA |
| Coverage,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig _50,NotPacked _41,NA |
| InnerCoverage,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig _50,NA,NA |
| Target,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,Target,NA |
| Depth,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotPacked,NA |
| DepthLessEqual,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotPacked _50,NA |
| DepthGreaterEqual,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotPacked _50,NA |
| StencilRef,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotPacked _50,NA |
| DispatchThreadID,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig |
| GroupID,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig |
| GroupIndex,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig |
| GroupThreadID,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotInSig |
| TessFactor,NA,NA,NA,NA,NA,NA,TessFactor,TessFactor,NA,NA,NA,NA,NA,NA,NA,NA |
| InsideTessFactor,NA,NA,NA,NA,NA,NA,TessFactor,TessFactor,NA,NA,NA,NA,NA,NA,NA,NA |
| ViewID,NotInSig _61,NA,NotInSig _61,NotInSig _61,NA,NA,NA,NotInSig _61,NA,NA,NA,NotInSig _61,NA,NotInSig _61,NA,NA |
| Barycentrics,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NotPacked _61,NA,NA |
| """ |
| table = [list(map(str.strip, line.split(','))) for line in SemanticInterpretationCSV.splitlines() if line.strip()] |
| for row in table[1:]: assert(len(row) == len(table[0])) # Ensure table is rectangular |
| # Make sure labels match enums, otherwise the table isn't aligned or in-sync |
| assert(table[0][1:] == SigPointKind.value_names()[:-1]) # exclude Invalid |
| if not ([row[0] for row in table[1:]] == SemanticKind.value_names()[:-1]): # exclude Invalid |
| assert(False and 'SemanticKind does not align with SemanticInterpretationCSV row labels') |
| self.interpretation_table = table |
| |
| def build_valrules(self): |
| self.add_valrule_msg("Bitcode.Valid", "TODO - Module must be bitcode-valid", "Module bitcode is invalid") |
| |
| self.add_valrule_msg("Container.PartMatches", "DXIL Container Parts must match Module", "Container part '%0' does not match expected for module.") |
| self.add_valrule_msg("Container.PartRepeated", "DXIL Container must have only one of each part type", "More than one container part '%0'.") |
| self.add_valrule_msg("Container.PartMissing", "DXIL Container requires certain parts, corresponding to module", "Missing part '%0' required by module.") |
| self.add_valrule_msg("Container.PartInvalid", "DXIL Container must not contain unknown parts", "Unknown part '%0' found in DXIL container.") |
| self.add_valrule_msg("Container.RootSignatureIncompatible", "Root Signature in DXIL Container must be compatible with shader", "Root Signature in DXIL container is not compatible with shader.") |
| |
| self.add_valrule("Meta.Required", "TODO - Required metadata missing") |
| self.add_valrule_msg("Meta.Known", "Named metadata should be known", "Named metadata '%0' is unknown") |
| self.add_valrule("Meta.Used", "All metadata must be used by dxil") |
| self.add_valrule_msg("Meta.Target", "Target triple must be 'dxil-ms-dx'", "Unknown target triple '%0'") |
| self.add_valrule("Meta.WellFormed", "TODO - Metadata must be well-formed in operand count and types") |
| self.add_valrule("Meta.SemanticLen", "Semantic length must be at least 1 and at most 64") |
| self.add_valrule_msg("Meta.InterpModeValid", "Interpolation mode must be valid", "Invalid interpolation mode for '%0'") |
| self.add_valrule_msg("Meta.SemaKindValid", "Semantic kind must be valid", "Semantic kind for '%0' is invalid") |
| self.add_valrule_msg("Meta.NoSemanticOverlap", "Semantics must not overlap", "Semantic '%0' overlap at %1") |
| self.add_valrule_msg("Meta.SemaKindMatchesName", "Semantic name must match system value, when defined.", "Semantic name %0 does not match System Value kind %1") |
| self.add_valrule_msg("Meta.DuplicateSysValue", "System value may only appear once in signature", "System value %0 appears more than once in the same signature.") |
| self.add_valrule_msg("Meta.SemanticIndexMax", "System value semantics have a maximum valid semantic index", "%0 semantic index exceeds maximum (%1)") |
| self.add_valrule_msg("Meta.SystemValueRows", "System value may only have 1 row", "rows for system value semantic %0 must be 1") |
| self.add_valrule_msg("Meta.SemanticShouldBeAllocated", "Semantic should have a valid packing location", "%0 Semantic '%1' should have a valid packing location") |
| self.add_valrule_msg("Meta.SemanticShouldNotBeAllocated", "Semantic should have a packing location of -1", "%0 Semantic '%1' should have a packing location of -1") |
| self.add_valrule("Meta.ValueRange", "Metadata value must be within range") |
| self.add_valrule("Meta.FlagsUsage", "Flags must match usage") |
| self.add_valrule("Meta.DenseResIDs", "Resource identifiers must be zero-based and dense") |
| self.add_valrule_msg("Meta.SignatureOverlap", "Signature elements may not overlap in packing location.", "signature element %0 at location (%1,%2) size (%3,%4) overlaps another signature element.") |
| self.add_valrule_msg("Meta.SignatureOutOfRange", "Signature elements must fit within maximum signature size", "signature element %0 at location (%1,%2) size (%3,%4) is out of range.") |
| self.add_valrule_msg("Meta.SignatureIndexConflict", "Only elements with compatible indexing rules may be packed together", "signature element %0 at location (%1,%2) size (%3,%4) has an indexing conflict with another signature element packed into the same row.") |
| self.add_valrule_msg("Meta.SignatureIllegalComponentOrder", "Component ordering for packed elements must be: arbitrary < system value < system generated value", "signature element %0 at location (%1,%2) size (%3,%4) violates component ordering rule (arb < sv < sgv).") |
| self.add_valrule_msg("Meta.SignatureDataWidth", "Data width must be identical for all elements packed into the same row.", "signature element %0 at location (%1, %2) size (%3, %4) has data width that differs from another element packed into the same row.") |
| self.add_valrule_msg("Meta.IntegerInterpMode", "Interpolation mode on integer must be Constant", "signature element %0 specifies invalid interpolation mode for integer component type.") |
| self.add_valrule_msg("Meta.InterpModeInOneRow", "Interpolation mode must be identical for all elements packed into the same row.", "signature element %0 at location (%1,%2) size (%3,%4) has interpolation mode that differs from another element packed into the same row.") |
| self.add_valrule("Meta.SemanticCompType", "%0 must be %1") |
| self.add_valrule_msg("Meta.ClipCullMaxRows", "Combined elements of SV_ClipDistance and SV_CullDistance must fit in two rows.", "ClipDistance and CullDistance occupy more than the maximum of 2 rows combined.") |
| self.add_valrule_msg("Meta.ClipCullMaxComponents", "Combined elements of SV_ClipDistance and SV_CullDistance must fit in 8 components", "ClipDistance and CullDistance use more than the maximum of 8 components combined.") |
| self.add_valrule("Meta.SignatureCompType", "signature %0 specifies unrecognized or invalid component type") |
| self.add_valrule("Meta.TessellatorPartition", "Invalid Tessellator Partitioning specified. Must be integer, pow2, fractional_odd or fractional_even.") |
| self.add_valrule("Meta.TessellatorOutputPrimitive", "Invalid Tessellator Output Primitive specified. Must be point, line, triangleCW or triangleCCW.") |
| self.add_valrule("Meta.MaxTessFactor", "Hull Shader MaxTessFactor must be [%0..%1]. %2 specified") |
| self.add_valrule("Meta.ValidSamplerMode", "Invalid sampler mode on sampler ") |
| self.add_valrule("Meta.FunctionAnnotation", "Cannot find function annotation for %0") |
| self.add_valrule("Meta.GlcNotOnAppendConsume", "globallycoherent cannot be used with append/consume buffers") |
| self.add_valrule_msg("Meta.StructBufAlignment", "StructuredBuffer stride not aligned","structured buffer element size must be a multiple of %0 bytes (actual size %1 bytes)") |
| self.add_valrule_msg("Meta.StructBufAlignmentOutOfBound", "StructuredBuffer stride out of bounds","structured buffer elements cannot be larger than %0 bytes (actual size %1 bytes)") |
| self.add_valrule("Meta.EntryFunction", "entrypoint not found") |
| self.add_valrule("Meta.InvalidControlFlowHint", "Invalid control flow hint") |
| self.add_valrule("Meta.BranchFlatten", "Can't use branch and flatten attributes together") |
| self.add_valrule("Meta.ForceCaseOnSwitch", "Attribute forcecase only works for switch") |
| self.add_valrule("Meta.ControlFlowHintNotOnControlFlow", "Control flow hint only works on control flow inst") |
| self.add_valrule("Meta.TextureType", "elements of typed buffers and textures must fit in four 32-bit quantities") |
| self.add_valrule("Meta.BarycentricsInterpolation", "SV_Barycentrics cannot be used with 'nointerpolation' type") |
| self.add_valrule("Meta.BarycentricsFloat3", "only 'float3' type is allowed for SV_Barycentrics.") |
| self.add_valrule("Meta.BarycentricsTwoPerspectives", "There can only be up to two input attributes of SV_Barycentrics with different perspective interpolation mode.") |
| |
| self.add_valrule("Instr.Oload", "DXIL intrinsic overload must be valid") |
| self.add_valrule_msg("Instr.CallOload", "Call to DXIL intrinsic must match overload signature", "Call to DXIL intrinsic '%0' does not match an allowed overload signature") |
| self.add_valrule("Instr.PtrBitCast", "Pointer type bitcast must be have same size") |
| self.add_valrule("Instr.MinPrecisonBitCast", "Bitcast on minprecison types is not allowed") |
| self.add_valrule("Instr.StructBitCast", "Bitcast on struct types is not allowed") |
| self.add_valrule("Instr.Status", "Resource status should only used by CheckAccessFullyMapped") |
| self.add_valrule("Instr.CheckAccessFullyMapped", "CheckAccessFullyMapped should only used on resource status") |
| self.add_valrule_msg("Instr.OpConst", "DXIL intrinsic requires an immediate constant operand", "%0 of %1 must be an immediate constant") |
| self.add_valrule("Instr.Allowed", "Instructions must be of an allowed type") |
| self.add_valrule("Instr.OpCodeReserved", "Instructions must not reference reserved opcodes") |
| self.add_valrule_msg("Instr.OperandRange", "DXIL intrinsic operand must be within defined range", "expect %0 between %1, got %2") |
| self.add_valrule("Instr.NoReadingUninitialized", "Instructions should not read uninitialized value") |
| self.add_valrule("Instr.NoGenericPtrAddrSpaceCast", "Address space cast between pointer types must have one part to be generic address space") |
| self.add_valrule("Instr.InBoundsAccess", "Access to out-of-bounds memory is disallowed") |
| self.add_valrule("Instr.OpConstRange", "Constant values must be in-range for operation") |
| self.add_valrule("Instr.ImmBiasForSampleB", "bias amount for sample_b must be in the range [%0,%1], but %2 was specified as an immediate") |
| # If streams have not been declared, you must use cut instead of cut_stream in GS - is there an equivalent rule here? |
| |
| # Need to clean up all error messages and actually implement. |
| # Midlevel |
| self.add_valrule("Instr.NoIndefiniteLog", "No indefinite logarithm") |
| self.add_valrule("Instr.NoIndefiniteAsin", "No indefinite arcsine") |
| self.add_valrule("Instr.NoIndefiniteAcos", "No indefinite arccosine") |
| self.add_valrule("Instr.NoIDivByZero", "No signed integer division by zero") |
| self.add_valrule("Instr.NoUDivByZero", "No unsigned integer division by zero") |
| self.add_valrule("Instr.NoIndefiniteDsxy", "No indefinite derivative calculation") |
| self.add_valrule("Instr.MinPrecisionNotPrecise", "Instructions marked precise may not refer to minprecision values") |
| |
| # Backend |
| self.add_valrule("Instr.OnlyOneAllocConsume", "RWStructuredBuffers may increment or decrement their counters, but not both.") |
| |
| # CCompiler |
| self.add_valrule("Instr.TextureOffset", "offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7") |
| # D3D12 |
| self.add_valrule_msg("Instr.CannotPullPosition", "pull-model evaluation of position disallowed", "%0 does not support pull-model evaluation of position") |
| #self.add_valrule("Instr.ERR_GUARANTEED_RACE_CONDITION_UAV", "TODO - race condition writing to shared resource detected, consider making this write conditional.") warning on fxc. |
| #self.add_valrule("Instr.ERR_GUARANTEED_RACE_CONDITION_GSM", "TODO - race condition writing to shared memory detected, consider making this write conditional.") warning on fxc. |
| #self.add_valrule("Instr.ERR_INFINITE_LOOP", "TODO - ERR_INFINITE_LOOP") fxc will report error if it can prove the loop is infinite. |
| self.add_valrule("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") |
| self.add_valrule("Instr.ResourceCoordinateMiss", "coord uninitialized") |
| self.add_valrule("Instr.ResourceCoordinateTooMany", "out of bound coord must be undef") |
| self.add_valrule("Instr.ResourceOffsetMiss", "offset uninitialized") |
| self.add_valrule("Instr.ResourceOffsetTooMany", "out of bound offset must be undef") |
| self.add_valrule("Instr.UndefResultForGetDimension", "GetDimensions used undef dimension %0 on %1") |
| self.add_valrule("Instr.SamplerModeForLOD", "lod instruction requires sampler declared in default mode") |
| self.add_valrule("Instr.SamplerModeForSample", "sample/_l/_d/_cl_s/gather instruction requires sampler declared in default mode") |
| self.add_valrule("Instr.SamplerModeForSampleC", "sample_c_*/gather_c instructions require sampler declared in comparison mode") |
| self.add_valrule("Instr.SampleCompType", "sample_* instructions require resource to be declared to return UNORM, SNORM or FLOAT.") |
| self.add_valrule("Instr.BarrierModeUselessUGroup", "sync can't specify both _ugroup and _uglobal. If both are needed, just specify _uglobal.") |
| self.add_valrule("Instr.BarrierModeNoMemory", "sync must include some form of memory barrier - _u (UAV) and/or _g (Thread Group Shared Memory). Only _t (thread group sync) is optional. ") |
| self.add_valrule("Instr.BarrierModeForNonCS", "sync in a non-Compute Shader must only sync UAV (sync_uglobal)") |
| self.add_valrule("Instr.WriteMaskForTypedUAVStore", "store on typed uav must write to all four components of the UAV") |
| self.add_valrule("Instr.ResourceKindForCalcLOD","lod requires resource declared as texture1D/2D/3D/Cube/CubeArray/1DArray/2DArray") |
| self.add_valrule("Instr.ResourceKindForSample", "sample/_l/_d requires resource declared as texture1D/2D/3D/Cube/1DArray/2DArray/CubeArray") |
| self.add_valrule("Instr.ResourceKindForSampleC", "samplec requires resource declared as texture1D/2D/Cube/1DArray/2DArray/CubeArray") |
| self.add_valrule("Instr.ResourceKindForGather", "gather requires resource declared as texture/2D/Cube/2DArray/CubeArray") |
| self.add_valrule("Instr.WriteMaskMatchValueForUAVStore", "uav store write mask must match store value mask, write mask is %0 and store value mask is %1") |
| self.add_valrule("Instr.ResourceKindForBufferLoadStore", "buffer load/store only works on Raw/Typed/StructuredBuffer") |
| self.add_valrule("Instr.ResourceKindForTextureStore", "texture store only works on Texture1D/1DArray/2D/2DArray/3D") |
| self.add_valrule("Instr.ResourceKindForGetDim", "Invalid resource kind on GetDimensions") |
| self.add_valrule("Instr.ResourceKindForTextureLoad", "texture load only works on Texture1D/1DArray/2D/2DArray/3D/MS2D/MS2DArray") |
| self.add_valrule("Instr.ResourceClassForSamplerGather", "sample, lod and gather should on srv resource.") |
| self.add_valrule("Instr.ResourceClassForUAVStore", "store should on uav resource.") |
| self.add_valrule("Instr.ResourceClassForLoad", "load can only run on UAV/SRV resource") |
| self.add_valrule("Instr.OffsetOnUAVLoad", "uav load don't support offset") |
| self.add_valrule("Instr.MipOnUAVLoad", "uav load don't support mipLevel/sampleIndex") |
| self.add_valrule("Instr.SampleIndexForLoad2DMS", "load on Texture2DMS/2DMSArray require sampleIndex") |
| self.add_valrule("Instr.CoordinateCountForRawTypedBuf", "raw/typed buffer don't need 2 coordinates") |
| self.add_valrule("Instr.CoordinateCountForStructBuf", "structured buffer require 2 coordinates") |
| self.add_valrule("Instr.MipLevelForGetDimension", "Use mip level on buffer when GetDimensions") |
| self.add_valrule("Instr.DxilStructUser", "Dxil struct types should only used by ExtractValue") |
| self.add_valrule("Instr.DxilStructUserOutOfBound", "Index out of bound when extract value from dxil struct types") |
| self.add_valrule("Instr.HandleNotFromCreateHandle", "Resource handle should returned by createHandle") |
| self.add_valrule("Instr.BufferUpdateCounterOnUAV", "BufferUpdateCounter valid only on UAV") |
| self.add_valrule("Instr.CBufferOutOfBound", "Cbuffer access out of bound") |
| self.add_valrule("Instr.CBufferClassForCBufferHandle", "Expect Cbuffer for CBufferLoad handle") |
| self.add_valrule("Instr.FailToResloveTGSMPointer", "TGSM pointers must originate from an unambiguous TGSM global variable.") |
| self.add_valrule("Instr.ExtractValue", "ExtractValue should only be used on dxil struct types and cmpxchg") |
| self.add_valrule("Instr.TGSMRaceCond", "Race condition writing to shared memory detected, consider making this write conditional") |
| self.add_valrule("Instr.AttributeAtVertexNoInterpolation", "Attribute %0 must have nointerpolation mode in order to use GetAttributeAtVertex function.") |
| self.add_valrule("Instr.CreateHandleImmRangeID", "Local resource must map to global resource.") |
| |
| # Some legacy rules: |
| # - space is only supported for shader targets 5.1 and higher |
| # - multiple rules regarding derivatives, which isn't a supported feature for DXIL |
| # - multiple rules regarding library functions, which isn't a supported feature for DXIL (at this time) |
| # - multiple rules regarding interfaces, which isn't a supported feature for DXIL |
| # - rules for DX9-style intrinsics, which aren't supported for DXIL |
| |
| self.add_valrule_msg("Types.NoVector", "Vector types must not be present", "Vector type '%0' is not allowed") |
| self.add_valrule_msg("Types.Defined", "Type must be defined based on DXIL primitives", "Type '%0' is not defined on DXIL primitives") |
| self.add_valrule_msg("Types.IntWidth", "Int type must be of valid width", "Int type '%0' has an invalid width") |
| self.add_valrule("Types.NoMultiDim", "Only one dimension allowed for array type") |
| self.add_valrule("Types.I8", "I8 can only used as immediate value for intrinsic") |
| |
| self.add_valrule_msg("Sm.Name", "Target shader model name must be known", "Unknown shader model '%0'") |
| self.add_valrule_msg("Sm.DxilVersion", "Target shader model requires specific Dxil Version", "Shader model requires Dxil Version %0,%1") |
| self.add_valrule_msg("Sm.Opcode", "Opcode must be defined in target shader model", "Opcode %0 not valid in shader model %1") |
| self.add_valrule("Sm.Operand", "Operand must be defined in target shader model") |
| self.add_valrule_msg("Sm.Semantic", "Semantic must be defined in target shader model", "Semantic '%0' is invalid as %1 %2") |
| self.add_valrule_msg("Sm.NoInterpMode", "Interpolation mode must be undefined for VS input/PS output/patch constant.", "Interpolation mode for '%0' is set but should be undefined") |
| self.add_valrule("Sm.NoPSOutputIdx", "Pixel shader output registers are not indexable.")# TODO restrict to PS |
| self.add_valrule("Sm.PSConsistentInterp", "Interpolation mode for PS input position must be linear_noperspective_centroid or linear_noperspective_sample when outputting oDepthGE or oDepthLE and not running at sample frequency (which is forced by inputting SV_SampleIndex or declaring an input linear_sample or linear_noperspective_sample)") |
| self.add_valrule("Sm.ThreadGroupChannelRange", "Declared Thread Group %0 size %1 outside valid range [%2..%3]") |
| self.add_valrule("Sm.MaxTheadGroup", "Declared Thread Group Count %0 (X*Y*Z) is beyond the valid maximum of %1") |
| self.add_valrule("Sm.MaxTGSMSize", "Total Thread Group Shared Memory storage is %0, exceeded %1") |
| self.add_valrule("Sm.ROVOnlyInPS", "RasterizerOrdered objects are only allowed in 5.0+ pixel shaders") |
| self.add_valrule("Sm.TessFactorForDomain", "Required TessFactor for domain not found declared anywhere in Patch Constant data") |
| self.add_valrule("Sm.TessFactorSizeMatchDomain", "TessFactor rows, columns (%0, %1) invalid for domain %2. Expected %3 rows and 1 column.") |
| self.add_valrule("Sm.InsideTessFactorSizeMatchDomain", "InsideTessFactor rows, columns (%0, %1) invalid for domain %2. Expected %3 rows and 1 column.") |
| self.add_valrule("Sm.DomainLocationIdxOOB", "DomainLocation component index out of bounds for the domain.") |
| self.add_valrule("Sm.HullPassThruControlPointCountMatch", "For pass thru hull shader, input control point count must match output control point count"); |
| self.add_valrule("Sm.OutputControlPointsTotalScalars", "Total number of scalars across all HS output control points must not exceed ") |
| self.add_valrule("Sm.IsoLineOutputPrimitiveMismatch", "Hull Shader declared with IsoLine Domain must specify output primitive point or line. Triangle_cw or triangle_ccw output are not compatible with the IsoLine Domain.") |
| self.add_valrule("Sm.TriOutputPrimitiveMismatch", "Hull Shader declared with Tri Domain must specify output primitive point, triangle_cw or triangle_ccw. Line output is not compatible with the Tri domain") |
| self.add_valrule("Sm.ValidDomain", "Invalid Tessellator Domain specified. Must be isoline, tri or quad") |
| self.add_valrule("Sm.PatchConstantOnlyForHSDS", "patch constant signature only valid in HS and DS") |
| self.add_valrule("Sm.StreamIndexRange", "Stream index (%0) must between 0 and %1") |
| self.add_valrule("Sm.PSOutputSemantic", "Pixel Shader allows output semantics to be SV_Target, SV_Depth, SV_DepthGreaterEqual, SV_DepthLessEqual, SV_Coverage or SV_StencilRef, %0 found") |
| self.add_valrule("Sm.PSMultipleDepthSemantic", "Pixel Shader only allows one type of depth semantic to be declared") |
| self.add_valrule("Sm.PSTargetIndexMatchesRow", "SV_Target semantic index must match packed row location") |
| self.add_valrule("Sm.PSTargetCol0", "SV_Target packed location must start at column 0") |
| self.add_valrule("Sm.PSCoverageAndInnerCoverage", "InnerCoverage and Coverage are mutually exclusive.") |
| self.add_valrule("Sm.GSOutputVertexCountRange", "GS output vertex count must be [0..%0]. %1 specified") |
| self.add_valrule("Sm.GSInstanceCountRange", "GS instance count must be [1..%0]. %1 specified") |
| self.add_valrule("Sm.DSInputControlPointCountRange", "DS input control point count must be [0..%0]. %1 specified") |
| self.add_valrule("Sm.HSInputControlPointCountRange", "HS input control point count must be [0..%0]. %1 specified") |
| self.add_valrule("Sm.ZeroHSInputControlPointWithInput", "When HS input control point count is 0, no input signature should exist") |
| self.add_valrule("Sm.OutputControlPointCountRange", "output control point count must be [0..%0]. %1 specified") |
| self.add_valrule("Sm.GSValidInputPrimitive", "GS input primitive unrecognized") |
| self.add_valrule("Sm.GSValidOutputPrimitiveTopology", "GS output primitive topology unrecognized") |
| self.add_valrule("Sm.AppendAndConsumeOnSameUAV", "BufferUpdateCounter inc and dec on a given UAV (%d) cannot both be in the same shader for shader model less than 5.1.") |
| self.add_valrule("Sm.InvalidTextureKindOnUAV", "Texture2DMS[Array] or TextureCube[Array] resources are not supported with UAVs") |
| self.add_valrule("Sm.InvalidResourceKind", "Invalid resources kind") |
| self.add_valrule("Sm.InvalidResourceCompType","Invalid resource return type") |
| self.add_valrule("Sm.SampleCountOnlyOn2DMS","Only Texture2DMS/2DMSArray could has sample count") |
| self.add_valrule("Sm.CounterOnlyOnStructBuf", "BufferUpdateCounter valid only on structured buffers") |
| self.add_valrule("Sm.GSTotalOutputVertexDataRange", "Declared output vertex count (%0) multiplied by the total number of declared scalar components of output data (%1) equals %2. This value cannot be greater than %3") |
| self.add_valrule_msg("Sm.MultiStreamMustBePoint", "When multiple GS output streams are used they must be pointlists", "Multiple GS output streams are used but '%0' is not pointlist") |
| self.add_valrule("Sm.CompletePosition", "Not all elements of SV_Position were written") |
| self.add_valrule("Sm.UndefinedOutput", "Not all elements of output %0 were written") |
| self.add_valrule("Sm.CSNoReturn", "Compute shaders can't return values, outputs must be written in writable resources (UAVs).") |
| self.add_valrule("Sm.CBufferTemplateTypeMustBeStruct", "D3D12 constant/texture buffer template element can only be a struct") |
| self.add_valrule_msg("Sm.ResourceRangeOverlap", "Resource ranges must not overlap", "Resource %0 with base %1 size %2 overlap with other resource with base %3 size %4 in space %5") |
| self.add_valrule_msg("Sm.CBufferOffsetOverlap", "CBuffer offsets must not overlap", "CBuffer %0 has offset overlaps at %1") |
| self.add_valrule_msg("Sm.CBufferElementOverflow", "CBuffer elements must not overflow", "CBuffer %0 size insufficient for element at offset %1") |
| self.add_valrule_msg("Sm.OpcodeInInvalidFunction", "Invalid DXIL opcode usage like StorePatchConstant in patch constant function", "opcode '%0' should only be used in '%1'") |
| self.add_valrule_msg("Sm.ViewIDNeedsSlot", "ViewID requires compatible space in pixel shader input signature", "Pixel shader input signature lacks available space for ViewID") |
| |
| # fxc relaxed check of gradient check. |
| #self.add_valrule("Uni.NoUniInDiv", "TODO - No instruction requiring uniform execution can be present in divergent block") |
| #self.add_valrule("Uni.GradientFlow", "TODO - No divergent gradient operations inside flow control") # a bit more specific than the prior rule |
| #self.add_valrule("Uni.ThreadSync", "TODO - Thread sync operation must be in non-varying flow control due to a potential race condition, adding a sync after reading any values controlling shader execution at this point") |
| self.add_valrule("Uni.NoWaveSensitiveGradient", "Gradient operations are not affected by wave-sensitive data or control flow.") |
| |
| self.add_valrule("Flow.Reducible", "Execution flow must be reducible") |
| self.add_valrule("Flow.NoRecusion", "Recursion is not permitted") |
| self.add_valrule("Flow.DeadLoop", "Loop must have break") |
| self.add_valrule_msg("Flow.FunctionCall", "Function with parameter is not permitted", "Function %0 with parameter is not permitted, it should be inlined") |
| |
| self.add_valrule_msg("Decl.DxilNsReserved", "The DXIL reserved prefixes must only be used by built-in functions and types", "Declaration '%0' uses a reserved prefix") |
| self.add_valrule_msg("Decl.DxilFnExtern", "External function must be a DXIL function", "External function '%0' is not a DXIL function") |
| self.add_valrule_msg("Decl.UsedInternal", "Internal declaration must be used", "Internal declaration '%0' is unused") |
| self.add_valrule_msg("Decl.NotUsedExternal", "External declaration should not be used", "External declaration '%0' is unused") |
| self.add_valrule_msg("Decl.UsedExternalFunction", "External function must be used", "External function '%0' is unused") |
| self.add_valrule_msg("Decl.FnIsCalled", "Functions can only be used by call instructions", "Function '%0' is used for something other than calling") |
| self.add_valrule_msg("Decl.FnFlattenParam", "Function parameters must not use struct types", "Type '%0' is a struct type but is used as a parameter in function '%1'") |
| self.add_valrule_msg("Decl.FnAttribute", "Functions should only contain known function attributes", "Function '%0' contains invalid attribute '%1' with value '%2'") |
| |
| # Assign sensible category names and build up an enumeration description |
| cat_names = { |
| "CONTAINER": "Container", |
| "BITCODE": "Bitcode", |
| "META": "Metadata", |
| "INSTR": "Instruction", |
| "FLOW": "Program flow", |
| "TYPES": "Type system", |
| "SM": "Shader model", |
| "UNI": "Uniform analysis", |
| "DECL": "Declaration" |
| } |
| valrule_enum = db_dxil_enum("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.category = vr.category |
| vrval.err_msg = vr.err_msg |
| valrule_enum.values.append(vrval) |
| self.enums.append(valrule_enum) |
| |
| def add_valrule(self, name, desc): |
| self.val_rules.append(db_dxil_valrule(name, len(self.val_rules), err_msg=desc, doc=desc)) |
| |
| def add_valrule_msg(self, name, desc, err_msg): |
| self.val_rules.append(db_dxil_valrule(name, len(self.val_rules), err_msg=err_msg, doc=desc)) |
| |
| def add_llvm_instr(self, kind, llvm_id, name, llvm_name, doc, oload_types, op_params): |
| i = db_dxil_inst(name, llvm_id=llvm_id, llvm_name=llvm_name, doc=doc, ops=op_params, oload_types=oload_types) |
| if kind == "TERM": i.is_bb_terminator=True |
| if kind == "BINARY": i.is_binary=True |
| if kind == "MEMORY": i.is_memory=True |
| if kind == "CAST": i.is_cast=True |
| self.instr.append(i) |
| |
| def add_dxil_op(self, name, code_id, code_class, doc, oload_types, fn_attr, op_params): |
| # 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=code_id, doc=doc, ops=op_params, dxil_class=code_class, |
| oload_types=oload_types, fn_attr=fn_attr) |
| self.instr.append(i) |
| def add_dxil_op_reserved(self, name, code_id): |
| # 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=code_id, doc="reserved", ops=op_params, dxil_class="Reserved", |
| oload_types="v", fn_attr="") |
| self.instr.append(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 |
| |
| def print_stats(self): |
| "Print some basic statistics on the instruction database." |
| print ("Instruction count: %d" % len(self.instr)) |
| print ("Max parameter count in instruction: %d" % max(len(i.ops) - 1 for i in self.instr)) |
| print ("Parameter count: %d" % sum(len(i.ops) - 1 for i in self.instr)) |
| |
| ############################################################################### |
| # HLSL-specific information. # |
| ############################################################################### |
| |
| class db_hlsl_attribute(object): |
| "An HLSL attribute declaration" |
| def __init__(self, title_name, scope, args, doc): |
| self.name = title_name.lower() # lowercase attribute name |
| self.title_name = title_name # title-case attribute name |
| self.scope = scope # one of l (loop), c (condition), s (switch), f (function) |
| self.args = args # list of arguments |
| self.doc = doc # documentation |
| |
| class db_hlsl_intrinsic(object): |
| "An HLSL intrinsic declaration" |
| def __init__(self, name, idx, opname, params, ns, ns_idx, doc, ro, rn, unsigned_op, overload_idx): |
| self.name = name # Function name |
| self.idx = idx # Unique number within namespace |
| self.opname = opname # D3D-style name |
| self.params = params # List of parameters |
| self.ns = ns # Function namespace |
| self.ns_idx = ns_idx # Namespace index |
| self.doc = doc # Documentation |
| id_prefix = "IOP" if ns == "Intrinsics" else "MOP" |
| self.enum_name = "%s_%s" % (id_prefix, name) # enum name |
| self.readonly = ro # Only read memory |
| self.readnone = rn # Not read memory |
| self.unsigned_op = unsigned_op # Unsigned opcode if exist |
| if unsigned_op != "": |
| self.unsigned_op = "%s_%s" % (id_prefix, unsigned_op) |
| self.overload_param_index = overload_idx # Parameter determines the overload type, -1 means ret type |
| self.key = ("%3d" % ns_idx) + "!" + name + "!" + ("%2d" % len(params)) + "!" + ("%3d" % idx) # Unique key |
| self.vulkanSpecific = ns.startswith("Vk") # Vulkan specific intrinsic - SPIRV change |
| |
| class db_hlsl_namespace(object): |
| "A grouping of HLSL intrinsics" |
| def __init__(self, name): |
| self.name = name |
| self.intrinsics = [] |
| |
| class db_hlsl_intrisic_param(object): |
| "An HLSL parameter declaration for an intrinsic" |
| def __init__(self, name, param_qual, template_id, template_list, component_id, component_list, rows, cols, type_name, idx, template_id_idx, component_id_idx): |
| self.name = name # Parameter name |
| self.param_qual = param_qual # Parameter qualifier expressions |
| self.template_id = template_id # Template ID (possibly identifier) |
| self.template_list = template_list # Template list (possibly identifier) |
| self.component_id = component_id # Component ID (possibly identifier) |
| self.component_list = component_list # Component list (possibly identifier) |
| self.rows = rows # Row count for parameter, possibly identifier |
| self.cols = cols # Row count for parameter, possibly identifier |
| self.type_name = type_name # Type name |
| self.idx = idx # Argument index |
| self.template_id_idx = template_id_idx # Template ID numeric value |
| self.component_id_idx = component_id_idx # Component ID numeric value |
| |
| class db_hlsl(object): |
| "A database of HLSL language data" |
| def __init__(self, intrinsic_defs): |
| self.base_types = { |
| "bool": "LICOMPTYPE_BOOL", |
| "int": "LICOMPTYPE_INT", |
| "int16_t": "LICOMPTYPE_INT16", |
| "uint": "LICOMPTYPE_UINT", |
| "uint16_t": "LICOMPTYPE_UINT16", |
| "u64": "LICOMPTYPE_UINT64", |
| "u16_32_64": "LICOMPTYPE_UINT16_32_64", |
| "any_int": "LICOMPTYPE_ANY_INT", |
| "any_int32": "LICOMPTYPE_ANY_INT32", |
| "uint_only": "LICOMPTYPE_UINT_ONLY", |
| "float16_t": "LICOMPTYPE_FLOAT16", |
| "float": "LICOMPTYPE_FLOAT", |
| "fldbl": "LICOMPTYPE_FLOAT_DOUBLE", |
| "any_float": "LICOMPTYPE_ANY_FLOAT", |
| "float_like": "LICOMPTYPE_FLOAT_LIKE", |
| "double": "LICOMPTYPE_DOUBLE", |
| "double_only": "LICOMPTYPE_DOUBLE_ONLY", |
| "numeric": "LICOMPTYPE_NUMERIC", |
| "numeric16_only": "LICOMPTYPE_NUMERIC16_ONLY", |
| "numeric32": "LICOMPTYPE_NUMERIC32", |
| "numeric32_only": "LICOMPTYPE_NUMERIC32_ONLY", |
| "any": "LICOMPTYPE_ANY", |
| "sampler1d": "LICOMPTYPE_SAMPLER1D", |
| "sampler2d": "LICOMPTYPE_SAMPLER2D", |
| "sampler3d": "LICOMPTYPE_SAMPLER3D", |
| "sampler_cube": "LICOMPTYPE_SAMPLERCUBE", |
| "sampler_cmp": "LICOMPTYPE_SAMPLERCMP", |
| "sampler": "LICOMPTYPE_SAMPLER", |
| "void": "LICOMPTYPE_VOID", |
| "string": "LICOMPTYPE_STRING", |
| "wave": "LICOMPTYPE_WAVE"} |
| self.trans_rowcol = { |
| "r": "IA_R", |
| "c": "IA_C", |
| "r2": "IA_R2", |
| "c2": "IA_C2"} |
| self.param_qual = { |
| "in": "AR_QUAL_IN", |
| "inout": "AR_QUAL_IN | AR_QUAL_OUT", |
| "out": "AR_QUAL_OUT", |
| "col_major": "AR_QUAL_COLMAJOR", |
| "row_major": "AR_QUAL_ROWMAJOR"} |
| self.intrinsics = [] |
| self.load_intrinsics(intrinsic_defs) |
| self.create_namespaces() |
| self.populate_attributes() |
| self.opcode_namespace = "hlsl::IntrinsicOp" |
| |
| def create_namespaces(self): |
| last_ns = None |
| self.namespaces = {} |
| for i in sorted(self.intrinsics, key=lambda x: x.key): |
| if last_ns is None or last_ns.name != i.ns: |
| last_ns = db_hlsl_namespace(i.ns) |
| self.namespaces[i.ns] = last_ns |
| last_ns.intrinsics.append(i) |
| |
| def load_intrinsics(self, intrinsic_defs): |
| import re |
| blank_re = re.compile(r"^\s*$") |
| comment_re = re.compile(r"^\s*//") |
| namespace_beg_re = re.compile(r"^namespace\s+(\w+)\s*{\s*$") |
| namespace_end_re = re.compile(r"^}\s*namespace\s*$") |
| intrinsic_re = re.compile(r"^\s*([^(]+)\s+\[\[(\S*)\]\]\s+(\w+)\s*\(\s*([^)]*)\s*\)\s*(:\s*\w+\s*)?;$") |
| operand_re = re.compile(r"^:\s*(\w+)\s*$") |
| bracket_cleanup_re = re.compile(r"<\s*(\S+)\s*,\s*(\S+)\s*>") # change <a,b> to <a@> to help split params and parse |
| params_split_re = re.compile(r"\s*,\s*") |
| ws_split_re = re.compile(r"\s+") |
| typeref_re = re.compile(r"\$type(\d+)$") |
| type_matrix_re = re.compile(r"(\S+)<(\S+)@(\S+)>$") |
| type_vector_re = re.compile(r"(\S+)<(\S+)>$") |
| type_any_re = re.compile(r"(\S+)<>$") |
| digits_re = re.compile(r"^\d+$") |
| opt_param_match_re = re.compile(r"^\$match<(\S+)@(\S+)>$") |
| ns_idx = 0 |
| num_entries = 0 |
| |
| def add_flag(val, new_val): |
| if val == "" or val == "0": |
| return new_val |
| return val + " | " + new_val |
| |
| def translate_rowcol(val): |
| digits_match = digits_re.match(val) |
| if digits_match: |
| return val |
| assert val in self.trans_rowcol, "unknown row/col %s" % val |
| return self.trans_rowcol[val] |
| |
| def process_arg(desc, idx, done_args, intrinsic_name): |
| "Process a single parameter description." |
| opt_list = [] |
| desc = desc.strip() |
| if desc == "...": |
| param_name = "..." |
| type_name = "..." |
| else: |
| opt_list = ws_split_re.split(desc) |
| assert len(opt_list) > 0, "malformed parameter desc %s" % (desc) |
| param_name = opt_list.pop() # last token is name |
| type_name = opt_list.pop() # next-to-last is type specifier |
| |
| param_qual = "0" |
| template_id = str(idx) |
| template_list = "LITEMPLATE_ANY" |
| component_id = str(idx) |
| component_list = "LICOMPTYPE_ANY" |
| rows = "1" |
| cols = "1" |
| if type_name == "$unspec": |
| assert idx == 0, "'$unspec' can only be used as the return type" |
| # template_id may be -1 in other places other than return type, for example in Stream.Append(). |
| # $unspec is a shorthand for return types only though. |
| template_id = "-1" |
| component_id = "0" |
| type_name = "void" |
| elif type_name == "...": |
| assert idx != 0, "'...' can only be used in the parameter list" |
| template_id = "-2" |
| component_id = "0" |
| type_name = "void" |
| else: |
| typeref_match = typeref_re.match(type_name) |
| if typeref_match: |
| template_id = typeref_match.group(1) |
| component_id = template_id |
| assert idx != 1, "Can't use $type on the first argument" |
| assert template_id != "0", "Can't match an input to the return type" |
| done_idx = int(template_id) - 1 |
| assert done_idx <= len(args) + 1, "$type must refer to a processed arg" |
| done_arg = done_args[done_idx] |
| type_name = done_arg.type_name |
| # Determine matrix/vector/any/scalar type names. |
| type_matrix_match = type_matrix_re.match(type_name) |
| if type_matrix_match: |
| base_type = type_matrix_match.group(1) |
| rows = type_matrix_match.group(2) |
| cols = type_matrix_match.group(3) |
| template_list = "LITEMPLATE_MATRIX" |
| else: |
| type_vector_match = type_vector_re.match(type_name) |
| if type_vector_match: |
| base_type = type_vector_match.group(1) |
| cols = type_vector_match.group(2) |
| template_list = "LITEMPLATE_VECTOR" |
| else: |
| type_any_match = type_any_re.match(type_name) |
| if type_any_match: |
| base_type = type_any_match.group(1) |
| rows = "r" |
| cols = "c" |
| template_list = "LITEMPLATE_ANY" |
| else: |
| base_type = type_name |
| if base_type.startswith("sampler") or base_type.startswith("string") or base_type.startswith("wave"): |
| template_list = "LITEMPLATE_OBJECT" |
| else: |
| template_list = "LITEMPLATE_SCALAR" |
| assert base_type in self.base_types, "Unknown base type '%s' in '%s'" % (base_type, desc) |
| component_list = self.base_types[base_type] |
| rows = translate_rowcol(rows) |
| cols = translate_rowcol(cols) |
| for opt in opt_list: |
| if opt in self.param_qual: |
| param_qual = add_flag(param_qual, self.param_qual[opt]) |
| else: |
| opt_param_match_match = opt_param_match_re.match(opt) |
| assert opt_param_match_match, "Unknown parameter qualifier '%s'" % (opt) |
| template_id = opt_param_match_match.group(1) |
| component_id = opt_param_match_match.group(2) |
| if component_list == "LICOMPTYPE_VOID": |
| if type_name == "void": |
| template_list = "LITEMPLATE_VOID" |
| rows = "0" |
| cols = "0" |
| if template_id == "0": |
| param_qual = "0" |
| # Keep these as numeric values. |
| template_id_idx = int(template_id) |
| component_id_idx = int(component_id) |
| # Verify that references don't point to the right (except for the return value). |
| assert idx == 0 or template_id_idx <= int(idx), "Argument '%s' has a forward reference" % (param_name) |
| assert idx == 0 or component_id_idx <= int(idx), "Argument '%s' has a forward reference" % (param_name) |
| if template_id == "-1": |
| template_id = "INTRIN_TEMPLATE_FROM_TYPE" |
| elif template_id == "-2": |
| template_id = "INTRIN_TEMPLATE_VARARGS" |
| if component_id == "-1": |
| component_id = "INTRIN_COMPTYPE_FROM_TYPE_ELT0" |
| return db_hlsl_intrisic_param(param_name, param_qual, template_id, template_list, component_id, component_list, rows, cols, type_name, idx, template_id_idx, component_id_idx) |
| |
| def process_attr(attr): |
| attrs = attr.split(',') |
| readonly = False # Only read memory |
| readnone = False # Not read memory |
| unsigned_op = "" # Unsigned opcode if exist |
| overload_param_index = -1 # Parameter determines the overload type, -1 means ret type. |
| for a in attrs: |
| if (a == ""): |
| continue |
| if (a == "ro"): |
| readonly = True |
| continue |
| if (a == "rn"): |
| readnone = True |
| continue |
| assign = a.split('=') |
| |
| if (len(assign) != 2): |
| assert False, "invalid attr %s" % (a) |
| continue |
| d = assign[0] |
| v = assign[1] |
| if (d == "unsigned_op"): |
| unsigned_op = v |
| continue |
| if (d == "overload"): |
| overload_param_index = int(v) |
| continue |
| assert False, "invalid attr %s" % (a) |
| |
| return readonly, readnone, unsigned_op, overload_param_index |
| |
| current_namespace = None |
| for line in intrinsic_defs: |
| if blank_re.match(line): continue |
| if comment_re.match(line): continue |
| match_obj = namespace_beg_re.match(line) |
| if match_obj: |
| assert not current_namespace, "cannot open namespace without closing prior one" |
| current_namespace = match_obj.group(1) |
| num_entries = 0 |
| ns_idx += 1 |
| continue |
| if namespace_end_re.match(line): |
| assert current_namespace, "cannot close namespace without previously opening it" |
| current_namespace = None |
| continue |
| match_obj = intrinsic_re.match(line) |
| if match_obj: |
| assert current_namespace, "instruction missing namespace %s" % (line) |
| # Get a D3D-style operand name for the instruction. |
| # Unused for DXIL. |
| opts = match_obj.group(1) |
| attr = match_obj.group(2) |
| name = match_obj.group(3) |
| params = match_obj.group(4) |
| op = match_obj.group(5) |
| if op: |
| operand_match = operand_re.match(op) |
| if operand_match: |
| op = operand_match.group(1) |
| if not op: |
| op = name |
| readonly, readnone, unsigned_op, overload_param_index = process_attr(attr) |
| # Add an entry for this intrinsic. |
| if bracket_cleanup_re.search(opts): |
| opts = bracket_cleanup_re.sub(r"<\1@\2>", opts) |
| if bracket_cleanup_re.search(params): |
| params = bracket_cleanup_re.sub(r"<\g<1>@\2>", params) |
| ret_desc = "out " + opts + " " + name |
| if len(params) > 0: |
| in_args = params_split_re.split(params) |
| else: |
| in_args = [] |
| arg_idx = 1 |
| args = [] |
| for in_arg in in_args: |
| args.append(process_arg(in_arg, arg_idx, args, name)) |
| arg_idx += 1 |
| # We have to process the return type description last |
| # to match the compiler's handling of it and allow |
| # the return type to match an input type. |
| # It needs to be the first entry, so prepend it. |
| args.insert(0, process_arg(ret_desc, 0, args, name)) |
| # TODO: verify a single level of indirection |
| self.intrinsics.append(db_hlsl_intrinsic( |
| name, num_entries, op, args, current_namespace, ns_idx, "pending doc for " + name, |
| readonly, readnone, unsigned_op, overload_param_index)) |
| num_entries += 1 |
| continue |
| assert False, "cannot parse line %s" % (line) |
| |
| def populate_attributes(self): |
| "Populate basic definitions for attributes." |
| attributes = [] |
| def add_attr(title_name, scope, doc): |
| attributes.append(db_hlsl_attribute(title_name, scope, [], doc)) |
| def add_attr_arg(title_name, scope, args, doc): |
| attributes.append(db_hlsl_attribute(title_name, scope, args, doc)) |
| add_attr("Allow_UAV_Condition", "l", "Allows a compute shader loop termination condition to be based off of a UAV read. The loop must not contain synchronization intrinsics") |
| add_attr("Branch", "c", "Evaluate only one side of the if statement depending on the given condition") |
| add_attr("Call", "s", "The bodies of the individual cases in the switch will be moved into hardware subroutines and the switch will be a series of subroutine calls") |
| add_attr("EarlyDepthStencil", "f", "Forces depth-stencil testing before a shader executes") |
| add_attr("FastOpt", "l", "Reduces the compile time but produces less aggressive optimizations") |
| add_attr("Flatten", "c", "Evaluate both sides of the if statement and choose between the two resulting values") |
| add_attr("ForceCase", "s", "Force a switch statement in the hardware") |
| add_attr("Loop", "l", "Generate code that uses flow control to execute each iteration of the loop") |
| add_attr_arg("ClipPlanes", "f", "Optional list of clip planes", [{"name":"ClipPlane", "type":"int", "count":6}]) |
| add_attr_arg("Domain", "f", "Defines the patch type used in the HS", [{"name":"DomainType", type:"string"}]) |
| add_attr_arg("Instance", "f", "Use this attribute to instance a geometry shader", [{"name":"Count", "type":"int"}]) |
| add_attr_arg("MaxTessFactor", "f", "Indicates the maximum value that the hull shader would return for any tessellation factor.", [{"name":"Count", "type":"int"}]) |
| add_attr_arg("MaxVertexCount", "f", "maxvertexcount doc", [{"name":"Count", "type":"int"}]) |
| add_attr_arg("NumThreads", "f", "Defines the number of threads to be executed in a single thread group.", [{"name":"x", "type":"int"},{"name":"z", "type":"int"},{"name":"y", "type":"int"}]) |
| add_attr_arg("OutputControlPoints", "f", "Defines the number of output control points per thread that will be created in the hull shader", [{"name":"Count", "type":"int"}]) |
| add_attr_arg("OutputTopology", "f", "Defines the output primitive type for the tessellator", [{"name":"Topology", "type":"string"}]) |
| add_attr_arg("Partitioning", "f", "Defines the tesselation scheme to be used in the hull shader", [{"name":"Scheme", "type":"scheme"}]) |
| add_attr_arg("PatchConstantFunc", "f", "Defines the function for computing patch constant data", [{"name":"FunctionName", "type":"string"}]) |
| add_attr_arg("RootSignature", "f", "RootSignature doc", [{"name":"SignatureName", "type":"string"}]) |
| add_attr_arg("Unroll", "l", "Unroll the loop until it stops executing or a max count", [{"name":"Count", "type":"int"}]) |
| self.attributes = attributes |
| |
| if __name__ == "__main__": |
| db = db_dxil() |
| print(db) |
| db.print_stats() |