diff --git a/opencl/test/unit_test/offline_compiler/mock/mock_offline_compiler.h b/opencl/test/unit_test/offline_compiler/mock/mock_offline_compiler.h index c1f3a84ad8..a6d3aa22f0 100644 --- a/opencl/test/unit_test/offline_compiler/mock/mock_offline_compiler.h +++ b/opencl/test/unit_test/offline_compiler/mock/mock_offline_compiler.h @@ -24,6 +24,7 @@ class MockOfflineCompiler : public OfflineCompiler { using OfflineCompiler::allowCaching; using OfflineCompiler::appendExtraInternalOptions; using OfflineCompiler::argHelper; + using OfflineCompiler::binaryOutputFile; using OfflineCompiler::cache; using OfflineCompiler::compilerProductHelper; using OfflineCompiler::dbgHash; @@ -57,6 +58,7 @@ class MockOfflineCompiler : public OfflineCompiler { using OfflineCompiler::irBinarySize; using OfflineCompiler::irHash; using OfflineCompiler::isSpirV; + using OfflineCompiler::onlySpirV; using OfflineCompiler::options; using OfflineCompiler::outputDirectory; using OfflineCompiler::outputFile; diff --git a/opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.cpp b/opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.cpp index 1038c1726c..4cb37fbf7c 100644 --- a/opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.cpp +++ b/opencl/test/unit_test/offline_compiler/ocloc_fatbinary_tests.cpp @@ -29,6 +29,7 @@ extern Environment *gEnvironment; namespace NEO { +extern std::set virtualFileList; auto searchInArchiveByFilename(const Ar::Ar &archive, const ConstStringRef &name) { const auto isSearchedFile = [&name](const auto &file) { @@ -461,6 +462,46 @@ TEST_F(OclocFatBinaryProductAcronymsTests, givenProductsAcronymsWithoutDashesWhe EXPECT_STREQ(output.c_str(), resString.str().c_str()); } +TEST_F(OclocFatBinaryProductAcronymsTests, givenBinaryOutputNameOptionWhenBuildingThenCorrectFileIsCreated) { + if (enabledProductsAcronyms.size() < 2) { + GTEST_SKIP(); + } + for (unsigned int product = 0; product < enabledProductsAcronyms.size() - 1; product++) { + auto acronym0 = enabledProductsAcronyms.at(product); + auto acronym1 = enabledProductsAcronyms.at(product + 1); + std::vector expected{acronym0, acronym1}; + + std::string acronymsTarget = acronym0.str() + "," + acronym1.str(); + auto got = NEO::getTargetProductsForFatbinary(acronymsTarget, oclocArgHelperWithoutInput.get()); + EXPECT_EQ(got, expected); + + oclocArgHelperWithoutInput->getPrinterRef().setSuppressMessages(false); + std::stringstream resString; + std::vector argv = { + "ocloc", + "-o", + "expected_output.bin", + "-file", + clFiles + "copybuffer.cl", + "-device", + acronymsTarget}; + + testing::internal::CaptureStdout(); + int retVal = buildFatBinary(argv, oclocArgHelperWithoutInput.get()); + auto output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(retVal, NEO::OclocErrorCode::SUCCESS); + + EXPECT_EQ(1u, NEO::virtualFileList.size()); + EXPECT_TRUE(NEO::virtualFileList.find("expected_output.bin") != NEO::virtualFileList.end()); + + for (const auto &product : expected) { + resString << "Build succeeded for : " << product.str() + ".\n"; + } + + EXPECT_STREQ(output.c_str(), resString.str().c_str()); + } +} + TEST_F(OclocFatBinaryProductAcronymsTests, givenTwoSameReleaseTargetsWhenGetProductsAcronymsThenDuplicatesAreNotFound) { if (enabledReleasesAcronyms.empty()) { GTEST_SKIP(); diff --git a/opencl/test/unit_test/offline_compiler/offline_compiler_tests.cpp b/opencl/test/unit_test/offline_compiler/offline_compiler_tests.cpp index 6cdf73bc90..86bf03cb10 100644 --- a/opencl/test/unit_test/offline_compiler/offline_compiler_tests.cpp +++ b/opencl/test/unit_test/offline_compiler/offline_compiler_tests.cpp @@ -2884,6 +2884,51 @@ TEST(OfflineCompilerTest, givenNonEmptyOutputDirectoryWhenWritingOutAllFilesTheD EXPECT_EQ("12345678", outputFileIt->second); } +TEST(OfflineCompilerTest, givenBinaryOutputFileWhenWritingOutAllFilesThenOnlyBinaryWithCorrectNameIsCreated) { + MockOfflineCompiler mockOfflineCompiler{}; + mockOfflineCompiler.interceptCreatedDirs = true; + mockOfflineCompiler.uniqueHelper->interceptOutput = true; + + mockOfflineCompiler.binaryOutputFile = "some_output_filename.bin"; + mockOfflineCompiler.elfBinary = {49, 50, 51, 52, 53, 54, 55, 56}; // ASCII codes of "12345678" + mockOfflineCompiler.irBinary = new char[4]; + mockOfflineCompiler.irBinarySize = 4; + + mockOfflineCompiler.writeOutAllFiles(); + + const auto outputFileIt = mockOfflineCompiler.uniqueHelper->interceptedFiles.find("some_output_filename.bin"); + ASSERT_NE(mockOfflineCompiler.uniqueHelper->interceptedFiles.end(), outputFileIt); + + EXPECT_EQ("12345678", outputFileIt->second); + + const auto outputFileIt2 = mockOfflineCompiler.uniqueHelper->interceptedFiles.find("some_output_filename.spv"); + EXPECT_EQ(mockOfflineCompiler.uniqueHelper->interceptedFiles.end(), outputFileIt2); +} + +TEST(OfflineCompilerTest, givenBinaryOutputFileWithSpirvOnlyWhenWritingOutAllFilesThenOnlyBinaryWithCorrectNameIsCreated) { + MockOfflineCompiler mockOfflineCompiler{}; + mockOfflineCompiler.interceptCreatedDirs = true; + mockOfflineCompiler.uniqueHelper->interceptOutput = true; + + mockOfflineCompiler.binaryOutputFile = "some_output_filename.bin"; + mockOfflineCompiler.elfBinary = {0, 0, 0, 0, 0, 0}; + mockOfflineCompiler.irBinary = new char[4]; + mockOfflineCompiler.irBinarySize = 4; + uint8_t data[] = {49, 50, 51, 52}; // ASCII codes of "1234" + memcpy(mockOfflineCompiler.irBinary, data, sizeof(data)); + mockOfflineCompiler.onlySpirV = true; + + mockOfflineCompiler.writeOutAllFiles(); + + const auto outputFileIt = mockOfflineCompiler.uniqueHelper->interceptedFiles.find("some_output_filename.bin"); + ASSERT_NE(mockOfflineCompiler.uniqueHelper->interceptedFiles.end(), outputFileIt); + + EXPECT_EQ("1234", outputFileIt->second); + + const auto outputFileIt2 = mockOfflineCompiler.uniqueHelper->interceptedFiles.find("some_output_filename.spv"); + EXPECT_EQ(mockOfflineCompiler.uniqueHelper->interceptedFiles.end(), outputFileIt2); +} + TEST(OfflineCompilerTest, givenLlvmInputOptionPassedWhenCmdLineParsedThenInputFileLlvmIsSetTrue) { std::vector argv = { "ocloc", @@ -3146,6 +3191,84 @@ TEST(OfflineCompilerTest, givenOptionsWhenCmdLineParsedThenOptionsAreAppendedToO EXPECT_TRUE(hasSubstr(options, std::string("options2"))); } +TEST(OfflineCompilerTest, givenDashOOptionWhenCmdLineParsedThenBinaryOutputNameIsSet) { + std::vector argv = { + "ocloc", + "-file", + clFiles + "copybuffer.cl", + "-o", + "nameOfFile.bin", + "-device", + gEnvironment->devicePrefix.c_str()}; + + auto mockOfflineCompiler = std::unique_ptr(new MockOfflineCompiler()); + ASSERT_NE(nullptr, mockOfflineCompiler); + + testing::internal::CaptureStdout(); + auto retVal = mockOfflineCompiler->parseCommandLine(argv.size(), argv); + std::string output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(OclocErrorCode::SUCCESS, retVal); + EXPECT_EQ(0u, output.size()); + + EXPECT_EQ("nameOfFile.bin", mockOfflineCompiler->binaryOutputFile); +} + +TEST(OfflineCompilerTest, givenDashOAndOtherInvalidOptionsWhenCmdLineParsedThenErrorReturned) { + std::vector argv = { + "ocloc", + "-file", + clFiles + "copybuffer.cl", + "-o", + "nameOfFile.bin", + "-device", + gEnvironment->devicePrefix.c_str(), + "empty"}; + + std::vector options = { + "-gen_file", + "-cpp_file", + "-output_no_suffix"}; + + for (const auto &op : options) { + argv[argv.size() - 1] = op; + auto mockOfflineCompiler = std::unique_ptr(new MockOfflineCompiler()); + ASSERT_NE(nullptr, mockOfflineCompiler); + + testing::internal::CaptureStdout(); + auto retVal = mockOfflineCompiler->parseCommandLine(argv.size(), argv); + std::string output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, retVal); + EXPECT_NE(0u, output.size()); + + EXPECT_TRUE(hasSubstr(output, std::string("Error: options: -gen_file/-cpp_file/-output_no_suffix/-output cannot be used with -o\n"))); + } + + std::vector argv2 = { + "ocloc", + "-file", + clFiles + "copybuffer.cl", + "-o", + "nameOfFile.bin", + "-output", + "abc", + "-device", + gEnvironment->devicePrefix.c_str()}; + + auto mockOfflineCompiler = std::unique_ptr(new MockOfflineCompiler()); + ASSERT_NE(nullptr, mockOfflineCompiler); + + testing::internal::CaptureStdout(); + auto retVal = mockOfflineCompiler->parseCommandLine(argv2.size(), argv2); + std::string output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(OclocErrorCode::INVALID_COMMAND_LINE, retVal); + EXPECT_NE(0u, output.size()); + + EXPECT_TRUE(hasSubstr(output, std::string("Error: options: -gen_file/-cpp_file/-output_no_suffix/-output cannot be used with -o\n"))); +} + TEST(OfflineCompilerTest, givenInputOptionsAndInternalOptionsFilesWhenOfflineCompilerIsInitializedThenCorrectOptionsAreSetAndRemainAfterBuild) { auto mockOfflineCompiler = std::unique_ptr(new MockOfflineCompiler()); ASSERT_NE(nullptr, mockOfflineCompiler); diff --git a/shared/offline_compiler/source/ocloc_fatbinary.cpp b/shared/offline_compiler/source/ocloc_fatbinary.cpp index 86a7326f48..bb6aaf308a 100644 --- a/shared/offline_compiler/source/ocloc_fatbinary.cpp +++ b/shared/offline_compiler/source/ocloc_fatbinary.cpp @@ -311,7 +311,7 @@ int buildFatBinary(const std::vector &args, OclocArgHelper *argHelp } else if ((ConstStringRef("-file") == currArg) && hasMoreArgs) { inputFileName = args[argIndex + 1]; ++argIndex; - } else if ((ConstStringRef("-output") == currArg) && hasMoreArgs) { + } else if (((ConstStringRef("-output") == currArg) || (ConstStringRef("-o") == currArg)) && hasMoreArgs) { outputFileName = args[argIndex + 1]; ++argIndex; } else if ((ConstStringRef("-out_dir") == currArg) && hasMoreArgs) { diff --git a/shared/offline_compiler/source/offline_compiler.cpp b/shared/offline_compiler/source/offline_compiler.cpp index a33345f3f3..259713754b 100644 --- a/shared/offline_compiler/source/offline_compiler.cpp +++ b/shared/offline_compiler/source/offline_compiler.cpp @@ -677,6 +677,9 @@ int OfflineCompiler::parseCommandLine(size_t numArgs, const std::vectorprintf("Error: options: -gen_file/-cpp_file/-output_no_suffix/-output cannot be used with -o\n"); + retVal = INVALID_COMMAND_LINE; + return retVal; + } unifyExcludeIrFlags(); if (DebugManager.flags.OverrideRevision.get() != -1) { @@ -923,10 +931,14 @@ Usage: ocloc [compile] -file -device [-output can be: %s - can be single target device. + -o Optional output file name. + Must not be used with: + -gen_file | -cpp_file | -output_no_suffix | -output + -output Optional output file base name. Default is input file's base name. - This base name will be used for all output - files. Proper sufixes (describing file formats) + This base name will be used for all output files. + For single target device proper suffixes (describing file formats) will be added automatically. -out_dir Optional output directory. @@ -1141,6 +1153,16 @@ void OfflineCompiler::writeOutAllFiles() { } } + if (!binaryOutputFile.empty()) { + std::string outputFile = generateFilePath(outputDirectory, binaryOutputFile, ""); + if (isOnlySpirV()) { + argHelper->saveOutput(outputFile, irBinary, irBinarySize); + } else if (!elfBinary.empty()) { + argHelper->saveOutput(outputFile, elfBinary.data(), elfBinary.size()); + } + return; + } + if (irBinary && !inputFileSpirV) { std::string irOutputFileName = generateFilePathForIr(fileBase) + generateOptsSuffix(); diff --git a/shared/offline_compiler/source/offline_compiler.h b/shared/offline_compiler/source/offline_compiler.h index f4d7d0a761..169e8fc067 100644 --- a/shared/offline_compiler/source/offline_compiler.h +++ b/shared/offline_compiler/source/offline_compiler.h @@ -136,6 +136,7 @@ All supported acronyms: %s. std::string productFamilyName; std::string inputFile; std::string outputFile; + std::string binaryOutputFile; std::string outputDirectory; std::string options; std::string internalOptions;