Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -dump-module command to slangc #6638

Merged
merged 11 commits into from
Mar 20, 2025
6 changes: 6 additions & 0 deletions include/slang.h
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,7 @@ typedef uint32_t SlangSizeT;
SaveGLSLModuleBinSource,

SkipDownstreamLinking, // bool, experimental
DumpModule,
CountOf,
};

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

virtual SLANG_NO_THROW DeclReflection* SLANG_MCALL getModuleReflection() = 0;

/** Disassemble a module.
*/
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
disassemble(slang::IBlob** outDisassembledBlob) = 0;
};

#define SLANG_UUID_IModule IModule::getTypeGuid()
Expand Down
8 changes: 8 additions & 0 deletions source/slang-record-replay/record/slang-module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,12 @@ IEntryPointRecorder* ModuleRecorder::getEntryPointRecorder(slang::IEntryPoint* e
return result.detach();
}
}

SlangResult ModuleRecorder::disassemble(ISlangBlob** outBlob)
{
// No need to record this call as it is just a query.
slangRecordLog(LogLevel::Verbose, "%s\n", __PRETTY_FUNCTION__);
auto res = m_actualModule->disassemble(outBlob);
return res;
}
} // namespace SlangRecord
2 changes: 2 additions & 0 deletions source/slang-record-replay/record/slang-module.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class ModuleRecorder : public IModuleRecorder, public IComponentTypeRecorder
ISlangBlob** outDiagnostics) override;
virtual SLANG_NO_THROW SlangInt32 SLANG_MCALL getDependencyFileCount() override;
virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(SlangInt32 index) override;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
disassemble(slang::IBlob** outDisassembly) override;

// Interfaces for `IComponentType`
virtual SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() override
Expand Down
12 changes: 12 additions & 0 deletions source/slang/slang-compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,18 @@ class Module : public ComponentType, public slang::IModule

// Source files that have been pulled into the module with `__include`.
Dictionary<SourceFile*, FileDecl*> m_mapSourceFileToFileDecl;

public:
SLANG_NO_THROW SlangResult SLANG_MCALL disassemble(slang::IBlob** outDisassembledBlob) override
{
if (!outDisassembledBlob)
return SLANG_E_INVALID_ARG;
String disassembly;
this->getIRModule()->getModuleInst()->dump(disassembly);
auto blob = StringUtil::createStringBlob(disassembly);
*outDisassembledBlob = blob.detach();
return SLANG_OK;
}
};
typedef Module LoadedModule;

Expand Down
19 changes: 14 additions & 5 deletions source/slang/slang-ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8950,24 +8950,33 @@ void IRInst::addBlock(IRBlock* block)
block->insertAtEnd(this);
}

void IRInst::dump()
void IRInst::dump(String& outStr)
{
StringBuilder sb;

if (auto intLit = as<IRIntLit>(this))
{
std::cout << intLit->getValue() << std::endl;
sb << intLit->getValue();
}
else if (auto stringLit = as<IRStringLit>(this))
{
std::cout << stringLit->getStringSlice().begin() << std::endl;
sb << stringLit->getStringSlice();
}
else
{
StringBuilder sb;
IRDumpOptions options;
StringWriter writer(&sb, Slang::WriterFlag::AutoFlush);
dumpIR(this, options, nullptr, &writer);
std::cout << sb.toString().begin() << std::endl;
}

outStr = sb.toString();
}

void IRInst::dump()
{
String s;
dump(s);
std::cout << s.begin() << std::endl;
}
} // namespace Slang

Expand Down
6 changes: 5 additions & 1 deletion source/slang/slang-ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -813,10 +813,14 @@ struct IRInst
///
void _insertAt(IRInst* inPrev, IRInst* inNext, IRInst* inParent);

/// Print the IR to stdout for debugging purposes
/// Print the IR to stdout for debugging purposes.
///
void dump();

/// Print the IR to a string for debugging purposes.
///
void dump(String& outStr);

/// Insert a basic block at the end of this func/code containing inst.
void addBlock(IRBlock* block);

Expand Down
84 changes: 83 additions & 1 deletion source/slang/slang-options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ void initCommandOptions(CommandOptions& options)
{OptionKind::VerifyDebugSerialIr,
"-verify-debug-serial-ir",
nullptr,
"Verify IR in the front-end."}};
"Verify IR in the front-end."},
{OptionKind::DumpModule, "-dump-module", nullptr, "Disassemble and print the module IR."}};
_addOptions(makeConstArrayView(debuggingOpts), options);

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Experimental !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
Expand Down Expand Up @@ -2947,6 +2948,87 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
Int index = 0;
SLANG_RETURN_ON_FAIL(_expectInt(arg, index));
linkage->m_optionSet.add(OptionKind::BindlessSpaceIndex, (int)index);
break;
}
case OptionKind::DumpModule:
{
CommandLineArg fileName;
SLANG_RETURN_ON_FAIL(m_reader.expectArg(fileName));
auto desc = slang::SessionDesc();
ComPtr<slang::ISession> session;
m_session->createSession(desc, session.writeRef());
ComPtr<slang::IBlob> diagnostics;

// Coerce Slang to load from the given file, without letting it automatically
// choose .slang-module files over .slang files.
// First try to load as source string, and fall back to loading as an IR Blob.
// Avoid guessing based on filename or inspect the file contents.
FILE* file;
fopen_s(&file, fileName.value.getBuffer(), "rb");
if (!file)
{
m_sink->diagnose(arg.loc, Diagnostics::cannotOpenFile, fileName.value);
return SLANG_FAIL;
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);
std::vector<char> buffer(size + 1);
size_t result = fread(buffer.data(), 1, size, file);
if (result != size)
{
m_sink->diagnoseRaw(Severity::Error, "Failed to read file");
return SLANG_FAIL;
}
buffer[size] = 0;
fclose(file);

ComPtr<slang::IModule> module;
module = session->loadModuleFromSourceString(
"module",
"path",
buffer.data(),
diagnostics.writeRef());
if (!module)
{
// Load buffer as an IR blob
ComPtr<slang::IBlob> blob;
blob = RawBlob::create(buffer.data(), size);

module = session->loadModuleFromIRBlob(
"module",
"path",
blob,
diagnostics.writeRef());
}

if (module)
{
ComPtr<slang::IBlob> disassemblyBlob;
if (SLANG_FAILED(module->disassemble(disassemblyBlob.writeRef())))
{
m_sink->diagnose(arg.loc, Diagnostics::cannotDisassemble, fileName.value);
return SLANG_FAIL;
}
else
{
m_sink->diagnoseRaw(
Severity::Note,
(const char*)disassemblyBlob->getBufferPointer());
}
}
else
{
if (diagnostics)
{
m_sink->diagnoseRaw(
Severity::Error,
(const char*)diagnostics->getBufferPointer());
}
return SLANG_FAIL;
}


break;
}
default:
Expand Down
53 changes: 53 additions & 0 deletions tests/ir/dump-module.slang
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// This tests slangc's -dump-module command.
// Dumping ".slang" should mean just that, and it should not automatically load up
// a ".slang-module" when available, because the intent of the -dump-module command
// is to see the file you requested. If there's a bug in slang-module output, it's
// important that -dump-module looks at the specific file you requested.

//TEST:COMPILE: tests/ir/dump-module.slang -o tests/ir/dump-module.slang-module -target spirv -embed-downstream-ir

//TEST:SIMPLE(filecheck=CHECK1): -dump-module tests/ir/dump-module.slang-module
//TEST:SIMPLE(filecheck=CHECK2): -dump-module tests/ir/dump-module.slang

module "export-library-generics";

public cbuffer Constants {
public float x;
public float y;
}

interface MyInterface
{
int myMethod(int a);
}

struct MyType : MyInterface
{
int myMethod(int a)
{
return a * 3;
}
}

int genericFunc<T: MyInterface>(T arg)
{
return arg.myMethod(3);
}

public int normalFuncUsesGeneric(int a)
{
MyType obj;
return genericFunc(obj);
}

public int normalFunc(int a, float b)
{
return a - floor(b);
}

// CHECK1:EmbeddedDownstreamIR(6 : Int,
// CHECK1: OpCapability Linkage

// CHECK2-NOT:EmbeddedDownstreamIR(6 : Int,
// CHECK2-NOT: OpCapability Linkage

Loading