Skip to content

Commit d12d64a

Browse files
committedMar 18, 2025
Add -dump-module command to slangc
The new -dump-module command to slangc will load and disassemble a slang module, similar to what would be seen by the -dump-ir command, except that -dump-ir tells slangc to print IR as it performs some compilation command. That is, -dump-ir requires some larger compilation task. -dump-module on the otherhand requires no additional goal and will simply load a module and print its IR to stdout independently from other compilation steps. Its intended purpose is to inspect .slang-module files on disk. It can also be used on .slang files which will be parsed and lowered if slang does not find an associated ".slang-module" version of the module on disk. The compilation API is extended with a new IModule::disassemble() method which retrieves the string representation of the dumped IR. Closes shader-slang#6599
1 parent 714ee76 commit d12d64a

File tree

8 files changed

+168
-9
lines changed

8 files changed

+168
-9
lines changed
 

‎include/slang.h

+6
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ typedef uint32_t SlangSizeT;
10151015
SaveGLSLModuleBinSource,
10161016

10171017
SkipDownstreamLinking, // bool, experimental
1018+
DumpModule,
10181019
CountOf,
10191020
};
10201021

@@ -4417,6 +4418,11 @@ struct IModule : public IComponentType
44174418
virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(SlangInt32 index) = 0;
44184419

44194420
virtual SLANG_NO_THROW DeclReflection* SLANG_MCALL getModuleReflection() = 0;
4421+
4422+
/** Disassemble a module.
4423+
*/
4424+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(
4425+
slang::IBlob** outDisassembledBlob) = 0;
44204426
};
44214427

44224428
#define SLANG_UUID_IModule IModule::getTypeGuid()

‎source/slang-record-replay/record/slang-module.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,12 @@ IEntryPointRecorder* ModuleRecorder::getEntryPointRecorder(slang::IEntryPoint* e
244244
return result.detach();
245245
}
246246
}
247+
248+
SlangResult ModuleRecorder::disassemble(ISlangBlob** outBlob)
249+
{
250+
// No need to record this call as it is just a query.
251+
slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
252+
auto res = m_actualModule->disassemble(outBlob);
253+
return res;
254+
}
247255
} // namespace SlangRecord

‎source/slang-record-replay/record/slang-module.h

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class ModuleRecorder : public IModuleRecorder, public IComponentTypeRecorder
5656
ISlangBlob** outDiagnostics) override;
5757
virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override;
5858
virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(SlangInt32 index) override;
59+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(slang::IBlob** outDisassembly) override;
5960

6061
// Interfaces for `IComponentType`
6162
virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override

‎source/slang/slang-compiler.h

+13
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,19 @@ class Module : public ComponentType, public slang::IModule
18321832

18331833
// Source files that have been pulled into the module with `__include`.
18341834
Dictionary<SourceFile*, FileDecl*> m_mapSourceFileToFileDecl;
1835+
1836+
public:
1837+
SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(
1838+
slang::IBlob** outDisassembledBlob) override
1839+
{
1840+
if (!outDisassembledBlob)
1841+
return SLANG_E_INVALID_ARG;
1842+
String disassembly;
1843+
this->getIRModule()->getModuleInst()->dump(disassembly);
1844+
auto blob = StringUtil::createStringBlob(disassembly);
1845+
*outDisassembledBlob = blob.detach();
1846+
return SLANG_OK;
1847+
}
18351848
};
18361849
typedef Module LoadedModule;
18371850

‎source/slang/slang-ir.cpp

+16-7
Original file line numberDiff line numberDiff line change
@@ -8950,24 +8950,33 @@ void IRInst::addBlock(IRBlock* block)
89508950
block->insertAtEnd(this);
89518951
}
89528952

8953-
void IRInst::dump()
8954-
{
8953+
void IRInst::dump(String &outStr)
8954+
{
8955+
StringBuilder sb;
8956+
89558957
if (auto intLit = as<IRIntLit>(this))
89568958
{
8957-
std::cout << intLit->getValue() << std::endl;
8959+
sb << intLit->getValue();
89588960
}
89598961
else if (auto stringLit = as<IRStringLit>(this))
89608962
{
8961-
std::cout << stringLit->getStringSlice().begin() << std::endl;
8963+
sb << stringLit->getStringSlice();
89628964
}
89638965
else
8964-
{
8965-
StringBuilder sb;
8966+
{
89668967
IRDumpOptions options;
89678968
StringWriter writer(&sb, Slang::WriterFlag::AutoFlush);
89688969
dumpIR(this, options, nullptr, &writer);
8969-
std::cout << sb.toString().begin() << std::endl;
89708970
}
8971+
8972+
outStr = sb.toString();
8973+
}
8974+
8975+
void IRInst::dump()
8976+
{
8977+
String s;
8978+
dump(s);
8979+
std::cout << s.begin() << std::endl;
89718980
}
89728981
} // namespace Slang
89738982

‎source/slang/slang-ir.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -813,10 +813,14 @@ struct IRInst
813813
///
814814
void _insertAt(IRInst* inPrev, IRInst* inNext, IRInst* inParent);
815815

816-
/// Print the IR to stdout for debugging purposes
816+
/// Print the IR to stdout for debugging purposes.
817817
///
818818
void dump();
819819

820+
/// Print the IR to a string for debugging purposes.
821+
///
822+
void dump(String &outStr);
823+
820824
/// Insert a basic block at the end of this func/code containing inst.
821825
void addBlock(IRBlock* block);
822826

‎source/slang/slang-options.cpp

+66-1
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,8 @@ void initCommandOptions(CommandOptions& options)
802802
{OptionKind::VerifyDebugSerialIr,
803803
"-verify-debug-serial-ir",
804804
nullptr,
805-
"Verify IR in the front-end."}};
805+
"Verify IR in the front-end."},
806+
{OptionKind::DumpModule, "-dump-module", nullptr, "Disassemble and print the module IR."}};
806807
_addOptions(makeConstArrayView(debuggingOpts), options);
807808

808809
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Experimental !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
@@ -2947,6 +2948,70 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
29472948
Int index = 0;
29482949
SLANG_RETURN_ON_FAIL(_expectInt(arg, index));
29492950
linkage->m_optionSet.add(OptionKind::BindlessSpaceIndex, (int)index);
2951+
break;
2952+
}
2953+
case OptionKind::DumpModule:
2954+
{
2955+
CommandLineArg fileName;
2956+
SLANG_RETURN_ON_FAIL(m_reader.expectArg(fileName));
2957+
auto desc = slang::SessionDesc();
2958+
ComPtr<slang::ISession> session;
2959+
m_session->createSession(desc, session.writeRef());
2960+
ComPtr<slang::IBlob> diagnostics;
2961+
2962+
// Coerce Slang to load from the given file, without letting it automatically
2963+
// choose .slang-module files over .slang files.
2964+
// First try to load as source string, and fall back to loading as an IR Blob.
2965+
// Avoid guessing based on filename or inspect the file contents.
2966+
FILE* file = fopen(fileName.value.getBuffer(), "rb");
2967+
fseek(file, 0, SEEK_END);
2968+
size_t size = ftell(file);
2969+
fseek(file, 0, SEEK_SET);
2970+
std::vector<char> buffer(size+1);
2971+
fread(buffer.data(), 1, size, file);
2972+
buffer[size] = 0;
2973+
fclose(file);
2974+
2975+
ComPtr<slang::IModule> module;
2976+
module = session->loadModuleFromSourceString("module", "path", buffer.data(), diagnostics.writeRef());
2977+
if (!module)
2978+
{
2979+
// Load buffer as an IR blob
2980+
ComPtr<slang::IBlob> blob;
2981+
blob = RawBlob::create(buffer.data(), size);
2982+
2983+
module = session->loadModuleFromIRBlob(
2984+
"module",
2985+
"path",
2986+
blob,
2987+
diagnostics.writeRef());
2988+
}
2989+
2990+
if (module)
2991+
{
2992+
ComPtr<slang::IBlob> disassemblyBlob;
2993+
if (SLANG_FAILED(module->disassemble(disassemblyBlob.writeRef())))
2994+
{
2995+
m_sink->diagnose(arg.loc, Diagnostics::cannotDisassemble, fileName.value);
2996+
return SLANG_FAIL;
2997+
}
2998+
else
2999+
{
3000+
m_sink->diagnoseRaw(Severity::Note, (const char*)disassemblyBlob->getBufferPointer());
3001+
}
3002+
}
3003+
else
3004+
{
3005+
if (diagnostics)
3006+
{
3007+
m_sink->diagnoseRaw(
3008+
Severity::Error,
3009+
(const char*)diagnostics->getBufferPointer());
3010+
}
3011+
return SLANG_FAIL;
3012+
}
3013+
3014+
29503015
break;
29513016
}
29523017
default:

‎tests/ir/dump-module.slang

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This tests slangc's -dump-module command.
2+
// Dumping ".slang" should mean just that, and it should not automatically load up
3+
// a ".slang-module" when available, because the intent of the -dump-module command
4+
// is to see the file you requested. If there's a bug in slang-module output, it's
5+
// important that -dump-module looks at the specific file you requested.
6+
7+
//TEST:COMPILE: tests/ir/dump-module.slang -o tests/ir/dump-module.slang-module -target spirv -embed-downstream-ir
8+
9+
//TEST:SIMPLE(filecheck=CHECK1): -dump-module tests/ir/dump-module.slang-module
10+
//TEST:SIMPLE(filecheck=CHECK2): -dump-module tests/ir/dump-module.slang
11+
12+
module "export-library-generics";
13+
14+
public cbuffer Constants {
15+
public float x;
16+
public float y;
17+
}
18+
19+
interface MyInterface
20+
{
21+
int myMethod(int a);
22+
}
23+
24+
struct MyType : MyInterface
25+
{
26+
int myMethod(int a)
27+
{
28+
return a * 3;
29+
}
30+
}
31+
32+
int genericFunc<T: MyInterface>(T arg)
33+
{
34+
return arg.myMethod(3);
35+
}
36+
37+
public int normalFuncUsesGeneric(int a)
38+
{
39+
MyType obj;
40+
return genericFunc(obj);
41+
}
42+
43+
public int normalFunc(int a, float b)
44+
{
45+
return a - floor(b);
46+
}
47+
48+
// CHECK1:EmbeddedDownstreamIR(6 : Int,
49+
// CHECK1: OpCapability Linkage
50+
51+
// CHECK2-NOT:EmbeddedDownstreamIR(6 : Int,
52+
// CHECK2-NOT: OpCapability Linkage
53+

0 commit comments

Comments
 (0)