diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-08-14 12:57:33 -0400 |
|---|---|---|
| committer | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-08-14 09:57:33 -0700 |
| commit | dc493d492d4d9c090dab410a0cb80eca490c32aa (patch) | |
| tree | f0de85ae64d53afd9b9144df23d2450436565d20 | |
| parent | ea200663ffe33d7b4c739c0d84e9c21a3ae2ffa6 (diff) | |
Small improvements around C/C++ testing (#1017)
* * Simplify some of test code around CPPCompiler
* Test using 'callable' with pass-through
* Small cpu doc improvements
* Improvements to Clang output parsing.
* Remove temporary file (base filename) .
* Improve handling of external errors - handle severity.
* On error dumping out to 'actual' file for runCPPCompilerCompile.
* Small fixes.
Set the source language type correctly for pass thru.
* Remove warning for test for clang backend c
| -rw-r--r-- | docs/cpu-target.md | 9 | ||||
| -rw-r--r-- | source/core/slang-gcc-compiler-util.cpp | 21 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 42 | ||||
| -rw-r--r-- | tests/cpp-compiler/c-compile-pass-through-shared-library.c | 24 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 240 | ||||
| -rw-r--r-- | tools/slang-test/test-context.cpp | 7 | ||||
| -rw-r--r-- | tools/slang-test/test-context.h | 1 |
7 files changed, 207 insertions, 137 deletions
diff --git a/docs/cpu-target.md b/docs/cpu-target.md index f86b7d6c5..a0b4f0180 100644 --- a/docs/cpu-target.md +++ b/docs/cpu-target.md @@ -15,10 +15,9 @@ Slang has preliminary support for producing CPU source and binaries. These limitations apply to Slang source, with C/C++ the limitations are whatever the compiler requires -* Only supports 64 bit targets (specifically it assumes all pointers are 64 bit) * Barriers are not supported (making these work would require an ABI change) * Atomics are not supported -* Complex resource types (such as say Texture2d) are work in progress +* Complex resource types (such as Texture2d) are work in progress * Out of bounds access to resources has undefined behavior * ParameterBlocks are not currently supported @@ -37,7 +36,9 @@ SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C or C++ compiler, whic Sometimes it is not important which C/C++ compiler is used, and this can be specified via the 'Generic C/C++' option. This will aim to use the compiler that is most likely binary compatible with the compiler that was used to build the slang binary being used. -To make it possible for slang to produce CPU code, we now need a mechanism to convert slang code into C/C++. The first iteration only supports C++ generation. If source is desired instead of a binary this can be specified via the SlangCompileTarget. These can be specified on the slangc command line as `-target c` or `-target cpp` +To make it possible for slang to produce CPU code, we now need a mechanism to convert slang code into C/C++. The first iteration only supports C++ generation. If source is desired instead of a binary this can be specified via the SlangCompileTarget. These can be specified on the slangc command line as `-target c` or `-target cpp`. + +Note that when using the 'pass through' mode for a CPU based target it is currently necessary to set an entry point, even though it's basically ignored. In the API the `SlangCompileTarget`s are @@ -65,7 +66,7 @@ Under the covers when slang is used to generate a binary via a C/C++ compiler, i Executing CPU Code ================== -In typically slang operation when code is compiled it produces either source or a binary that can then be loaded by another API such as a rendering API. With CPU code the binary produced could be saved to a file and then executed as an exe or a shared library/dll. In practice though it is not uncommon to want to be able to execute compiled code immediately. Having to save off to a file and then load again can be awkward. It is also not necessarily the case that code needs to be saved to a file to be executed. +In typical slang operation when code is compiled it produces either source or a binary that can then be loaded by another API such as a rendering API. With CPU code the binary produced could be saved to a file and then executed as an exe or a shared library/dll. In practice though it is not uncommon to want to be able to execute compiled code immediately. Having to save off to a file and then load again can be awkward. It is also not necessarily the case that code needs to be saved to a file to be executed. To handle being able call code directly, code can be compiled using the SLANG_HOST_CALLABLE code target type. To access the code that has been produced use the function diff --git a/source/core/slang-gcc-compiler-util.cpp b/source/core/slang-gcc-compiler-util.cpp index c48e779d1..b7cc85cb3 100644 --- a/source/core/slang-gcc-compiler-util.cpp +++ b/source/core/slang-gcc-compiler-util.cpp @@ -184,6 +184,16 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse outLineParseResult = LineParseResult::Start; return SLANG_OK; } + + if (SLANG_SUCCEEDED(_parseErrorType(split0, outMsg.type))) + { + // Command line errors can be just contain 'error:' etc. Can be seen on apple/clang + outMsg.stage = OutputMessage::Stage::Compile; + outMsg.text = split[1].trim(); + outLineParseResult = LineParseResult::Single; + return SLANG_OK; + } + outLineParseResult = LineParseResult::Ignore; return SLANG_OK; } @@ -193,8 +203,9 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse const auto split1 = split[1].trim(); const auto text = split[2].trim(); - // Check for special handling for clang - if (split0.startsWith(UnownedStringSlice::fromLiteral("clang"))) + // Check for special handling for clang (Can be Clang or clang apparently) + if (split0.startsWith(UnownedStringSlice::fromLiteral("clang")) || + split0.startsWith(UnownedStringSlice::fromLiteral("Clang")) ) { // Extract the type SLANG_RETURN_ON_FAIL(_parseErrorType(split[1].trim(), outMsg.type)); @@ -387,11 +398,11 @@ static SlangResult _parseGCCFamilyLine(const UnownedStringSlice& line, LineParse /* static */SlangResult GCCCompilerUtil::calcArgs(const CompileOptions& options, CommandLine& cmdLine) { PlatformKind platformKind = (options.platform == PlatformKind::Unknown) ? PlatformUtil::getPlatformKind() : options.platform; - - cmdLine.addArg("-fvisibility=hidden"); - + if (options.sourceType == SourceType::CPP) { + cmdLine.addArg("-fvisibility=hidden"); + // Need C++14 for partial specialization cmdLine.addArg("-std=c++14"); } diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 8ead63784..5b8643b02 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -768,7 +768,7 @@ namespace Slang return result; } - void reportExternalCompileError(const char* compilerName, SlangResult res, const UnownedStringSlice& diagnostic, DiagnosticSink* sink) + void reportExternalCompileError(const char* compilerName, Severity severity, SlangResult res, const UnownedStringSlice& diagnostic, DiagnosticSink* sink) { StringBuilder builder; if (compilerName) @@ -792,10 +792,15 @@ namespace Slang PlatformUtil::appendResult(res, builder); } + sink->diagnoseRaw(severity, builder.getUnownedSlice()); + } + + void reportExternalCompileError(const char* compilerName, SlangResult res, const UnownedStringSlice& diagnostic, DiagnosticSink* sink) + { // TODO(tfoley): need a better policy for how we translate diagnostics // back into the Slang world (although we should always try to generate // HLSL that doesn't produce any diagnostics...) - sink->diagnoseRaw(SLANG_FAILED(res) ? Severity::Error : Severity::Warning, builder.getUnownedSlice()); + reportExternalCompileError(compilerName, SLANG_FAILED(res) ? Severity::Error : Severity::Warning, res, diagnostic, sink); } static String _getDisplayPath(DiagnosticSink* sink, SourceFile* sourceFile) @@ -1341,10 +1346,16 @@ SlangResult dissassembleDXILUsingDXC( CPPCompiler::CompileOptions options; + // Set the source type + options.sourceType = (rawSourceLanguage == SourceLanguage::C) ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP; + // Generate a path a temporary filename for output module String modulePath; SLANG_RETURN_ON_FAIL(File::generateTemporary(UnownedStringSlice::fromLiteral("slang-generated"), modulePath)); + // Remove the temporary path/file when done + temporaryFileSet.add(modulePath); + options.modulePath = modulePath; options.sourceFiles.add(compileSourcePath); @@ -1483,17 +1494,36 @@ SlangResult dissassembleDXILUsingDXC( builder << "link "; } + // + Severity severity = Severity::Error; + switch (msg.type) { - case OutputMessage::Type::Error: builder << "error"; break; - case OutputMessage::Type::Unknown: builder << "warning"; break; - case OutputMessage::Type::Info: builder << "info"; break; + case OutputMessage::Type::Unknown: + case OutputMessage::Type::Error: + { + severity = Severity::Error; + builder << "error"; + break; + } + case OutputMessage::Type::Warning: + { + severity = Severity::Warning; + builder << "warning"; + break; + } + case OutputMessage::Type::Info: + { + severity = Severity::Note; + builder << "info"; + break; + } default: break; } builder << " " << msg.code << ": " << msg.text; - reportExternalCompileError(compilerText.getBuffer(), SLANG_OK, builder.getUnownedSlice(), sink); + reportExternalCompileError(compilerText.getBuffer(), severity, SLANG_OK, builder.getUnownedSlice(), sink); } } diff --git a/tests/cpp-compiler/c-compile-pass-through-shared-library.c b/tests/cpp-compiler/c-compile-pass-through-shared-library.c new file mode 100644 index 000000000..9f33bc430 --- /dev/null +++ b/tests/cpp-compiler/c-compile-pass-through-shared-library.c @@ -0,0 +1,24 @@ +//TEST(smoke,shared-library):CPP_COMPILER_COMPILE: -pass-through c -entry test -target callable + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#if defined(_MSC_VER) +# define DLL_EXPORT __declspec(dllexport) +#else +# define DLL_EXPORT __attribute__((__visibility__("default"))) +#endif + +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif + +EXTERN_C DLL_EXPORT int test(int intValue, const char* textValue, char* outTextValue) +{ + strcpy(outTextValue, textValue); + return intValue; +} + diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 8137733ee..99a139e3c 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -1326,11 +1326,18 @@ static String _calcSummary(const CPPCompiler::Output& inOutput) return builder; } -static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) +static String _calcModulePath(const TestInput& input) { - CPPCompilerSet* compilerSet = context->getCPPCompilerSet(); - CPPCompiler* compiler = compilerSet ? compilerSet->getDefaultCompiler() : nullptr; + // Make the module name the same as the source file + auto filePath = input.filePath; + String directory = Path::getParentDirectory(input.outputStem); + String moduleName = Path::getFileNameWithoutExt(filePath); + return Path::combine(directory, moduleName); +} +static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) +{ + CPPCompiler* compiler = context->getDefaultCPPCompiler(); if (!compiler) { return TestResult::Ignored; @@ -1344,7 +1351,6 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) _initSlangCompiler(context, cmdLine); cmdLine.addArg(input.filePath); - for (auto arg : input.testOptions->args) { cmdLine.addArg(arg); @@ -1352,29 +1358,26 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) ExecuteResult exeRes; TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); - if (context->isCollectingRequirements()) { return TestResult::Pass; } + // Dump out what happened + { + String actualOutputPath = outputStem + ".actual"; + Slang::File::writeAllText(actualOutputPath, getOutput(exeRes)); + } + if (exeRes.resultCode != 0) { return TestResult::Fail; } - String actualOutput = exeRes.standardOutput; + String modulePath = _calcModulePath(input); - // Make the module name the same as the source file - auto filePath = input.filePath; - String directory = Path::getParentDirectory(input.outputStem); - String moduleName = Path::getFileNameWithoutExt(filePath); - String ext = Path::getFileExt(filePath); - String modulePath = Path::combine(directory, moduleName); - - UnownedStringSlice targetExt = UnownedStringSlice::fromLiteral("c"); - // Find the target + UnownedStringSlice targetExt = UnownedStringSlice::fromLiteral("c"); Index index = cmdLine.findArgIndex(UnownedStringSlice::fromLiteral("-target")); if (index >= 0 && index + 1 < cmdLine.getArgCount()) { @@ -1389,6 +1392,8 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) options.includePaths.add("tests/cross-compile"); + String actualOutput = exeRes.standardOutput; + // Create a filename to write this out to String cppSource = modulePath + "." + targetExt; Slang::File::writeAllText(cppSource, actualOutput); @@ -1420,11 +1425,9 @@ static TestResult runCPPCompilerCompile(TestContext* context, TestInput& input) return TestResult::Pass; } -static TestResult runCPPCompilerExecute(TestContext* context, TestInput& input) +static TestResult runCPPCompilerSharedLibrary(TestContext* context, TestInput& input) { - CPPCompilerSet* compilerSet = context->getCPPCompilerSet(); - CPPCompiler* compiler = compilerSet ? compilerSet->getDefaultCompiler() : nullptr; - + CPPCompiler* compiler = context->getDefaultCPPCompiler(); if (!compiler) { return TestResult::Ignored; @@ -1436,101 +1439,110 @@ static TestResult runCPPCompilerExecute(TestContext* context, TestInput& input) return TestResult::Pass; } - auto filePath = input.filePath; auto outputStem = input.outputStem; + auto filePath = input.filePath; String actualOutputPath = outputStem + ".actual"; File::remove(actualOutputPath); // Make the module name the same as the source file - String directory = Path::getParentDirectory(input.outputStem); - String moduleName = Path::getFileNameWithoutExt(filePath); + String modulePath = _calcModulePath(input); String ext = Path::getFileExt(filePath); - String modulePath = Path::combine(directory, moduleName); // Remove the binary.. - { - StringBuilder moduleExePath; - moduleExePath << modulePath; - moduleExePath << ProcessUtil::getExecutableSuffix(); - File::remove(moduleExePath); - } + String sharedLibraryPath = SharedLibrary::calcPlatformPath(modulePath.getUnownedSlice()); + File::remove(sharedLibraryPath); // Set up the compilation options CPPCompiler::CompileOptions options; options.sourceType = (ext == "c") ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP; + // Build a shared library + options.targetType = CPPCompiler::TargetType::SharedLibrary; + // Compile this source options.sourceFiles.add(filePath); options.modulePath = modulePath; + options.includePaths.add("."); + CPPCompiler::Output output; if (SLANG_FAILED(compiler->compile(options, output))) { return TestResult::Fail; } - String actualOutput; - - // If the actual compilation failed, then the output will be if (SLANG_FAILED(output.result)) { - actualOutput = _calcSummary(output); - } - else - { - // Execute the binary and see what we get - - CommandLine cmdLine; + // Compilation failed + String actualOutput = _calcSummary(output); - StringBuilder exePath; - exePath << modulePath << ProcessUtil::getExecutableSuffix(); + // Write the output + Slang::File::writeAllText(actualOutputPath, actualOutput); - cmdLine.setExecutablePath(exePath); + // Check that they are the same + { + // Read the expected + String expectedOutput; + try + { + String expectedOutputPath = outputStem + ".expected"; + expectedOutput = Slang::File::readAllText(expectedOutputPath); + } + catch (Slang::IOException) + { + } - ExecuteResult exeRes; - if (SLANG_FAILED(ProcessUtil::execute(cmdLine, exeRes))) + // Compare if they are the same + if (!StringUtil::areLinesEqual(actualOutput.getUnownedSlice(), expectedOutput.getUnownedSlice())) + { + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); + return TestResult::Fail; + } + } + } + else + { + SharedLibrary::Handle handle; + if (SLANG_FAILED(SharedLibrary::loadWithPlatformPath(sharedLibraryPath.getBuffer(), handle))) { return TestResult::Fail; } - // Write the output, and compare to expected - actualOutput = getOutput(exeRes); - } + const int inValue = 10; + const char inBuffer[] = "Hello World!"; - // Write the output - Slang::File::writeAllText(actualOutputPath, actualOutput); + char buffer[128] = ""; + int value = 0; - // Check that they are the same - { - // Read the expected - String expectedOutput; - try + typedef int(*TestFunc)(int intValue, const char* textValue, char* outTextValue); + + // We could capture output if we passed in a ISlangWriter - but for that to work we'd need a + TestFunc testFunc = (TestFunc)SharedLibrary::findFuncByName(handle, "test"); + if (testFunc) { - String expectedOutputPath = outputStem + ".expected"; - expectedOutput = Slang::File::readAllText(expectedOutputPath); + value = testFunc(inValue, inBuffer, buffer); } - catch (Slang::IOException) + else { + printf("Unable to access 'test' function\n"); } - // Compare if they are the same - if (!StringUtil::areLinesEqual(actualOutput.getUnownedSlice(), expectedOutput.getUnownedSlice())) + SharedLibrary::unload(handle); + + if (!(inValue == value && strcmp(inBuffer, buffer) == 0)) { - context->reporter->dumpOutputDifference(expectedOutput, actualOutput); return TestResult::Fail; } } - + return TestResult::Pass; } -static TestResult runCPPCompilerSharedLibrary(TestContext* context, TestInput& input) +static TestResult runCPPCompilerExecute(TestContext* context, TestInput& input) { - CPPCompilerSet* compilerSet = context->getCPPCompilerSet(); - CPPCompiler* compiler = compilerSet ? compilerSet->getDefaultCompiler() : nullptr; - + CPPCompiler* compiler = context->getDefaultCPPCompiler(); if (!compiler) { return TestResult::Ignored; @@ -1549,100 +1561,84 @@ static TestResult runCPPCompilerSharedLibrary(TestContext* context, TestInput& i File::remove(actualOutputPath); // Make the module name the same as the source file - String directory = Path::getParentDirectory(input.outputStem); - String moduleName = Path::getFileNameWithoutExt(filePath); String ext = Path::getFileExt(filePath); - - String modulePath = Path::combine(directory, moduleName); - - // Remove the binary.. - String sharedLibraryPath = SharedLibrary::calcPlatformPath(modulePath.getUnownedSlice()); - File::remove(sharedLibraryPath); + String modulePath = _calcModulePath(input); + // Remove the binary.. + { + StringBuilder moduleExePath; + moduleExePath << modulePath; + moduleExePath << ProcessUtil::getExecutableSuffix(); + File::remove(moduleExePath); + } + // Set up the compilation options CPPCompiler::CompileOptions options; options.sourceType = (ext == "c") ? CPPCompiler::SourceType::C : CPPCompiler::SourceType::CPP; - - // Build a shared library - options.targetType = CPPCompiler::TargetType::SharedLibrary; // Compile this source options.sourceFiles.add(filePath); options.modulePath = modulePath; - options.includePaths.add("."); - CPPCompiler::Output output; if (SLANG_FAILED(compiler->compile(options, output))) { return TestResult::Fail; } + String actualOutput; + + // If the actual compilation failed, then the output will be if (SLANG_FAILED(output.result)) { - // Compilation failed - String actualOutput = _calcSummary(output); - - // Write the output - Slang::File::writeAllText(actualOutputPath, actualOutput); - - // Check that they are the same - { - // Read the expected - String expectedOutput; - try - { - String expectedOutputPath = outputStem + ".expected"; - expectedOutput = Slang::File::readAllText(expectedOutputPath); - } - catch (Slang::IOException) - { - } - - // Compare if they are the same - if (!StringUtil::areLinesEqual(actualOutput.getUnownedSlice(), expectedOutput.getUnownedSlice())) - { - context->reporter->dumpOutputDifference(expectedOutput, actualOutput); - return TestResult::Fail; - } - } + actualOutput = _calcSummary(output); } else { - SharedLibrary::Handle handle; - if (SLANG_FAILED(SharedLibrary::loadWithPlatformPath(sharedLibraryPath.getBuffer(), handle))) + // Execute the binary and see what we get + + CommandLine cmdLine; + + StringBuilder exePath; + exePath << modulePath << ProcessUtil::getExecutableSuffix(); + + cmdLine.setExecutablePath(exePath); + + ExecuteResult exeRes; + if (SLANG_FAILED(ProcessUtil::execute(cmdLine, exeRes))) { return TestResult::Fail; } - const int inValue = 10; - const char inBuffer[] = "Hello World!"; - - char buffer[128] = ""; - int value = 0; + // Write the output, and compare to expected + actualOutput = getOutput(exeRes); + } - typedef int (*TestFunc)(int intValue, const char* textValue, char* outTextValue); + // Write the output + Slang::File::writeAllText(actualOutputPath, actualOutput); - // We could capture output if we passed in a ISlangWriter - but for that to work we'd need a - TestFunc testFunc = (TestFunc)SharedLibrary::findFuncByName(handle, "test"); - if (testFunc) + // Check that they are the same + { + // Read the expected + String expectedOutput; + try { - value = testFunc(inValue, inBuffer, buffer); + String expectedOutputPath = outputStem + ".expected"; + expectedOutput = Slang::File::readAllText(expectedOutputPath); } - else + catch (Slang::IOException) { - printf("Unable to access 'test' function\n"); } - SharedLibrary::unload(handle); - - if (!(inValue == value && strcmp(inBuffer, buffer) == 0)) + // Compare if they are the same + if (!StringUtil::areLinesEqual(actualOutput.getUnownedSlice(), expectedOutput.getUnownedSlice())) { + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); return TestResult::Fail; } } - + return TestResult::Pass; } diff --git a/tools/slang-test/test-context.cpp b/tools/slang-test/test-context.cpp index 5581ab6db..5da998f03 100644 --- a/tools/slang-test/test-context.cpp +++ b/tools/slang-test/test-context.cpp @@ -102,3 +102,10 @@ CPPCompilerSet* TestContext::getCPPCompilerSet() } return cppCompilerSet; } + +Slang::CPPCompiler* TestContext::getDefaultCPPCompiler() +{ + CPPCompilerSet* set = getCPPCompilerSet(); + return set ? set->getDefaultCompiler() : nullptr; +} + diff --git a/tools/slang-test/test-context.h b/tools/slang-test/test-context.h index a66047687..66486e7bc 100644 --- a/tools/slang-test/test-context.h +++ b/tools/slang-test/test-context.h @@ -93,6 +93,7 @@ class TestContext /// Get compiler factory Slang::CPPCompilerSet* getCPPCompilerSet(); + Slang::CPPCompiler* getDefaultCPPCompiler(); /// Ctor TestContext(); |
