diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-12-03 09:58:59 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-12-03 09:58:59 -0500 |
| commit | 9653dcc2c9d5d20d3d0e8918aaf1d5b09e963060 (patch) | |
| tree | aa1b42132361b2920d9f9301f0b0e44ea82d6b8c | |
| parent | a3651d99fb8f3a046365d60751d1f3f806e48f7a (diff) | |
getStringHash on string literals (#1140)
* WIP getStringHash
* Have a use.
* Add slang-string-hash.h/.cpp
* Use StringSlicePool for holding strings for StringHash.
Add outputBuffer to string-literal-hash.slang so value can be tested.
Ignore the GlobalHashedStringLiterals instruction on emit.
* Add all the hashed string literals to ProgramLayout.
* Add reflection support for hashed string literals to reflection test.
* Fix string literal hash test.
* Small fixes to pass test suite.
* Fix issue in serialization where IRUse is not correctly initialized.
* Fix problem initializing IRUse for string hash pass.
Remove hack from slang-ir-specialize - specially handling if user is not null.
* * Use shared builder when replacing getStringHash
* Comments for functions in slang-ir-string-hash
* Do not allow zero length string literals. Could be allowed, but doing so would require StringSlicePool to have a special case (or some other mechanism)
24 files changed, 452 insertions, 14 deletions
@@ -2098,6 +2098,21 @@ extern "C" SlangReflectionType* const* specializationArgs, ISlangBlob** outDiagnostics); + /// Get the number of hashed strings + SLANG_API SlangUInt spReflection_getHashedStringCount( + SlangReflection* reflection); + + /// Get a hashed string. The number of chars is writtent in outCount. Note the count does *NOT* including terminating 0, + /// and the returned pointer will not generally have a terminating zero. + SLANG_API const char* spReflection_getHashedString( + SlangReflection* reflection, + SlangUInt index, + size_t* outCount); + + /// Calculate a string hash. + // Count should *NOT* include terminating zero. + SLANG_API int spCalcStringHash(const char* chars, size_t count); + #ifdef __cplusplus } @@ -2735,6 +2750,13 @@ namespace slang (SlangReflectionType* const*) specializationArgs, outDiagnostics); } + + SlangUInt getHashedStringCount() const { return spReflection_getHashedStringCount((SlangReflection*)this); } + + const char* getHashedString(SlangUInt index, size_t* outCount) const + { + return spReflection_getHashedString((SlangReflection*)this, index, outCount); + } }; typedef ISlangBlob IBlob; diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 7785239a2..f0ec44080 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1197,6 +1197,11 @@ for (auto op : binaryOps) } }}}} +// Specialized function + +__intrinsic_op +int getStringHash(String string); + // Operators to apply to `enum` types __generic<E : __EnumType> diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index c681a5f6b..6f21f991d 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -1219,6 +1219,11 @@ for (auto op : binaryOps) SLANG_RAW("#line 1198 \"core.meta.slang\"") SLANG_RAW("\n") SLANG_RAW("\n") +SLANG_RAW("// Specialized function\n") +SLANG_RAW("\n") +SLANG_RAW("__intrinsic_op\n") +SLANG_RAW("int getStringHash(String string);\n") +SLANG_RAW("\n") SLANG_RAW("// Operators to apply to `enum` types\n") SLANG_RAW("\n") SLANG_RAW("__generic<E : __EnumType>\n") diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index d43b2ca82..15121babf 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -250,6 +250,7 @@ DIAGNOSTIC(30049, Note, thisIsImmutableByDefault, "a 'this' parameter is an imm DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$0'") DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on type '$1'") +DIAGNOSTIC(30043, Error, getStringHashRequiresStringLiteral, "getStringHash parameter can only accept a non-zero length string literal") DIAGNOSTIC(30060, Error, expectedAType, "expected a type got a '$0'") @@ -265,9 +266,6 @@ DIAGNOSTIC(33071, Error, expectedAStringLiteral, "expected a string literal") DIAGNOSTIC( -1, Note, noteExplicitConversionPossible, "explicit conversion from '$0' to '$1' is possible") DIAGNOSTIC(30080, Error, ambiguousConversion, "more than one implicit conversion exists from '$0' to '$1'") - - - // Attributes DIAGNOSTIC(31000, Error, unknownAttributeName, "unknown attribute '$0'") DIAGNOSTIC(31001, Error, attributeArgumentCountMismatch, "attribute '$0' expects $1 arguments ($2 provided)") diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index bf7dd3129..2c1bcbe6d 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -1650,6 +1650,11 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO bool needClose = false; switch(inst->op) { + case kIROp_GlobalHashedStringLiterals: + /* Don't need to to output anything for this instruction - it's used for reflecting string literals that + are hashed with 'getStringHash' */ + break; + case kIROp_IntLit: case kIROp_FloatLit: case kIROp_BoolLit: @@ -3033,6 +3038,11 @@ void CLikeSourceEmitter::emitGlobalInst(IRInst* inst) switch(inst->op) { + case kIROp_GlobalHashedStringLiterals: + /* Don't need to to output anything for this instruction - it's used for reflecting string literals that + are hashed with 'getStringHash' */ + break; + case kIROp_Func: emitFunc((IRFunc*) inst); break; diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 4caa8acf0..53a8ca89d 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -169,7 +169,7 @@ INST(InterfaceType, interface, 0, PARENT) INST_RANGE(Type, VoidType, InterfaceType) /*IRGlobalValueWithCode*/ - /* IRGlobalValueWIthParams*/ + /* IRGlobalValueWithParams*/ INST(Func, func, 0, PARENT) INST(Generic, generic, 0, PARENT) INST_RANGE(GlobalValueWithParams, Func, Generic) @@ -184,6 +184,8 @@ INST(StructKey, key, 0, 0) INST(GlobalGenericParam, global_generic_param, 0, 0) INST(WitnessTable, witness_table, 0, 0) +INST(GlobalHashedStringLiterals, global_hashed_string_literals, 0, 0) + INST(Module, module, 0, PARENT) INST(Block, block, 0, PARENT) @@ -369,6 +371,8 @@ INST(Select, select, 3, 0) INST(Dot, dot, 2, 0) +INST(GetStringHash, getStringHash, 1, 0) + INST(Mul_Vector_Matrix, mulVectorMatrix, 2, 0) INST(Mul_Matrix_Vector, mulMatrixVector, 2, 0) INST(Mul_Matrix_Matrix, mulMatrixMatrix, 2, 0) diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 59cc625f5..0678b5962 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -4,6 +4,7 @@ #include "slang-ir.h" #include "slang-ir-insts.h" #include "slang-mangle.h" +#include "slang-ir-string-hash.h" namespace Slang { @@ -1374,6 +1375,7 @@ LinkedIR linkIR( }); irModules.addRange(linkage->m_libModules.getBuffer()->readRef(), linkage->m_libModules.getCount()); + // Add any modules that were loaded as libraries for (IRModule* irModule : irModules) { @@ -1387,8 +1389,21 @@ LinkedIR linkIR( // insertGlobalValueSymbols(sharedContext, targetProgram->getExistingIRModuleForLayout()); - auto context = state->getContext(); + + // Combine all of the contents of IRGlobalHashedStringLiterals + { + StringSlicePool pool; + IRBuilder& builder = sharedContext->builderStorage; + for (IRModule* irModule : irModules) + { + findGlobalHashedStringLiterals(irModule, pool); + } + addGlobalHashedStringLiterals(pool, *builder.sharedBuilder); + } + + // Set up shared and builder insert point + context->shared = sharedContext; context->builder = &sharedContext->builderStorage; diff --git a/source/slang/slang-ir-serialize.cpp b/source/slang/slang-ir-serialize.cpp index fe997cca8..a6ed35cc6 100644 --- a/source/slang/slang-ir-serialize.cpp +++ b/source/slang/slang-ir-serialize.cpp @@ -1222,10 +1222,12 @@ static int _calcFixSourceLoc(const IRSerialData::DebugSourceInfo& info, SourceVi { const Ser::InstIndex* srcOperandIndices; const int numOperands = data.getOperands(srcInst, &srcOperandIndices); - + + auto dstOperands = dstInst->getOperands(); + for (int j = 0; j < numOperands; j++) { - dstInst->setOperand(j, insts[int(srcOperandIndices[j])]); + dstOperands[j].init(dstInst, insts[int(srcOperandIndices[j])]); } } } diff --git a/source/slang/slang-ir-specialize.cpp b/source/slang/slang-ir-specialize.cpp index fe6b82184..5f84da478 100644 --- a/source/slang/slang-ir-specialize.cpp +++ b/source/slang/slang-ir-specialize.cpp @@ -136,6 +136,7 @@ struct SpecializationContext for( auto use = inst->firstUse; use; use = use->nextUse ) { auto user = use->getUser(); + addToWorkList(user); } } diff --git a/source/slang/slang-ir-string-hash.cpp b/source/slang/slang-ir-string-hash.cpp new file mode 100644 index 000000000..b9e2d4045 --- /dev/null +++ b/source/slang/slang-ir-string-hash.cpp @@ -0,0 +1,108 @@ +// slang-ir-string-hash.cpp +#include "slang-ir-string-hash.h" + +#include "slang-ir.h" +#include "slang-ir-insts.h" + +namespace Slang { + +static void _findGetStringHashRec(IRInst* inst, List<IRGetStringHash*>& outInsts) +{ + for (IRInst* child = inst->getFirstDecorationOrChild(); child; child = child->getNextInst()) + { + if (IRGetStringHash* getInst = as<IRGetStringHash>(child)) + { + outInsts.add(getInst); + } + _findGetStringHashRec(child, outInsts); + } +} + +void replaceGetStringHash(IRModule* module, SharedIRBuilder& sharedBuilder, StringSlicePool& ioPool) +{ + IRBuilder builder; + builder.sharedBuilder = &sharedBuilder; + + builder.setInsertInto(module->getModuleInst()); + + List<IRGetStringHash*> insts; + _findGetStringHashRec(module->getModuleInst(), insts); + + // Then we want to add the GlobalHashedString instruction in the root + for (auto inst : insts) + { + IRStringLit* stringLit = inst->getStringLit(); + ioPool.add(stringLit->getStringSlice()); + + // Okay work out what the hash is + const int hash = GetHashCode(stringLit->getStringSlice()); + + IRInst* intLit = builder.getIntValue(builder.getIntType(), int32_t(hash)); + + // Okay we want to replace all uses with the literal + inst->replaceUsesWith(intLit); + inst->removeAndDeallocate(); + } +} + +void replaceGetStringHashWithGlobal(IRModule* module, SharedIRBuilder& sharedBuilder) +{ + StringSlicePool pool; + replaceGetStringHash(module, sharedBuilder, pool); + addGlobalHashedStringLiterals(pool, sharedBuilder); +} + +void findGlobalHashedStringLiterals(IRModule* module, StringSlicePool& pool) +{ + IRModuleInst* moduleInst = module->getModuleInst(); + + for (IRInst* child = moduleInst->getFirstDecorationOrChild(); child; child = child->getNextInst()) + { + if (IRGlobalHashedStringLiterals* hashedStringLits = as<IRGlobalHashedStringLiterals>(child)) + { + const Index count = hashedStringLits->getOperandCount(); + for (Index i = 0; i < count; ++i) + { + IRStringLit* stringLit = as<IRStringLit>(hashedStringLits->getOperand(i)); + pool.add(stringLit->getStringSlice()); + } + } + } +} + +void addGlobalHashedStringLiterals(const StringSlicePool& pool, SharedIRBuilder& sharedBuilder) +{ + if (pool.getNumSlices() <= StringSlicePool::kNumDefaultHandles) + { + return; + } + + IRBuilder builder; + builder.sharedBuilder = &sharedBuilder; + + // + IRModule* module = builder.getModule(); + + // We need to add a global instruction that references all of these string literals + builder.setInsertInto(module->getModuleInst()); + + Index numSlices = Index(pool.getNumSlices() - StringSlicePool::kNumDefaultHandles); + + IRInst* globalHashedInst = createEmptyInst(module, kIROp_GlobalHashedStringLiterals, int(numSlices)); + builder.addInst(globalHashedInst); + + auto operands = globalHashedInst->getOperands(); + + for (Index i = 0; i < numSlices; ++i) + { + UnownedStringSlice slice = pool.getSlice(StringSlicePool::Handle(i + StringSlicePool::kNumDefaultHandles)); + IRStringLit* stringLit = builder.getStringValue(slice); + + operands[i].init(globalHashedInst, stringLit); + } + + // Mark to keep alive + builder.addKeepAliveDecoration(globalHashedInst); +} + +} diff --git a/source/slang/slang-ir-string-hash.h b/source/slang/slang-ir-string-hash.h new file mode 100644 index 000000000..13dbe12df --- /dev/null +++ b/source/slang/slang-ir-string-hash.h @@ -0,0 +1,30 @@ +// slang-ir-string-hash.h +#pragma once + +#include "slang-ir.h" + +#include "../core/slang-string-slice-pool.h" + +namespace Slang +{ + +struct IRModule; +struct SharedIRBuilder; + +// Find all of the calls to 'getStringHash' and replace them with the an int literal of the value. Place all +// of the string literal values into the ioPool. +void replaceGetStringHash(IRModule* module, SharedIRBuilder& sharedBuilder, StringSlicePool& ioPool); + +// Does the same as replaceGetStringHash, but also adds a GlobalHashedStringLiterals instruction of any +// string literals referenced via 'getStringHash'. If there are none, it does nothing. +void replaceGetStringHashWithGlobal(IRModule* module, SharedIRBuilder& sharedBuilder); + +// Finds the global GlobalHashedStringLiterals instruction for the module if there is one, and then +// adds all of it's strings to ioPool. +void findGlobalHashedStringLiterals(IRModule* module, StringSlicePool& ioPool); + +// Given a pool, with > 0 strings adds a GlobalHashedStringLiterals to the module. +void addGlobalHashedStringLiterals(const StringSlicePool& pool, SharedIRBuilder& sharedBuilder); + + +} // namespace Slang diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 9bdc7c3c9..ac7eab198 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -695,7 +695,6 @@ struct IRBoolLit : IRConstant IR_LEAF_ISA(BoolLit); }; - // Get the compile-time constant integer value of an instruction, // if it has one, and assert-fail otherwise. IRIntegerValue GetIntVal(IRInst* inst); @@ -1066,6 +1065,18 @@ SIMPLE_IR_TYPE(OutType, OutTypeBase) SIMPLE_IR_TYPE(InOutType, OutTypeBase) SIMPLE_IR_TYPE(ExistentialBoxType, PtrTypeBase) +struct IRGlobalHashedStringLiterals : IRInst +{ + IR_LEAF_ISA(GlobalHashedStringLiterals) +}; + +struct IRGetStringHash : IRInst +{ + IR_LEAF_ISA(GetStringHash) + + IRStringLit* getStringLit() { return as<IRStringLit>(getOperand(0)); } +}; + /// Get the type pointed to be `ptrType`, or `nullptr` if it is not a pointer(-like) type. /// /// The given IR `builder` will be used if new instructions need to be created. diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 2a04b53b2..8b8a67f1e 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -14,6 +14,8 @@ #include "slang-ir-ssa.h" #include "slang-ir-strip.h" #include "slang-ir-validate.h" +#include "slang-ir-string-hash.h" + #include "slang-mangle.h" #include "slang-type-layout.h" #include "slang-visitor.h" @@ -776,6 +778,24 @@ LoweredValInfo emitCallToDeclRef( } else { + switch (intrinsicOp) + { + case kIROp_GetStringHash: + { + IRStringLit* stringLit = as<IRStringLit>(args[0]); + + if (stringLit == nullptr || stringLit->getStringSlice() == UnownedStringSlice()) + { + auto sink = context->getSink(); + + sink->diagnose(funcDecl, Diagnostics::getStringHashRequiresStringLiteral); + + return LoweredValInfo(); + } + + } + } + // The intrinsic op maps to a single IR instruction, // so we will emit an instruction with the chosen // opcode, and the arguments to the call as its operands. @@ -6737,6 +6757,9 @@ IRModule* generateIRForTranslationUnit( // call graph) based on constraints imposed by different instructions. propagateConstExpr(module, compileRequest->getSink()); + // Replace calls to getStringHash, and save all the unique string lits in a GlobalHashedStringLiterals inst + replaceGetStringHashWithGlobal(module, *sharedBuilder); + // TODO: give error messages if any `undefined` or // `unreachable` instructions remain. diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 5ea81c809..9e11152c4 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -5,6 +5,8 @@ #include "slang-compiler.h" #include "slang-type-layout.h" +#include "slang-ir-string-hash.h" + #include "../../slang.h" namespace Slang { @@ -2974,6 +2976,11 @@ RefPtr<ProgramLayout> generateParameterBindings( RefPtr<ProgramLayout> programLayout = new ProgramLayout(); programLayout->targetProgram = targetProgram; + { + auto& pool = programLayout->hashedStringLiteralPool; + program->enumerateIRModules([&](IRModule* module) { findGlobalHashedStringLiterals(module, pool); }); + } + // Try to find rules based on the selected code-generation target auto layoutContext = getInitialLayoutContextForTarget(targetReq, programLayout); diff --git a/source/slang/slang-reflection.cpp b/source/slang/slang-reflection.cpp index 59130b0fc..306c45c14 100644 --- a/source/slang/slang-reflection.cpp +++ b/source/slang/slang-reflection.cpp @@ -1499,3 +1499,27 @@ SLANG_API SlangReflectionType* spReflection_specializeType( return convert(specializedType); } + +SLANG_API SlangUInt spReflection_getHashedStringCount( + SlangReflection* reflection) +{ + auto programLayout = convert(reflection); + return programLayout->hashedStringLiteralPool.getNumSlices() - StringSlicePool::kNumDefaultHandles; +} + +SLANG_API const char* spReflection_getHashedString( + SlangReflection* reflection, + SlangUInt index, + size_t* outCount) +{ + auto programLayout = convert(reflection); + UnownedStringSlice slice = programLayout->hashedStringLiteralPool.getSlice(StringSlicePool::Handle(index + StringSlicePool::kNumDefaultHandles)); + *outCount = slice.size(); + return slice.begin(); +} + +SLANG_API int spCalcStringHash(const char* chars, size_t count) +{ + UnownedStringSlice slice(chars, count); + return GetHashCode(slice); +} diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index fc638c3dd..1d3892d78 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -753,6 +753,9 @@ public: /// arguments that have been used to specialize the program. /// List<RefPtr<TypeLayout>> taggedUnionTypeLayouts; + + /// Holds all of the string literals that have been hashed + StringSlicePool hashedStringLiteralPool; }; StructTypeLayout* getGlobalStructLayout( diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 3d3fa4375..a9c7f4558 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -227,6 +227,7 @@ <ClInclude Include="slang-ir-specialize-resources.h" /> <ClInclude Include="slang-ir-specialize.h" /> <ClInclude Include="slang-ir-ssa.h" /> + <ClInclude Include="slang-ir-string-hash.h" /> <ClInclude Include="slang-ir-strip.h" /> <ClInclude Include="slang-ir-union.h" /> <ClInclude Include="slang-ir-validate.h" /> @@ -305,6 +306,7 @@ <ClCompile Include="slang-ir-specialize-resources.cpp" /> <ClCompile Include="slang-ir-specialize.cpp" /> <ClCompile Include="slang-ir-ssa.cpp" /> + <ClCompile Include="slang-ir-string-hash.cpp" /> <ClCompile Include="slang-ir-strip.cpp" /> <ClCompile Include="slang-ir-union.cpp" /> <ClCompile Include="slang-ir-validate.cpp" /> @@ -332,13 +334,17 @@ <ClCompile Include="slang.cpp" /> </ItemGroup> <ItemGroup> + <None Include="..\core\core.natvis" /> + <None Include="slang.natvis" /> + </ItemGroup> + <ItemGroup> <CustomBuild Include="core.meta.slang"> <FileType>Document</FileType> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../bin/windows-x86/debug/slang-generate" %(Identity)</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command> - <Outputs>../../core.meta.slang.h</Outputs> + <Outputs>%(Identity).h</Outputs> <Message>slang-generate %(Identity)</Message> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs> @@ -351,7 +357,7 @@ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command> - <Outputs>../../hlsl.meta.slang.h</Outputs> + <Outputs>%(Identity).h</Outputs> <Message>slang-generate %(Identity)</Message> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs> @@ -360,10 +366,6 @@ </CustomBuild> </ItemGroup> <ItemGroup> - <Natvis Include="..\core\core.natvis" /> - <Natvis Include="slang.natvis" /> - </ItemGroup> - <ItemGroup> <ProjectReference Include="..\core\core.vcxproj"> <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project> </ProjectReference> diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index 3764a6f11..c562d28e3 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -132,6 +132,9 @@ <ClInclude Include="slang-ir-ssa.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="slang-ir-string-hash.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="slang-ir-strip.h"> <Filter>Header Files</Filter> </ClInclude> @@ -362,6 +365,9 @@ <ClCompile Include="slang-ir-ssa.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="slang-ir-string-hash.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="slang-ir-strip.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/tests/ir/string-literal-hash-reflection.slang b/tests/ir/string-literal-hash-reflection.slang new file mode 100644 index 000000000..85d6ac3e4 --- /dev/null +++ b/tests/ir/string-literal-hash-reflection.slang @@ -0,0 +1,15 @@ +//TEST:REFLECTION:-stage compute -entry computeMain -target hlsl + +import string_literal_module; + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out +RWStructuredBuffer<int> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain( + uint tid : SV_DispatchThreadIndex) +{ + int value = doSomethingElse() + getStringHash("Hello \t\n\0x083 World"); + outputBuffer[tid] = value; +} + diff --git a/tests/ir/string-literal-hash-reflection.slang.expected b/tests/ir/string-literal-hash-reflection.slang.expected new file mode 100644 index 000000000..4b3726efe --- /dev/null +++ b/tests/ir/string-literal-hash-reflection.slang.expected @@ -0,0 +1,44 @@ +result code = 0 +standard error = { +} +standard output = { +{ + "parameters": [ + { + "name": "outputBuffer", + "binding": {"kind": "unorderedAccess", "index": 0}, + "type": { + "kind": "resource", + "baseShape": "structuredBuffer", + "access": "readWrite", + "resultType": { + "kind": "scalar", + "scalarType": "int32" + } + } + } + ], + "entryPoints": [ + { + "name": "computeMain", + "stage:": "compute", + "parameters": [ + { + "name": "tid", + "semanticName": "SV_DISPATCHTHREADINDEX", + "type": { + "kind": "scalar", + "scalarType": "uint32" + } + } + ], + "threadGroupSize": [4, 1, 1] + } + ], + "hashedStrings": { + "Hello \t\n\0x083 World": -215446506, + "Try another": 900483678 + } + +} +} diff --git a/tests/ir/string-literal-hash.slang b/tests/ir/string-literal-hash.slang new file mode 100644 index 000000000..b6ab8bf4e --- /dev/null +++ b/tests/ir/string-literal-hash.slang @@ -0,0 +1,17 @@ +//TEST(compute):COMPARE_COMPUTE: -cpu +//TEST(compute):COMPARE_COMPUTE: +//TEST(compute):COMPARE_COMPUTE: -vk + +import string_literal_module; + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer +RWStructuredBuffer<int> outputBuffer; + +[numthreads(4, 1, 1)] +void computeMain( + uint3 tid : SV_DispatchThreadID) +{ + int value = doSomethingElse() + getStringHash("Hello \t\n\0x083 World"); + outputBuffer[tid.x] = value; +} + diff --git a/tests/ir/string-literal-hash.slang.expected.txt b/tests/ir/string-literal-hash.slang.expected.txt new file mode 100644 index 000000000..8398d4098 --- /dev/null +++ b/tests/ir/string-literal-hash.slang.expected.txt @@ -0,0 +1,4 @@ +28D4D674 +28D4D674 +28D4D674 +28D4D674 diff --git a/tests/ir/string-literal-module.slang b/tests/ir/string-literal-module.slang new file mode 100644 index 000000000..96fb62088 --- /dev/null +++ b/tests/ir/string-literal-module.slang @@ -0,0 +1,6 @@ +//TEST_IGNORE_FILE: + +int doSomethingElse() +{ + return getStringHash("Try another"); +} diff --git a/tools/slang-reflection-test/slang-reflection-test-main.cpp b/tools/slang-reflection-test/slang-reflection-test-main.cpp index a327f1960..9dc507250 100644 --- a/tools/slang-reflection-test/slang-reflection-test-main.cpp +++ b/tools/slang-reflection-test/slang-reflection-test-main.cpp @@ -33,6 +33,15 @@ static void writeRawChar(PrettyWriter& writer, int c) writeRaw(writer, buffer, buffer + 1); } + +static void writeHexChar(PrettyWriter& writer, int c) +{ + char v = char(c) + (c < 10 ? '0' : ('a' - 10)); + + char buffer[] = { v, 0 }; + writeRaw(writer, buffer, buffer + 1); +} + static void adjust(PrettyWriter& writer) { if (!writer.startOfLine) @@ -77,6 +86,43 @@ static void write(PrettyWriter& writer, char const* text, size_t length = 0) } } +static void writeEscapedString(PrettyWriter& writer, char const* text, size_t length) +{ + adjust(writer); + + writeRawChar(writer, '"'); + + for (size_t i = 0; i < length; ++i) + { + const char c = text[i]; + switch (c) + { + case '\n': write(writer, "\\n"); break; + case '\t': write(writer, "\\t"); break; + case '\b': write(writer, "\\b"); break; + case '\f': write(writer, "\\f"); break; + case '\0': write(writer, "\\0"); break; + case '"': write(writer, "\\\""); break; + default: + { + if (c < ' ' || int(c) >= 128) + { + // Not strictly right - as we should decode as a unicode code point and write that. + write(writer, "\\u00"); + writeHexChar(writer, (c >> 4) & 0xf); + writeHexChar(writer, c & 0xf); + } + else + { + writeRawChar(writer, c); + } + } + } + } + + writeRawChar(writer, '"'); +} + static void write(PrettyWriter& writer, uint64_t val) { adjust(writer); @@ -1011,6 +1057,36 @@ static void emitReflectionJSON( dedent(writer); write(writer, "\n]"); } + + { + SlangUInt count = programReflection->getHashedStringCount(); + if (count) + { + write(writer, ",\n\"hashedStrings\": {\n"); + indent(writer); + + for (SlangUInt i = 0; i < count; ++i) + { + if (i) + { + write(writer, ",\n"); + } + + size_t charsCount; + const char* chars = programReflection->getHashedString(i, &charsCount); + const int hash = spCalcStringHash(chars, charsCount); + + writeEscapedString(writer, chars, charsCount); + write(writer, ": "); + + write(writer, hash); + } + + dedent(writer); + write(writer, "\n}\n"); + } + } + dedent(writer); write(writer, "\n}\n"); } |
