dxbc2dxil.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // dxbc2dxil.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // Provides the entry point for the dxbc2dxil console program. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "dxc/Support/Global.h"
  12. #include "dxc/Support/Unicode.h"
  13. #include "dxc/DxilContainer/DxilContainer.h"
  14. #include "dxc/DxilContainer/DxilContainerReader.h"
  15. #include "dxc/DXIL/DxilConstants.h"
  16. #include "llvm/Support/Path.h"
  17. #include "llvm/Support/raw_ostream.h"
  18. #include "llvm/Support/MemoryBuffer.h"
  19. #include "llvm/Support/ManagedStatic.h"
  20. #include "llvm/IR/LLVMContext.h"
  21. #include "llvm/IR/Module.h"
  22. #include "llvm/Bitcode/ReaderWriter.h"
  23. #include <atlbase.h>
  24. #include "dxc/Support/microcom.h"
  25. #include "Support/DXIncludes.h"
  26. #include "dxc/Support/FileIOHelper.h"
  27. #include "dxc/dxcapi.h"
  28. #include "DxbcConverter.h"
  29. #include <fstream>
  30. using namespace llvm;
  31. using std::string;
  32. using std::wstring;
  33. using std::unique_ptr;
  34. class Converter {
  35. public:
  36. Converter();
  37. static void PrintUsage();
  38. void ParseCommandLine(int NumArgs, _In_z_count_(NumArgs + 1) wchar_t **ppArgs);
  39. static void CmdLineError(const wchar_t *pFormat, ...);
  40. void Run();
  41. protected:
  42. wstring m_InputFile;
  43. wstring m_OutputFile;
  44. bool m_bUsage;
  45. bool m_bDisasmDxbc;
  46. bool m_bEmitLLVM;
  47. bool m_bEmitBC;
  48. wstring m_ExtraOptions;
  49. private:
  50. DxcCreateInstanceProc m_pfnDXCompiler_DxcCreateInstance;
  51. DxcCreateInstanceProc m_pfnDxilConv_DxcCreateInstance;
  52. HRESULT CreateDxcLibrary(_Outptr_ IDxcLibrary **ppLibrary);
  53. HRESULT CreateDxcCompiler(_Outptr_ IDxcCompiler **ppCompiler);
  54. HRESULT CreateDxbcConverter(_Outptr_ IDxbcConverter **ppConverter);
  55. HRESULT GetDxcCreateInstance(LPCWSTR dllFileName, DxcCreateInstanceProc *ppFn);
  56. static bool CheckOption(const wchar_t *pStr, const wchar_t *pOption);
  57. };
  58. Converter::Converter()
  59. : m_bUsage(false)
  60. , m_bDisasmDxbc(false)
  61. , m_bEmitLLVM(false)
  62. , m_bEmitBC(false)
  63. , m_pfnDXCompiler_DxcCreateInstance(nullptr)
  64. , m_pfnDxilConv_DxcCreateInstance(nullptr) {
  65. }
  66. void Converter::PrintUsage() {
  67. wprintf(L"\n");
  68. wprintf(L"Usage: dxbc2dxil.exe <input_file> <options>\n");
  69. wprintf(L"\n");
  70. wprintf(L" /?, /h, /help print this message\n");
  71. wprintf(L"\n");
  72. wprintf(L" /o <file_name> output file name\n");
  73. wprintf(L" /disasm-dxbc print DXBC disassembly and exit\n");
  74. wprintf(L" /emit-llvm print DXIL disassembly and exit\n");
  75. wprintf(L" /emit-bc emit LLVM bitcode rather than DXIL container\n");
  76. wprintf(L"\n");
  77. }
  78. bool Converter::CheckOption(const wchar_t *pStr, const wchar_t *pOption) {
  79. if (!pStr || (pStr[0] != L'-' && pStr[0] != L'/'))
  80. return false;
  81. return _wcsicmp(&pStr[1], pOption) == 0;
  82. }
  83. void Converter::ParseCommandLine(int NumArgs, _In_z_count_(NumArgs + 1) wchar_t **ppArgs) {
  84. try {
  85. bool bSeenHelp = false;
  86. bool bSeenInputFile = false;
  87. bool bSeenOutputFile = false;
  88. int iArg = 1;
  89. while(iArg < NumArgs)
  90. {
  91. if (bSeenHelp) CmdLineError(L"too many options");
  92. if (CheckOption(ppArgs[iArg], L"help") ||
  93. CheckOption(ppArgs[iArg], L"h") ||
  94. CheckOption(ppArgs[iArg], L"?")) {
  95. if (!bSeenInputFile && !bSeenOutputFile) {
  96. m_bUsage = bSeenHelp = true;
  97. }
  98. else CmdLineError(L"too many options");
  99. }
  100. else if (CheckOption(ppArgs[iArg], L"o")) {
  101. iArg++;
  102. if (!bSeenOutputFile && iArg < NumArgs) {
  103. m_OutputFile = wstring(ppArgs[iArg]);
  104. bSeenOutputFile = true;
  105. }
  106. else CmdLineError(L"/o output_filename can be specified only once");
  107. }
  108. else if (CheckOption(ppArgs[iArg], L"disasm-dxbc")) {
  109. m_bDisasmDxbc = true;
  110. }
  111. else if (CheckOption(ppArgs[iArg], L"emit-llvm")) {
  112. m_bEmitLLVM = true;
  113. }
  114. else if (CheckOption(ppArgs[iArg], L"emit-bc")) {
  115. m_bEmitBC = true;
  116. }
  117. else if (CheckOption(ppArgs[iArg], L"no-dxil-cleanup")) {
  118. m_ExtraOptions += L" -no-dxil-cleanup";
  119. }
  120. else if (ppArgs[iArg] && (ppArgs[iArg][0] == L'-' || ppArgs[iArg][0] == L'/')) {
  121. CmdLineError(L"unrecognized option: %s", ppArgs[iArg]);
  122. }
  123. else {
  124. if (!bSeenInputFile) {
  125. m_InputFile = wstring(ppArgs[iArg]);
  126. bSeenInputFile = true;
  127. }
  128. else CmdLineError(L"input file name can be specified only once (%s)", ppArgs[iArg]);
  129. }
  130. iArg++;
  131. }
  132. if (!bSeenInputFile) CmdLineError(L"must specify input file name");
  133. if (!bSeenOutputFile && !(m_bDisasmDxbc || m_bEmitLLVM))
  134. CmdLineError(L"cannot output binary to the console; must specify output file name");
  135. if ((m_bDisasmDxbc?1:0) + (m_bEmitLLVM?1:0) + (m_bEmitBC?1:0) > 1)
  136. CmdLineError(L"/disasm-dxbc, /emit-llvm and /emit-bc are mutually exclusive");
  137. }
  138. catch(const wstring &Msg) {
  139. wprintf(L"%s: %s\n", ppArgs[0], Msg.c_str());
  140. PrintUsage();
  141. exit(1);
  142. }
  143. catch(...) {
  144. wprintf(L"%s: Failed to parse command line\n", ppArgs[0]);
  145. PrintUsage();
  146. exit(1);
  147. }
  148. }
  149. void Converter::CmdLineError(const wchar_t *pFormat, ...) {
  150. const int kBufSize = 4*1024;
  151. wchar_t buf[kBufSize + 1];
  152. int idx = 0;
  153. va_list args;
  154. va_start(args, pFormat);
  155. idx += vswprintf_s(&buf[idx], kBufSize, pFormat, args);
  156. va_end(args);
  157. // idx is the number of characters written, not including the terminating
  158. // null character, or a negative value if an output error occurs
  159. if (idx < 0) idx = 0;
  160. _Analysis_assume_(0 <= idx && idx <= kBufSize);
  161. buf[idx] = L'\0';
  162. throw wstring(buf);
  163. }
  164. void Converter::Run() {
  165. // Usage
  166. if (m_bUsage) {
  167. PrintUsage();
  168. return;
  169. }
  170. // Load DXBC blob.
  171. CComHeapPtr<void> pDxbcPtr;
  172. DWORD DxbcSize;
  173. hlsl::ReadBinaryFile(m_InputFile.c_str(), &pDxbcPtr, &DxbcSize);
  174. // Disassemble Dxbc blob and exit.
  175. if (m_bDisasmDxbc) {
  176. CComPtr<IDxcLibrary> library;
  177. IFT(CreateDxcLibrary(&library));
  178. CComPtr<IDxcBlobEncoding> source;
  179. IFT(library->CreateBlobWithEncodingFromPinned((LPBYTE)pDxbcPtr.m_pData, DxbcSize,
  180. CP_ACP, &source));
  181. CComPtr<IDxcCompiler> compiler;
  182. IFT(CreateDxcCompiler(&compiler));
  183. CComPtr<IDxcBlobEncoding> pDisasmBlob;
  184. IFT(compiler->Disassemble(source, &pDisasmBlob.p));
  185. const char *pText = (const char *)pDisasmBlob->GetBufferPointer();
  186. IFTPTR(pText);
  187. if (m_OutputFile.empty())
  188. printf("%s", pText);
  189. else
  190. hlsl::WriteBinaryFile(m_OutputFile.c_str(), pText, strlen(pText));
  191. return;
  192. }
  193. // Convert DXBC to DXIL.
  194. CComPtr<IDxbcConverter> converter;
  195. IFT(CreateDxbcConverter(&converter));
  196. void *pDxilPtr;
  197. UINT32 DxilSize;
  198. IFT(converter->Convert(pDxbcPtr, DxbcSize, m_ExtraOptions.empty() ? nullptr : m_ExtraOptions.c_str(),
  199. &pDxilPtr, &DxilSize, nullptr));
  200. CComHeapPtr<void> pDxil(pDxilPtr);
  201. // Determine output.
  202. const void *pOutput = pDxil; // DXIL blob (in DXBC container).
  203. UINT32 OutputSize = DxilSize;
  204. if (m_bEmitLLVM || m_bEmitBC) {
  205. // Retrieve DXIL.
  206. hlsl::DxilContainerReader dxilReader;
  207. IFT(dxilReader.Load(pDxil, DxilSize));
  208. UINT uDxilBlob;
  209. IFT(dxilReader.FindFirstPartKind(hlsl::DFCC_DXIL, &uDxilBlob));
  210. IFTBOOL(uDxilBlob != DXIL_CONTAINER_BLOB_NOT_FOUND, DXC_E_INCORRECT_DXBC);
  211. const char *pDxilBlob;
  212. UINT32 DxilBlobSize;
  213. IFTBOOL(dxilReader.GetPartContent(uDxilBlob, (const void **)&pDxilBlob, &DxilBlobSize) == S_OK, DXC_E_INCORRECT_DXBC);
  214. // Retrieve LLVM bitcode.
  215. const hlsl::DxilProgramHeader *pHeader = (const hlsl::DxilProgramHeader *)pDxilBlob;
  216. const char *pBitcode = hlsl::GetDxilBitcodeData(pHeader);
  217. UINT32 BitcodeSize = hlsl::GetDxilBitcodeSize(pHeader);
  218. IFTBOOL(BitcodeSize + sizeof(hlsl::DxilProgramHeader) <= DxilBlobSize, DXC_E_INCORRECT_DXBC);
  219. IFTBOOL(pHeader->BitcodeHeader.DxilMagic == *((const uint32_t *)"DXIL"), DXC_E_INCORRECT_DXBC);
  220. IFTBOOL(hlsl::DXIL::GetDxilVersionMajor(pHeader->BitcodeHeader.DxilVersion) == 1, DXC_E_INCORRECT_DXBC);
  221. IFTBOOL(hlsl::DXIL::GetDxilVersionMinor(pHeader->BitcodeHeader.DxilVersion) == 0, DXC_E_INCORRECT_DXBC);
  222. if (m_bEmitLLVM) {
  223. // Disassemble LLVM module and exit.
  224. unique_ptr<MemoryBuffer> pBitcodeBuf(MemoryBuffer::getMemBuffer(StringRef(pBitcode, BitcodeSize), "", false));
  225. ErrorOr<std::unique_ptr<Module>> pModule(parseBitcodeFile(pBitcodeBuf->getMemBufferRef(), getGlobalContext()));
  226. if (std::error_code ec = pModule.getError()) {
  227. throw hlsl::Exception(DXC_E_INCORRECT_DXBC);
  228. }
  229. string StreamStr;
  230. raw_string_ostream Stream(StreamStr);
  231. pModule.get()->print(Stream, nullptr);
  232. Stream.flush();
  233. if (m_OutputFile.empty())
  234. printf("%s", StreamStr.c_str());
  235. else {
  236. std::ofstream ofs(m_OutputFile);
  237. if (!ofs)
  238. throw hlsl::Exception(E_ABORT, "unable to open output file");
  239. ofs << StreamStr;
  240. }
  241. return;
  242. }
  243. else if (m_bEmitBC) {
  244. // Emit only LLVM IR, e.g., to disassemble with llvm-dis.exe.
  245. pOutput = pBitcode;
  246. OutputSize = BitcodeSize;
  247. }
  248. }
  249. hlsl::WriteBinaryFile(m_OutputFile.c_str(), pOutput, OutputSize);
  250. }
  251. HRESULT Converter::CreateDxcLibrary(_Outptr_ IDxcLibrary **ppLibrary) {
  252. if (m_pfnDXCompiler_DxcCreateInstance == nullptr) {
  253. IFR(GetDxcCreateInstance(L"dxcompiler.dll", &m_pfnDXCompiler_DxcCreateInstance));
  254. }
  255. IFR((*m_pfnDXCompiler_DxcCreateInstance)(CLSID_DxcLibrary, __uuidof(IDxcLibrary), reinterpret_cast<LPVOID*>(ppLibrary)));
  256. return S_OK;
  257. }
  258. HRESULT Converter::CreateDxcCompiler(_Outptr_ IDxcCompiler **ppCompiler) {
  259. if (m_pfnDXCompiler_DxcCreateInstance == nullptr) {
  260. IFR(GetDxcCreateInstance(L"dxcompiler.dll", &m_pfnDXCompiler_DxcCreateInstance));
  261. }
  262. IFR((*m_pfnDXCompiler_DxcCreateInstance)(CLSID_DxcCompiler, __uuidof(IDxcCompiler), reinterpret_cast<LPVOID*>(ppCompiler)));
  263. return S_OK;
  264. }
  265. HRESULT Converter::CreateDxbcConverter(_Outptr_ IDxbcConverter **ppConverter) {
  266. if (m_pfnDxilConv_DxcCreateInstance == nullptr) {
  267. IFR(GetDxcCreateInstance(L"dxilconv.dll", &m_pfnDxilConv_DxcCreateInstance));
  268. }
  269. IFR((*m_pfnDxilConv_DxcCreateInstance)(CLSID_DxbcConverter, __uuidof(IDxbcConverter), reinterpret_cast<LPVOID*>(ppConverter)));
  270. return S_OK;
  271. }
  272. HRESULT Converter::GetDxcCreateInstance(LPCWSTR dllFileName, DxcCreateInstanceProc *ppFn) {
  273. HMODULE hModule = LoadLibraryExW(dllFileName, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
  274. if (hModule == NULL) {
  275. return HRESULT_FROM_WIN32(GetLastError());
  276. }
  277. FARPROC pFn = GetProcAddress(hModule, "DxcCreateInstance");
  278. if (pFn == NULL) {
  279. return HRESULT_FROM_WIN32(GetLastError());
  280. }
  281. *ppFn = reinterpret_cast<DxcCreateInstanceProc>(pFn);
  282. return S_OK;
  283. }
  284. int __cdecl wmain(int argc, _In_z_count_(argc + 1) wchar_t **argv) {
  285. llvm_shutdown_obj Y;
  286. try {
  287. Converter C;
  288. C.ParseCommandLine(argc, argv);
  289. C.Run();
  290. }
  291. catch (const std::bad_alloc) {
  292. printf("Conversion failed - out of memory.\n");
  293. }
  294. catch (const hlsl::Exception &E) {
  295. try {
  296. const char *pMsg = E.what();
  297. Unicode::acp_char printBuffer[128]; // printBuffer is safe to treat as UTF-8 because we use ASCII contents only
  298. if (pMsg == nullptr || *pMsg == '\0') {
  299. sprintf_s(printBuffer, _countof(printBuffer), "Conversion failed - error code 0x%08x.", E.hr);
  300. pMsg = printBuffer;
  301. }
  302. std::string textMessage;
  303. bool lossy;
  304. if (!Unicode::UTF8ToConsoleString(pMsg, &textMessage, &lossy) || lossy) {
  305. // Do a direct assignment as a last-ditch effort and print out as UTF-8.
  306. textMessage = pMsg;
  307. }
  308. printf("%s\n", textMessage.c_str());
  309. }
  310. catch (...) {
  311. printf("Conversion failed - unable to retrieve error message.\n");
  312. }
  313. return 1;
  314. }
  315. catch (...) {
  316. printf("Conversion failed - unable to retrieve error message.\n");
  317. return 1;
  318. }
  319. return 0;
  320. }