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:
David Neto 2020-11-02 15:44:27 +00:00 committed by Commit Bot service account
parent f0740ae0f2
commit 0a06243a70
4 changed files with 279 additions and 1 deletions

View File

@ -1700,7 +1700,7 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
if (failed()) {
return {};
}
if (identifier_values_.count(id)) {
if (identifier_values_.count(id) || parser_impl_.IsScalarSpecConstant(id)) {
return TypedExpression(
parser_impl_.ConvertType(def_use_mgr_->GetDef(id)->type_id()),
std::make_unique<ast::IdentifierExpression>(namer_.Name(id)));

View File

@ -40,6 +40,7 @@
#include "src/ast/bool_literal.h"
#include "src/ast/builtin.h"
#include "src/ast/builtin_decoration.h"
#include "src/ast/constant_id_decoration.h"
#include "src/ast/decorated_variable.h"
#include "src/ast/float_literal.h"
#include "src/ast/scalar_constructor_expression.h"
@ -534,6 +535,9 @@ bool ParserImpl::ParseInternalModuleExceptFunctions() {
if (!RegisterTypes()) {
return false;
}
if (!EmitScalarSpecConstants()) {
return false;
}
if (!EmitModuleScopeVariables()) {
return false;
}
@ -947,6 +951,82 @@ bool ParserImpl::RegisterTypes() {
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,
const spvtools::opt::analysis::Type* type) {
if (!success_) {

View File

@ -246,6 +246,12 @@ class ParserImpl : Reader {
/// @returns true if parser is still successful.
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.
/// This is a no-op if the parser has already failed.
/// @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.
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:
/// Converts a specific SPIR-V type to a Tint type. Integer case
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.
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
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
function_to_ep_info_;

View File

@ -15,6 +15,7 @@
#include <string>
#include "gmock/gmock.h"
#include "src/reader/spirv/function.h"
#include "src/reader/spirv/parser_impl.h"
#include "src/reader/spirv/parser_impl_test_helper.h"
#include "src/reader/spirv/spirv_tools_helpers_test.h"
@ -1489,6 +1490,186 @@ TEST_F(
})")) << 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 spirv
} // namespace reader