From d8ebd343e245818832b1a37a860a93555d47538d Mon Sep 17 00:00:00 2001 From: Simon Moll Date: Thu, 13 Mar 2025 10:24:18 +0100 Subject: [PATCH] [SER] HitObject_MakeNop|Miss DXIL opcodes and verification tests Specification: https://github.com/microsoft/hlsl-specs/blob/main/proposals/0027-shader-execution-reordering.md DXC SER implementation tracker: #7214 --- include/dxc/DXIL/DxilConstants.h | 12 ++- include/dxc/DXIL/DxilInstructions.h | 74 +++++++++++++++++++ include/dxc/DXIL/DxilOperations.h | 2 + lib/DXIL/DxilOperations.cpp | 64 ++++++++++++---- .../ser_hitobject_make_passing.ll | 46 ++++++++++++ utils/hct/hctdb.py | 53 ++++++++++++- utils/hct/hctdb_instrhelp.py | 1 + 7 files changed, 230 insertions(+), 22 deletions(-) create mode 100644 tools/clang/test/DXILValidation/ser_hitobject_make_passing.ll diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index 54131f3948..0a9c6a4ffd 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -520,9 +520,7 @@ enum class OpCode : unsigned { ReservedB27 = 289, // reserved ReservedB28 = 290, // reserved ReservedB29 = 291, // reserved - ReservedB3 = 265, // reserved ReservedB30 = 292, // reserved - ReservedB4 = 266, // reserved ReservedB5 = 267, // reserved ReservedB6 = 268, // reserved ReservedB7 = 269, // reserved @@ -909,6 +907,10 @@ enum class OpCode : unsigned { WriteSamplerFeedbackLevel = 176, // updates a feedback texture for a sampling // operation with a mipmap-level offset + // Shader Execution Reordering + HitObject_MakeMiss = 265, // Creates a new HitObject representing a miss + HitObject_MakeNop = 266, // Creates an empty nop HitObject + // Synchronization AtomicBinOp = 78, // performs an atomic operation on two operands AtomicCompareExchange = 79, // atomic compare and exchange to memory @@ -1281,6 +1283,10 @@ enum class OpCodeClass : unsigned { WriteSamplerFeedbackGrad, WriteSamplerFeedbackLevel, + // Shader Execution Reordering + HitObject_MakeMiss, + HitObject_MakeNop, + // Synchronization AtomicBinOp, AtomicCompareExchange, @@ -1345,7 +1351,7 @@ enum class OpCodeClass : unsigned { NumOpClasses_Dxil_1_7 = 153, NumOpClasses_Dxil_1_8 = 174, - NumOpClasses = 175 // exclusive last value of enumeration + NumOpClasses = 177 // exclusive last value of enumeration }; // OPCODECLASS-ENUM:END diff --git a/include/dxc/DXIL/DxilInstructions.h b/include/dxc/DXIL/DxilInstructions.h index 11ab8e3b8d..6a28a2a806 100644 --- a/include/dxc/DXIL/DxilInstructions.h +++ b/include/dxc/DXIL/DxilInstructions.h @@ -8813,5 +8813,79 @@ struct DxilInst_AllocateRayQuery2 { llvm::APInt(32, (uint64_t)val))); } }; + +/// This instruction Creates a new HitObject representing a miss +struct DxilInst_HitObject_MakeMiss { + llvm::Instruction *Instr; + // Construction and identification + DxilInst_HitObject_MakeMiss(llvm::Instruction *pInstr) : Instr(pInstr) {} + operator bool() const { + return hlsl::OP::IsDxilOpFuncCallInst(Instr, + hlsl::OP::OpCode::HitObject_MakeMiss); + } + // Validation support + bool isAllowed() const { return true; } + bool isArgumentListValid() const { + if (11 != llvm::dyn_cast(Instr)->getNumArgOperands()) + return false; + return true; + } + // Metadata + bool requiresUniformInputs() const { return false; } + // Operand indexes + enum OperandIdx { + arg_RayFlags = 1, + arg_MissShaderIndex = 2, + arg_Origin_X = 3, + arg_Origin_Y = 4, + arg_Origin_Z = 5, + arg_TMin = 6, + arg_Direction_X = 7, + arg_Direction_Y = 8, + arg_Direction_Z = 9, + arg_TMax = 10, + }; + // Accessors + llvm::Value *get_RayFlags() const { return Instr->getOperand(1); } + void set_RayFlags(llvm::Value *val) { Instr->setOperand(1, val); } + llvm::Value *get_MissShaderIndex() const { return Instr->getOperand(2); } + void set_MissShaderIndex(llvm::Value *val) { Instr->setOperand(2, val); } + llvm::Value *get_Origin_X() const { return Instr->getOperand(3); } + void set_Origin_X(llvm::Value *val) { Instr->setOperand(3, val); } + llvm::Value *get_Origin_Y() const { return Instr->getOperand(4); } + void set_Origin_Y(llvm::Value *val) { Instr->setOperand(4, val); } + llvm::Value *get_Origin_Z() const { return Instr->getOperand(5); } + void set_Origin_Z(llvm::Value *val) { Instr->setOperand(5, val); } + llvm::Value *get_TMin() const { return Instr->getOperand(6); } + void set_TMin(llvm::Value *val) { Instr->setOperand(6, val); } + llvm::Value *get_Direction_X() const { return Instr->getOperand(7); } + void set_Direction_X(llvm::Value *val) { Instr->setOperand(7, val); } + llvm::Value *get_Direction_Y() const { return Instr->getOperand(8); } + void set_Direction_Y(llvm::Value *val) { Instr->setOperand(8, val); } + llvm::Value *get_Direction_Z() const { return Instr->getOperand(9); } + void set_Direction_Z(llvm::Value *val) { Instr->setOperand(9, val); } + llvm::Value *get_TMax() const { return Instr->getOperand(10); } + void set_TMax(llvm::Value *val) { Instr->setOperand(10, val); } +}; + +/// This instruction Creates an empty nop HitObject +struct DxilInst_HitObject_MakeNop { + llvm::Instruction *Instr; + // Construction and identification + DxilInst_HitObject_MakeNop(llvm::Instruction *pInstr) : Instr(pInstr) {} + operator bool() const { + return hlsl::OP::IsDxilOpFuncCallInst(Instr, + hlsl::OP::OpCode::HitObject_MakeNop); + } + // Validation support + bool isAllowed() const { return true; } + bool isArgumentListValid() const { + if (1 != llvm::dyn_cast(Instr)->getNumArgOperands()) + return false; + return true; + } + // Metadata + bool requiresUniformInputs() const { return false; } +}; // INSTR-HELPER:END } // namespace hlsl diff --git a/include/dxc/DXIL/DxilOperations.h b/include/dxc/DXIL/DxilOperations.h index 3514701327..e522e06204 100644 --- a/include/dxc/DXIL/DxilOperations.h +++ b/include/dxc/DXIL/DxilOperations.h @@ -64,6 +64,7 @@ class OP { void RemoveFunction(llvm::Function *F); llvm::LLVMContext &GetCtx() { return m_Ctx; } llvm::Type *GetHandleType() const; + llvm::Type *GetHitObjectType() const; llvm::Type *GetNodeHandleType() const; llvm::Type *GetNodeRecordHandleType() const; llvm::Type *GetResourcePropertiesType() const; @@ -146,6 +147,7 @@ class OP { llvm::Module *m_pModule; llvm::Type *m_pHandleType; + llvm::Type *m_pHitObjectType; llvm::Type *m_pNodeHandleType; llvm::Type *m_pNodeRecordHandleType; llvm::Type *m_pResourcePropertiesType; diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index b3e552da18..86049fee9c 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -2670,24 +2670,29 @@ const OP::OpCodeProperty OP::m_OpCodeProps[(unsigned)OP::OpCode::NumOpCodes] = { false}, Attribute::None, }, + + // Shader Execution Reordering void, h, f, d, i1, i8, i16, + // i32, i64, udt, obj , function attribute { - OC::ReservedB3, - "ReservedB3", - OCC::Reserved, - "reserved", + OC::HitObject_MakeMiss, + "HitObject_MakeMiss", + OCC::HitObject_MakeMiss, + "hitObject_MakeMiss", {true, false, false, false, false, false, false, false, false, false, false}, - Attribute::None, + Attribute::ReadNone, }, { - OC::ReservedB4, - "ReservedB4", - OCC::Reserved, - "reserved", + OC::HitObject_MakeNop, + "HitObject_MakeNop", + OCC::HitObject_MakeNop, + "hitObject_MakeNop", {true, false, false, false, false, false, false, false, false, false, false}, - Attribute::None, + Attribute::ReadNone, }, + + // void, h, f, d, i1, i8, i16, i32, i64, udt, obj , function attribute { OC::ReservedB5, "ReservedB5", @@ -3750,6 +3755,14 @@ void OP::GetMinShaderModelAndMask(OpCode C, bool bWithTranslation, minor = 9; return; } + // Instructions: HitObject_MakeMiss=265, HitObject_MakeNop=266 + if ((265 <= op && op <= 266)) { + major = 6; + minor = 9; + mask = + SFLAG(Library) | SFLAG(RayGeneration) | SFLAG(ClosestHit) | SFLAG(Miss); + return; + } // OPCODE-SMMASK:END } @@ -3851,6 +3864,8 @@ OP::OP(LLVMContext &Ctx, Module *pModule) m_pHandleType = GetOrCreateStructType(m_Ctx, Type::getInt8PtrTy(m_Ctx), "dx.types.Handle", pModule); + m_pHitObjectType = GetOrCreateStructType(m_Ctx, Type::getInt8PtrTy(m_Ctx), + "dx.types.HitObject", pModule); m_pNodeHandleType = GetOrCreateStructType(m_Ctx, Type::getInt8PtrTy(m_Ctx), "dx.types.NodeHandle", pModule); m_pNodeRecordHandleType = GetOrCreateStructType( @@ -3993,6 +4008,7 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { Type *pF64 = Type::getDoubleTy(m_Ctx); Type *pSDT = GetSplitDoubleType(); // Split double type. Type *p4I32 = GetFourI32Type(); // 4 i32s in a struct. + Type *pHit = GetHitObjectType(); Type *udt = pOverloadType; Type *obj = pOverloadType; @@ -5871,14 +5887,28 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pV); A(pI32); break; - case OpCode::ReservedB3: - A(pV); + + // Shader Execution Reordering + case OpCode::HitObject_MakeMiss: + A(pHit); + A(pI32); + A(pI32); A(pI32); + A(pF32); + A(pF32); + A(pF32); + A(pF32); + A(pF32); + A(pF32); + A(pF32); + A(pF32); break; - case OpCode::ReservedB4: - A(pV); + case OpCode::HitObject_MakeNop: + A(pHit); A(pI32); break; + + // case OpCode::ReservedB5: A(pV); A(pI32); @@ -6288,8 +6318,8 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::ReservedB0: case OpCode::ReservedB1: case OpCode::ReservedB2: - case OpCode::ReservedB3: - case OpCode::ReservedB4: + case OpCode::HitObject_MakeMiss: + case OpCode::HitObject_MakeNop: case OpCode::ReservedB5: case OpCode::ReservedB6: case OpCode::ReservedB7: @@ -6431,6 +6461,8 @@ Type *OP::GetHandleType() const { return m_pHandleType; } Type *OP::GetNodeHandleType() const { return m_pNodeHandleType; } +Type *OP::GetHitObjectType() const { return m_pHitObjectType; } + Type *OP::GetNodeRecordHandleType() const { return m_pNodeRecordHandleType; } Type *OP::GetResourcePropertiesType() const { diff --git a/tools/clang/test/DXILValidation/ser_hitobject_make_passing.ll b/tools/clang/test/DXILValidation/ser_hitobject_make_passing.ll new file mode 100644 index 0000000000..88b71ff3e0 --- /dev/null +++ b/tools/clang/test/DXILValidation/ser_hitobject_make_passing.ll @@ -0,0 +1,46 @@ +; RUN: %dxv %s | FileCheck %s + +; CHECK: Validation succeeded. + +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +%dx.types.HitObject = type { i8* } + +; Function Attrs: nounwind +define void @"\01?main@@YAXXZ"() #0 { + ; Test HitObject_MakeMiss (opcode 265) + %r265 = call %dx.types.HitObject @dx.op.hitObject_MakeMiss(i32 265, i32 4, i32 0, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 1.000000e+00, float 0.000000e+00, float 0.000000e+00, float 9.999000e+03) ; HitObject_MakeMiss(RayFlags,MissShaderIndex,Origin_X,Origin_Y,Origin_Z,TMin,Direction_X,Direction_Y,Direction_Z,TMax) + + ; Test HitObject_MakeNop (opcode 266) + %r266 = call %dx.types.HitObject @dx.op.hitObject_MakeNop(i32 266) ; HitObject_MakeNop() + + ret void +} + +; Function Attrs: nounwind readnone +declare %dx.types.HitObject @dx.op.hitObject_MakeMiss(i32, i32, i32, float, float, float, float, float, float, float, float) #1 + +; Function Attrs: nounwind readnone +declare %dx.types.HitObject @dx.op.hitObject_MakeNop(i32) #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone } + +!dx.version = !{!0} +!dx.valver = !{!0} +!dx.shaderModel = !{!1} +!dx.typeAnnotations = !{!2} +!dx.entryPoints = !{!9, !11} + +!0 = !{i32 1, i32 9} +!1 = !{!"lib", i32 6, i32 9} +!2 = !{i32 1, void ()* @"\01?main@@YAXXZ", !3} +!3 = !{!4} +!4 = !{i32 1, !5, !5} +!5 = !{} +!9 = !{null, !"", null, null, !10} +!10 = !{i32 0, i64 0} +!11 = !{void ()* @"\01?main@@YAXXZ", !"\01?main@@YAXXZ", null, null, !12} +!12 = !{i32 8, i32 7, i32 5, !13} +!13 = !{i32 0} diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 6f4611db32..089dd86aef 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -699,6 +699,15 @@ def populate_categories_and_models(self): self.name_idx[i].category = "Extended Command Information" self.name_idx[i].shader_stages = ("vertex",) self.name_idx[i].shader_model = 6, 8 + for i in ("HitObject_MakeMiss,HitObject_MakeNop").split(","): + self.name_idx[i].category = "Shader Execution Reordering" + self.name_idx[i].shader_model = 6, 9 + self.name_idx[i].shader_stages = ( + "library", + "raygeneration", + "closesthit", + "miss", + ) def populate_llvm_instructions(self): # Add instructions that map to LLVM instructions. @@ -5550,7 +5559,43 @@ def UFI(name, **mappings): next_op_idx = self.reserve_dxil_op_range("ReservedA", next_op_idx, 3) # Shader Execution Reordering - next_op_idx = self.reserve_dxil_op_range("ReservedB", next_op_idx, 31) + next_op_idx = self.reserve_dxil_op_range("ReservedB", next_op_idx, 3) + + self.add_dxil_op( + "HitObject_MakeMiss", + next_op_idx, + "HitObject_MakeMiss", + "Creates a new HitObject representing a miss", + "v", + "rn", + [ + db_dxil_param(0, "hit_object", "", "HitObject with a committed miss"), + db_dxil_param(2, "i32", "RayFlags", "ray flags"), + db_dxil_param(3, "i32", "MissShaderIndex", "Miss shader index"), + db_dxil_param(4, "f", "Origin_X", "Origin x of the ray"), + db_dxil_param(5, "f", "Origin_Y", "Origin y of the ray"), + db_dxil_param(6, "f", "Origin_Z", "Origin z of the ray"), + db_dxil_param(7, "f", "TMin", "Tmin of the ray"), + db_dxil_param(8, "f", "Direction_X", "Direction x of the ray"), + db_dxil_param(9, "f", "Direction_Y", "Direction y of the ray"), + db_dxil_param(10, "f", "Direction_Z", "Direction z of the ray"), + db_dxil_param(11, "f", "TMax", "Tmax of the ray"), + ], + ) + next_op_idx += 1 + + self.add_dxil_op( + "HitObject_MakeNop", + next_op_idx, + "HitObject_MakeNop", + "Creates an empty nop HitObject", + "v", + "rn", + [db_dxil_param(0, "hit_object", "", "Empty nop HitObject")], + ) + next_op_idx += 1 + + next_op_idx = self.reserve_dxil_op_range("ReservedB", next_op_idx, 26, 5) # Reserved block C next_op_idx = self.reserve_dxil_op_range("ReservedC", next_op_idx, 10) @@ -8145,10 +8190,12 @@ def add_dxil_op_reserved(self, name, code_id): ) self.instr.append(i) - def reserve_dxil_op_range(self, group_name, start_id, count): + def reserve_dxil_op_range(self, group_name, start_id, count, start_reserved_id=0): "Reserve a range of dxil opcodes for future use; returns next id" for i in range(0, count): - self.add_dxil_op_reserved("{0}{1}".format(group_name, i), start_id + i) + self.add_dxil_op_reserved( + "{0}{1}".format(group_name, start_reserved_id + i), start_id + i + ) return start_id + count def get_instr_by_llvm_name(self, llvm_name): diff --git a/utils/hct/hctdb_instrhelp.py b/utils/hct/hctdb_instrhelp.py index 2a0359d274..85869aa135 100644 --- a/utils/hct/hctdb_instrhelp.py +++ b/utils/hct/hctdb_instrhelp.py @@ -620,6 +620,7 @@ def print_opfunc_table(self): "noderecordhandle": "A(pNodeRecordHandle);", "nodeproperty": "A(nodeProperty);", "noderecordproperty": "A(nodeRecordProperty);", + "hit_object": "A(pHit);", } last_category = None for i in self.instrs: