spirv-opt: Add support to prevent functions from being inlined if they have DontInline flag (#3858)
This commit add support for optimizer to not inline functions with DontInline control flag, so that the [noinline] attribute in HLSL will be useful in DXC SPIR-V generation.
This is part of work of github.com/microsoft/DirectXShaderCompiler/issues/3158
diff --git a/source/opt/function.h b/source/opt/function.h
index b7c17a6..4b20dcb 100644
--- a/source/opt/function.h
+++ b/source/opt/function.h
@@ -94,6 +94,9 @@
// Returns function's return type id
inline uint32_t type_id() const { return def_inst_->type_id(); }
+ // Returns the function's control mask
+ inline uint32_t control_mask() const { return def_inst_->GetSingleWordInOperand(0); }
+
// Returns the entry basic block for this function.
const std::unique_ptr<BasicBlock>& entry() const { return blocks_.front(); }
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 6021a7c..eaf29aa 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -727,6 +727,12 @@
bool InlinePass::IsInlinableFunction(Function* func) {
// We can only inline a function if it has blocks.
if (func->cbegin() == func->cend()) return false;
+
+ // Do not inline functions with DontInline flag.
+ if (func->control_mask() & SpvFunctionControlDontInlineMask) {
+ return false;
+ }
+
// Do not inline functions with returns in loops. Currently early return
// functions are inlined by wrapping them in a one trip loop and implementing
// the returns as a branch to the loop's merge block. However, this can only
diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp
index 951721b..c0ca6da 100644
--- a/test/opt/inline_test.cpp
+++ b/test/opt/inline_test.cpp
@@ -2405,6 +2405,38 @@
SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
}
+TEST_F(InlineTest, DontInlineFuncWithDontInline) {
+ // Check that the function with DontInline flag is not inlined.
+ const std::string text = R"(
+; CHECK: %foo = OpFunction %int DontInline
+; CHECK: OpReturnValue %int_0
+
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main"
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+OpName %main "main"
+OpName %foo "foo"
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%void = OpTypeVoid
+%6 = OpTypeFunction %void
+%7 = OpTypeFunction %int
+%main = OpFunction %void None %6
+%8 = OpLabel
+%9 = OpFunctionCall %int %foo
+OpReturn
+OpFunctionEnd
+%foo = OpFunction %int DontInline %7
+%10 = OpLabel
+OpReturnValue %int_0
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
+}
+
TEST_F(InlineTest, InlineFuncWithOpKillNotInContinue) {
const std::string before =
R"(OpCapability Shader