diff --git a/include/luisa/luisa-compute.h b/include/luisa/luisa-compute.h index 879be3258..91b7054e4 100644 --- a/include/luisa/luisa-compute.h +++ b/include/luisa/luisa-compute.h @@ -212,6 +212,7 @@ #include #include #include +#include #include #include #include diff --git a/include/luisa/xir/builder.h b/include/luisa/xir/builder.h index bb806f246..5b65180b5 100644 --- a/include/luisa/xir/builder.h +++ b/include/luisa/xir/builder.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +25,9 @@ #include #include +namespace luisa::compute::xir { +class ClockInst; +}// namespace luisa::compute::xir namespace luisa::compute::xir { class LC_XIR_API Builder { @@ -101,6 +104,8 @@ class LC_XIR_API Builder { LoadInst *load(const Type *type, Value *variable) noexcept; StoreInst *store(Value *variable, Value *value) noexcept; + ClockInst *clock() noexcept; + OutlineInst *outline() noexcept; RayQueryInst *ray_query(Value *query_object) noexcept; }; diff --git a/include/luisa/xir/instruction.h b/include/luisa/xir/instruction.h index b528752fd..28b068e1a 100644 --- a/include/luisa/xir/instruction.h +++ b/include/luisa/xir/instruction.h @@ -1,5 +1,7 @@ #pragma once +#include "instruction.h" + #include namespace luisa::compute::xir { @@ -24,17 +26,31 @@ enum struct DerivedInstructionTag { RETURN, // basic block terminator: return (early returns are removed after control flow normalization) PHI, // basic block beginning: phi nodes - // variable instructions + /* variable instructions */ ALLOCA, LOAD, STORE, GEP, + /* atomic instructions */ + ATOMIC,// operates on buffers / shared memory + + /* ALU (arithmetic logic unit) instructions */ + ALU,// all are pure functions, free to move/eliminate + + /* CTA (cooperative thread array) instructions */ + CTA,// volatile, may involve synchronization and cannot be moved/eliminated + + /* resource instructions */ + RESOURCE, + /* other instructions */ - CALL, // user or external function calls + CALL, // user or external function calls + CAST, // type casts + PRINT,// kernel print + CLOCK,// kernel clock + INTRINSIC,// intrinsic function calls - CAST, // type casts - PRINT, // kernel print ASSERT,// assertion ASSUME,// assumption @@ -44,6 +60,8 @@ enum struct DerivedInstructionTag { RAY_QUERY,// ray queries }; +class ControlFlowMerge; + class LC_XIR_API Instruction : public IntrusiveNode> { private: @@ -70,6 +88,9 @@ class LC_XIR_API Instruction : public IntrusiveNode; @@ -158,14 +179,14 @@ class DerivedConditionalBranchInstruction : public DerivedInstruction::DerivedInstruction; }; -class LC_XIR_API InstructionMergeMixin { +class LC_XIR_API ControlFlowMerge { private: BasicBlock *_merge_block{nullptr}; protected: - InstructionMergeMixin() noexcept = default; - ~InstructionMergeMixin() noexcept = default; + ControlFlowMerge() noexcept = default; + ~ControlFlowMerge() noexcept = default; public: void set_merge_block(BasicBlock *block) noexcept { _merge_block = block; } @@ -174,4 +195,15 @@ class LC_XIR_API InstructionMergeMixin { BasicBlock *create_merge_block(bool overwrite_existing = false) noexcept; }; +template + requires std::derived_from +class ControlFlowMergeMixin : public Base, + public ControlFlowMerge { +public: + using Base::Base; + [[nodiscard]] ControlFlowMerge *control_flow_merge() noexcept final { + return static_cast(this); + } +}; + }// namespace luisa::compute::xir diff --git a/include/luisa/xir/instructions/clock.h b/include/luisa/xir/instructions/clock.h new file mode 100644 index 000000000..b44588d66 --- /dev/null +++ b/include/luisa/xir/instructions/clock.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace luisa::compute::xir { + +class LC_XIR_API ClockInst : public DerivedInstruction { +public: + ClockInst() noexcept; +}; + +}// namespace luisa::compute::xir diff --git a/include/luisa/xir/instructions/if.h b/include/luisa/xir/instructions/if.h index 8fd9bf10f..8a81446e4 100644 --- a/include/luisa/xir/instructions/if.h +++ b/include/luisa/xir/instructions/if.h @@ -16,10 +16,9 @@ class BasicBlock; // { merge_block } // // Note: this instruction must be the terminator of a basic block. -class IfInst final : public DerivedConditionalBranchInstruction, - public InstructionMergeMixin { +class IfInst final : public ControlFlowMergeMixin> { public: - using DerivedConditionalBranchInstruction::DerivedConditionalBranchInstruction; + using ControlFlowMergeMixin::ControlFlowMergeMixin; }; }// namespace luisa::compute::xir diff --git a/include/luisa/xir/instructions/intrinsic.h b/include/luisa/xir/instructions/intrinsic.h index b998fbe3b..558c1bef6 100644 --- a/include/luisa/xir/instructions/intrinsic.h +++ b/include/luisa/xir/instructions/intrinsic.h @@ -294,9 +294,6 @@ enum struct IntrinsicOp { // shader execution re-ordering SHADER_EXECUTION_REORDER,// (uint hint, uint hint_bits): void - - // clock - CLOCK,// (): uint64 }; [[nodiscard]] LC_XIR_API luisa::string to_string(IntrinsicOp op) noexcept; diff --git a/include/luisa/xir/instructions/loop.h b/include/luisa/xir/instructions/loop.h index a2d820c26..b1209de7e 100644 --- a/include/luisa/xir/instructions/loop.h +++ b/include/luisa/xir/instructions/loop.h @@ -5,8 +5,7 @@ namespace luisa::compute::xir { // Note: this instruction must be the terminator of a basic block. -class LC_XIR_API LoopInst final : public DerivedTerminatorInstruction, - public InstructionMergeMixin { +class LC_XIR_API LoopInst final : public ControlFlowMergeMixin> { public: static constexpr size_t operand_index_prepare_block = 0u; @@ -36,8 +35,7 @@ class LC_XIR_API LoopInst final : public DerivedTerminatorInstruction, - public InstructionMergeMixin { +class LC_XIR_API SimpleLoopInst final : public ControlFlowMergeMixin> { public: static constexpr size_t operand_index_body_block = 0u; diff --git a/include/luisa/xir/instructions/outline.h b/include/luisa/xir/instructions/outline.h index 920eae741..69d3a1a7a 100644 --- a/include/luisa/xir/instructions/outline.h +++ b/include/luisa/xir/instructions/outline.h @@ -6,10 +6,9 @@ namespace luisa::compute::xir { class BasicBlock; -class OutlineInst final : public DerivedBranchInstruction, - public InstructionMergeMixin { +class OutlineInst final : public ControlFlowMergeMixin> { public: - using DerivedBranchInstruction::DerivedBranchInstruction; + using ControlFlowMergeMixin::ControlFlowMergeMixin; }; }// namespace luisa::compute::xir diff --git a/include/luisa/xir/instructions/ray_query.h b/include/luisa/xir/instructions/ray_query.h index f8438f2e0..b4050cd39 100644 --- a/include/luisa/xir/instructions/ray_query.h +++ b/include/luisa/xir/instructions/ray_query.h @@ -4,8 +4,7 @@ namespace luisa::compute::xir { -class LC_XIR_API RayQueryInst final : public DerivedTerminatorInstruction, - public InstructionMergeMixin { +class LC_XIR_API RayQueryInst final : public ControlFlowMergeMixin> { public: static constexpr size_t operand_index_query_object = 0u; diff --git a/include/luisa/xir/instructions/switch.h b/include/luisa/xir/instructions/switch.h index 8f47a8631..f8c830a56 100644 --- a/include/luisa/xir/instructions/switch.h +++ b/include/luisa/xir/instructions/switch.h @@ -15,8 +15,7 @@ namespace luisa::compute::xir { // { merge_block } // // Note: this instruction must be the terminator of a basic block. -class LC_XIR_API SwitchInst final : public DerivedTerminatorInstruction, - public InstructionMergeMixin { +class LC_XIR_API SwitchInst final : public ControlFlowMergeMixin> { public: using case_value_type = int; diff --git a/src/backends/fallback/fallback_codegen.cpp b/src/backends/fallback/fallback_codegen.cpp index e197a2075..a6023088c 100644 --- a/src/backends/fallback/fallback_codegen.cpp +++ b/src/backends/fallback/fallback_codegen.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -2535,11 +2536,6 @@ class FallbackCodegen { case xir::IntrinsicOp::INDIRECT_DISPATCH_SET_KERNEL: break; case xir::IntrinsicOp::INDIRECT_DISPATCH_SET_COUNT: break; case xir::IntrinsicOp::SHADER_EXECUTION_REORDER: return nullptr;// no-op on the LLVM side - case xir::IntrinsicOp::CLOCK: { - auto call = b.CreateIntrinsic(llvm::Intrinsic::readcyclecounter, {}, {}); - auto llvm_result_type = _translate_type(inst->type(), true); - return b.CreateZExtOrTrunc(call, llvm_result_type); - } } LUISA_INFO("unsupported intrinsic op type: {}", static_cast(inst->op())); LUISA_NOT_IMPLEMENTED(); @@ -2872,6 +2868,14 @@ class FallbackCodegen { } case xir::DerivedInstructionTag::AUTO_DIFF: LUISA_NOT_IMPLEMENTED(); case xir::DerivedInstructionTag::RAY_QUERY: LUISA_NOT_IMPLEMENTED(); + case xir::DerivedInstructionTag::CLOCK: { + auto call = b.CreateIntrinsic(llvm::Intrinsic::readcyclecounter, {}, {}); + auto llvm_result_type = _translate_type(inst->type(), true); + return b.CreateZExtOrTrunc(call, llvm_result_type); + } + case xir::DerivedInstructionTag::ALU: break; + case xir::DerivedInstructionTag::CTA: break; + case xir::DerivedInstructionTag::RESOURCE: break; } LUISA_ERROR_WITH_LOCATION("Invalid instruction."); } diff --git a/src/xir/CMakeLists.txt b/src/xir/CMakeLists.txt index 8a3fb691f..89d51dde2 100644 --- a/src/xir/CMakeLists.txt +++ b/src/xir/CMakeLists.txt @@ -20,6 +20,7 @@ set(LUISA_COMPUTE_XIR_SOURCES instructions/assume.cpp instructions/call.cpp instructions/cast.cpp + instructions/clock.cpp instructions/gep.cpp instructions/intrinsic.cpp instructions/load.cpp diff --git a/src/xir/builder.cpp b/src/xir/builder.cpp index 5118dac07..b6bfc9b64 100644 --- a/src/xir/builder.cpp +++ b/src/xir/builder.cpp @@ -149,17 +149,21 @@ GEPInst *Builder::gep(const Type *type, Value *base, luisa::span i LoadInst *Builder::load(const Type *type, Value *variable) noexcept { LUISA_ASSERT(variable->is_lvalue(), "Load source must be an lvalue."); - LUISA_ASSERT(type == variable->type(), "Type mismatch in Load"); + LUISA_ASSERT(type == variable->type(), "Type mismatch in Load"); return _create_and_append_instruction(type, variable); } StoreInst *Builder::store(Value *variable, Value *value) noexcept { LUISA_ASSERT(variable->is_lvalue(), "Store destination must be an lvalue."); LUISA_ASSERT(!value->is_lvalue(), "Store source cannot be an lvalue."); - LUISA_ASSERT(variable->type() == value->type(), "Type mismatch in Store"); + LUISA_ASSERT(variable->type() == value->type(), "Type mismatch in Store"); return _create_and_append_instruction(variable, value); } +ClockInst *Builder::clock() noexcept { + return _create_and_append_instruction(); +} + OutlineInst *Builder::outline() noexcept { return _create_and_append_instruction(); } diff --git a/src/xir/instruction.cpp b/src/xir/instruction.cpp index 3e167fbf7..6f268ec3a 100644 --- a/src/xir/instruction.cpp +++ b/src/xir/instruction.cpp @@ -55,6 +55,10 @@ void Instruction::replace_self_with(Instruction *node) noexcept { remove_self(); } +const ControlFlowMerge *Instruction::control_flow_merge() const noexcept { + return const_cast(this)->control_flow_merge(); +} + TerminatorInstruction::TerminatorInstruction() noexcept : Instruction{nullptr} {} @@ -141,7 +145,7 @@ const BasicBlock *ConditionalBranchTerminatorInstruction::false_block() const no return const_cast(this)->false_block(); } -BasicBlock *InstructionMergeMixin::create_merge_block(bool overwrite_existing) noexcept { +BasicBlock *ControlFlowMerge::create_merge_block(bool overwrite_existing) noexcept { LUISA_ASSERT(merge_block() == nullptr || overwrite_existing, "Merge block already exists."); auto block = Pool::current()->create(); diff --git a/src/xir/instructions/clock.cpp b/src/xir/instructions/clock.cpp new file mode 100644 index 000000000..a04bc1168 --- /dev/null +++ b/src/xir/instructions/clock.cpp @@ -0,0 +1,9 @@ +#include +#include + +namespace luisa::compute::xir { + +ClockInst::ClockInst() noexcept + : DerivedInstruction{Type::of()} {} + +}// namespace luisa::compute::xir diff --git a/src/xir/instructions/intrinsic_name_map.inl.h b/src/xir/instructions/intrinsic_name_map.inl.h index ade9b758f..9198e68bc 100644 --- a/src/xir/instructions/intrinsic_name_map.inl.h +++ b/src/xir/instructions/intrinsic_name_map.inl.h @@ -224,7 +224,6 @@ luisa::string to_string(IntrinsicOp op) noexcept { case IntrinsicOp::INDIRECT_DISPATCH_SET_KERNEL: return "indirect_dispatch_set_kernel"; case IntrinsicOp::INDIRECT_DISPATCH_SET_COUNT: return "indirect_dispatch_set_count"; case IntrinsicOp::SHADER_EXECUTION_REORDER: return "shader_execution_reorder"; - case IntrinsicOp::CLOCK: return "clock"; } LUISA_ERROR_WITH_LOCATION("Unknown intrinsic operation: {}.", static_cast(op)); @@ -454,7 +453,6 @@ IntrinsicOp intrinsic_op_from_string(luisa::string_view name) noexcept { {"indirect_dispatch_set_kernel", IntrinsicOp::INDIRECT_DISPATCH_SET_KERNEL}, {"indirect_dispatch_set_count", IntrinsicOp::INDIRECT_DISPATCH_SET_COUNT}, {"shader_execution_reorder", IntrinsicOp::SHADER_EXECUTION_REORDER}, - {"clock", IntrinsicOp::CLOCK}, }; auto iter = m.find(name); LUISA_ASSERT(iter != m.end(), "Unknown intrinsic operation: {}.", name); diff --git a/src/xir/translators/ast2xir.cpp b/src/xir/translators/ast2xir.cpp index 0117ef5cd..08c788baa 100644 --- a/src/xir/translators/ast2xir.cpp +++ b/src/xir/translators/ast2xir.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace luisa::compute::xir { @@ -709,7 +710,7 @@ class AST2XIRContext { case CallOp::TEXTURE3D_SAMPLE_GRAD: return resource_call(IntrinsicOp::TEXTURE3D_SAMPLE_GRAD); case CallOp::TEXTURE3D_SAMPLE_GRAD_LEVEL: return resource_call(IntrinsicOp::TEXTURE3D_SAMPLE_GRAD_LEVEL); case CallOp::SHADER_EXECUTION_REORDER: return resource_call(IntrinsicOp::SHADER_EXECUTION_REORDER); - case CallOp::CLOCK: return pure_call(IntrinsicOp::CLOCK); + case CallOp::CLOCK: return b.clock(); } LUISA_NOT_IMPLEMENTED(); } diff --git a/src/xir/translators/xir2text.cpp b/src/xir/translators/xir2text.cpp index ff1250fe3..e91dcf77d 100644 --- a/src/xir/translators/xir2text.cpp +++ b/src/xir/translators/xir2text.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,10 @@ class XIR2TextTranslator final { _main << " " << _value_ident(inst->condition()); } + void _emit_clock_inst(const ClockInst *inst [[maybe_unused]]) noexcept { + _main << "clock"; + } + void _emit_if_inst(const IfInst *inst, int indent) noexcept { _main << "if " << _value_ident(inst->condition()) << ", then "; _emit_basic_block(inst->true_block(), indent); @@ -469,6 +474,12 @@ class XIR2TextTranslator final { case DerivedInstructionTag::ASSUME: _emit_assume_inst(static_cast(inst)); break; + case DerivedInstructionTag::CLOCK: + _emit_clock_inst(static_cast(inst)); + break; + case DerivedInstructionTag::ALU: break; + case DerivedInstructionTag::CTA: break; + case DerivedInstructionTag::RESOURCE: break; } _main << ";"; _emit_use_debug_info(_main, inst->use_list());