[lit][SPIRV] convert runCodeTest to lit and remove dependence on effcee (#6077)

Convert runCodeTest to lit FileCheck test and remove all the code
related to runCodeTest.

Remove effcee and re2 from git submodule since nothing is dependent on
them after the change.
This commit is contained in:
Xiang Li 2023-12-14 06:50:32 -08:00 коммит произвёл GitHub
Родитель ad3958c2d8
Коммит 5f7834de56
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 82 добавлений и 891 удалений

6
.gitmodules поставляемый
Просмотреть файл

@ -7,12 +7,6 @@
[submodule "external/googletest"]
path = external/googletest
url = https://github.com/google/googletest
[submodule "external/re2"]
path = external/re2
url = https://github.com/google/re2
[submodule "external/effcee"]
path = external/effcee
url = https://github.com/google/effcee
[submodule "external/DirectX-Headers"]
path = external/DirectX-Headers
url = https://github.com/microsoft/DirectX-Headers.git

45
external/CMakeLists.txt поставляемый
Просмотреть файл

@ -72,49 +72,4 @@ if (${ENABLE_SPIRV_CODEGEN})
set_property(TARGET ${target} PROPERTY FOLDER "External dependencies")
endforeach()
# We need Effcee for SPIR-V codegen testing.
if (${SPIRV_BUILD_TESTS})
# re2 is needed since it's required by effcee.
set(DXC_RE2_DIR "${DXC_EXTERNAL_ROOT_DIR}/re2" CACHE STRING "Location of RE2 source")
set(DXC_EFFCEE_DIR "${DXC_EXTERNAL_ROOT_DIR}/effcee" CACHE STRING "Location of Effcee source")
if (NOT TARGET re2)
if (IS_DIRECTORY ${DXC_RE2_DIR})
# Avoid exception handling warning from MSVC.
# This add_compile_options() will only affect the current directory and its subdirectories.
if (WIN32)
add_compile_options(/EHs)
else(WIN32)
# Disable all warnings in subproject RE2
add_compile_options(-w)
endif(WIN32)
# Don't build/run re2's tests.
set(RE2_BUILD_TESTING OFF CACHE BOOL "Skip RE2 tests")
add_subdirectory(${DXC_RE2_DIR} EXCLUDE_FROM_ALL)
endif()
endif()
if (NOT TARGET effcee)
if (IS_DIRECTORY ${DXC_EFFCEE_DIR})
# Configure effcee
set(EFFCEE_RE2_DIR "${DXC_RE2_DIR}" CACHE STRING "Location of RE2 for effcee")
set(EFFCEE_ENABLE_SHARED_CRT ON CACHE BOOL "Enable using shared C Runtime")
# Don't build/run effcee's tests.
set(EFFCEE_BUILD_TESTING OFF CACHE BOOL "Skip effcee tests")
set(EFFCEE_BUILD_SAMPLES OFF CACHE BOOL "Skip building effcee examples")
add_subdirectory(${DXC_EFFCEE_DIR} EXCLUDE_FROM_ALL)
endif()
endif()
# Organize these targets better in Visual Studio
set(SPIRV_TEST_DEP_TARGETS
effcee
re2
)
foreach(target ${SPIRV_TEST_DEP_TARGETS})
set_property(TARGET ${target} PROPERTY FOLDER "External dependencies")
endforeach()
endif()
endif()

1
external/effcee поставляемый

@ -1 +0,0 @@
Subproject commit 66edefd2bb641de8a2f46b476de21f227fc03a28

1
external/re2 поставляемый

@ -1 +0,0 @@
Subproject commit b76a3eac1dfc7f0fe1d6a64cb59eab868056f099

Просмотреть файл

@ -0,0 +1,12 @@
// RUN: %dxc -T ps_6_0 -E PSMain -fcgl %s -spirv | FileCheck %s
struct PSInput
{
float4 color : COLOR;
};
// CHECK: OpFunctionCall %v4float %src_PSMain
float4 PSMain(PSInput input) : SV_TARGET
{
return input.color;
}

Просмотреть файл

@ -0,0 +1,12 @@
// RUN: not %dxc -T ps_6_0 -E PSMain -fcgl %s -spirv 2>&1 | FileCheck %s
struct PSInput
{
float4 color : COLOR;
};
// CHECK: error: cannot initialize return object of type 'float4' with an lvalue of type 'PSInput'
float4 PSMain(PSInput input) : SV_TARGET
{
return input;
}

Просмотреть файл

@ -0,0 +1,53 @@
// RUN: not %dxc -T vs_6_2 -E main -DTY=float -DARRAY=[4] -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -DTY=double4 -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -DTY=int4 -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -DTY=float1x4 -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -DTY=float3 -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -DTY=InvalidType -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: %dxc -T vs_6_2 -E main -DTY=ValidType -fcgl %s -spirv | FileCheck %s --check-prefix=VALID_TY
// RUN: %dxc -T vs_6_2 -E main -DTY=float4 -fcgl %s -spirv | FileCheck %s --check-prefix=VALID_FLOAT
// RUN: %dxc -T vs_6_2 -E main -DTY=min10float4 -fcgl %s -spirv | FileCheck %s --check-prefix=VALID_FLOAT
// RUN: %dxc -T vs_6_2 -E main -DTY=min16float4 -fcgl %s -spirv | FileCheck %s --check-prefix=VALID_FLOAT
// RUN: %dxc -T vs_6_2 -E main -DTY=half4 -fcgl %s -spirv | FileCheck %s --check-prefix=VALID_FLOAT
// RUN: not %dxc -T vs_6_2 -E main -enable-16bit-types -DTY=half4 -fcgl %s -spirv 2>&1 | FileCheck %s
// RUN: not %dxc -T vs_6_2 -E main -enable-16bit-types -DTY=min10float4 -fcgl %s -spirv 2>&1 | FileCheck %s
// CHECK: error: SV_Position must be a 4-component 32-bit float vector or a composite which recursively contains only such a vector
// VALID_TY: %ValidType = OpTypeStruct %v4float
// VALID_TY: %output = OpTypeStruct %ValidType
// VALID_FLOAT: %output = OpTypeStruct %v4float
struct InvalidType {
float3 x;
};
struct ValidType {
float4 x;
};
#ifndef ARRAY
#define ARRAY
#endif
#define POSITION TY x ARRAY
struct output {
POSITION;
};
output main() : SV_Position
{
output result;
return result;
}

Просмотреть файл

@ -0,0 +1,4 @@
// RUN: %dxc -T ps_6_0 -E PSMain -Zi -fcgl %s -spirv | FileCheck %s
float4 PSMain(float4 color : COLOR) : SV_TARGET { return color; }
// CHECK: float4 PSMain(float4 color : COLOR) : SV_TARGET { return color; }

Просмотреть файл

@ -7,9 +7,6 @@ set(LLVM_LINK_COMPONENTS
)
add_clang_unittest(ClangSPIRVTests
CodeGenSpirvTest.cpp
FileTestFixture.cpp
FileTestUtils.cpp
SpirvBasicBlockTest.cpp
SpirvContextTest.cpp
SpirvTestOptions.cpp
@ -28,7 +25,6 @@ target_link_libraries(ClangSPIRVTests
clangSPIRV
clangTooling
dxcompiler
effcee
SPIRV-Tools
)
@ -38,7 +34,7 @@ target_link_libraries(ClangSPIRVTests
add_dependencies(ClangSPIRVTests dxcompiler)
target_include_directories(ClangSPIRVTests
PRIVATE ${SPIRV_TOOLS_INCLUDE_DIR} ${DXC_EFFCEE_DIR})
PRIVATE ${SPIRV_TOOLS_INCLUDE_DIR})
if (NOT CLANG_INCLUDE_TESTS)
set_output_directory(ClangSPIRVTests

Просмотреть файл

@ -1,167 +0,0 @@
//===- unittests/SPIRV/CodeGenSPIRVTest.cpp ---- Run CodeGenSPIRV tests ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "FileTestFixture.h"
namespace {
using clang::spirv::FileTest;
// === Partial output tests ===
TEST_F(FileTest, InlinedCodeTest) {
const std::string command(R"(// RUN: %dxc -T ps_6_0 -E PSMain)");
const std::string code = command + R"(
struct PSInput
{
float4 color : COLOR;
};
// CHECK: OpFunctionCall %v4float %src_PSMain
float4 PSMain(PSInput input) : SV_TARGET
{
return input.color;
})";
runCodeTest(code);
}
TEST_F(FileTest, InlinedCodeWithErrorTest) {
const std::string command(R"(// RUN: %dxc -T ps_6_0 -E PSMain)");
const std::string code = command + R"(
struct PSInput
{
float4 color : COLOR;
};
// CHECK: error: cannot initialize return object of type 'float4' with an lvalue of type 'PSInput'
float4 PSMain(PSInput input) : SV_TARGET
{
return input;
})";
runCodeTest(code, Expect::Failure);
}
std::string getVertexPositionTypeTestShader(const std::string &subType,
const std::string &positionType,
const std::string &check,
bool use16bit) {
const std::string code = std::string(R"(// RUN: %dxc -T vs_6_2 -E main)") +
(use16bit ? R"( -enable-16bit-types)" : R"()") + R"(
)" + subType + R"(
struct output {
)" + positionType + R"(
};
output main() : SV_Position
{
output result;
return result;
}
)" + check;
return code;
}
const char *kInvalidPositionTypeForVSErrorMessage =
"// CHECK: error: SV_Position must be a 4-component 32-bit float vector or "
"a composite which recursively contains only such a vector";
TEST_F(FileTest, PositionInVSWithArrayType) {
runCodeTest(
getVertexPositionTypeTestShader(
"", "float x[4];", kInvalidPositionTypeForVSErrorMessage, false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithDoubleType) {
runCodeTest(
getVertexPositionTypeTestShader(
"", "double4 x;", kInvalidPositionTypeForVSErrorMessage, false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithIntType) {
runCodeTest(getVertexPositionTypeTestShader(
"", "int4 x;", kInvalidPositionTypeForVSErrorMessage, false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithMatrixType) {
runCodeTest(
getVertexPositionTypeTestShader(
"", "float1x4 x;", kInvalidPositionTypeForVSErrorMessage, false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithInvalidFloatVectorType) {
runCodeTest(
getVertexPositionTypeTestShader(
"", "float3 x;", kInvalidPositionTypeForVSErrorMessage, false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithInvalidInnerStructType) {
runCodeTest(getVertexPositionTypeTestShader(
R"(
struct InvalidType {
float3 x;
};)",
"InvalidType x;", kInvalidPositionTypeForVSErrorMessage,
false),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithValidInnerStructType) {
runCodeTest(getVertexPositionTypeTestShader(R"(
struct validType {
float4 x;
};)",
"validType x;", R"(
// CHECK: %validType = OpTypeStruct %v4float
// CHECK: %output = OpTypeStruct %validType
)",
false));
}
TEST_F(FileTest, PositionInVSWithValidFloatType) {
runCodeTest(getVertexPositionTypeTestShader("", "float4 x;", R"(
// CHECK: %output = OpTypeStruct %v4float
)",
false));
}
TEST_F(FileTest, PositionInVSWithValidMin10Float4Type) {
runCodeTest(getVertexPositionTypeTestShader("", "min10float4 x;", R"(
// CHECK: %output = OpTypeStruct %v4float
)",
false));
}
TEST_F(FileTest, PositionInVSWithValidMin16Float4Type) {
runCodeTest(getVertexPositionTypeTestShader("", "min16float4 x;", R"(
// CHECK: %output = OpTypeStruct %v4float
)",
false));
}
TEST_F(FileTest, PositionInVSWithValidHalf4Type) {
runCodeTest(getVertexPositionTypeTestShader("", "half4 x;", R"(
// CHECK: %output = OpTypeStruct %v4float
)",
false));
}
TEST_F(FileTest, PositionInVSWithInvalidHalf4Type) {
runCodeTest(getVertexPositionTypeTestShader(
"", "half4 x;", kInvalidPositionTypeForVSErrorMessage, true),
Expect::Failure);
}
TEST_F(FileTest, PositionInVSWithInvalidMin10Float4Type) {
runCodeTest(
getVertexPositionTypeTestShader(
"", "min10float4 x;", kInvalidPositionTypeForVSErrorMessage, true),
Expect::Failure);
}
TEST_F(FileTest, SourceCodeWithoutFilePath) {
const std::string command(R"(// RUN: %dxc -T ps_6_0 -E PSMain -Zi)");
const std::string code = command + R"(
float4 PSMain(float4 color : COLOR) : SV_TARGET { return color; }
// CHECK: float4 PSMain(float4 color : COLOR) : SV_TARGET { return color; }
)";
runCodeTest(code);
}
} // namespace

Просмотреть файл

@ -1,207 +0,0 @@
//===- FileTestFixture.cpp ------------- File Test Fixture Implementation -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "FileTestFixture.h"
#include <fstream>
#include "FileTestUtils.h"
#include "effcee/effcee.h"
namespace clang {
namespace spirv {
bool FileTest::parseInputFile() {
std::stringstream inputSS;
std::ifstream inputFile;
inputFile.exceptions(std::ifstream::failbit);
try {
inputFile.open(inputFilePath);
inputSS << inputFile.rdbuf();
} catch (...) {
fprintf(
stderr,
"Error: Exception occurred while opening/reading the input file %s\n",
inputFilePath.c_str());
return false;
}
// Close the input file.
inputFile.close();
checkCommands = inputSS.str();
return parseCommand();
}
bool FileTest::parseCommand() {
// Effcee skips any input line which doesn't have a CHECK directive, therefore
// we can pass the entire input to effcee. This way, any warning/error message
// provided by effcee also reflects the correct line number in the input file.
const char hlslStartLabel[] = "// RUN:";
const auto runCmdStartPos = checkCommands.find(hlslStartLabel);
if (runCmdStartPos != std::string::npos) {
const auto runCmdEndPos = checkCommands.find('\n', runCmdStartPos);
const auto runCommand = checkCommands.substr(runCmdStartPos, runCmdEndPos);
if (!utils::processRunCommandArgs(runCommand, &targetProfile, &entryPoint,
&targetEnv, &restArgs)) {
// An error has occured when parsing the Run command.
return false;
}
} else {
fprintf(stderr, "Error: Missing \"RUN:\" command.\n");
return false;
}
// Everything was successful.
return true;
}
void FileTest::runFileTest(llvm::StringRef filename, Expect expect) {
bool runValidation = true;
if (beforeHLSLLegalization)
assert(runValidation);
inputFilePath = utils::getAbsPathOfInputDataFile(filename);
// Parse the input file.
ASSERT_TRUE(parseInputFile());
// Feed the HLSL source into the Compiler.
std::string errorMessages;
const bool compileOk = utils::compileFileWithSpirvGeneration(
inputFilePath, entryPoint, targetProfile, restArgs, &generatedBinary,
&errorMessages);
checkTestResult(filename, compileOk, errorMessages, expect, runValidation);
}
void FileTest::runCodeTest(llvm::StringRef code, Expect expect,
bool runValidation) {
if (beforeHLSLLegalization)
assert(runValidation);
inputFilePath = "(Inlined HLSL code)";
checkCommands = code;
ASSERT_TRUE(parseCommand());
// Feed the HLSL source into the Compiler.
std::string errorMessages;
const bool compileOk = utils::compileCodeWithSpirvGeneration(
inputFilePath, code, entryPoint, targetProfile, restArgs,
&generatedBinary, &errorMessages);
checkTestResult(inputFilePath, compileOk, errorMessages, expect,
runValidation);
}
void FileTest::checkTestResult(llvm::StringRef filename, const bool compileOk,
const std::string &errorMessages, Expect expect,
bool runValidation) {
effcee::Result result(effcee::Result::Status::Ok);
if (expect == Expect::Success) {
ASSERT_TRUE(compileOk);
// Disassemble the generated SPIR-V binary.
ASSERT_TRUE(
utils::disassembleSpirvBinary(generatedBinary, &generatedSpirvAsm,
true /* generateHeader */, targetEnv));
auto options = effcee::Options()
.SetChecksName(filename.str())
.SetInputName("<codegen>");
// Run CHECK commands via effcee on disassembly.
result = effcee::Match(generatedSpirvAsm, checkCommands, options);
// Print effcee's error message (if any).
if (result.status() != effcee::Result::Status::Ok) {
fprintf(stderr, "%s\n", result.message().c_str());
}
// All checks must have passed.
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
// HLSL Change: Explicit braces
if (runValidation) {
EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
beforeHLSLLegalization, glLayout,
dxLayout, scalarLayout));
}
} else if (expect == Expect::Warning) {
ASSERT_TRUE(compileOk);
// Still check that we can disassemble the generated SPIR-V binary.
ASSERT_TRUE(utils::disassembleSpirvBinary(
generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
auto options = effcee::Options()
.SetChecksName(filename.str())
.SetInputName("<message>");
// Run CHECK commands via effcee on warning messages.
result = effcee::Match(errorMessages, checkCommands, options);
// Print effcee's error message (if any).
if (result.status() != effcee::Result::Status::Ok) {
fprintf(stderr, "%s\n", result.message().c_str());
}
// All checks must have passed.
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
// HLSL Change: explicit braces
if (runValidation) {
EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
beforeHLSLLegalization, glLayout,
dxLayout, scalarLayout));
}
} else if (expect == Expect::Failure) {
ASSERT_FALSE(compileOk);
auto options = effcee::Options()
.SetChecksName(filename.str())
.SetInputName("<message>");
// Run CHECK commands via effcee on error messages.
result = effcee::Match(errorMessages, checkCommands, options);
// Print effcee's error message (if any).
if (result.status() != effcee::Result::Status::Ok) {
fprintf(stderr, "%s\n", result.message().c_str());
}
// All checks must have passed.
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
} else {
ASSERT_TRUE(compileOk);
// Disassemble the generated SPIR-V binary.
ASSERT_TRUE(utils::disassembleSpirvBinary(
generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
std::string valMessages;
EXPECT_FALSE(utils::validateSpirvBinary(
targetEnv, generatedBinary, beforeHLSLLegalization, glLayout, dxLayout,
scalarLayout, &valMessages));
auto options = effcee::Options()
.SetChecksName(filename.str())
.SetInputName("<val-message>");
// Run CHECK commands via effcee on error messages.
result = effcee::Match(valMessages, checkCommands, options);
// All checks over validation message must have passed.
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
}
}
} // end namespace spirv
} // end namespace clang

Просмотреть файл

@ -1,81 +0,0 @@
//===- FileTestFixute.h ---- Test Fixture for File Check style tests ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_UNITTESTS_SPIRV_FILE_TEST_FIXTURE_H
#define LLVM_CLANG_UNITTESTS_SPIRV_FILE_TEST_FIXTURE_H
#include "spirv-tools/libspirv.h"
#include "llvm/ADT/StringRef.h"
#include "gtest/gtest.h"
namespace clang {
namespace spirv {
class FileTest : public ::testing::Test {
public:
/// \brief Expected test result to be
enum class Expect {
Success, // Success (with or without warnings) - check disassembly
Warning, // Success (with warnings) - check warning message
Failure, // Failure (with errors) - check error message
ValFailure, // Validation failure (with errors) - check error message
};
FileTest()
: targetEnv(SPV_ENV_VULKAN_1_0), beforeHLSLLegalization(false),
glLayout(false), dxLayout(false), scalarLayout(false) {}
void setBeforeHLSLLegalization() { beforeHLSLLegalization = true; }
/// \brief Runs a test with the given input HLSL file.
///
/// The first line of HLSL code must start with "// RUN:" and following DXC
/// arguments to run the test. Next lines must be proper HLSL code for the
/// test. It uses file check style output check e.g., "// CHECK: ...".
void runFileTest(llvm::StringRef path, Expect expect = Expect::Success);
/// \brief Runs a test with the given HLSL code.
///
/// The first line of code must start with "// RUN:" and following DXC
/// arguments to run the test. Next lines must be proper HLSL code for the
/// test. It uses file check style output check e.g., "// CHECK: ...".
void runCodeTest(llvm::StringRef code, Expect expect = Expect::Success,
bool runValidation = true);
private:
/// \brief Reads in the given input file and parses the command to get
/// arguments to run DXC.
bool parseInputFile();
/// \brief Parses the command and gets arguments to run DXC.
bool parseCommand();
/// \brief Checks the compile result. Reports whether the expected compile
/// result matches the actual result and whether the expected validation
/// result matches the actual one or not.
void checkTestResult(llvm::StringRef filename, const bool compileOk,
const std::string &errorMessages, Expect expect,
bool runValidation);
std::string targetProfile; ///< Target profile (argument of -T)
std::string entryPoint; ///< Entry point name (argument of -E)
std::vector<std::string> restArgs; ///< All the other arguments
std::string inputFilePath; ///< Path to the input test file
std::vector<uint32_t> generatedBinary; ///< The generated SPIR-V Binary
std::string checkCommands; ///< CHECK commands that verify output
std::string generatedSpirvAsm; ///< Disassembled binary (SPIR-V code)
spv_target_env targetEnv; ///< Environment to validate against
bool beforeHLSLLegalization;
bool glLayout;
bool dxLayout;
bool scalarLayout;
};
} // end namespace spirv
} // end namespace clang
#endif

Просмотреть файл

@ -1,274 +0,0 @@
//===- FileTestUtils.cpp ---- Implementation of FileTestUtils -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "FileTestUtils.h"
#include <algorithm>
#include <sstream>
#include "dxc/Support/HLSLOptions.h"
#include "SpirvTestOptions.h"
#include "gtest/gtest.h"
namespace clang {
namespace spirv {
namespace utils {
bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
std::string *generatedSpirvAsm, bool generateHeader,
spv_target_env target_env) {
spvtools::SpirvTools spirvTools(target_env);
spirvTools.SetMessageConsumer(
[](spv_message_level_t, const char *, const spv_position_t &,
const char *message) { fprintf(stdout, "%s\n", message); });
uint32_t options = SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
if (!generateHeader)
options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER;
return spirvTools.Disassemble(binary, generatedSpirvAsm, options);
}
bool validateSpirvBinary(spv_target_env env, std::vector<uint32_t> &binary,
bool beforeHlslLegalization, bool glLayout,
bool dxLayout, bool scalarLayout,
std::string *message) {
spvtools::ValidatorOptions options;
options.SetBeforeHlslLegalization(beforeHlslLegalization);
if (dxLayout || scalarLayout) {
options.SetScalarBlockLayout(true);
} else if (glLayout) {
// The default for spirv-val.
} else {
options.SetRelaxBlockLayout(true);
}
spvtools::SpirvTools spirvTools(env);
spirvTools.SetMessageConsumer([message](spv_message_level_t, const char *,
const spv_position_t &,
const char *msg) {
if (message)
*message = msg;
else
fprintf(stdout, "%s\n", msg);
});
return spirvTools.Validate(binary.data(), binary.size(), options);
}
bool processRunCommandArgs(const llvm::StringRef runCommandLine,
std::string *targetProfile, std::string *entryPoint,
spv_target_env *targetEnv,
std::vector<std::string> *restArgs) {
std::istringstream buf(runCommandLine);
std::istream_iterator<std::string> start(buf), end;
std::vector<std::string> tokens(start, end);
if (tokens.size() < 3 || tokens[1].find("RUN") == std::string::npos ||
tokens[2].find("%dxc") == std::string::npos) {
fprintf(stderr, "The only supported format is: \"// RUN: %%dxc -T "
"<profile> -E <entry>\"\n");
return false;
}
std::ostringstream rest;
for (uint32_t i = 3; i < tokens.size(); ++i) {
if (tokens[i] == "-T" && (++i) < tokens.size()) {
*targetProfile = tokens[i];
} else if (tokens[i] == "-E" && (++i) < tokens.size()) {
*entryPoint = tokens[i];
} else if (tokens[i].substr(0, 17) == "-fspv-target-env=") {
std::string targetEnvStr = tokens[i].substr(17);
if (targetEnvStr == "vulkan1.0")
*targetEnv = SPV_ENV_VULKAN_1_0;
else if (targetEnvStr == "vulkan1.1")
*targetEnv = SPV_ENV_VULKAN_1_1;
else if (targetEnvStr == "vulkan1.1spirv1.4")
*targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
else if (targetEnvStr == "vulkan1.2")
*targetEnv = SPV_ENV_VULKAN_1_2;
else if (targetEnvStr == "vulkan1.3")
*targetEnv = SPV_ENV_UNIVERSAL_1_6;
else if (targetEnvStr == "universal1.5")
*targetEnv = SPV_ENV_UNIVERSAL_1_5;
else {
fprintf(stderr, "Error: Found unknown target environment.\n");
return false;
}
// Also push target environment to restArgs so that it gets passed to the
// compile command.
restArgs->push_back(tokens[i]);
} else {
restArgs->push_back(tokens[i]);
}
}
if (targetProfile->empty()) {
fprintf(stderr, "Error: Missing target profile argument (-T).\n");
return false;
}
// lib_6_* profile doesn't need an entryPoint
if (targetProfile->c_str()[0] != 'l' && entryPoint->empty()) {
fprintf(stderr, "Error: Missing entry point argument (-E).\n");
return false;
}
return true;
}
void convertIDxcBlobToUint32(const CComPtr<IDxcBlob> &blob,
std::vector<uint32_t> *binaryWords) {
size_t num32BitWords = (blob->GetBufferSize() + 3) / 4;
std::string binaryStr((char *)blob->GetBufferPointer(),
blob->GetBufferSize());
binaryStr.resize(num32BitWords * 4, 0);
binaryWords->resize(num32BitWords, 0);
memcpy(binaryWords->data(), binaryStr.data(), binaryStr.size());
}
std::string getAbsPathOfInputDataFile(const llvm::StringRef filename) {
std::string path = clang::spirv::testOptions::inputDataDir;
#ifdef _WIN32
const char sep = '\\';
std::replace(path.begin(), path.end(), '/', '\\');
#else
const char sep = '/';
#endif
if (path[path.size() - 1] != sep) {
path = path + sep;
}
path += filename;
return path;
}
bool compileFileWithSpirvGeneration(const llvm::StringRef inputFilePath,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages) {
return compileWithSpirvGeneration({inputFilePath, ""}, entryPoint,
targetProfile, restArgs, generatedBinary,
errorMessages);
}
bool compileCodeWithSpirvGeneration(const llvm::StringRef inputFilePath,
const llvm::StringRef code,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages) {
return compileWithSpirvGeneration({inputFilePath, code}, entryPoint,
targetProfile, restArgs, generatedBinary,
errorMessages);
}
bool compileWithSpirvGeneration(const SourceCodeInfo &srcInfo,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages) {
std::wstring srcFile(srcInfo.inputFilePath.begin(),
srcInfo.inputFilePath.end());
std::wstring entry(entryPoint.begin(), entryPoint.end());
std::wstring profile(targetProfile.begin(), targetProfile.end());
std::vector<std::wstring> rest;
for (const auto &arg : restArgs)
rest.emplace_back(arg.begin(), arg.end());
bool success = true;
try {
dxc::DxcDllSupport dllSupport;
IFT(dllSupport.Initialize());
if (hlsl::options::initHlslOptTable())
throw std::bad_alloc();
CComPtr<IDxcLibrary> pLibrary;
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcOperationResult> pResult;
CComPtr<IDxcBlobEncoding> pErrorBuffer;
CComPtr<IDxcBlob> pCompiledBlob;
CComPtr<IDxcIncludeHandler> pIncludeHandler;
HRESULT resultStatus;
bool requires_opt = false;
for (const auto &arg : rest)
if (arg == L"-O3" || arg == L"-O0" || arg.substr(0, 8) == L"-Oconfig")
requires_opt = true;
std::vector<LPCWSTR> flags;
// lib_6_* profile doesn't need an entryPoint
if (profile.c_str()[0] != 'l') {
flags.push_back(L"-E");
flags.push_back(entry.c_str());
}
flags.push_back(L"-T");
flags.push_back(profile.c_str());
flags.push_back(L"-spirv");
// Disable legalization and optimization for testing, unless the caller
// wants to run a specific optimization recipe (with -Oconfig).
if (!requires_opt)
flags.push_back(L"-fcgl");
// Disable validation. We'll run it manually.
flags.push_back(L"-Vd");
for (const auto &arg : rest)
flags.push_back(arg.c_str());
IFT(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
CComPtr<IDxcBlobEncoding> pSource;
if (srcInfo.code.empty()) {
IFT(pLibrary->CreateBlobFromFile(srcFile.c_str(), nullptr, &pSource));
} else {
IFT(pLibrary->CreateBlobWithEncodingOnHeapCopy(
srcInfo.code.data(), static_cast<uint32_t>(srcInfo.code.size()),
CP_UTF8, &pSource));
}
IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
IFT(dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
IFT(pCompiler->Compile(pSource, srcFile.c_str(), entry.c_str(),
profile.c_str(), flags.data(), flags.size(), nullptr,
0, pIncludeHandler, &pResult));
// Compilation is done. We can clean up the HlslOptTable.
hlsl::options::cleanupHlslOptTable();
// Get compilation results.
IFT(pResult->GetStatus(&resultStatus));
// Get diagnostics string.
IFT(pResult->GetErrorBuffer(&pErrorBuffer));
const std::string diagnostics((char *)pErrorBuffer->GetBufferPointer(),
pErrorBuffer->GetBufferSize());
*errorMessages = diagnostics;
if (SUCCEEDED(resultStatus)) {
CComPtr<IDxcBlobEncoding> pStdErr;
IFT(pResult->GetResult(&pCompiledBlob));
convertIDxcBlobToUint32(pCompiledBlob, generatedBinary);
success = true;
} else {
success = false;
}
} catch (...) {
// An exception has occured while running the compiler with SPIR-V
// Generation
success = false;
}
return success;
}
} // end namespace utils
} // end namespace spirv
} // end namespace clang

Просмотреть файл

@ -1,104 +0,0 @@
//===- FileTestUtils.h ---- Utilities For Running File Tests --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_UNITTESTS_SPIRV_FILETESTUTILS_H
#define LLVM_CLANG_UNITTESTS_SPIRV_FILETESTUTILS_H
#include <string>
#include <vector>
#include "dxc/Support/Global.h"
#include "dxc/Support/WinIncludes.h"
#include "dxc/Support/dxcapi.use.h"
#include "spirv-tools/libspirv.hpp"
#include "llvm/ADT/StringRef.h"
namespace clang {
namespace spirv {
namespace utils {
/// \brief Passes the given SPIR-V binary to SPIR-V tools disassembler. The
/// SPIR-V assembly is returned via 'generatedSpirvAsm' argument.
/// Returns true on success, and false on failure.
bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
std::string *generatedSpirvAsm,
bool generateHeader = false,
spv_target_env = SPV_ENV_VULKAN_1_1);
/// \brief Runs the SPIR-V Tools validation on the given SPIR-V binary.
/// Returns true if validation is successful; false otherwise.
bool validateSpirvBinary(spv_target_env, std::vector<uint32_t> &binary,
bool beforeHlslLegalization, bool glLayout,
bool dxLayout, bool scalarLayout,
std::string *message = nullptr);
/// \brief Parses the Target Profile, Entry Point, and Target Environment from
/// the Run command returns the target profile, entry point, target environment,
/// and the rest via arguments. Returns true on success, and false otherwise.
bool processRunCommandArgs(const llvm::StringRef runCommandLine,
std::string *targetProfile, std::string *entryPoint,
spv_target_env *targetEnv,
std::vector<std::string> *restArgs);
/// \brief Converts an IDxcBlob into a vector of 32-bit unsigned integers which
/// is returned via the 'binaryWords' argument.
void convertIDxcBlobToUint32(const CComPtr<IDxcBlob> &blob,
std::vector<uint32_t> *binaryWords);
/// \brief Returns the absolute path to the input file of the test.
/// The input file is expected to be located in the directory given by the
/// testOptions::inputDataDir
std::string getAbsPathOfInputDataFile(const llvm::StringRef filename);
/// \brief Passes the HLSL input file to the DXC compiler with SPIR-V CodeGen.
/// Returns the generated SPIR-V binary via 'generatedBinary' argument.
/// Returns true on success, and false on failure. Writes error messages to
/// errorMessages and stderr on failure.
bool compileFileWithSpirvGeneration(const llvm::StringRef inputFilePath,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages);
/// \brief Passes the string HLSL code to the DXC compiler with SPIR-V CodeGen.
/// Returns the generated SPIR-V binary via 'generatedBinary' argument.
/// Returns true on success, and false on failure. Writes error messages to
/// errorMessages and stderr on failure.
bool compileCodeWithSpirvGeneration(const llvm::StringRef inputFilePath,
const llvm::StringRef code,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages);
/// \brief A struct to keep the input file path and HLSL code information.
struct SourceCodeInfo {
const llvm::StringRef inputFilePath;
const llvm::StringRef code;
};
/// \brief Passes the HLSL source information to the DXC compiler with SPIR-V
/// CodeGen. Returns the generated SPIR-V binary via 'generatedBinary' argument.
/// Returns true on success, and false on failure. Writes error messages to
/// errorMessages and stderr on failure. If srcInfo.code is an empty string, it
/// reads the HLSL input from srcInfo.inputFilePath file.
bool compileWithSpirvGeneration(const SourceCodeInfo &srcInfo,
const llvm::StringRef entryPoint,
const llvm::StringRef targetProfile,
const std::vector<std::string> &restArgs,
std::vector<uint32_t> *generatedBinary,
std::string *errorMessages);
} // end namespace utils
} // end namespace spirv
} // end namespace clang
#endif