From f0633a7d0b1615f6b115d53763bedeeb857b7f3c Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 16 Jan 2019 08:16:53 -0800 Subject: Add proper IR codegen support for local static const variables (#779) Previously the IR codegen logic was treating function-scope `static const` variables just like `static` variables, which results in them generating less efficient output HLSL/GLSL. This change special-cases function-local `static const` variables with logic that mirrors how we handle global-scope `static const` variables. The approach in this change attempts to find a simpler solution to deal with `static const` variables inside of generic functions than what is currently done for `static` variables in generic functions, but I haven't tested whether that works in practice, so I didn't apply the same approach to the plain `static` case. That would make a good follow-on change. I've included a single test case to demonstrate that with this fix the Slang compiler generates output DXBC that uses an indexable "immediate" constant buffer, whereas without the fix it generates an array in local memory (slow). --- source/slang/lower-to-ir.cpp | 46 ++++++++++++++++++++++ tests/cross-compile/function-static-const.slang | 27 +++++++++++++ .../cross-compile/function-static-const.slang.hlsl | 27 +++++++++++++ tools/slang-test/slang-test-main.cpp | 7 ++++ 4 files changed, 107 insertions(+) create mode 100644 tests/cross-compile/function-static-const.slang create mode 100644 tests/cross-compile/function-static-const.slang.hlsl diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 2242619d0..388ca884e 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -4107,10 +4107,56 @@ struct DeclLoweringVisitor : DeclVisitor IRGenContext* getContet() { return &subContextStorage; } }; + LoweredValInfo lowerFunctionStaticConstVarDecl( + VarDeclBase* decl) + { + // We need to insert the constant at a level above + // the function being emitted. This will usually + // be the global scope, but it might be an outer + // generic if we are lowering a generic function. + // + NestedContext nestedContext(this); + auto subBuilder = nestedContext.getBuilder(); + auto subContext = nestedContext.getContet(); + + subBuilder->setInsertInto(subBuilder->getFunc()->getParent()); + + IRType* subVarType = lowerType(subContext, decl->getType()); + + IRGlobalConstant* irConstant = subBuilder->createGlobalConstant(subVarType); + addVarDecorations(subContext, irConstant, decl); + addNameHint(context, irConstant, decl); + maybeSetRate(context, irConstant, decl); + subBuilder->addHighLevelDeclDecoration(irConstant, decl); + + LoweredValInfo constantVal = LoweredValInfo::ptr(irConstant); + setValue(context, decl, constantVal); + + if( auto initExpr = decl->initExpr ) + { + NestedContext nestedInitContext(this); + auto initBuilder = nestedInitContext.getBuilder(); + auto initContext = nestedInitContext.getContet(); + + initBuilder->setInsertInto(irConstant); + + IRBlock* entryBlock = initBuilder->emitBlock(); + initBuilder->setInsertInto(entryBlock); + + LoweredValInfo initVal = lowerRValueExpr(initContext, initExpr); + initBuilder->emitReturn(getSimpleVal(initContext, initVal)); + } + + return constantVal; + } LoweredValInfo lowerFunctionStaticVarDecl( VarDeclBase* decl) { + // We know the variable is `static`, but it might also be `const. + if(decl->HasModifier()) + return lowerFunctionStaticConstVarDecl(decl); + // A global variable may need to be generic, if one // of the outer declarations is generic. NestedContext nestedContext(this); diff --git a/tests/cross-compile/function-static-const.slang b/tests/cross-compile/function-static-const.slang new file mode 100644 index 000000000..7d933a04a --- /dev/null +++ b/tests/cross-compile/function-static-const.slang @@ -0,0 +1,27 @@ +// function-static-const.slang + +//TEST:CROSS_COMPILE:-target dxbc-assembly -entry main -stage fragment -profile sm_5_0 + +// This test ensures that we compile `static const` variables inside +// of functions to reasonable HLSL output so that we don't introduce +// unexpected overhead by treating them as just `static`. +// + +int test(int val) +{ + static const int kArray[] = { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }; + return kArray[val]; +} + +cbuffer C +{ + int index; +} + +float4 main() : SV_Target +{ + return test(index); +} diff --git a/tests/cross-compile/function-static-const.slang.hlsl b/tests/cross-compile/function-static-const.slang.hlsl new file mode 100644 index 000000000..a4f1118eb --- /dev/null +++ b/tests/cross-compile/function-static-const.slang.hlsl @@ -0,0 +1,27 @@ +// function-static-const.slang +//TEST_IGNORE_FILE + +#pragma pack_matrix(column_major) + +struct SLANG_ParameterGroup_C_0 +{ + int index_0; +}; + +cbuffer C_0 : register(b0) +{ + SLANG_ParameterGroup_C_0 C_0; +} + +static const int kArray_0[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + +int test_0(int val_0) +{ + return kArray_0[val_0]; +} + +vector main() : SV_TARGET +{ + int _S1 = test_0(C_0.index_0); + return (vector) _S1; +} diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index c0e5bd95b..1f6b4cdb0 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -697,6 +697,13 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input) expectedSpawner.pushArgument("dxc"); break; } + case SLANG_DXBC_ASM: + { + expectedSpawner.pushArgument(filePath + ".hlsl"); + expectedSpawner.pushArgument("-pass-through"); + expectedSpawner.pushArgument("fxc"); + break; + } default: { expectedSpawner.pushArgument(filePath + ".glsl"); -- cgit v1.2.3