Skip to content

Commit 4bfe9c8

Browse files
authored
Fixed PdbUtils not actually normalizing the file paths (microsoft#6317)
There was a typo in dxcpdbutils where the normalized file path wasn't actually being used. This change fixes that and adds thorough testing for the DxcPdbUtils path normalization. Also fixed another case where `/dir/my_file` gets normalized to `.\\dir\my_file`.
1 parent fafbc42 commit 4bfe9c8

File tree

4 files changed

+220
-52
lines changed

4 files changed

+220
-52
lines changed

include/dxc/Support/Path.h

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,24 @@ inline bool IsAbsoluteOrCurDirRelative(const char *Path) {
6464
return IsAbsoluteOrCurDirRelativeImpl<char>(Path, strlen(Path));
6565
}
6666

67+
template <typename CharT, typename StringTy>
68+
void RemoveDoubleSlashes(StringTy &Path, CharT Slash) {
69+
// Remove double slashes.
70+
bool SeenNonSlash = false;
71+
for (unsigned i = 0; i < Path.size();) {
72+
// Remove this slash if:
73+
// 1. It is preceded by another slash.
74+
// 2. It is NOT part of a series of leading slashes. (E.G. \\, which on
75+
// windows is a network path).
76+
if (Path[i] == Slash && i > 0 && Path[i - 1] == Slash && SeenNonSlash) {
77+
Path.erase(Path.begin() + i);
78+
continue;
79+
}
80+
SeenNonSlash |= Path[i] != Slash;
81+
i++;
82+
}
83+
}
84+
6785
// This is the new ground truth of how paths are normalized. There had been
6886
// many inconsistent path normalization littered all over the code base.
6987
// 1. All slashes are changed to system native: `\` for windows and `/` for all
@@ -96,28 +114,16 @@ StringTy NormalizePathImpl(const CharT *Path, size_t Length) {
96114
PathCopy[i] = SlashTo;
97115
}
98116

99-
// Remove double slashes.
100-
bool SeenNonSlash = false;
101-
for (unsigned i = 0; i < PathCopy.size();) {
102-
// Remove this slash if:
103-
// 1. It is preceded by another slash.
104-
// 2. It is NOT part of a series of leading slashes. (E.G. \\, which on
105-
// windows is a network path).
106-
if (PathCopy[i] == SlashTo && i > 0 && PathCopy[i - 1] == SlashTo &&
107-
SeenNonSlash) {
108-
PathCopy.erase(PathCopy.begin() + i);
109-
continue;
110-
}
111-
SeenNonSlash |= PathCopy[i] != SlashTo;
112-
i++;
113-
}
117+
RemoveDoubleSlashes<CharT, StringTy>(PathCopy, SlashTo);
114118

115119
// If relative path, prefix with dot.
116120
if (IsAbsoluteOrCurDirRelativeImpl<CharT>(PathCopy.c_str(),
117121
PathCopy.size())) {
118122
return PathCopy;
119123
} else {
120-
return StringTy(1, CharT('.')) + StringTy(1, SlashTo) + PathCopy;
124+
PathCopy = StringTy(1, CharT('.')) + StringTy(1, SlashTo) + PathCopy;
125+
RemoveDoubleSlashes<CharT, StringTy>(PathCopy, SlashTo);
126+
return PathCopy;
121127
}
122128
}
123129

tools/clang/tools/dxcompiler/dxcpdbutils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ struct DxcPdbUtils : public IDxcPdbUtils2
421421
&source.Content));
422422

423423
std::string normalizedPath = hlsl::NormalizePath(name);
424-
IFR(Utf8ToBlobWide(name, &source.Name));
424+
IFR(Utf8ToBlobWide(normalizedPath, &source.Name));
425425
// First file is the main file
426426
if (m_SourceFiles.empty()) {
427427
m_MainFileName = source.Name;

tools/clang/unittests/HLSL/CompilerTest.cpp

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ class CompilerTest : public ::testing::Test {
192192
TEST_METHOD(CompileWhenIncludeLocalThenLoadRelative)
193193
TEST_METHOD(CompileWhenIncludeSystemThenLoadNotRelative)
194194
TEST_METHOD(CompileWhenAllIncludeCombinations)
195+
TEST_METHOD(TestPdbUtilsPathNormalizations)
195196
TEST_METHOD(CompileWhenIncludeSystemMissingThenLoadAttempt)
196197
TEST_METHOD(CompileWhenIncludeFlagsThenIncludeUsed)
197198
TEST_METHOD(CompileThenCheckDisplayIncludeProcess)
@@ -2884,50 +2885,148 @@ TEST_F(CompilerTest, CompileWhenIncludeThenLoadUsed) {
28842885
pInclude->GetAllFileNames().c_str());
28852886
}
28862887

2887-
TEST_F(CompilerTest, CompileWhenAllIncludeCombinations) {
2888-
static auto NormalizeForPlatform = [](const std::wstring &s) -> std::wstring {
2888+
static std::wstring NormalizeForPlatform(const std::wstring &s) {
28892889
#ifdef _WIN32
2890-
wchar_t From = L'/';
2891-
wchar_t To = L'\\';
2890+
wchar_t From = L'/';
2891+
wchar_t To = L'\\';
28922892
#else
2893-
wchar_t From = L'\\';
2894-
wchar_t To = L'/';
2893+
wchar_t From = L'\\';
2894+
wchar_t To = L'/';
28952895
#endif
2896-
std::wstring ret = s;
2897-
for (wchar_t &c : ret) {
2898-
if (c == From)
2899-
c = To;
2896+
std::wstring ret = s;
2897+
for (wchar_t &c : ret) {
2898+
if (c == From)
2899+
c = To;
2900+
}
2901+
return ret;
2902+
};
2903+
2904+
class SimpleIncludeHanlder : public IDxcIncludeHandler {
2905+
DXC_MICROCOM_REF_FIELD(m_dwRef)
2906+
public:
2907+
DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
2908+
dxc::DxcDllSupport &m_dllSupport;
2909+
HRESULT m_defaultErrorCode = E_FAIL;
2910+
SimpleIncludeHanlder(dxc::DxcDllSupport &dllSupport)
2911+
: m_dwRef(0), m_dllSupport(dllSupport) {}
2912+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
2913+
void **ppvObject) override {
2914+
return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
2915+
}
2916+
2917+
std::wstring Path;
2918+
std::string Content;
2919+
2920+
HRESULT STDMETHODCALLTYPE LoadSource(
2921+
LPCWSTR pFilename, // Filename as written in #include statement
2922+
IDxcBlob **ppIncludeSource // Resultant source object for included file
2923+
) override {
2924+
if (pFilename == Path) {
2925+
MultiByteStringToBlob(m_dllSupport, Content, CP_UTF8, ppIncludeSource);
2926+
return S_OK;
29002927
}
2901-
return ret;
2928+
return E_FAIL;
2929+
}
2930+
};
2931+
2932+
TEST_F(CompilerTest, TestPdbUtilsPathNormalizations) {
2933+
#include "TestHeaders/TestPdbUtilsPathNormalizations.h"
2934+
struct TestCase {
2935+
std::string MainName;
2936+
std::string IncludeName;
29022937
};
29032938

2904-
class SimpleIncludeHanlder : public IDxcIncludeHandler {
2905-
DXC_MICROCOM_REF_FIELD(m_dwRef)
2906-
public:
2907-
DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
2908-
dxc::DxcDllSupport &m_dllSupport;
2909-
HRESULT m_defaultErrorCode = E_FAIL;
2910-
SimpleIncludeHanlder(dxc::DxcDllSupport &dllSupport)
2911-
: m_dwRef(0), m_dllSupport(dllSupport) {}
2912-
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
2913-
void **ppvObject) override {
2914-
return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
2915-
}
2939+
TestCase tests[] = {
2940+
{R"(main.hlsl)", R"(include.h)"},
2941+
{R"(.\5Cmain.hlsl)", R"(.\5Cinclude.h)"},
2942+
{R"(/path/main.hlsl)", R"(/path/include.h)"},
2943+
{R"(\5Cpath/main.hlsl)", R"(\5Cpath\5Cinclude.h)"},
2944+
{R"(..\5Cmain.hlsl)", R"(..\5Cinclude.h)"},
2945+
{R"(..\5Cdir\5Cmain.hlsl)", R"(..\5Cdir\5Cinclude.h)"},
2946+
{R"(F:\5C\5Cdir\5Cmain.hlsl)", R"(F:\5C\5Cdir\5Cinclude.h)"},
2947+
{R"(\5C\5Cdir\5Cmain.hlsl)", R"(\5C\5Cdir\5Cinclude.h)"},
2948+
{R"(\5C\5C\5Cdir\5Cmain.hlsl)", R"(\5C\5C\5Cdir\5Cinclude.h)"},
2949+
{R"(//dir\5Cmain.hlsl)", R"(//dir/include.h)"},
2950+
{R"(///dir/main.hlsl)", R"(///dir\5Cinclude.h)"},
2951+
};
29162952

2917-
std::wstring Path;
2918-
std::string Content;
2953+
for (TestCase &test : tests) {
2954+
std::string oldPdb = kTestPdbUtilsPathNormalizationsIR;
29192955

2920-
HRESULT STDMETHODCALLTYPE LoadSource(
2921-
LPCWSTR pFilename, // Filename as written in #include statement
2922-
IDxcBlob **ppIncludeSource // Resultant source object for included file
2923-
) override {
2924-
if (pFilename == NormalizeForPlatform(Path)) {
2925-
MultiByteStringToBlob(m_dllSupport, Content, CP_UTF8, ppIncludeSource);
2926-
return S_OK;
2927-
}
2928-
return E_FAIL;
2956+
size_t findPos = std::string::npos;
2957+
std::string mainPattern = "<MAIN_FILE>";
2958+
std::string includePattern = "<INCLUDE_FILE>";
2959+
while ((findPos = oldPdb.find(mainPattern)) != std::string::npos) {
2960+
oldPdb.replace(oldPdb.begin() + findPos,
2961+
oldPdb.begin() + findPos + mainPattern.size(),
2962+
test.MainName);
29292963
}
2930-
};
2964+
while ((findPos = oldPdb.find(includePattern)) != std::string::npos) {
2965+
oldPdb.replace(oldPdb.begin() + findPos,
2966+
oldPdb.begin() + findPos + includePattern.size(),
2967+
test.IncludeName);
2968+
}
2969+
2970+
CComPtr<IDxcAssembler> pAssembler;
2971+
VERIFY_SUCCEEDED(
2972+
m_dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
2973+
CComPtr<IDxcUtils> pUtils;
2974+
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcUtils, &pUtils));
2975+
2976+
CComPtr<IDxcBlobEncoding> pBlobEncoding;
2977+
VERIFY_SUCCEEDED(pUtils->CreateBlobFromPinned(oldPdb.data(), oldPdb.size(),
2978+
CP_UTF8, &pBlobEncoding));
2979+
2980+
CComPtr<IDxcOperationResult> pOpResult;
2981+
VERIFY_SUCCEEDED(
2982+
pAssembler->AssembleToContainer(pBlobEncoding, &pOpResult));
2983+
VerifyOperationSucceeded(pOpResult);
2984+
CComPtr<IDxcBlob> pDxil;
2985+
VERIFY_SUCCEEDED(pOpResult->GetResult(&pDxil));
2986+
2987+
CComPtr<IDxcPdbUtils> pPdbUtils;
2988+
VERIFY_SUCCEEDED(
2989+
m_dllSupport.CreateInstance(CLSID_DxcPdbUtils, &pPdbUtils));
2990+
VERIFY_SUCCEEDED(pPdbUtils->Load(pDxil));
2991+
2992+
CComBSTR pMainName;
2993+
CComBSTR pIncludeName;
2994+
CComBSTR pMainName2;
2995+
CComPtr<IDxcBlobEncoding> pIncludeContent;
2996+
CComPtr<IDxcBlobEncoding> pMainContent;
2997+
VERIFY_SUCCEEDED(pPdbUtils->GetSourceName(0, &pMainName));
2998+
VERIFY_SUCCEEDED(pPdbUtils->GetSourceName(1, &pIncludeName));
2999+
VERIFY_SUCCEEDED(pPdbUtils->GetMainFileName(&pMainName2));
3000+
VERIFY_SUCCEEDED(pPdbUtils->GetSource(0, &pMainContent));
3001+
VERIFY_SUCCEEDED(pPdbUtils->GetSource(1, &pIncludeContent));
3002+
3003+
VERIFY_ARE_EQUAL(0, wcscmp(pMainName.m_str, pMainName2.m_str));
3004+
3005+
CComPtr<IDxcBlobUtf8> pMainContentUtf8;
3006+
CComPtr<IDxcBlobUtf8> pIncludeContentUtf8;
3007+
VERIFY_SUCCEEDED(pMainContent.QueryInterface(&pMainContentUtf8));
3008+
VERIFY_SUCCEEDED(pIncludeContent.QueryInterface(&pIncludeContentUtf8));
3009+
3010+
CComPtr<SimpleIncludeHanlder> pRecompileInclude =
3011+
new SimpleIncludeHanlder(m_dllSupport);
3012+
pRecompileInclude->Content =
3013+
std::string(pIncludeContentUtf8->GetStringPointer(),
3014+
pIncludeContentUtf8->GetStringLength());
3015+
pRecompileInclude->Path = pIncludeName;
3016+
3017+
CComPtr<IDxcOperationResult> pRecompileOpResult;
3018+
const WCHAR *args[] = {L"-Zi"};
3019+
3020+
CComPtr<IDxcCompiler> pCompiler;
3021+
VERIFY_SUCCEEDED(CreateCompiler(&pCompiler));
3022+
VERIFY_SUCCEEDED(pCompiler->Compile(
3023+
pMainContentUtf8, pMainName, L"main", L"ps_6_0", args, _countof(args),
3024+
nullptr, 0, pRecompileInclude, &pRecompileOpResult));
3025+
VerifyOperationSucceeded(pRecompileOpResult);
3026+
}
3027+
}
3028+
3029+
TEST_F(CompilerTest, CompileWhenAllIncludeCombinations) {
29313030
struct File {
29323031
std::wstring name;
29333032
std::string content;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
3+
static const llvm::StringRef kTestPdbUtilsPathNormalizationsIR = R"x(
4+
target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
5+
target triple = "dxil-ms-dx"
6+
7+
define void @main() {
8+
entry:
9+
call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float 0.000000e+00), !dbg !30 ; line:2 col:28 ; StoreOutput(outputSigId,rowIndex,colIndex,value)
10+
ret void, !dbg !30 ; line:2 col:28
11+
}
12+
13+
; Function Attrs: nounwind
14+
declare void @dx.op.storeOutput.f32(i32, i32, i32, i8, float) #0
15+
16+
attributes #0 = { nounwind }
17+
18+
!llvm.dbg.cu = !{!0}
19+
!llvm.module.flags = !{!10, !11}
20+
!llvm.ident = !{!12}
21+
!dx.source.contents = !{!13, !14}
22+
!dx.source.defines = !{!2}
23+
!dx.source.mainFileName = !{!15}
24+
!dx.source.args = !{!16}
25+
!dx.version = !{!17}
26+
!dx.valver = !{!18}
27+
!dx.shaderModel = !{!19}
28+
!dx.typeAnnotations = !{!20}
29+
!dx.viewIdState = !{!23}
30+
!dx.entryPoints = !{!24}
31+
32+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "dxc(private) 1.7.0.4135 (pdb_header_fix, 24cf4a146)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3)
33+
!1 = !DIFile(filename: "<MAIN_FILE>", directory: "")
34+
!2 = !{}
35+
!3 = !{!4, !8}
36+
!4 = !DISubprogram(name: "main", scope: !1, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: false, function: void ()* @main)
37+
!5 = !DISubroutineType(types: !6)
38+
!6 = !{!7}
39+
!7 = !DIBasicType(name: "float", size: 32, align: 32, encoding: DW_ATE_float)
40+
!8 = !DISubprogram(name: "foo", linkageName: "\01?foo@@YAMXZ", scope: !9, file: !9, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false)
41+
!9 = !DIFile(filename: "<INCLUDE_FILE>", directory: "")
42+
!10 = !{i32 2, !"Dwarf Version", i32 4}
43+
!11 = !{i32 2, !"Debug Info Version", i32 3}
44+
!12 = !{!"dxc(private) 1.7.0.4135 (pdb_header_fix, 24cf4a146)"}
45+
!13 = !{!"<MAIN_FILE>", !"#include \22include.h\22\0D\0Afloat main() : SV_Target { return foo(); }\0D\0A"}
46+
!14 = !{!"<INCLUDE_FILE>", !"float foo() {\0D\0A return 0;\0D\0A}\0D\0A"}
47+
!15 = !{!"<MAIN_FILE>"}
48+
!16 = !{!"-E", !"main", !"-T", !"ps_6_0", !"/Zi", !"-Qembed_debug"}
49+
!17 = !{i32 1, i32 0}
50+
!18 = !{i32 1, i32 8}
51+
!19 = !{!"ps", i32 6, i32 0}
52+
!20 = !{i32 1, void ()* @main, !21}
53+
!21 = !{!22}
54+
!22 = !{i32 0, !2, !2}
55+
!23 = !{[2 x i32] [i32 0, i32 1]}
56+
!24 = !{void ()* @main, !"main", !25, null, null}
57+
!25 = !{null, !26, null}
58+
!26 = !{!27}
59+
!27 = !{i32 0, !"SV_Target", i8 9, i8 16, !28, i8 0, i32 1, i8 1, i32 0, i8 0, !29}
60+
!28 = !{i32 0}
61+
!29 = !{i32 3, i32 1}
62+
!30 = !DILocation(line: 2, column: 28, scope: !4)
63+
)x";

0 commit comments

Comments
 (0)