2121#include " WexTestClass.h"
2222#include " dxc/Test/HlslTestUtils.h"
2323#include " dxc/Test/DxcTestUtils.h"
24+ #include " dxc/Support/Global.h" // for IFT macro
2425#include " dxc/dxcapi.h"
2526#include " dxc/DxilContainer/DxilContainer.h"
2627
@@ -40,6 +41,7 @@ class LinkerTest
4041 TEST_CLASS_SETUP (InitSupport);
4142
4243 TEST_METHOD (RunLinkResource);
44+ TEST_METHOD (RunLinkModulesDifferentVersions);
4345 TEST_METHOD (RunLinkResourceWithBinding);
4446 TEST_METHOD (RunLinkAllProfiles);
4547 TEST_METHOD (RunLinkFailNoDefine);
@@ -158,14 +160,14 @@ class LinkerTest
158160
159161 void LinkCheckMsg (LPCWSTR pEntryName, LPCWSTR pShaderModel, IDxcLinker *pLinker,
160162 ArrayRef<LPCWSTR> libNames, llvm::ArrayRef<LPCSTR> pErrorMsgs,
161- llvm::ArrayRef<LPCWSTR> pArguments = {}) {
163+ llvm::ArrayRef<LPCWSTR> pArguments = {}, bool bRegex= false ) {
162164 CComPtr<IDxcOperationResult> pResult;
163165 VERIFY_SUCCEEDED (pLinker->Link (pEntryName, pShaderModel,
164166 libNames.data (), libNames.size (),
165167 pArguments.data (), pArguments.size (),
166168 &pResult));
167169 CheckOperationResultMsgs (pResult, pErrorMsgs.data (), pErrorMsgs.size (),
168- false , false );
170+ false , bRegex );
169171 }
170172};
171173
@@ -274,6 +276,97 @@ TEST_F(LinkerTest, RunLinkAllProfiles) {
274276 Link (L" cs_main" , L" cs_6_0" , pLinker, {libName, libResName}, {},{});
275277}
276278
279+ TEST_F (LinkerTest, RunLinkModulesDifferentVersions) {
280+ CComPtr<IDxcLinker> pLinker1, pLinker2, pLinker3;
281+ CreateLinker (&pLinker1);
282+ CreateLinker (&pLinker2);
283+ CreateLinker (&pLinker3);
284+
285+ LPCWSTR libName = L" entry" ;
286+ LPCWSTR option[] = {L" -Zi" , L" -Qembed_debug" };
287+ CComPtr<IDxcBlob> pEntryLib;
288+ CompileLib (L" ..\\ CodeGenHLSL\\ lib_entries2.hlsl" , &pEntryLib, option);
289+
290+ // the 2nd blob is the "good" blob that stays constant
291+ CComPtr<IDxcBlob> pResLib;
292+ CompileLib (L" ..\\ CodeGenHLSL\\ lib_resource2.hlsl" , &pResLib);
293+ LPCWSTR libResName = L" res" ;
294+
295+ // modify the first compiled blob to force it to have a different compiler
296+ // version
297+ CComPtr<IDxcContainerReflection> containerReflection;
298+ IFT (m_dllSupport.CreateInstance (CLSID_DxcContainerReflection,
299+ &containerReflection));
300+ IFT (containerReflection->Load (pEntryLib));
301+ UINT part_index;
302+ VERIFY_SUCCEEDED (containerReflection->FindFirstPartKind (
303+ hlsl::DFCC_CompilerVersion, &part_index));
304+ CComPtr<IDxcBlob> pBlob;
305+ IFT (containerReflection->GetPartContent (part_index, &pBlob));
306+ void *pBlobPtr = pBlob->GetBufferPointer ();
307+
308+ std::string commonMismatchStr =
309+ " error: Cannot link libraries with conflicting compiler versions." ;
310+
311+ hlsl::DxilCompilerVersion *pDCV = (hlsl::DxilCompilerVersion *)pBlobPtr;
312+ if (pDCV->VersionStringListSizeInBytes ) {
313+ // first just get both strings, the compiler version and the commit hash
314+ char *pVersionStringListPtr =
315+ (char *)pBlobPtr + sizeof (hlsl::DxilCompilerVersion);
316+ std::string commitHashStr (pVersionStringListPtr);
317+ std::string oldCommitHashStr = commitHashStr;
318+ std::string compilerVersionStr (pVersionStringListPtr +
319+ commitHashStr.size () + 1 );
320+ std::string oldCompilerVersionStr = compilerVersionStr;
321+
322+ // flip a character to change the commit hash
323+ *pVersionStringListPtr = commitHashStr[0 ] == ' a' ? ' b' : ' a' ;
324+ // store the modified compiler version part.
325+ RegisterDxcModule (libName, pEntryLib, pLinker1);
326+ // and the "good" version part
327+ RegisterDxcModule (libResName, pResLib, pLinker1);
328+ // 2 blobs with different compiler versions should not be linkable
329+ LinkCheckMsg (L" hs_main" , L" hs_6_1" , pLinker1, {libName, libResName},
330+ {commonMismatchStr.c_str ()});
331+
332+ // reset the modified part back to normal
333+ *pVersionStringListPtr = oldCommitHashStr[0 ];
334+
335+ // flip a character to change the compiler version
336+ *(pVersionStringListPtr + commitHashStr.size () + 1 ) =
337+ compilerVersionStr[0 ] == ' 1' ? ' 2' : ' 1' ;
338+ // store the modified compiler version part.
339+ RegisterDxcModule (libName, pEntryLib, pLinker2);
340+ // and the "good" version part
341+ RegisterDxcModule (libResName, pResLib, pLinker2);
342+ // 2 blobs with different compiler versions should not be linkable
343+ LinkCheckMsg (L" hs_main" , L" hs_6_1" , pLinker2, {libName, libResName},
344+ {commonMismatchStr.c_str ()});
345+
346+ // reset the modified part back to normal
347+ *(pVersionStringListPtr + commitHashStr.size () + 1 ) =
348+ oldCompilerVersionStr[0 ];
349+ } else {
350+ // The blob can't be changed by adding data, since the
351+ // the data after this header could be a subsequent header.
352+ // The test should announce that it can't make any version string
353+ // modifications
354+ WEX::Logging::Log::Warning (
355+ L" Cannot modify compiler version string for test" );
356+ }
357+
358+ // finally, test that a difference is detected if a member of the struct, say
359+ // the major member, is different.
360+ pDCV->Major = pDCV->Major == 2 ? 1 : 2 ;
361+ // store the modified compiler version part.
362+ RegisterDxcModule (libName, pEntryLib, pLinker3);
363+ // and the "good" version part
364+ RegisterDxcModule (libResName, pResLib, pLinker3);
365+ // 2 blobs with different compiler versions should not be linkable
366+ LinkCheckMsg (L" hs_main" , L" hs_6_1" , pLinker3, {libName, libResName},
367+ {commonMismatchStr.c_str ()});
368+ }
369+
277370TEST_F (LinkerTest, RunLinkFailNoDefine) {
278371 CComPtr<IDxcBlob> pEntryLib;
279372 CompileLib (L" ..\\ CodeGenHLSL\\ lib_cs_entry.hlsl" , &pEntryLib);
0 commit comments