mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-08 07:33:33 +00:00
spirv-reader: support scalar spec constants
Translate OpSpecConstantTrue, OpSpecConstantFalse, and OpSpecConstant. The latter only can be used with integer or float scalars. If the constant has a SpecId decoration, then generate a module-scope decorated constant. Otherwise generate a module-scope constant without decorations. Register the ID so we know to use the declared const identifier in expressions later in the module. Bug: tint:156 Change-Id: Icd6e9b60225ced7ee99963c4f85cec1eb0e3ae6b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31541 Commit-Queue: David Neto <dneto@google.com> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
f0740ae0f2
commit
0a06243a70
@ -1700,7 +1700,7 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
|
|||||||
if (failed()) {
|
if (failed()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (identifier_values_.count(id)) {
|
if (identifier_values_.count(id) || parser_impl_.IsScalarSpecConstant(id)) {
|
||||||
return TypedExpression(
|
return TypedExpression(
|
||||||
parser_impl_.ConvertType(def_use_mgr_->GetDef(id)->type_id()),
|
parser_impl_.ConvertType(def_use_mgr_->GetDef(id)->type_id()),
|
||||||
std::make_unique<ast::IdentifierExpression>(namer_.Name(id)));
|
std::make_unique<ast::IdentifierExpression>(namer_.Name(id)));
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "src/ast/bool_literal.h"
|
#include "src/ast/bool_literal.h"
|
||||||
#include "src/ast/builtin.h"
|
#include "src/ast/builtin.h"
|
||||||
#include "src/ast/builtin_decoration.h"
|
#include "src/ast/builtin_decoration.h"
|
||||||
|
#include "src/ast/constant_id_decoration.h"
|
||||||
#include "src/ast/decorated_variable.h"
|
#include "src/ast/decorated_variable.h"
|
||||||
#include "src/ast/float_literal.h"
|
#include "src/ast/float_literal.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
@ -534,6 +535,9 @@ bool ParserImpl::ParseInternalModuleExceptFunctions() {
|
|||||||
if (!RegisterTypes()) {
|
if (!RegisterTypes()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!EmitScalarSpecConstants()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!EmitModuleScopeVariables()) {
|
if (!EmitModuleScopeVariables()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -947,6 +951,82 @@ bool ParserImpl::RegisterTypes() {
|
|||||||
return success_;
|
return success_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParserImpl::EmitScalarSpecConstants() {
|
||||||
|
if (!success_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Generate a module-scope const declaration for each instruction
|
||||||
|
// that is OpSpecConstantTrue, OpSpecConstantFalse, or OpSpecConstant.
|
||||||
|
for (auto& inst : module_->types_values()) {
|
||||||
|
// These will be populated for a valid scalar spec constant.
|
||||||
|
ast::type::Type* ast_type = nullptr;
|
||||||
|
std::unique_ptr<ast::ScalarConstructorExpression> ast_expr;
|
||||||
|
|
||||||
|
switch (inst.opcode()) {
|
||||||
|
case SpvOpSpecConstantTrue:
|
||||||
|
case SpvOpSpecConstantFalse: {
|
||||||
|
ast_type = ConvertType(inst.type_id());
|
||||||
|
ast_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::BoolLiteral>(
|
||||||
|
ast_type, inst.opcode() == SpvOpSpecConstantTrue));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SpvOpSpecConstant: {
|
||||||
|
ast_type = ConvertType(inst.type_id());
|
||||||
|
const uint32_t literal_value = inst.GetSingleWordInOperand(0);
|
||||||
|
if (ast_type->IsI32()) {
|
||||||
|
ast_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::SintLiteral>(
|
||||||
|
ast_type, static_cast<int32_t>(literal_value)));
|
||||||
|
} else if (ast_type->IsU32()) {
|
||||||
|
ast_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::UintLiteral>(
|
||||||
|
ast_type, static_cast<uint32_t>(literal_value)));
|
||||||
|
} else if (ast_type->IsF32()) {
|
||||||
|
float float_value;
|
||||||
|
// Copy the bits so we can read them as a float.
|
||||||
|
std::memcpy(&float_value, &literal_value, sizeof(float_value));
|
||||||
|
ast_expr = std::make_unique<ast::ScalarConstructorExpression>(
|
||||||
|
std::make_unique<ast::FloatLiteral>(ast_type, float_value));
|
||||||
|
} else {
|
||||||
|
return Fail() << " invalid result type for OpSpecConstant "
|
||||||
|
<< inst.PrettyPrint();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ast_type && ast_expr) {
|
||||||
|
auto ast_var =
|
||||||
|
MakeVariable(inst.result_id(), ast::StorageClass::kNone, ast_type);
|
||||||
|
ast::VariableDecorationList spec_id_decos;
|
||||||
|
for (const auto& deco : GetDecorationsFor(inst.result_id())) {
|
||||||
|
if ((deco.size() == 2) && (deco[0] == SpvDecorationSpecId)) {
|
||||||
|
auto cid = std::make_unique<ast::ConstantIdDecoration>(deco[1]);
|
||||||
|
spec_id_decos.push_back(std::move(cid));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (spec_id_decos.empty()) {
|
||||||
|
// Register it as a named constant, without specialization id.
|
||||||
|
ast_var->set_is_const(true);
|
||||||
|
ast_var->set_constructor(std::move(ast_expr));
|
||||||
|
ast_module_.AddGlobalVariable(std::move(ast_var));
|
||||||
|
} else {
|
||||||
|
auto ast_deco_var =
|
||||||
|
std::make_unique<ast::DecoratedVariable>(std::move(ast_var));
|
||||||
|
ast_deco_var->set_is_const(true);
|
||||||
|
ast_deco_var->set_constructor(std::move(ast_expr));
|
||||||
|
ast_deco_var->set_decorations(std::move(spec_id_decos));
|
||||||
|
ast_module_.AddGlobalVariable(std::move(ast_deco_var));
|
||||||
|
}
|
||||||
|
scalar_spec_constants_.insert(inst.result_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success_;
|
||||||
|
}
|
||||||
|
|
||||||
void ParserImpl::MaybeGenerateAlias(uint32_t type_id,
|
void ParserImpl::MaybeGenerateAlias(uint32_t type_id,
|
||||||
const spvtools::opt::analysis::Type* type) {
|
const spvtools::opt::analysis::Type* type) {
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
|
@ -246,6 +246,12 @@ class ParserImpl : Reader {
|
|||||||
/// @returns true if parser is still successful.
|
/// @returns true if parser is still successful.
|
||||||
bool RegisterTypes();
|
bool RegisterTypes();
|
||||||
|
|
||||||
|
/// Emit const definitions for scalar specialization constants generated
|
||||||
|
/// by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant.
|
||||||
|
/// This is a no-op if the parser has already failed.
|
||||||
|
/// @returns true if parser is still successful.
|
||||||
|
bool EmitScalarSpecConstants();
|
||||||
|
|
||||||
/// Emits module-scope variables.
|
/// Emits module-scope variables.
|
||||||
/// This is a no-op if the parser has already failed.
|
/// This is a no-op if the parser has already failed.
|
||||||
/// @returns true if parser is still successful.
|
/// @returns true if parser is still successful.
|
||||||
@ -373,6 +379,14 @@ class ParserImpl : Reader {
|
|||||||
/// @returns true if the given string is a valid WGSL identifier.
|
/// @returns true if the given string is a valid WGSL identifier.
|
||||||
static bool IsValidIdentifier(const std::string& str);
|
static bool IsValidIdentifier(const std::string& str);
|
||||||
|
|
||||||
|
/// Returns true if the given SPIR-V ID is a declared specialization constant,
|
||||||
|
/// generated by one of OpConstantTrue, OpConstantFalse, or OpSpecConstant
|
||||||
|
/// @param id a SPIR-V result ID
|
||||||
|
/// @returns true if the ID is a scalar spec constant.
|
||||||
|
bool IsScalarSpecConstant(uint32_t id) {
|
||||||
|
return scalar_spec_constants_.find(id) != scalar_spec_constants_.end();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Converts a specific SPIR-V type to a Tint type. Integer case
|
/// Converts a specific SPIR-V type to a Tint type. Integer case
|
||||||
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
|
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
|
||||||
@ -489,6 +503,9 @@ class ParserImpl : Reader {
|
|||||||
// The struct types with only read-only members.
|
// The struct types with only read-only members.
|
||||||
std::unordered_set<ast::type::Type*> read_only_struct_types_;
|
std::unordered_set<ast::type::Type*> read_only_struct_types_;
|
||||||
|
|
||||||
|
// The IDs of scalar spec constants
|
||||||
|
std::unordered_set<uint32_t> scalar_spec_constants_;
|
||||||
|
|
||||||
// Maps function_id to a list of entrypoint information
|
// Maps function_id to a list of entrypoint information
|
||||||
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
|
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
|
||||||
function_to_ep_info_;
|
function_to_ep_info_;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
#include "src/reader/spirv/function.h"
|
||||||
#include "src/reader/spirv/parser_impl.h"
|
#include "src/reader/spirv/parser_impl.h"
|
||||||
#include "src/reader/spirv/parser_impl_test_helper.h"
|
#include "src/reader/spirv/parser_impl_test_helper.h"
|
||||||
#include "src/reader/spirv/spirv_tools_helpers_test.h"
|
#include "src/reader/spirv/spirv_tools_helpers_test.h"
|
||||||
@ -1489,6 +1490,186 @@ TEST_F(
|
|||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_DeclareConst_True) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
OpDecorate %c SpecId 12
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%c = OpSpecConstantTrue %bool
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
DecoratedVariableConst{
|
||||||
|
Decorations{
|
||||||
|
ConstantIdDecoration{12}
|
||||||
|
}
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__bool
|
||||||
|
{
|
||||||
|
ScalarConstructor{true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_DeclareConst_False) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
OpDecorate %c SpecId 12
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%c = OpSpecConstantFalse %bool
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
DecoratedVariableConst{
|
||||||
|
Decorations{
|
||||||
|
ConstantIdDecoration{12}
|
||||||
|
}
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__bool
|
||||||
|
{
|
||||||
|
ScalarConstructor{false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_DeclareConst_U32) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
OpDecorate %c SpecId 12
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
%c = OpSpecConstant %uint 42
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
DecoratedVariableConst{
|
||||||
|
Decorations{
|
||||||
|
ConstantIdDecoration{12}
|
||||||
|
}
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__u32
|
||||||
|
{
|
||||||
|
ScalarConstructor{42}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_DeclareConst_I32) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
OpDecorate %c SpecId 12
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%c = OpSpecConstant %int 42
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
DecoratedVariableConst{
|
||||||
|
Decorations{
|
||||||
|
ConstantIdDecoration{12}
|
||||||
|
}
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__i32
|
||||||
|
{
|
||||||
|
ScalarConstructor{42}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_DeclareConst_F32) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
OpDecorate %c SpecId 12
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%c = OpSpecConstant %float 2.5
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
DecoratedVariableConst{
|
||||||
|
Decorations{
|
||||||
|
ConstantIdDecoration{12}
|
||||||
|
}
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__f32
|
||||||
|
{
|
||||||
|
ScalarConstructor{2.500000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest,
|
||||||
|
ModuleScopeVar_ScalarSpecConstant_DeclareConst_F32_WithoutSpecId) {
|
||||||
|
// When we don't have a spec ID, declare an undecorated module-scope constant.
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%c = OpSpecConstant %float 2.5
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
VariableConst{
|
||||||
|
myconst
|
||||||
|
none
|
||||||
|
__f32
|
||||||
|
{
|
||||||
|
ScalarConstructor{2.500000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_ScalarSpecConstant_UsedInFunction) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %c "myconst"
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%c = OpSpecConstant %float 2.5
|
||||||
|
%floatfn = OpTypeFunction %float
|
||||||
|
%100 = OpFunction %float None %floatfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%1 = OpIAdd %float %c %c
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(
|
||||||
|
VariableConst{
|
||||||
|
x_1
|
||||||
|
none
|
||||||
|
__f32
|
||||||
|
{
|
||||||
|
Binary{
|
||||||
|
Identifier{myconst}
|
||||||
|
add
|
||||||
|
Identifier{myconst}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})"))
|
||||||
|
<< ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
Loading…
x
Reference in New Issue
Block a user