diff options
| author | Yong He <yonghe@outlook.com> | 2025-04-28 11:42:22 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-28 11:42:22 -0700 |
| commit | c39c29bf4c52a85d7c83cc8b66ae45e265f9e078 (patch) | |
| tree | 969339828d49d7db92ed9294a17bd34cc021db84 /tools/slang-test | |
| parent | 8f6c6e333c06ae1c3b9f00396563c14a2ae09b4d (diff) | |
Add Slang Byte Code generation and interpreter. (#6896)
* Add Slang Byte Code generation and interpreter.
* Fix compile issues.
* format code
* More compile fix.
* Fix clang issue.
* Fix more clang issues.
* Another clang fix.
* Fix clang issues.
* Fix another clang issue.
* Fix wasm build.
* Update building.md
* Fix test-server.
* Fix compile error.
* Fix bug.
---------
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
Diffstat (limited to 'tools/slang-test')
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 83 | ||||
| -rw-r--r-- | tools/slang-test/slangi-tool-impl.h | 234 | ||||
| -rw-r--r-- | tools/slang-test/slangi-tool.cpp | 10 | ||||
| -rw-r--r-- | tools/slang-test/slangi-tool.h | 19 |
4 files changed, 344 insertions, 2 deletions
diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 3f7e41cf6..c0697b4a4 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -28,6 +28,7 @@ #include "options.h" #include "parse-diagnostic-util.h" #include "slangc-tool.h" +#include "slangi-tool.h" #include "test-context.h" #include "test-reporter.h" @@ -860,7 +861,7 @@ Result spawnAndWaitSharedLibrary( stdWriters.setWriter(SLANG_WRITER_CHANNEL_STD_ERROR, &stdError); stdWriters.setWriter(SLANG_WRITER_CHANNEL_STD_OUTPUT, &stdOut); - if (exeName == "slangc") + if (exeName == "slangc" || exeName == "slangi") { stdWriters.setWriter(SLANG_WRITER_CHANNEL_DIAGNOSTIC, &stdError); } @@ -902,7 +903,7 @@ Result spawnAndWaitProxy( // Get the name of the thing to execute String exeName = Path::getFileNameWithoutExt(inCmdLine.m_executableLocation.m_pathOrName); - if (exeName == "slangc") + if (exeName == "slangc" || exeName == "slangi") { // If the test is slangc there is a command line version we can just directly use // return spawnAndWaitExe(context, testPath, inCmdLine, outRes); @@ -1065,6 +1066,7 @@ static PassThroughFlags _getPassThroughFlagsForTarget(SlangCompileTarget target) case SLANG_CUDA_SOURCE: case SLANG_METAL: case SLANG_WGSL: + case SLANG_HOST_VM: { return 0; } @@ -1303,6 +1305,10 @@ static SlangResult _extractTestRequirements(const CommandLine& cmdLine, TestRequ { return _extractSlangCTestRequirements(cmdLine, ioInfo); } + else if (exeName == "slangi") + { + return SLANG_OK; + } else if (exeName == "slang-reflection-test") { return _extractReflectionTestRequirements(cmdLine, ioInfo); @@ -1562,6 +1568,12 @@ String findExpectedPath(const TestInput& input, const char* postFix) return ""; } +static SlangResult _initSlangInterpreter(TestContext* context, CommandLine& ioCmdLine) +{ + ioCmdLine.setExecutableLocation(ExecutableLocation(context->options.binDir, "slangi")); + return SLANG_OK; +} + static SlangResult _initSlangCompiler(TestContext* context, CommandLine& ioCmdLine) { ioCmdLine.setExecutableLocation(ExecutableLocation(context->options.binDir, "slangc")); @@ -2373,6 +2385,67 @@ TestResult runSimpleLineTest(TestContext* context, TestInput& input) return _validateOutput(context, input, actualOutput, false); } +TestResult runInterpreterTest(TestContext* context, TestInput& input) +{ + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we + // expect + auto outputStem = input.outputStem; + + CommandLine cmdLine; + + List<String> args; + + for (Index i = 0; i < input.testOptions->args.getCount(); i++) + { + auto& arg = input.testOptions->args[i]; + if (arg == "-disasm") + cmdLine.addArg(arg); + else if (arg == "-entry") + { + cmdLine.addArg(arg); + i++; + if (i < input.testOptions->args.getCount()) + { + cmdLine.addArg(input.testOptions->args[i]); + } + } + else + { + args.add(arg); + } + } + + cmdLine.addArg(input.filePath); + + for (auto arg : args) + { + cmdLine.addArg(arg); + } + + if (SLANG_FAILED(_initSlangInterpreter(context, cmdLine))) + { + return TestResult::Ignored; + } + + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); + + if (context->isCollectingRequirements()) + { + return TestResult::Pass; + } + + String actualOutput = getOutput(exeRes); + + return _validateOutput( + context, + input, + actualOutput, + false, + "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n", + [&input](auto e, auto a) { return _areResultsEqual(input.testOptions->type, e, a); }); +} + TestResult runCompile(TestContext* context, TestInput& input) { auto outputStem = input.outputStem; @@ -3952,6 +4025,7 @@ static const TestCommandInfo s_testCommandInfos[] = { {"SIMPLE", &runSimpleTest, 0}, {"SIMPLE_EX", &runSimpleTest, 0}, {"SIMPLE_LINE", &runSimpleLineTest, 0}, + {"INTERPRET", &runInterpreterTest, 0}, {"REFLECTION", &runReflectionTest, 0}, {"CPU_REFLECTION", &runReflectionTest, 0}, {"COMMAND_LINE_SIMPLE", &runSimpleCompareCommandLineTest, 0}, @@ -4851,6 +4925,11 @@ SlangResult innerMain(int argc, char** argv) context.setInnerMainFunc("slangc", &SlangCTool::innerMain); } + { + // We can set the slangc command line tool, to just use the function defined here + context.setInnerMainFunc("slangi", &SlangITool::innerMain); + } + SLANG_RETURN_ON_FAIL( Options::parse(argc, argv, &categorySet, StdWriters::getError(), &context.options)); diff --git a/tools/slang-test/slangi-tool-impl.h b/tools/slang-test/slangi-tool-impl.h new file mode 100644 index 000000000..b442800f2 --- /dev/null +++ b/tools/slang-test/slangi-tool-impl.h @@ -0,0 +1,234 @@ +namespace SlangITool +{ +static void printCallback(const char* message, void* userData) +{ + auto stdWriters = (StdWriters*)userData; + if (stdWriters) + { + stdWriters->getOut().print("%s", message); + } +} + +static SlangResult compileAndInterpret( + slang::IGlobalSession* sharedSession, + StdWriters* stdWriters, + UnownedStringSlice fileName, + const char* entryPointName, + bool disasm, + int argc, + const char* const* argv) +{ + auto maybePrintDiagnostic = [&](const ComPtr<slang::IBlob>& diagnosticBlob) + { + if (diagnosticBlob) + { + const char* diagText = (const char*)diagnosticBlob->getBufferPointer(); + stdWriters->getError().print("%s\n", diagText); + } + }; + + ComPtr<slang::IGlobalSession> globalSession; + SLANG_RETURN_ON_FAIL(slang_createGlobalSession(SLANG_API_VERSION, globalSession.writeRef())); + slang::TargetDesc targetDesc = {}; + targetDesc.format = SLANG_HOST_VM; + slang::SessionDesc sessionDesc = {}; + sessionDesc.targetCount = 1; + sessionDesc.targets = &targetDesc; + sessionDesc.compilerOptionEntryCount = 0; + String pathName = Path::getParentDirectory(fileName); + String moduleName = Path::getFileNameWithoutExt(fileName); + const char* searchPaths[] = {pathName.getBuffer()}; + if (pathName.getLength()) + { + sessionDesc.searchPathCount = 1; + sessionDesc.searchPaths = searchPaths; + } + ComPtr<slang::ISession> session; + SLANG_RETURN_ON_FAIL(globalSession->createSession(sessionDesc, session.writeRef())); + + ComPtr<slang::IBlob> diagnosticBlob; + auto module = session->loadModule(moduleName.getBuffer(), diagnosticBlob.writeRef()); + if (!module) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + ComPtr<slang::IEntryPoint> entryPoint; + if (SLANG_FAILED(module->findAndCheckEntryPoint( + entryPointName, + SLANG_STAGE_DISPATCH, + entryPoint.writeRef(), + diagnosticBlob.writeRef()))) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + + ComPtr<slang::IComponentType> compositeComponent; + slang::IComponentType* components[] = {module, entryPoint.get()}; + if (SLANG_FAILED(session->createCompositeComponentType( + components, + 2, + compositeComponent.writeRef(), + diagnosticBlob.writeRef()))) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + + ComPtr<slang::IComponentType> linkedProgram; + if (SLANG_FAILED(compositeComponent->link(linkedProgram.writeRef(), diagnosticBlob.writeRef()))) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + ComPtr<slang::IBlob> code; + + if (SLANG_FAILED(linkedProgram->getTargetCode(0, code.writeRef(), diagnosticBlob.writeRef()))) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + + if (code->getBufferSize() == 0) + { + return SLANG_FAIL; + } + + if (disasm) + { + ComPtr<slang::IBlob> disasmBlob; + if (SLANG_FAILED(slang_disassembleByteCode(code, disasmBlob.writeRef()))) + { + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + const char* disasmText = (const char*)disasmBlob->getBufferPointer(); + stdWriters->getOut().print("%s\n", disasmText); + return SLANG_OK; + } + + // Create a byte code runner and interpret the code. + ComPtr<slang::IByteCodeRunner> runner; + slang::ByteCodeRunnerDesc runnerDesc = {}; + SLANG_RETURN_ON_FAIL(slang_createByteCodeRunner(&runnerDesc, runner.writeRef())); + runner->setPrintCallback(printCallback, stdWriters); + + if (SLANG_FAILED(runner->loadModule(code))) + { + runner->getErrorString(diagnosticBlob.writeRef()); + maybePrintDiagnostic(diagnosticBlob); + } + auto funcIndex = runner->findFunctionByName(entryPointName); + if (funcIndex < 0) + { + stdWriters->getError().print("Function '%s' not found in byte code.\n", entryPointName); + return SLANG_FAIL; + } + + if (SLANG_FAILED(runner->selectFunctionByIndex((uint32_t)funcIndex))) + { + runner->getErrorString(diagnosticBlob.writeRef()); + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + + struct Arguments + { + uint32_t argc; + const char* const* argv; + }; + Arguments args; + args.argc = argc; + args.argv = argv; + void* arguments = nullptr; + size_t argSize = 0; + slang::ByteCodeFuncInfo funcInfo; + if (SLANG_FAILED(runner->getFunctionInfo((uint32_t)funcIndex, &funcInfo))) + { + runner->getErrorString(diagnosticBlob.writeRef()); + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + if (funcInfo.parameterCount == 2) + { + arguments = &args; + argSize = sizeof(Arguments); + } + if (SLANG_FAILED(runner->execute(arguments, argSize))) + { + runner->getErrorString(diagnosticBlob.writeRef()); + maybePrintDiagnostic(diagnosticBlob); + return SLANG_FAIL; + } + return SLANG_OK; +} + +SlangResult innerMain( + StdWriters* stdWriters, + slang::IGlobalSession* sharedSession, + int argc, + const char* const* argv) +{ + StdWriters::setSingleton(stdWriters); + + // Assume we will used the shared session + ComPtr<slang::IGlobalSession> session(sharedSession); + + // The sharedSession always has a pre-loaded core module. + // This differed test checks if the command line has an option to setup the core module. + // If so we *don't* use the sharedSession, and create a new session without the core module just + // for this compilation. + if (TestToolUtil::hasDeferredCoreModule(Index(argc - 1), argv + 1)) + { + SLANG_RETURN_ON_FAIL( + slang_createGlobalSessionWithoutCoreModule(SLANG_API_VERSION, session.writeRef())); + } + + String entryPointName = toSlice("main"); + UnownedStringSlice fileName; + bool disasm = false; + int innerArgIndex = 0; + if (argc < 2) + { + return SLANG_FAIL; + } + for (auto i = 1; i < argc; i++) + { + auto arg = UnownedStringSlice(argv[i]); + if (arg == "-entry") + { + entryPointName = UnownedStringSlice(argv[++i]); + } + else if (arg == "-disasm") + { + disasm = true; + } + else if (arg.startsWith("-")) + { + return SLANG_FAIL; + } + else + { + fileName = arg; + innerArgIndex = i; + break; + } + } + if (!fileName.getLength()) + { + return SLANG_FAIL; + } + + auto result = compileAndInterpret( + session, + stdWriters, + fileName, + entryPointName.getBuffer(), + disasm, + argc - innerArgIndex, + argv + innerArgIndex); + + return result; +} +} // namespace SlangITool diff --git a/tools/slang-test/slangi-tool.cpp b/tools/slang-test/slangi-tool.cpp new file mode 100644 index 000000000..ef9d311f7 --- /dev/null +++ b/tools/slang-test/slangi-tool.cpp @@ -0,0 +1,10 @@ +// test-context.cpp +#include "slangi-tool.h" + +#include "../../source/core/slang-exception.h" +#include "../../source/core/slang-io.h" +#include "../../source/core/slang-test-tool-util.h" + +using namespace Slang; + +#include "slangi-tool-impl.h" diff --git a/tools/slang-test/slangi-tool.h b/tools/slang-test/slangi-tool.h new file mode 100644 index 000000000..1cba47018 --- /dev/null +++ b/tools/slang-test/slangi-tool.h @@ -0,0 +1,19 @@ +// slangi-tool.h + +#ifndef SLANGI_TOOL_H_INCLUDED +#define SLANGI_TOOL_H_INCLUDED + +#include "../../source/core/slang-std-writers.h" + +/* The slangi 'tool' interface, such that slangc like functionality is available directly without +invoking slangc command line tool, or need for a dll/shared library. */ +namespace SlangITool +{ +SlangResult innerMain( + Slang::StdWriters* stdWriters, + SlangSession* session, + int argc, + const char* const* argv); +}; + +#endif // SLANGI_TOOL_H_INCLUDED |
