spirv-tools: vendor as full-fork recipe (path=source, patches baked)
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2023 Google LLC.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "extract_source.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "source/latest_version_spirv_header.h"
|
||||
#include "source/opt/log.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "tools/util/cli_consumer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6;
|
||||
|
||||
// Extract a string literal from a given range.
|
||||
// Copies all the characters from `begin` to the first '\0' it encounters, while
|
||||
// removing escape patterns.
|
||||
// Not finding a '\0' before reaching `end` fails the extraction.
|
||||
//
|
||||
// Returns `true` if the extraction succeeded.
|
||||
// `output` value is undefined if false is returned.
|
||||
spv_result_t ExtractStringLiteral(const spv_position_t& loc, const char* begin,
|
||||
const char* end, std::string* output) {
|
||||
size_t sourceLength = std::distance(begin, end);
|
||||
std::string escapedString;
|
||||
escapedString.resize(sourceLength);
|
||||
|
||||
size_t writeIndex = 0;
|
||||
size_t readIndex = 0;
|
||||
for (; readIndex < sourceLength; writeIndex++, readIndex++) {
|
||||
const char read = begin[readIndex];
|
||||
if (read == '\0') {
|
||||
escapedString.resize(writeIndex);
|
||||
output->append(escapedString);
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (read == '\\') {
|
||||
++readIndex;
|
||||
}
|
||||
escapedString[writeIndex] = begin[readIndex];
|
||||
}
|
||||
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
|
||||
"Missing NULL terminator for literal string.");
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
spv_result_t extractOpString(const spv_position_t& loc,
|
||||
const spv_parsed_instruction_t& instruction,
|
||||
std::string* output) {
|
||||
assert(output != nullptr);
|
||||
assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpString));
|
||||
if (instruction.num_operands != 2) {
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
|
||||
"Missing operands for OpString.");
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
const auto& operand = instruction.operands[1];
|
||||
const char* stringBegin =
|
||||
reinterpret_cast<const char*>(instruction.words + operand.offset);
|
||||
const char* stringEnd = reinterpret_cast<const char*>(
|
||||
instruction.words + operand.offset + operand.num_words);
|
||||
return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
|
||||
}
|
||||
|
||||
spv_result_t extractOpSourceContinued(
|
||||
const spv_position_t& loc, const spv_parsed_instruction_t& instruction,
|
||||
std::string* output) {
|
||||
assert(output != nullptr);
|
||||
assert(instruction.opcode ==
|
||||
static_cast<unsigned>(spv::Op::OpSourceContinued));
|
||||
if (instruction.num_operands != 1) {
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
|
||||
"Missing operands for OpSourceContinued.");
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
const auto& operand = instruction.operands[0];
|
||||
const char* stringBegin =
|
||||
reinterpret_cast<const char*>(instruction.words + operand.offset);
|
||||
const char* stringEnd = reinterpret_cast<const char*>(
|
||||
instruction.words + operand.offset + operand.num_words);
|
||||
return ExtractStringLiteral(loc, stringBegin, stringEnd, output);
|
||||
}
|
||||
|
||||
spv_result_t extractOpSource(const spv_position_t& loc,
|
||||
const spv_parsed_instruction_t& instruction,
|
||||
spv::Id* filename, std::string* code) {
|
||||
assert(filename != nullptr && code != nullptr);
|
||||
assert(instruction.opcode == static_cast<unsigned>(spv::Op::OpSource));
|
||||
// OpCode [ Source Language | Version | File (optional) | Source (optional) ]
|
||||
if (instruction.num_words < 3) {
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
|
||||
"Missing operands for OpSource.");
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
*filename = 0;
|
||||
*code = "";
|
||||
if (instruction.num_words < 4) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
*filename = instruction.words[3];
|
||||
|
||||
if (instruction.num_words < 5) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
const char* stringBegin =
|
||||
reinterpret_cast<const char*>(instruction.words + 4);
|
||||
const char* stringEnd =
|
||||
reinterpret_cast<const char*>(instruction.words + instruction.num_words);
|
||||
return ExtractStringLiteral(loc, stringBegin, stringEnd, code);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ExtractSourceFromModule(
|
||||
const std::vector<uint32_t>& binary,
|
||||
std::unordered_map<std::string, std::string>* output) {
|
||||
auto context = spvtools::SpirvTools(kDefaultEnvironment);
|
||||
context.SetMessageConsumer(spvtools::utils::CLIMessageConsumer);
|
||||
|
||||
// There is nothing valuable in the header.
|
||||
spvtools::HeaderParser headerParser = [](const spv_endianness_t,
|
||||
const spv_parsed_header_t&) {
|
||||
return SPV_SUCCESS;
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, std::string> stringMap;
|
||||
std::vector<std::pair<spv::Id, std::string>> sources;
|
||||
spv::Op lastOpcode = spv::Op::Max;
|
||||
size_t instructionIndex = 0;
|
||||
|
||||
spvtools::InstructionParser instructionParser =
|
||||
[&stringMap, &sources, &lastOpcode,
|
||||
&instructionIndex](const spv_parsed_instruction_t& instruction) {
|
||||
const spv_position_t loc = {0, 0, instructionIndex + 1};
|
||||
spv_result_t result = SPV_SUCCESS;
|
||||
|
||||
if (instruction.opcode == static_cast<unsigned>(spv::Op::OpString)) {
|
||||
std::string content;
|
||||
result = extractOpString(loc, instruction, &content);
|
||||
if (result == SPV_SUCCESS) {
|
||||
stringMap.emplace(instruction.result_id, std::move(content));
|
||||
}
|
||||
} else if (instruction.opcode ==
|
||||
static_cast<unsigned>(spv::Op::OpSource)) {
|
||||
spv::Id filenameId;
|
||||
std::string code;
|
||||
result = extractOpSource(loc, instruction, &filenameId, &code);
|
||||
if (result == SPV_SUCCESS) {
|
||||
sources.emplace_back(std::make_pair(filenameId, std::move(code)));
|
||||
}
|
||||
} else if (instruction.opcode ==
|
||||
static_cast<unsigned>(spv::Op::OpSourceContinued)) {
|
||||
if (lastOpcode != spv::Op::OpSource) {
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", loc,
|
||||
"OpSourceContinued MUST follow an OpSource.");
|
||||
return SPV_ERROR_INVALID_BINARY;
|
||||
}
|
||||
|
||||
assert(sources.size() > 0);
|
||||
result = extractOpSourceContinued(loc, instruction,
|
||||
&sources.back().second);
|
||||
}
|
||||
|
||||
++instructionIndex;
|
||||
lastOpcode = static_cast<spv::Op>(instruction.opcode);
|
||||
return result;
|
||||
};
|
||||
|
||||
if (!context.Parse(binary, headerParser, instructionParser)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string defaultName = "unnamed-";
|
||||
size_t unnamedCount = 0;
|
||||
for (auto & [ id, code ] : sources) {
|
||||
std::string filename;
|
||||
const auto it = stringMap.find(id);
|
||||
if (it == stringMap.cend() || it->second.empty()) {
|
||||
filename = "unnamed-" + std::to_string(unnamedCount) + ".hlsl";
|
||||
++unnamedCount;
|
||||
} else {
|
||||
filename = it->second;
|
||||
}
|
||||
|
||||
if (output->count(filename) != 0) {
|
||||
spvtools::Error(spvtools::utils::CLIMessageConsumer, "", {},
|
||||
"Source file name conflict.");
|
||||
return false;
|
||||
}
|
||||
output->insert({filename, code});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user