mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-04 08:20:06 +00:00
spirv-as: Assume target from spvasm text (#5893)
* spirv-as: Assume target from spvasm text Assume the desired SPIR-V target version when the command line option is not provided. This is especially important when the source being assembled does not match the default (latest version), because it may create an invalid SPIR-V binary result. Also, clarify the wording for where a copy of spirv-headers is expected (to match expected capitalization). * fix formatting * Add unit tests, static asserts on supported SPIR-V versions Rework the algorithm so it accepts earlier termination of the version string. That made testing easier and more intuitive. * Fix compilation problems --------- Co-authored-by: mmoult <30711895+mmoult@users.noreply.github.com>
This commit is contained in:
parent
f3c4a5053f
commit
a9d884e58d
2
external/CMakeLists.txt
vendored
2
external/CMakeLists.txt
vendored
@ -45,7 +45,7 @@ if (IS_DIRECTORY ${SPIRV_HEADER_DIR})
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"SPIRV-Headers was not found - please checkout a copy under external/.")
|
||||
"SPIRV-Headers was not found - please checkout a copy at external/spirv-headers.")
|
||||
endif()
|
||||
|
||||
if (NOT ${SPIRV_SKIP_TESTS})
|
||||
|
@ -14,11 +14,13 @@
|
||||
|
||||
#include "source/spirv_target_env.h"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "source/latest_version_spirv_header.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
@ -128,32 +130,43 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
||||
return SPV_SPIRV_VERSION_WORD(0, 0);
|
||||
}
|
||||
|
||||
static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = {
|
||||
{"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
|
||||
{"vulkan1.0", SPV_ENV_VULKAN_1_0},
|
||||
{"vulkan1.1", SPV_ENV_VULKAN_1_1},
|
||||
{"vulkan1.2", SPV_ENV_VULKAN_1_2},
|
||||
{"vulkan1.3", SPV_ENV_VULKAN_1_3},
|
||||
{"spv1.0", SPV_ENV_UNIVERSAL_1_0},
|
||||
{"spv1.1", SPV_ENV_UNIVERSAL_1_1},
|
||||
{"spv1.2", SPV_ENV_UNIVERSAL_1_2},
|
||||
{"spv1.3", SPV_ENV_UNIVERSAL_1_3},
|
||||
{"spv1.4", SPV_ENV_UNIVERSAL_1_4},
|
||||
{"spv1.5", SPV_ENV_UNIVERSAL_1_5},
|
||||
{"spv1.6", SPV_ENV_UNIVERSAL_1_6},
|
||||
{"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
|
||||
{"opencl1.2", SPV_ENV_OPENCL_1_2},
|
||||
{"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
|
||||
{"opencl2.0", SPV_ENV_OPENCL_2_0},
|
||||
{"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1},
|
||||
{"opencl2.1", SPV_ENV_OPENCL_2_1},
|
||||
{"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2},
|
||||
{"opencl2.2", SPV_ENV_OPENCL_2_2},
|
||||
{"opengl4.0", SPV_ENV_OPENGL_4_0},
|
||||
{"opengl4.1", SPV_ENV_OPENGL_4_1},
|
||||
{"opengl4.2", SPV_ENV_OPENGL_4_2},
|
||||
{"opengl4.3", SPV_ENV_OPENGL_4_3},
|
||||
{"opengl4.5", SPV_ENV_OPENGL_4_5},
|
||||
// When a new SPIR-V version is released, update this table.
|
||||
static_assert(spv::Version == 0x10600);
|
||||
constexpr auto ordered_universal_envs = std::array<spv_target_env, 7>{
|
||||
SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2,
|
||||
SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5,
|
||||
SPV_ENV_UNIVERSAL_1_6,
|
||||
};
|
||||
|
||||
// When a new SPIR-V version is released, update this table.
|
||||
static_assert(spv::Version == 0x10600);
|
||||
inline constexpr std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] =
|
||||
{
|
||||
{"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
|
||||
{"vulkan1.0", SPV_ENV_VULKAN_1_0},
|
||||
{"vulkan1.1", SPV_ENV_VULKAN_1_1},
|
||||
{"vulkan1.2", SPV_ENV_VULKAN_1_2},
|
||||
{"vulkan1.3", SPV_ENV_VULKAN_1_3},
|
||||
{"spv1.0", SPV_ENV_UNIVERSAL_1_0},
|
||||
{"spv1.1", SPV_ENV_UNIVERSAL_1_1},
|
||||
{"spv1.2", SPV_ENV_UNIVERSAL_1_2},
|
||||
{"spv1.3", SPV_ENV_UNIVERSAL_1_3},
|
||||
{"spv1.4", SPV_ENV_UNIVERSAL_1_4},
|
||||
{"spv1.5", SPV_ENV_UNIVERSAL_1_5},
|
||||
{"spv1.6", SPV_ENV_UNIVERSAL_1_6},
|
||||
{"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
|
||||
{"opencl1.2", SPV_ENV_OPENCL_1_2},
|
||||
{"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
|
||||
{"opencl2.0", SPV_ENV_OPENCL_2_0},
|
||||
{"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1},
|
||||
{"opencl2.1", SPV_ENV_OPENCL_2_1},
|
||||
{"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2},
|
||||
{"opencl2.2", SPV_ENV_OPENCL_2_2},
|
||||
{"opengl4.0", SPV_ENV_OPENGL_4_0},
|
||||
{"opengl4.1", SPV_ENV_OPENGL_4_1},
|
||||
{"opengl4.2", SPV_ENV_OPENGL_4_2},
|
||||
{"opengl4.3", SPV_ENV_OPENGL_4_3},
|
||||
{"opengl4.5", SPV_ENV_OPENGL_4_5},
|
||||
};
|
||||
|
||||
bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
||||
@ -172,6 +185,59 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spvReadEnvironmentFromText(const std::vector<char>& text,
|
||||
spv_target_env* env) {
|
||||
// Version is expected to match "; Version: 1.X"
|
||||
// Version string must occur in header, that is, initial lines of comments
|
||||
// Once a non-comment line occurs, the header has ended
|
||||
for (std::size_t i = 0; i < text.size(); ++i) {
|
||||
char c = text[i];
|
||||
|
||||
if (c == ';') {
|
||||
// Try to match against the expected version string
|
||||
constexpr const char* kVersionPrefix = "; Version: 1.";
|
||||
constexpr const auto kPrefixLength = 13;
|
||||
// 'minor_digit_pos' is the expected position of the version digit.
|
||||
const auto minor_digit_pos = i + kPrefixLength;
|
||||
if (minor_digit_pos >= text.size()) return false;
|
||||
|
||||
// Match the prefix.
|
||||
auto j = 1;
|
||||
for (; j < kPrefixLength; ++j) {
|
||||
if (kVersionPrefix[j] != text[i + j]) break;
|
||||
}
|
||||
// j will match the prefix length if all characters before matched
|
||||
if (j == kPrefixLength) {
|
||||
// This expects only one digit in the minor number.
|
||||
static_assert(((spv::Version >> 8) & 0xff) < 10);
|
||||
char minor = text[minor_digit_pos];
|
||||
char next_char =
|
||||
minor_digit_pos + 1 < text.size() ? text[minor_digit_pos + 1] : 0;
|
||||
if (std::isdigit(minor) && !std::isdigit(next_char)) {
|
||||
const auto index = minor - '0';
|
||||
assert(index >= 0);
|
||||
if (static_cast<size_t>(index) < ordered_universal_envs.size()) {
|
||||
*env = ordered_universal_envs[index];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no match, determine whether the header has ended (in which case,
|
||||
// assumption has failed.)
|
||||
// Skip until the next line.
|
||||
i = j;
|
||||
for (; i < text.size(); ++i) {
|
||||
if (text[i] == '\n') break;
|
||||
}
|
||||
} else if (!std::isspace(c)) {
|
||||
// Allow blanks, but end the search if we find something else.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12))
|
||||
#define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8))
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#define SOURCE_SPIRV_TARGET_ENV_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/libspirv.h"
|
||||
|
||||
@ -46,4 +48,9 @@ std::string spvLogStringForEnv(spv_target_env env);
|
||||
// occur to satisfy this limit.
|
||||
std::string spvTargetEnvList(const int pad, const int wrap);
|
||||
|
||||
// Reads the target environment from the header comments of disassembly. Returns
|
||||
// true if valid name found, false otherwise.
|
||||
bool spvReadEnvironmentFromText(const std::vector<char>& text,
|
||||
spv_target_env* env);
|
||||
|
||||
#endif // SOURCE_SPIRV_TARGET_ENV_H_
|
||||
|
@ -170,5 +170,60 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
{VK(99, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0},
|
||||
}));
|
||||
|
||||
} // namespace
|
||||
// A test case for parsing the text header of disassembly.
|
||||
struct ParseEnvInDisassemblyCase {
|
||||
std::string text;
|
||||
bool success; // Expect to successfully parse?
|
||||
spv_target_env env; // The parsed environment, if successful.
|
||||
};
|
||||
|
||||
using TargetParseEnvInDisassemblyTest =
|
||||
::testing::TestWithParam<ParseEnvInDisassemblyCase>;
|
||||
|
||||
constexpr spv_target_env kSentinelEnv = SPV_ENV_OPENCL_2_2;
|
||||
|
||||
TEST_P(TargetParseEnvInDisassemblyTest, Samples) {
|
||||
const std::string& text = GetParam().text;
|
||||
const std::vector<char> text_vec(text.begin(), text.end());
|
||||
spv_target_env got_env = kSentinelEnv;
|
||||
bool parsed = spvReadEnvironmentFromText(text_vec, &got_env);
|
||||
EXPECT_EQ(parsed, GetParam().success);
|
||||
EXPECT_EQ(got_env, GetParam().env) << '"' << text << '"';
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TargetTextParsing, TargetParseEnvInDisassemblyTest,
|
||||
ValuesIn(std::vector<ParseEnvInDisassemblyCase>{
|
||||
{"; Version: 1.0", true, SPV_ENV_UNIVERSAL_1_0},
|
||||
{"; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
{"; Version: 1.2", true, SPV_ENV_UNIVERSAL_1_2},
|
||||
{"; Version: 1.3", true, SPV_ENV_UNIVERSAL_1_3},
|
||||
{"; Version: 1.4", true, SPV_ENV_UNIVERSAL_1_4},
|
||||
{"; Version: 1.5", true, SPV_ENV_UNIVERSAL_1_5},
|
||||
{"; Version: 1.6", true, SPV_ENV_UNIVERSAL_1_6},
|
||||
{"; Version: 1.7", false, kSentinelEnv},
|
||||
{"; Version: 1.8", false, kSentinelEnv},
|
||||
{"; Version: 1.9", false, kSentinelEnv},
|
||||
{"; Version: 2.0", false, kSentinelEnv},
|
||||
|
||||
// Check trailing text
|
||||
{"; Version: 1.1\n", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
{"; Version: 1.1\t", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
{"; Version: 1.1 ", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
{"; Version: 1.1x", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
// Not a digit.
|
||||
{"; Version: 1.10", false, kSentinelEnv},
|
||||
|
||||
// Unexpected prefix
|
||||
{";Version: 1.1", false, kSentinelEnv},
|
||||
|
||||
// Leading spaces
|
||||
{" \t ; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
// Previous lines
|
||||
{"; SPIR-V\n; Version: 1.1", true, SPV_ENV_UNIVERSAL_1_1},
|
||||
|
||||
// After a non-header line
|
||||
{"OpCapability Shader\n; Version: 1.1", false, kSentinelEnv}}));
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace spvtools
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "tools/io.h"
|
||||
#include "tools/util/flags.h"
|
||||
|
||||
static const auto kDefaultEnvironment = "spv1.6";
|
||||
constexpr auto kDefaultTarget = SPV_ENV_UNIVERSAL_1_6;
|
||||
static const std::string kHelpText =
|
||||
R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
|
||||
|
||||
@ -48,12 +48,13 @@ Options:
|
||||
)";
|
||||
|
||||
// clang-format off
|
||||
FLAG_SHORT_bool( h, /* default_value= */ false, /* required= */ false);
|
||||
FLAG_LONG_bool( help, /* default_value= */ false, /* required= */false);
|
||||
FLAG_LONG_bool( version, /* default_value= */ false, /* required= */ false);
|
||||
FLAG_LONG_bool( preserve_numeric_ids, /* default_value= */ false, /* required= */ false);
|
||||
FLAG_SHORT_string(o, /* default_value= */ "", /* required= */ false);
|
||||
FLAG_LONG_string( target_env, /* default_value= */ kDefaultEnvironment, /* required= */ false);
|
||||
// flag name= default_value= required=
|
||||
FLAG_SHORT_bool( h, false, false);
|
||||
FLAG_LONG_bool( help, false, false);
|
||||
FLAG_LONG_bool( version, false, false);
|
||||
FLAG_LONG_bool( preserve_numeric_ids, false, false);
|
||||
FLAG_SHORT_string(o, "", false);
|
||||
FLAG_LONG_string( target_env, "", false);
|
||||
// clang-format on
|
||||
|
||||
int main(int, const char** argv) {
|
||||
@ -68,17 +69,8 @@ int main(int, const char** argv) {
|
||||
}
|
||||
|
||||
if (flags::version.value()) {
|
||||
spv_target_env target_env;
|
||||
bool success = spvParseTargetEnv(kDefaultEnvironment, &target_env);
|
||||
assert(success && "Default environment should always parse.");
|
||||
if (!success) {
|
||||
fprintf(stderr,
|
||||
"error: invalid default target environment. Please report this "
|
||||
"issue.");
|
||||
return 1;
|
||||
}
|
||||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf("Target: %s\n", spvTargetEnvDescription(target_env));
|
||||
printf("Target: %s\n", spvTargetEnvDescription(kDefaultTarget));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -92,13 +84,6 @@ int main(int, const char** argv) {
|
||||
options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
|
||||
}
|
||||
|
||||
spv_target_env target_env;
|
||||
if (!spvParseTargetEnv(flags::target_env.value().c_str(), &target_env)) {
|
||||
fprintf(stderr, "error: Unrecognized target env: %s\n",
|
||||
flags::target_env.value().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (flags::positional_arguments.size() != 1) {
|
||||
fprintf(stderr, "error: exactly one input file must be specified.\n");
|
||||
return 1;
|
||||
@ -108,6 +93,20 @@ int main(int, const char** argv) {
|
||||
std::vector<char> contents;
|
||||
if (!ReadTextFile(inFile.c_str(), &contents)) return 1;
|
||||
|
||||
// Can only deduce target after the file has been read
|
||||
spv_target_env target_env;
|
||||
if (flags::target_env.value().empty()) {
|
||||
if (!spvReadEnvironmentFromText(contents, &target_env)) {
|
||||
// Revert to default version since deduction failed
|
||||
target_env = kDefaultTarget;
|
||||
}
|
||||
} else if (!spvParseTargetEnv(flags::target_env.value().c_str(),
|
||||
&target_env)) {
|
||||
fprintf(stderr, "error: Unrecognized target env: %s\n",
|
||||
flags::target_env.value().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
spv_binary binary;
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
spv_context context = spvContextCreate(target_env);
|
||||
|
Loading…
Reference in New Issue
Block a user