reader/spirv - add type hierarchy

Don't create disjoint AST type nodes.
Instead use a new bespoke type hierarchy that can Build() the required
AST nodes.

Change-Id: I523f97054de2c553095056c0bafc17c48064cf53
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49966
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-05-10 17:25:21 +00:00 committed by Commit Bot service account
parent c705b6caac
commit cbbe576415
16 changed files with 1641 additions and 552 deletions

View File

@ -590,6 +590,8 @@ libtint_source_set("libtint_spv_reader_src") {
"reader/spirv/function.h",
"reader/spirv/namer.cc",
"reader/spirv/namer.h",
"reader/spirv/parser_type.cc",
"reader/spirv/parser_type.h",
"reader/spirv/parser.cc",
"reader/spirv/parser.h",
"reader/spirv/parser_impl.cc",

View File

@ -360,6 +360,8 @@ if(${TINT_BUILD_SPV_READER})
reader/spirv/function.h
reader/spirv/namer.cc
reader/spirv/namer.h
reader/spirv/parser_type.cc
reader/spirv/parser_type.h
reader/spirv/parser.cc
reader/spirv/parser.h
reader/spirv/parser_impl.cc
@ -627,6 +629,7 @@ if(${TINT_BUILD_TESTS})
reader/spirv/parser_impl_test_helper.h
reader/spirv/parser_impl_test.cc
reader/spirv/parser_impl_user_name_test.cc
reader/spirv/parser_type_test.cc
reader/spirv/parser_test.cc
reader/spirv/spirv_tools_helpers_test.cc
reader/spirv/spirv_tools_helpers_test.h

View File

@ -617,26 +617,32 @@ class ProgramBuilder {
/// @param subtype the array element type
/// @param n the array size. 0 represents a runtime-array
/// @param stride the array stride
/// @param stride the array stride. 0 represents implicit stride
/// @return the tint AST type for a array of size `n` of type `T`
ast::Array* array(typ::Type subtype, uint32_t n, uint32_t stride) const {
subtype = MaybeCreateTypename(subtype);
return array(subtype, n,
{builder->create<ast::StrideDecoration>(stride)});
ast::DecorationList decos;
if (stride) {
decos.emplace_back(builder->create<ast::StrideDecoration>(stride));
}
return array(subtype, n, std::move(decos));
}
/// @param source the Source of the node
/// @param subtype the array element type
/// @param n the array size. 0 represents a runtime-array
/// @param stride the array stride
/// @param stride the array stride. 0 represents implicit stride
/// @return the tint AST type for a array of size `n` of type `T`
ast::Array* array(const Source& source,
typ::Type subtype,
uint32_t n,
uint32_t stride) const {
subtype = MaybeCreateTypename(subtype);
return array(source, subtype, n,
{builder->create<ast::StrideDecoration>(stride)});
ast::DecorationList decos;
if (stride) {
decos.emplace_back(builder->create<ast::StrideDecoration>(stride));
}
return array(source, subtype, n, std::move(decos));
}
/// @return the tint AST type for an array of size `N` of type `T`

View File

@ -713,6 +713,7 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi,
const spvtools::opt::Function& function,
const EntryPointInfo* ep_info)
: parser_impl_(*pi),
ty_(pi->type_manager()),
builder_(pi->builder()),
ir_context_(*(pi->ir_context())),
def_use_mgr_(ir_context_.get_def_use_mgr()),
@ -733,6 +734,7 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi,
FunctionEmitter::FunctionEmitter(FunctionEmitter&& other)
: parser_impl_(other.parser_impl_),
ty_(other.ty_),
builder_(other.builder_),
ir_context_(other.ir_context_),
def_use_mgr_(ir_context_.get_def_use_mgr()),
@ -875,7 +877,7 @@ bool FunctionEmitter::Emit() {
auto* body = create<ast::BlockStatement>(Source{}, statements);
builder_.AST().AddFunction(create<ast::Function>(
decl.source, builder_.Symbols().Register(decl.name),
std::move(decl.params), decl.return_type, body,
std::move(decl.params), decl.return_type->Build(builder_), body,
std::move(decl.decorations), ast::DecorationList{}));
// Maintain the invariant by repopulating the one and only element.
@ -943,7 +945,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
return success();
}
ast::Type* FunctionEmitter::GetVariableStoreType(
const Type* FunctionEmitter::GetVariableStoreType(
const spvtools::opt::Instruction& var_decl_inst) {
const auto type_id = var_decl_inst.type_id();
auto* var_ref_type = type_mgr_->GetType(type_id);
@ -2042,7 +2044,7 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
<< id;
return {};
case SkipReason::kPointSizeBuiltinValue: {
return {create<ast::F32>(),
return {ty_.F32(),
create<ast::ScalarConstructorExpression>(
Source{}, create<ast::FloatLiteral>(Source{}, 1.0f))};
}
@ -2072,7 +2074,7 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
case SkipReason::kSampleMaskOutBuiltinPointer:
// The result type is always u32.
auto name = namer_.Name(sample_mask_out_id);
return TypedExpression{builder_.ty.u32(),
return TypedExpression{ty_.U32(),
create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name))};
}
@ -2348,14 +2350,10 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
const std::string guard_name = block_info.flow_guard_name;
if (!guard_name.empty()) {
// Declare the guard variable just before the "if", initialized to true.
auto* guard_var = create<ast::Variable>(
Source{}, // source
builder_.Symbols().Register(guard_name), // symbol
ast::StorageClass::kFunction, // storage_class
builder_.ty.bool_(), // type
false, // is_const
MakeTrue(Source{}), // constructor
ast::DecorationList{}); // decorations
auto* guard_var =
create<ast::Variable>(Source{}, builder_.Symbols().Register(guard_name),
ast::StorageClass::kFunction, builder_.ty.bool_(),
false, MakeTrue(Source{}), ast::DecorationList{});
auto* guard_decl = create<ast::VariableDeclStatement>(Source{}, guard_var);
AddStatement(guard_decl);
}
@ -2557,7 +2555,7 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
// The rest of this module can handle up to 64 bit switch values.
// The Tint AST handles 32-bit values.
const uint32_t value32 = uint32_t(value & 0xFFFFFFFF);
if (selector.type->is_unsigned_scalar_or_vector()) {
if (selector.type->IsUnsignedScalarOrVector()) {
selectors.emplace_back(create<ast::UintLiteral>(Source{}, value32));
} else {
selectors.emplace_back(create<ast::SintLiteral>(Source{}, value32));
@ -2914,13 +2912,10 @@ bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
const auto phi_var_name = GetDefInfo(id)->phi_var;
TINT_ASSERT(!phi_var_name.empty());
auto* var = create<ast::Variable>(
Source{}, // source
builder_.Symbols().Register(phi_var_name), // symbol
ast::StorageClass::kFunction, // storage_class
parser_impl_.ConvertType(def_inst->type_id()), // type
false, // is_const
nullptr, // constructor
ast::DecorationList{}); // decorations
Source{}, builder_.Symbols().Register(phi_var_name),
ast::StorageClass::kFunction,
parser_impl_.ConvertType(def_inst->type_id())->Build(builder_), false,
nullptr, ast::DecorationList{});
AddStatement(create<ast::VariableDeclStatement>(Source{}, var));
}
@ -3102,9 +3097,9 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
case SkipReason::kSampleMaskOutBuiltinPointer:
ptr_id = sample_mask_out_id;
if (!rhs.type->Is<ast::U32>()) {
if (!rhs.type->Is<U32>()) {
// WGSL requires sample_mask_out to be signed.
rhs = TypedExpression{builder_.ty.u32(),
rhs = TypedExpression{ty_.U32(),
create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.u32(),
ast::ExpressionList{rhs.expr})};
@ -3148,7 +3143,7 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
ast::Expression* id_expr = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name));
auto expr = TypedExpression{
builder_.ty.i32(),
ty_.I32(),
create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.i32(), ast::ExpressionList{id_expr})};
return EmitConstDefinition(inst, expr);
@ -3159,10 +3154,10 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
Source{}, builder_.Symbols().Register(name));
auto* load_result_type = parser_impl_.ConvertType(inst.type_id());
ast::Expression* ast_expr = nullptr;
if (load_result_type->Is<ast::I32>()) {
if (load_result_type->Is<I32>()) {
ast_expr = create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.i32(), ast::ExpressionList{id_expr});
} else if (load_result_type->Is<ast::U32>()) {
} else if (load_result_type->Is<U32>()) {
ast_expr = id_expr;
} else {
return Fail() << "loading the whole SampleMask input array is not "
@ -3181,8 +3176,8 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
}
// The load result type is the pointee type of its operand.
TINT_ASSERT(expr.type->Is<ast::Pointer>());
expr.type = expr.type->As<ast::Pointer>()->type();
TINT_ASSERT(expr.type->Is<Pointer>());
expr.type = expr.type->As<Pointer>()->type;
return EmitConstDefOrWriteToHoistedVar(inst, expr);
}
@ -3284,7 +3279,7 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
const auto opcode = inst.opcode();
ast::Type* ast_type =
const Type* ast_type =
inst.type_id() != 0 ? parser_impl_.ConvertType(inst.type_id()) : nullptr;
auto binary_op = ConvertBinaryOp(opcode);
@ -3329,8 +3324,9 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
}
if (opcode == SpvOpBitcast) {
return {ast_type, create<ast::BitcastExpression>(
Source{}, ast_type, MakeOperand(inst, 0).expr)};
return {ast_type,
create<ast::BitcastExpression>(Source{}, ast_type->Build(builder_),
MakeOperand(inst, 0).expr)};
}
if (opcode == SpvOpShiftLeftLogical || opcode == SpvOpShiftRightLogical ||
@ -3390,9 +3386,9 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
for (uint32_t iarg = 0; iarg < inst.NumInOperands(); ++iarg) {
operands.emplace_back(MakeOperand(inst, iarg).expr);
}
return {ast_type, create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(ast_type),
std::move(operands))};
return {ast_type,
create<ast::TypeConstructorExpression>(
Source{}, ast_type->Build(builder_), std::move(operands))};
}
if (opcode == SpvOpCompositeExtract) {
@ -3457,7 +3453,7 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst(
auto* func = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name));
ast::ExpressionList operands;
ast::Type* first_operand_type = nullptr;
const Type* first_operand_type = nullptr;
// All parameters to GLSL.std.450 extended instructions are IDs.
for (uint32_t iarg = 2; iarg < inst.NumInOperands(); ++iarg) {
TypedExpression operand = MakeOperand(inst, iarg);
@ -3703,7 +3699,7 @@ TypedExpression FunctionEmitter::MakeAccessChain(
type_mgr_->FindPointerToType(pointee_type_id, storage_class);
auto* ast_pointer_type = parser_impl_.ConvertType(pointer_type_id);
TINT_ASSERT(ast_pointer_type);
TINT_ASSERT(ast_pointer_type->Is<ast::Pointer>());
TINT_ASSERT(ast_pointer_type->Is<Pointer>());
current_expr = TypedExpression{ast_pointer_type, next_expr};
}
return current_expr;
@ -3887,8 +3883,8 @@ TypedExpression FunctionEmitter::MakeVectorShuffle(
// Generate an ast::TypeConstructor expression.
// Assume the literal indices are valid, and there is a valid number of them.
auto source = GetSourceForInst(inst);
ast::Vector* result_type =
parser_impl_.ConvertType(inst.type_id())->As<ast::Vector>();
const Vector* result_type =
As<Vector>(parser_impl_.ConvertType(inst.type_id()));
ast::ExpressionList values;
for (uint32_t i = 2; i < inst.NumInOperands(); ++i) {
const auto index = inst.GetSingleWordInOperand(i);
@ -3910,16 +3906,15 @@ TypedExpression FunctionEmitter::MakeVectorShuffle(
source, expr.expr, Swizzle(sub_index)));
} else if (index == 0xFFFFFFFF) {
// By rule, this maps to OpUndef. Instead, make it zero.
values.emplace_back(parser_impl_.MakeNullValue(result_type->type()));
values.emplace_back(parser_impl_.MakeNullValue(result_type->type));
} else {
Fail() << "invalid vectorshuffle ID %" << inst.result_id()
<< ": index too large: " << index;
return {};
}
}
return {result_type,
create<ast::TypeConstructorExpression>(
source, builder_.ty.MaybeCreateTypename(result_type), values)};
return {result_type, create<ast::TypeConstructorExpression>(
source, result_type->Build(builder_), values)};
}
bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
@ -3988,8 +3983,8 @@ bool FunctionEmitter::RegisterLocallyDefinedValues() {
if (type) {
if (type->AsPointer()) {
if (auto* ast_type = parser_impl_.ConvertType(inst.type_id())) {
if (auto* ptr = ast_type->As<ast::Pointer>()) {
info->storage_class = ptr->storage_class();
if (auto* ptr = ast_type->As<Pointer>()) {
info->storage_class = ptr->storage_class;
}
}
switch (inst.opcode()) {
@ -4033,21 +4028,21 @@ ast::StorageClass FunctionEmitter::GetStorageClassForPointerValue(uint32_t id) {
const auto type_id = def_use_mgr_->GetDef(id)->type_id();
if (type_id) {
auto* ast_type = parser_impl_.ConvertType(type_id);
if (auto* ptr = As<ast::Pointer>(ast_type)) {
return ptr->storage_class();
if (auto* ptr = As<Pointer>(ast_type)) {
return ptr->storage_class;
}
}
return ast::StorageClass::kNone;
}
ast::Type* FunctionEmitter::RemapStorageClass(ast::Type* type,
const Type* FunctionEmitter::RemapStorageClass(const Type* type,
uint32_t result_id) {
if (auto* ast_ptr_type = type->As<ast::Pointer>()) {
if (auto* ast_ptr_type = As<Pointer>(type)) {
// Remap an old-style storage buffer pointer to a new-style storage
// buffer pointer.
const auto sc = GetStorageClassForPointerValue(result_id);
if (ast_ptr_type->storage_class() != sc) {
return builder_.ty.pointer(ast_ptr_type->type(), sc);
if (ast_ptr_type->storage_class != sc) {
return ty_.Pointer(ast_ptr_type->type, sc);
}
}
return type;
@ -4230,30 +4225,27 @@ TypedExpression FunctionEmitter::MakeNumericConversion(
return {};
}
ast::Type* expr_type = nullptr;
const Type* expr_type = nullptr;
if ((opcode == SpvOpConvertSToF) || (opcode == SpvOpConvertUToF)) {
if (arg_expr.type->is_integer_scalar_or_vector()) {
if (arg_expr.type->IsIntegerScalarOrVector()) {
expr_type = requested_type;
} else {
Fail() << "operand for conversion to floating point must be integral "
"scalar or vector, but got: "
<< arg_expr.type->type_name();
"scalar or vector";
}
} else if (inst.opcode() == SpvOpConvertFToU) {
if (arg_expr.type->is_float_scalar_or_vector()) {
if (arg_expr.type->IsFloatScalarOrVector()) {
expr_type = parser_impl_.GetUnsignedIntMatchingShape(arg_expr.type);
} else {
Fail() << "operand for conversion to unsigned integer must be floating "
"point scalar or vector, but got: "
<< arg_expr.type->type_name();
"point scalar or vector";
}
} else if (inst.opcode() == SpvOpConvertFToS) {
if (arg_expr.type->is_float_scalar_or_vector()) {
if (arg_expr.type->IsFloatScalarOrVector()) {
expr_type = parser_impl_.GetSignedIntMatchingShape(arg_expr.type);
} else {
Fail() << "operand for conversion to signed integer must be floating "
"point scalar or vector, but got: "
<< arg_expr.type->type_name();
"point scalar or vector";
}
}
if (expr_type == nullptr) {
@ -4265,14 +4257,14 @@ TypedExpression FunctionEmitter::MakeNumericConversion(
params.push_back(arg_expr.expr);
TypedExpression result{
expr_type, create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(expr_type),
std::move(params))};
Source{}, expr_type->Build(builder_), std::move(params))};
if (AstTypesEquivalent(requested_type, expr_type)) {
if (requested_type == expr_type) {
return result;
}
return {requested_type, create<ast::BitcastExpression>(
Source{}, requested_type, result.expr)};
return {requested_type,
create<ast::BitcastExpression>(
Source{}, requested_type->Build(builder_), result.expr)};
}
bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
@ -4296,7 +4288,7 @@ bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
<< inst.PrettyPrint();
}
if (result_type->Is<ast::Void>()) {
if (result_type->Is<Void>()) {
return nullptr !=
AddStatement(create<ast::CallStatement>(Source{}, call_expr));
}
@ -4359,7 +4351,7 @@ TypedExpression FunctionEmitter::MakeIntrinsicCall(
Source{}, builder_.Symbols().Register(name));
ast::ExpressionList params;
ast::Type* first_operand_type = nullptr;
const Type* first_operand_type = nullptr;
for (uint32_t iarg = 0; iarg < inst.NumInOperands(); ++iarg) {
TypedExpression operand = MakeOperand(inst, iarg);
if (first_operand_type == nullptr) {
@ -4391,8 +4383,8 @@ TypedExpression FunctionEmitter::MakeSimpleSelect(
// - you can't select over pointers or pointer vectors, unless you also have
// a VariablePointers* capability, which is not allowed in by WebGPU.
auto* op_ty = operand1.type;
if (op_ty->Is<ast::Vector>() || op_ty->is_float_scalar() ||
op_ty->is_integer_scalar() || op_ty->Is<ast::Bool>()) {
if (op_ty->Is<Vector>() || op_ty->IsFloatScalar() ||
op_ty->IsIntegerScalar() || op_ty->Is<Bool>()) {
ast::ExpressionList params;
params.push_back(operand1.expr);
params.push_back(operand2.expr);
@ -4430,9 +4422,9 @@ const spvtools::opt::Instruction* FunctionEmitter::GetImage(
return image;
}
ast::Texture* FunctionEmitter::GetImageType(
const Texture* FunctionEmitter::GetImageType(
const spvtools::opt::Instruction& image) {
ast::Pointer* ptr_type = parser_impl_.GetTypeForHandleVar(image);
const Pointer* ptr_type = parser_impl_.GetTypeForHandleVar(image);
if (!parser_impl_.success()) {
Fail();
return {};
@ -4441,7 +4433,7 @@ ast::Texture* FunctionEmitter::GetImageType(
Fail() << "invalid texture type for " << image.PrettyPrint();
return {};
}
auto* result = ptr_type->type()->UnwrapAll()->As<ast::Texture>();
auto* result = ptr_type->type->UnwrapAll()->As<Texture>();
if (!result) {
Fail() << "invalid texture type for " << image.PrettyPrint();
return {};
@ -4496,12 +4488,12 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
}
}
ast::Pointer* texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image);
const Pointer* texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image);
if (!texture_ptr_type) {
return Fail();
}
ast::Texture* texture_type =
texture_ptr_type->type()->UnwrapAll()->As<ast::Texture>();
const Texture* texture_type =
texture_ptr_type->type->UnwrapAll()->As<Texture>();
if (!texture_type) {
return Fail();
@ -4604,7 +4596,7 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
}
TypedExpression lod = MakeOperand(inst, arg_index);
// When sampling from a depth texture, the Lod operand must be an I32.
if (texture_type->Is<ast::DepthTexture>()) {
if (texture_type->Is<DepthTexture>()) {
// Convert it to a signed integer type.
lod = ToI32(lod);
}
@ -4612,11 +4604,11 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
image_operands_mask ^= SpvImageOperandsLodMask;
arg_index++;
} else if ((opcode == SpvOpImageFetch) &&
(texture_type->Is<ast::SampledTexture>() ||
texture_type->Is<ast::DepthTexture>())) {
(texture_type->Is<SampledTexture>() ||
texture_type->Is<DepthTexture>())) {
// textureLoad on sampled texture and depth texture requires an explicit
// level-of-detail parameter.
params.push_back(parser_impl_.MakeNullValue(builder_.ty.i32()));
params.push_back(parser_impl_.MakeNullValue(ty_.I32()));
}
if (arg_index + 1 < num_args &&
(image_operands_mask & SpvImageOperandsGradMask)) {
@ -4637,7 +4629,7 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
return Fail() << "ConstOffset is only permitted for sampling operations: "
<< inst.PrettyPrint();
}
switch (texture_type->dim()) {
switch (texture_type->dims) {
case ast::TextureDimension::k2d:
case ast::TextureDimension::k2dArray:
case ast::TextureDimension::k3d:
@ -4676,8 +4668,8 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
// The result type, derived from the SPIR-V instruction.
auto* result_type = parser_impl_.ConvertType(inst.type_id());
auto* result_component_type = result_type;
if (auto* result_vector_type = result_type->As<ast::Vector>()) {
result_component_type = result_vector_type->type();
if (auto* result_vector_type = As<Vector>(result_type)) {
result_component_type = result_vector_type->type;
}
// For depth textures, the arity might mot match WGSL:
@ -4691,11 +4683,11 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
// dref gather vec4 ImageFetch vec4 TODO(dneto)
// Construct a 4-element vector with the result from the builtin in the
// first component.
if (texture_type->Is<ast::DepthTexture>()) {
if (texture_type->Is<DepthTexture>()) {
if (is_non_dref_sample || (opcode == SpvOpImageFetch)) {
value = create<ast::TypeConstructorExpression>(
Source{},
builder_.ty.MaybeCreateTypename(result_type), // a vec4
result_type->Build(builder_), // a vec4
ast::ExpressionList{
value, parser_impl_.MakeNullValue(result_component_type),
parser_impl_.MakeNullValue(result_component_type),
@ -4714,13 +4706,13 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
}
auto* expected_component_type =
parser_impl_.ConvertType(spirv_image_type->GetSingleWordInOperand(0));
if (!AstTypesEquivalent(expected_component_type, result_component_type)) {
if (expected_component_type != result_component_type) {
// This occurs if one is signed integer and the other is unsigned integer,
// or vice versa. Perform a bitcast.
value = create<ast::BitcastExpression>(Source{}, result_type, call_expr);
value = create<ast::BitcastExpression>(
Source{}, result_type->Build(builder_), call_expr);
}
if (!expected_component_type->Is<ast::F32>() &&
IsSampledImageAccess(opcode)) {
if (!expected_component_type->Is<F32>() && IsSampledImageAccess(opcode)) {
// WGSL permits sampled image access only on float textures.
// Reject this case in the SPIR-V reader, at least until SPIR-V validation
// catches up with this rule and can reject it earlier in the workflow.
@ -4763,7 +4755,7 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
}
exprs.push_back(
create<ast::CallExpression>(Source{}, dims_ident, dims_args));
if (ast::IsTextureArray(texture_type->dim())) {
if (ast::IsTextureArray(texture_type->dims)) {
auto* layers_ident = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register("textureNumLayers"));
exprs.push_back(create<ast::CallExpression>(
@ -4772,9 +4764,8 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
}
auto* result_type = parser_impl_.ConvertType(inst.type_id());
TypedExpression expr = {
result_type,
create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(result_type), exprs)};
result_type, create<ast::TypeConstructorExpression>(
Source{}, result_type->Build(builder_), exprs)};
return EmitConstDefOrWriteToHoistedVar(inst, expr);
}
case SpvOpImageQueryLod:
@ -4794,9 +4785,9 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
auto* result_type = parser_impl_.ConvertType(inst.type_id());
// The SPIR-V result type must be integer scalar. The WGSL bulitin
// returns i32. If they aren't the same then convert the result.
if (!result_type->Is<ast::I32>()) {
if (!result_type->Is<I32>()) {
ast_expr = create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(result_type),
Source{}, result_type->Build(builder_),
ast::ExpressionList{ast_expr});
}
TypedExpression expr{result_type, ast_expr};
@ -4840,28 +4831,27 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
if (!raw_coords.type) {
return {};
}
ast::Texture* texture_type = GetImageType(*image);
const Texture* texture_type = GetImageType(*image);
if (!texture_type) {
return {};
}
ast::TextureDimension dim = texture_type->dim();
ast::TextureDimension dim = texture_type->dims;
// Number of regular coordinates.
uint32_t num_axes = ast::NumCoordinateAxes(dim);
bool is_arrayed = ast::IsTextureArray(dim);
if ((num_axes == 0) || (num_axes > 3)) {
Fail() << "unsupported image dimensionality for "
<< texture_type->type_name() << " prompted by "
<< texture_type->TypeInfo().name << " prompted by "
<< inst.PrettyPrint();
}
const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0);
uint32_t num_coords_supplied = 0;
auto* component_type = raw_coords.type;
if (component_type->is_float_scalar() ||
component_type->is_integer_scalar()) {
if (component_type->IsFloatScalar() || component_type->IsIntegerScalar()) {
num_coords_supplied = 1;
} else if (auto* vec_type = raw_coords.type->As<ast::Vector>()) {
component_type = vec_type->type();
num_coords_supplied = vec_type->size();
} else if (auto* vec_type = As<Vector>(raw_coords.type)) {
component_type = vec_type->type;
num_coords_supplied = vec_type->size;
}
if (num_coords_supplied == 0) {
Fail() << "bad or unsupported coordinate type for image access: "
@ -4884,10 +4874,8 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
// will actually use them.
auto prefix_swizzle_expr = [this, num_axes, component_type,
raw_coords]() -> ast::Expression* {
auto* swizzle_type = (num_axes == 1)
? component_type
: static_cast<ast::Type*>(
builder_.ty.vec(component_type, num_axes));
auto* swizzle_type =
(num_axes == 1) ? component_type : ty_.Vector(component_type, num_axes);
auto* swizzle = create<ast::MemberAccessorExpression>(
Source{}, raw_coords.expr, PrefixSwizzle(num_axes));
return ToSignedIfUnsigned({swizzle_type, swizzle}).expr;
@ -4921,32 +4909,32 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
ast::Expression* FunctionEmitter::ConvertTexelForStorage(
const spvtools::opt::Instruction& inst,
TypedExpression texel,
ast::Texture* texture_type) {
auto* storage_texture_type = texture_type->As<ast::StorageTexture>();
const Texture* texture_type) {
auto* storage_texture_type = As<StorageTexture>(texture_type);
auto* src_type = texel.type;
if (!storage_texture_type) {
Fail() << "writing to other than storage texture: " << inst.PrettyPrint();
return nullptr;
}
const auto format = storage_texture_type->image_format();
const auto format = storage_texture_type->format;
auto* dest_type = parser_impl_.GetTexelTypeForFormat(format);
if (!dest_type) {
Fail();
return nullptr;
}
if (AstTypesEquivalent(src_type, dest_type)) {
if (src_type == dest_type) {
return texel.expr;
}
const uint32_t dest_count =
dest_type->is_scalar() ? 1 : dest_type->As<ast::Vector>()->size();
dest_type->IsScalar() ? 1 : dest_type->As<Vector>()->size;
if (dest_count == 3) {
Fail() << "3-channel storage textures are not supported: "
<< inst.PrettyPrint();
return nullptr;
}
const uint32_t src_count =
src_type->is_scalar() ? 1 : src_type->As<ast::Vector>()->size();
src_type->IsScalar() ? 1 : src_type->As<Vector>()->size;
if (src_count < dest_count) {
Fail() << "texel has too few components for storage texture: " << src_count
<< " provided but " << dest_count
@ -4961,29 +4949,29 @@ ast::Expression* FunctionEmitter::ConvertTexelForStorage(
: create<ast::MemberAccessorExpression>(Source{}, texel.expr,
PrefixSwizzle(dest_count));
if (!(dest_type->is_float_scalar_or_vector() ||
dest_type->is_unsigned_scalar_or_vector() ||
dest_type->is_signed_scalar_or_vector())) {
if (!(dest_type->IsFloatScalarOrVector() ||
dest_type->IsUnsignedScalarOrVector() ||
dest_type->IsSignedScalarOrVector())) {
Fail() << "invalid destination type for storage texture write: "
<< dest_type->type_name();
<< dest_type->TypeInfo().name;
return nullptr;
}
if (!(src_type->is_float_scalar_or_vector() ||
src_type->is_unsigned_scalar_or_vector() ||
src_type->is_signed_scalar_or_vector())) {
if (!(src_type->IsFloatScalarOrVector() ||
src_type->IsUnsignedScalarOrVector() ||
src_type->IsSignedScalarOrVector())) {
Fail() << "invalid texel type for storage texture write: "
<< inst.PrettyPrint();
return nullptr;
}
if (dest_type->is_float_scalar_or_vector() &&
!src_type->is_float_scalar_or_vector()) {
if (dest_type->IsFloatScalarOrVector() &&
!src_type->IsFloatScalarOrVector()) {
Fail() << "can only write float or float vector to a storage image with "
"floating texel format: "
<< inst.PrettyPrint();
return nullptr;
}
if (!dest_type->is_float_scalar_or_vector() &&
src_type->is_float_scalar_or_vector()) {
if (!dest_type->IsFloatScalarOrVector() &&
src_type->IsFloatScalarOrVector()) {
Fail()
<< "float or float vector can only be written to a storage image with "
"floating texel format: "
@ -4991,36 +4979,37 @@ ast::Expression* FunctionEmitter::ConvertTexelForStorage(
return nullptr;
}
if (dest_type->is_float_scalar_or_vector()) {
if (dest_type->IsFloatScalarOrVector()) {
return texel_prefix;
}
// The only remaining cases are signed/unsigned source, and signed/unsigned
// destination.
if (dest_type->is_unsigned_scalar_or_vector() ==
src_type->is_unsigned_scalar_or_vector()) {
if (dest_type->IsUnsignedScalarOrVector() ==
src_type->IsUnsignedScalarOrVector()) {
return texel_prefix;
}
// We must do a bitcast conversion.
return create<ast::BitcastExpression>(Source{}, dest_type, texel_prefix);
return create<ast::BitcastExpression>(Source{}, dest_type->Build(builder_),
texel_prefix);
}
TypedExpression FunctionEmitter::ToI32(TypedExpression value) {
if (!value.type || value.type->Is<ast::I32>()) {
if (!value.type || value.type->Is<I32>()) {
return value;
}
return {builder_.ty.i32(),
return {ty_.I32(),
create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.i32(), ast::ExpressionList{value.expr})};
}
TypedExpression FunctionEmitter::ToSignedIfUnsigned(TypedExpression value) {
if (!value.type || !value.type->is_unsigned_scalar_or_vector()) {
if (!value.type || !value.type->IsUnsignedScalarOrVector()) {
return value;
}
if (auto* vec_type = value.type->As<ast::Vector>()) {
auto new_type = builder_.ty.vec(builder_.ty.i32(), vec_type->size());
return {new_type,
builder_.Construct(new_type, ast::ExpressionList{value.expr})};
if (auto* vec_type = value.type->As<Vector>()) {
auto* new_type = ty_.Vector(ty_.I32(), vec_type->size);
return {new_type, builder_.Construct(new_type->Build(builder_),
ast::ExpressionList{value.expr})};
}
return ToI32(value);
}
@ -5073,14 +5062,12 @@ TypedExpression FunctionEmitter::MakeOuterProduct(
// Synthesize the result.
auto col = MakeOperand(inst, 0);
auto row = MakeOperand(inst, 1);
auto* col_ty = col.type->As<ast::Vector>();
auto* row_ty = row.type->As<ast::Vector>();
auto* result_ty = parser_impl_.ConvertType(inst.type_id())->As<ast::Matrix>();
if (!col_ty || !col_ty || !result_ty ||
!AstTypesEquivalent(result_ty->type(), col_ty->type()) ||
!AstTypesEquivalent(result_ty->type(), row_ty->type()) ||
result_ty->columns() != row_ty->size() ||
result_ty->rows() != col_ty->size()) {
auto* col_ty = As<Vector>(col.type);
auto* row_ty = As<Vector>(row.type);
auto* result_ty = As<Matrix>(parser_impl_.ConvertType(inst.type_id()));
if (!col_ty || !col_ty || !result_ty || result_ty->type != col_ty->type ||
result_ty->type != row_ty->type || result_ty->columns != row_ty->size ||
result_ty->rows != col_ty->size) {
Fail() << "invalid outer product instruction: bad types "
<< inst.PrettyPrint();
return {};
@ -5096,11 +5083,11 @@ TypedExpression FunctionEmitter::MakeOuterProduct(
// | c.z * r.x c.z * r.y |
ast::ExpressionList result_columns;
for (uint32_t icol = 0; icol < result_ty->columns(); icol++) {
for (uint32_t icol = 0; icol < result_ty->columns; icol++) {
ast::ExpressionList result_row;
auto* row_factor = create<ast::MemberAccessorExpression>(Source{}, row.expr,
Swizzle(icol));
for (uint32_t irow = 0; irow < result_ty->rows(); irow++) {
for (uint32_t irow = 0; irow < result_ty->rows; irow++) {
auto* column_factor = create<ast::MemberAccessorExpression>(
Source{}, col.expr, Swizzle(irow));
auto* elem = create<ast::BinaryExpression>(
@ -5108,11 +5095,10 @@ TypedExpression FunctionEmitter::MakeOuterProduct(
result_row.push_back(elem);
}
result_columns.push_back(create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(col_ty), result_row));
Source{}, col_ty->Build(builder_), result_row));
}
return {result_ty, create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(result_ty),
result_columns)};
Source{}, result_ty->Build(builder_), result_columns)};
}
bool FunctionEmitter::MakeVectorInsertDynamic(
@ -5142,8 +5128,7 @@ bool FunctionEmitter::MakeVectorInsertDynamic(
auto* temp_var = create<ast::Variable>(
Source{}, registered_temp_name, ast::StorageClass::kFunction,
builder_.ty.MaybeCreateTypename(ast_type), false, src_vector.expr,
ast::DecorationList{});
ast_type->Build(builder_), false, src_vector.expr, ast::DecorationList{});
AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));
auto* lhs = create<ast::ArrayAccessorExpression>(
@ -5189,7 +5174,7 @@ bool FunctionEmitter::MakeCompositeInsert(
auto* temp_var = create<ast::Variable>(
Source{}, registered_temp_name, ast::StorageClass::kFunction,
builder_.ty.MaybeCreateTypename(ast_type), false, src_composite.expr,
ast_type->Build(builder_), false, src_composite.expr,
ast::DecorationList{});
AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));

View File

@ -515,7 +515,7 @@ class FunctionEmitter {
/// @param type the AST type
/// @param result_id the SPIR-V ID for the locally defined value
/// @returns an possibly updated type
ast::Type* RemapStorageClass(ast::Type* type, uint32_t result_id);
const Type* RemapStorageClass(const Type* type, uint32_t result_id);
/// Marks locally defined values when they should get a 'const'
/// definition in WGSL, or a 'var' definition at an outer scope.
@ -856,7 +856,7 @@ class FunctionEmitter {
/// Function parameters
ast::VariableList params;
/// Function return type
ast::Type* return_type;
const Type* return_type;
/// Function decorations
ast::DecorationList decorations;
};
@ -869,7 +869,7 @@ class FunctionEmitter {
/// @returns the store type for the OpVariable instruction, or
/// null on failure.
ast::Type* GetVariableStoreType(
const Type* GetVariableStoreType(
const spvtools::opt::Instruction& var_decl_inst);
/// Returns an expression for an instruction operand. Signedness conversion is
@ -937,7 +937,7 @@ class FunctionEmitter {
/// Get the AST texture the SPIR-V image memory object declaration.
/// @param inst the SPIR-V memory object declaration for the image.
/// @returns a texture type, or null on error
ast::Texture* GetImageType(const spvtools::opt::Instruction& inst);
const Texture* GetImageType(const spvtools::opt::Instruction& inst);
/// Get the expression for the image operand from the first operand to the
/// given instruction.
@ -974,7 +974,7 @@ class FunctionEmitter {
ast::Expression* ConvertTexelForStorage(
const spvtools::opt::Instruction& inst,
TypedExpression texel,
ast::Texture* texture_type);
const Texture* texture_type);
/// Returns an expression for an OpSelect, if its operands are scalars
/// or vectors. These translate directly to WGSL select. Otherwise, return
@ -1128,6 +1128,7 @@ class FunctionEmitter {
using StatementsStack = std::vector<StatementBlock>;
ParserImpl& parser_impl_;
TypeManager& ty_;
ProgramBuilder& builder_;
spvtools::opt::IRContext& ir_context_;
spvtools::opt::analysis::DefUseManager* def_use_mgr_;

View File

@ -202,7 +202,7 @@ TEST_F(SpvUnaryConversionTest, ConvertSToF_Scalar_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
HasSubstr("operand for conversion to floating point must be "
"integral scalar or vector, but got: __bool"));
"integral scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertSToF_Vector_BadArgType) {
@ -220,7 +220,7 @@ TEST_F(SpvUnaryConversionTest, ConvertSToF_Vector_BadArgType) {
EXPECT_THAT(
p->error(),
HasSubstr("operand for conversion to floating point must be integral "
"scalar or vector, but got: __vec_2__bool"));
"scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertSToF_Scalar_FromSigned) {
@ -344,7 +344,7 @@ TEST_F(SpvUnaryConversionTest, ConvertUToF_Scalar_BadArgType) {
auto fe = p->function_emitter(100);
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(), Eq("operand for conversion to floating point must be "
"integral scalar or vector, but got: __bool"));
"integral scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertUToF_Vector_BadArgType) {
@ -361,7 +361,7 @@ TEST_F(SpvUnaryConversionTest, ConvertUToF_Vector_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
Eq("operand for conversion to floating point must be integral "
"scalar or vector, but got: __vec_2__bool"));
"scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertUToF_Scalar_FromSigned) {
@ -486,7 +486,7 @@ TEST_F(SpvUnaryConversionTest, ConvertFToS_Scalar_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
Eq("operand for conversion to signed integer must be floating "
"point scalar or vector, but got: __u32"));
"point scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertFToS_Vector_BadArgType) {
@ -503,7 +503,7 @@ TEST_F(SpvUnaryConversionTest, ConvertFToS_Vector_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
Eq("operand for conversion to signed integer must be floating "
"point scalar or vector, but got: __vec_2__bool"));
"point scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertFToS_Scalar_ToSigned) {
@ -628,7 +628,7 @@ TEST_F(SpvUnaryConversionTest, ConvertFToU_Scalar_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
Eq("operand for conversion to unsigned integer must be floating "
"point scalar or vector, but got: __u32"));
"point scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertFToU_Vector_BadArgType) {
@ -645,7 +645,7 @@ TEST_F(SpvUnaryConversionTest, ConvertFToU_Vector_BadArgType) {
EXPECT_FALSE(fe.EmitBody());
EXPECT_THAT(p->error(),
Eq("operand for conversion to unsigned integer must be floating "
"point scalar or vector, but got: __vec_2__bool"));
"point scalar or vector"));
}
TEST_F(SpvUnaryConversionTest, ConvertFToU_Scalar_ToSigned_IsError) {

View File

@ -836,7 +836,7 @@ TEST_F(SpvParserMemoryTest, RemapStorageBuffer_TypesAndVarDeclarations) {
Struct S {
[[block]]
StructMember{[[ offset 0 ]] field0: __u32}
StructMember{[[ offset 4 ]] field1: __alias_RTArr__array__u32_stride_4}
StructMember{[[ offset 4 ]] field1: __type_name_RTArr}
}
Variable{
Decorations{

View File

@ -240,7 +240,7 @@ TypedExpression::TypedExpression(const TypedExpression&) = default;
TypedExpression& TypedExpression::operator=(const TypedExpression&) = default;
TypedExpression::TypedExpression(ast::Type* type_in, ast::Expression* expr_in)
TypedExpression::TypedExpression(const Type* type_in, ast::Expression* expr_in)
: type(type_in), expr(expr_in) {}
ParserImpl::ParserImpl(const std::vector<uint32_t>& spv_binary)
@ -305,7 +305,7 @@ Program ParserImpl::program() {
return tint::Program(std::move(builder_));
}
ast::Type* ParserImpl::ConvertType(uint32_t type_id) {
const Type* ParserImpl::ConvertType(uint32_t type_id) {
if (!success_) {
return nullptr;
}
@ -322,18 +322,18 @@ ast::Type* ParserImpl::ConvertType(uint32_t type_id) {
}
auto maybe_generate_alias = [this, type_id,
spirv_type](ast::Type* type) -> ast::Type* {
spirv_type](const Type* type) -> const Type* {
if (type != nullptr) {
return MaybeGenerateAlias(type_id, spirv_type, type);
}
return {};
return type;
};
switch (spirv_type->kind()) {
case spvtools::opt::analysis::Type::kVoid:
return maybe_generate_alias(builder_.ty.void_());
return maybe_generate_alias(ty_.Void());
case spvtools::opt::analysis::Type::kBool:
return maybe_generate_alias(builder_.ty.bool_());
return maybe_generate_alias(ty_.Bool());
case spvtools::opt::analysis::Type::kInteger:
return maybe_generate_alias(ConvertType(spirv_type->AsInteger()));
case spvtools::opt::analysis::Type::kFloat:
@ -362,7 +362,7 @@ ast::Type* ParserImpl::ConvertType(uint32_t type_id) {
case spvtools::opt::analysis::Type::kImage:
// Fake it for sampler and texture types. These are handled in an
// entirely different way.
return maybe_generate_alias(builder_.ty.void_());
return maybe_generate_alias(ty_.Void());
default:
break;
}
@ -774,36 +774,36 @@ bool ParserImpl::RegisterEntryPoints() {
return success_;
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::Integer* int_ty) {
if (int_ty->width() == 32) {
return int_ty->IsSigned() ? static_cast<ast::Type*>(builder_.ty.i32())
: static_cast<ast::Type*>(builder_.ty.u32());
return int_ty->IsSigned() ? static_cast<const Type*>(ty_.I32())
: static_cast<const Type*>(ty_.U32());
}
Fail() << "unhandled integer width: " << int_ty->width();
return nullptr;
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::Float* float_ty) {
if (float_ty->width() == 32) {
return builder_.ty.f32();
return ty_.F32();
}
Fail() << "unhandled float width: " << float_ty->width();
return nullptr;
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::Vector* vec_ty) {
const auto num_elem = vec_ty->element_count();
auto* ast_elem_ty = ConvertType(type_mgr_->GetId(vec_ty->element_type()));
if (ast_elem_ty == nullptr) {
return nullptr;
return ast_elem_ty;
}
return builder_.ty.vec(ast_elem_ty, num_elem);
return ty_.Vector(ast_elem_ty, num_elem);
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::Matrix* mat_ty) {
const auto* vec_ty = mat_ty->element_type()->AsVector();
const auto* scalar_ty = vec_ty->element_type();
@ -813,23 +813,23 @@ ast::Type* ParserImpl::ConvertType(
if (ast_scalar_ty == nullptr) {
return nullptr;
}
return builder_.ty.mat(ast_scalar_ty, num_columns, num_rows);
return ty_.Matrix(ast_scalar_ty, num_columns, num_rows);
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::RuntimeArray* rtarr_ty) {
auto* ast_elem_ty = ConvertType(type_mgr_->GetId(rtarr_ty->element_type()));
if (ast_elem_ty == nullptr) {
return nullptr;
}
ast::DecorationList decorations;
if (!ParseArrayDecorations(rtarr_ty, &decorations)) {
uint32_t array_stride = 0;
if (!ParseArrayDecorations(rtarr_ty, &array_stride)) {
return nullptr;
}
return builder_.ty.array(ast_elem_ty, 0, std::move(decorations));
return ty_.Array(ast_elem_ty, 0, array_stride);
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
const spvtools::opt::analysis::Array* arr_ty) {
const auto elem_type_id = type_mgr_->GetId(arr_ty->element_type());
auto* ast_elem_ty = ConvertType(elem_type_id);
@ -863,21 +863,19 @@ ast::Type* ParserImpl::ConvertType(
<< num_elem;
return nullptr;
}
ast::DecorationList decorations;
if (!ParseArrayDecorations(arr_ty, &decorations)) {
uint32_t array_stride = 0;
if (!ParseArrayDecorations(arr_ty, &array_stride)) {
return nullptr;
}
if (remap_buffer_block_type_.count(elem_type_id)) {
remap_buffer_block_type_.insert(type_mgr_->GetId(arr_ty));
}
return builder_.ty.array(ast_elem_ty, static_cast<uint32_t>(num_elem),
std::move(decorations));
return ty_.Array(ast_elem_ty, static_cast<uint32_t>(num_elem), array_stride);
}
bool ParserImpl::ParseArrayDecorations(
const spvtools::opt::analysis::Type* spv_type,
ast::DecorationList* decorations) {
uint32_t* array_stride) {
bool has_array_stride = false;
const auto type_id = type_mgr_->GetId(spv_type);
for (auto& decoration : this->GetDecorationsFor(type_id)) {
@ -892,7 +890,7 @@ bool ParserImpl::ParseArrayDecorations(
<< ": multiple ArrayStride decorations";
}
has_array_stride = true;
decorations->push_back(create<ast::StrideDecoration>(Source{}, stride));
*array_stride = stride;
} else {
return Fail() << "invalid array type ID " << type_id
<< ": unknown decoration "
@ -904,7 +902,7 @@ bool ParserImpl::ParseArrayDecorations(
return true;
}
ast::Type* ParserImpl::ConvertType(
const Type* ParserImpl::ConvertType(
uint32_t type_id,
const spvtools::opt::analysis::Struct* struct_ty) {
// Compute the struct decoration.
@ -930,6 +928,7 @@ ast::Type* ParserImpl::ConvertType(
// Compute members
ast::StructMemberList ast_members;
const auto members = struct_ty->element_types();
TypeList ast_member_types;
unsigned num_non_writable_members = 0;
for (uint32_t member_index = 0; member_index < members.size();
++member_index) {
@ -940,6 +939,8 @@ ast::Type* ParserImpl::ConvertType(
return nullptr;
}
ast_member_types.emplace_back(ast_member_ty);
// Scan member for built-in decorations. Some vertex built-ins are handled
// specially, and should not generate a structure member.
bool create_ast_member = true;
@ -1003,8 +1004,8 @@ ast::Type* ParserImpl::ConvertType(
}
const auto member_name = namer_.GetMemberName(type_id, member_index);
auto* ast_struct_member = create<ast::StructMember>(
Source{}, builder_.Symbols().Register(member_name), ast_member_ty,
std::move(ast_member_decorations));
Source{}, builder_.Symbols().Register(member_name),
ast_member_ty->Build(builder_), std::move(ast_member_decorations));
ast_members.push_back(ast_struct_member);
}
@ -1030,7 +1031,7 @@ ast::Type* ParserImpl::ConvertType(
read_only_struct_types_.insert(ast_struct->name());
}
AddConstructedType(sym, ast_struct);
return ast_struct;
return ty_.Struct(sym, std::move(ast_member_types));
}
void ParserImpl::AddConstructedType(Symbol name, ast::NamedType* type) {
@ -1040,7 +1041,7 @@ void ParserImpl::AddConstructedType(Symbol name, ast::NamedType* type) {
}
}
ast::Type* ParserImpl::ConvertType(uint32_t type_id,
const Type* ParserImpl::ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Pointer*) {
const auto* inst = def_use_mgr_->GetDef(type_id);
const auto pointee_type_id = inst->GetSingleWordInOperand(1);
@ -1080,8 +1081,7 @@ ast::Type* ParserImpl::ConvertType(uint32_t type_id,
}
}
ast_elem_ty = builder_.ty.MaybeCreateTypename(ast_elem_ty);
return builder_.ty.pointer(ast_elem_ty, ast_storage_class);
return ty_.Pointer(ast_elem_ty, ast_storage_class);
}
bool ParserImpl::RegisterTypes() {
@ -1114,7 +1114,7 @@ bool ParserImpl::EmitScalarSpecConstants() {
// that is OpSpecConstantTrue, OpSpecConstantFalse, or OpSpecConstant.
for (auto& inst : module_->types_values()) {
// These will be populated for a valid scalar spec constant.
ast::Type* ast_type = nullptr;
const Type* ast_type = nullptr;
ast::ScalarConstructorExpression* ast_expr = nullptr;
switch (inst.opcode()) {
@ -1129,15 +1129,15 @@ bool ParserImpl::EmitScalarSpecConstants() {
case SpvOpSpecConstant: {
ast_type = ConvertType(inst.type_id());
const uint32_t literal_value = inst.GetSingleWordInOperand(0);
if (ast_type->Is<ast::I32>()) {
if (ast_type->Is<I32>()) {
ast_expr = create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>(
Source{}, static_cast<int32_t>(literal_value)));
} else if (ast_type->Is<ast::U32>()) {
} else if (ast_type->Is<U32>()) {
ast_expr = create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>(
Source{}, static_cast<uint32_t>(literal_value)));
} else if (ast_type->Is<ast::F32>()) {
} else if (ast_type->Is<F32>()) {
float float_value;
// Copy the bits so we can read them as a float.
std::memcpy(&float_value, &literal_value, sizeof(float_value));
@ -1173,12 +1173,12 @@ bool ParserImpl::EmitScalarSpecConstants() {
return success_;
}
ast::Type* ParserImpl::MaybeGenerateAlias(
const Type* ParserImpl::MaybeGenerateAlias(
uint32_t type_id,
const spvtools::opt::analysis::Type* type,
ast::Type* ast_type) {
const Type* ast_type) {
if (!success_) {
return {};
return nullptr;
}
// We only care about arrays, and runtime arrays.
@ -1202,16 +1202,17 @@ ast::Type* ParserImpl::MaybeGenerateAlias(
auto* ast_underlying_type = ast_type;
if (ast_underlying_type == nullptr) {
Fail() << "internal error: no type registered for SPIR-V ID: " << type_id;
return {};
return nullptr;
}
const auto name = namer_.GetName(type_id);
const auto sym = builder_.Symbols().Register(name);
auto ast_alias_type = builder_.ty.alias(sym, ast_underlying_type);
auto ast_alias_type =
builder_.ty.alias(sym, ast_underlying_type->Build(builder_));
// Record this new alias as the AST type for this SPIR-V ID.
AddConstructedType(sym, ast_alias_type);
return ast_alias_type;
return ty_.Alias(sym, ast_underlying_type);
}
bool ParserImpl::EmitModuleScopeVariables() {
@ -1252,7 +1253,7 @@ bool ParserImpl::EmitModuleScopeVariables() {
if (!success_) {
return false;
}
ast::Type* ast_type;
const Type* ast_type = nullptr;
if (spirv_storage_class == SpvStorageClassUniformConstant) {
// These are opaque handles: samplers or textures
ast_type = GetTypeForHandleVar(var);
@ -1266,14 +1267,14 @@ bool ParserImpl::EmitModuleScopeVariables() {
"SPIR-V type with ID: "
<< var.type_id();
}
if (!ast_type->Is<ast::Pointer>()) {
if (!ast_type->Is<Pointer>()) {
return Fail() << "variable with ID " << var.result_id()
<< " has non-pointer type " << var.type_id();
}
}
auto* ast_store_type = ast_type->As<ast::Pointer>()->type();
auto ast_storage_class = ast_type->As<ast::Pointer>()->storage_class();
auto* ast_store_type = ast_type->As<Pointer>()->type;
auto ast_storage_class = ast_type->As<Pointer>()->storage_class;
ast::Expression* ast_constructor = nullptr;
if (var.NumInOperands() > 1) {
// SPIR-V initializers are always constants.
@ -1336,7 +1337,7 @@ const spvtools::opt::analysis::IntConstant* ParserImpl::GetArraySize(
ast::Variable* ParserImpl::MakeVariable(uint32_t id,
ast::StorageClass sc,
ast::Type* type,
const Type* type,
bool is_const,
ast::Expression* constructor,
ast::DecorationList decorations) {
@ -1347,14 +1348,14 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
if (sc == ast::StorageClass::kStorage) {
bool read_only = false;
if (auto* tn = type->As<ast::TypeName>()) {
read_only = read_only_struct_types_.count(tn->name()) > 0;
if (auto* tn = type->As<Named>()) {
read_only = read_only_struct_types_.count(tn->name) > 0;
}
// Apply the access(read) or access(read_write) modifier.
auto access = read_only ? ast::AccessControl::kReadOnly
: ast::AccessControl::kReadWrite;
type = builder_.ty.access(access, type);
type = ty_.AccessControl(type, access);
}
for (auto& deco : GetDecorationsFor(id)) {
@ -1396,7 +1397,7 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
"SampleMask must be an array of 1 element.";
}
special_builtins_[id] = spv_builtin;
type = builder_.ty.u32();
type = ty_.U32();
break;
}
default:
@ -1439,8 +1440,8 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
std::string name = namer_.Name(id);
return create<ast::Variable>(Source{}, builder_.Symbols().Register(name), sc,
builder_.ty.MaybeCreateTypename(type), is_const,
constructor, decorations);
type->Build(builder_), is_const, constructor,
decorations);
}
TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
@ -1476,26 +1477,26 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
// So canonicalization should map that way too.
// Currently "null<type>" is missing from the WGSL parser.
// See https://bugs.chromium.org/p/tint/issues/detail?id=34
if (ast_type->Is<ast::U32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>(
if (ast_type->Is<U32>()) {
return {ty_.U32(), create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>(
source, spirv_const->GetU32()))};
}
if (ast_type->Is<ast::I32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>(
if (ast_type->Is<I32>()) {
return {ty_.I32(), create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>(
source, spirv_const->GetS32()))};
}
if (ast_type->Is<ast::F32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>(
if (ast_type->Is<F32>()) {
return {ty_.F32(), create<ast::ScalarConstructorExpression>(
Source{}, create<ast::FloatLiteral>(
source, spirv_const->GetFloat()))};
}
if (ast_type->Is<ast::Bool>()) {
if (ast_type->Is<Bool>()) {
const bool value = spirv_const->AsNullConstant()
? false
: spirv_const->AsBoolConstant()->value();
return {ast_type, create<ast::ScalarConstructorExpression>(
return {ty_.Bool(), create<ast::ScalarConstructorExpression>(
Source{}, create<ast::BoolLiteral>(source, value))};
}
auto* spirv_composite_const = spirv_const->AsCompositeConstant();
@ -1521,9 +1522,8 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
}
ast_components.emplace_back(ast_component.expr);
}
return {original_ast_type,
create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(original_ast_type),
return {original_ast_type, create<ast::TypeConstructorExpression>(
Source{}, original_ast_type->Build(builder_),
std::move(ast_components))};
}
auto* spirv_null_const = spirv_const->AsNullConstant();
@ -1535,7 +1535,7 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
return {};
}
ast::Expression* ParserImpl::MakeNullValue(ast::Type* type) {
ast::Expression* ParserImpl::MakeNullValue(const Type* type) {
// TODO(dneto): Use the no-operands constructor syntax when it becomes
// available in Tint.
// https://github.com/gpuweb/gpuweb/issues/685
@ -1549,93 +1549,89 @@ ast::Expression* ParserImpl::MakeNullValue(ast::Type* type) {
auto* original_type = type;
type = type->UnwrapIfNeeded();
if (type->Is<ast::Bool>()) {
if (type->Is<Bool>()) {
return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::BoolLiteral>(Source{}, false));
}
if (type->Is<ast::U32>()) {
if (type->Is<U32>()) {
return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>(Source{}, 0u));
}
if (type->Is<ast::I32>()) {
if (type->Is<I32>()) {
return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>(Source{}, 0));
}
if (type->Is<ast::F32>()) {
if (type->Is<F32>()) {
return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::FloatLiteral>(Source{}, 0.0f));
}
if (type->Is<ast::TypeName>()) {
if (type->Is<Alias>()) {
// TODO(amaiorano): No type constructor for TypeName (yet?)
ast::ExpressionList ast_components;
return create<ast::TypeConstructorExpression>(Source{}, original_type,
std::move(ast_components));
return create<ast::TypeConstructorExpression>(
Source{}, original_type->Build(builder_), std::move(ast_components));
}
if (auto* vec_ty = type->As<ast::Vector>()) {
if (auto* vec_ty = type->As<Vector>()) {
ast::ExpressionList ast_components;
for (size_t i = 0; i < vec_ty->size(); ++i) {
ast_components.emplace_back(MakeNullValue(vec_ty->type()));
for (size_t i = 0; i < vec_ty->size; ++i) {
ast_components.emplace_back(MakeNullValue(vec_ty->type));
}
return create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(type),
std::move(ast_components));
Source{}, type->Build(builder_), std::move(ast_components));
}
if (auto* mat_ty = type->As<ast::Matrix>()) {
if (auto* mat_ty = type->As<Matrix>()) {
// Matrix components are columns
auto column_ty = builder_.ty.vec(mat_ty->type(), mat_ty->rows());
auto* column_ty = ty_.Vector(mat_ty->type, mat_ty->rows);
ast::ExpressionList ast_components;
for (size_t i = 0; i < mat_ty->columns(); ++i) {
for (size_t i = 0; i < mat_ty->columns; ++i) {
ast_components.emplace_back(MakeNullValue(column_ty));
}
return create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(type),
std::move(ast_components));
Source{}, type->Build(builder_), std::move(ast_components));
}
if (auto* arr_ty = type->As<ast::Array>()) {
if (auto* arr_ty = type->As<Array>()) {
ast::ExpressionList ast_components;
for (size_t i = 0; i < arr_ty->size(); ++i) {
ast_components.emplace_back(MakeNullValue(arr_ty->type()));
for (size_t i = 0; i < arr_ty->size; ++i) {
ast_components.emplace_back(MakeNullValue(arr_ty->type));
}
return create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(original_type),
std::move(ast_components));
Source{}, original_type->Build(builder_), std::move(ast_components));
}
if (auto* struct_ty = type->As<ast::Struct>()) {
if (auto* struct_ty = type->As<Struct>()) {
ast::ExpressionList ast_components;
for (auto* member : struct_ty->members()) {
ast_components.emplace_back(MakeNullValue(member->type()));
for (auto* member : struct_ty->members) {
ast_components.emplace_back(MakeNullValue(member));
}
return create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.MaybeCreateTypename(original_type),
std::move(ast_components));
Source{}, original_type->Build(builder_), std::move(ast_components));
}
Fail() << "can't make null value for type: " << type->type_name();
Fail() << "can't make null value for type: " << type->TypeInfo().name;
return nullptr;
}
TypedExpression ParserImpl::MakeNullExpression(ast::Type* type) {
TypedExpression ParserImpl::MakeNullExpression(const Type* type) {
return {type, MakeNullValue(type)};
}
ast::Type* ParserImpl::UnsignedTypeFor(ast::Type* type) {
if (type->Is<ast::I32>()) {
return builder_.ty.u32();
const Type* ParserImpl::UnsignedTypeFor(const Type* type) {
if (type->Is<I32>()) {
return ty_.U32();
}
if (auto* v = type->As<ast::Vector>()) {
if (v->type()->Is<ast::I32>()) {
return builder_.ty.vec(builder_.ty.u32(), v->size());
if (auto* v = type->As<Vector>()) {
if (v->type->Is<I32>()) {
return ty_.Vector(ty_.U32(), v->size);
}
}
return {};
}
ast::Type* ParserImpl::SignedTypeFor(ast::Type* type) {
if (type->Is<ast::U32>()) {
return builder_.ty.i32();
const Type* ParserImpl::SignedTypeFor(const Type* type) {
if (type->Is<U32>()) {
return ty_.I32();
}
if (auto* v = type->As<ast::Vector>()) {
if (v->type()->Is<ast::U32>()) {
return builder_.ty.vec(builder_.ty.i32(), v->size());
if (auto* v = type->As<Vector>()) {
if (v->type->Is<U32>()) {
return ty_.Vector(ty_.I32(), v->size);
}
}
return {};
@ -1674,13 +1670,14 @@ TypedExpression ParserImpl::RectifyOperandSignedness(
if (auto* unsigned_ty = UnsignedTypeFor(type)) {
// Conversion is required.
return {unsigned_ty,
create<ast::BitcastExpression>(Source{}, unsigned_ty, expr.expr)};
create<ast::BitcastExpression>(
Source{}, unsigned_ty->Build(builder_), expr.expr)};
}
} else if (requires_signed) {
if (auto* signed_ty = SignedTypeFor(type)) {
// Conversion is required.
return {signed_ty,
create<ast::BitcastExpression>(Source{}, signed_ty, expr.expr)};
return {signed_ty, create<ast::BitcastExpression>(
Source{}, signed_ty->Build(builder_), expr.expr)};
}
}
// We should not reach here.
@ -1689,21 +1686,22 @@ TypedExpression ParserImpl::RectifyOperandSignedness(
TypedExpression ParserImpl::RectifySecondOperandSignedness(
const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type,
const Type* first_operand_type,
TypedExpression&& second_operand_expr) {
if (!AstTypesEquivalent(first_operand_type, second_operand_expr.type) &&
if ((first_operand_type != second_operand_expr.type) &&
AssumesSecondOperandSignednessMatchesFirstOperand(inst.opcode())) {
// Conversion is required.
return {first_operand_type,
create<ast::BitcastExpression>(Source{}, first_operand_type,
create<ast::BitcastExpression>(Source{},
first_operand_type->Build(builder_),
second_operand_expr.expr)};
}
// No conversion necessary.
return std::move(second_operand_expr);
}
ast::Type* ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type) {
const Type* ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst,
const Type* first_operand_type) {
const auto opcode = inst.opcode();
if (AssumesResultSignednessMatchesFirstOperand(opcode)) {
return first_operand_type;
@ -1718,66 +1716,63 @@ ast::Type* ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst,
return nullptr;
}
ast::Type* ParserImpl::GetSignedIntMatchingShape(ast::Type* other) {
const Type* ParserImpl::GetSignedIntMatchingShape(const Type* other) {
if (other == nullptr) {
Fail() << "no type provided";
}
auto i32 = builder_.ty.i32();
if (other->Is<ast::F32>() || other->Is<ast::U32>() || other->Is<ast::I32>()) {
return i32;
if (other->Is<F32>() || other->Is<U32>() || other->Is<I32>()) {
return ty_.I32();
}
auto* vec_ty = other->As<ast::Vector>();
if (vec_ty) {
return builder_.ty.vec(i32, vec_ty->size());
if (auto* vec_ty = other->As<Vector>()) {
return ty_.Vector(ty_.I32(), vec_ty->size);
}
Fail() << "required numeric scalar or vector, but got " << other->type_name();
Fail() << "required numeric scalar or vector, but got "
<< other->TypeInfo().name;
return nullptr;
}
ast::Type* ParserImpl::GetUnsignedIntMatchingShape(ast::Type* other) {
const Type* ParserImpl::GetUnsignedIntMatchingShape(const Type* other) {
if (other == nullptr) {
Fail() << "no type provided";
return nullptr;
}
auto u32 = builder_.ty.u32();
if (other->Is<ast::F32>() || other->Is<ast::U32>() || other->Is<ast::I32>()) {
return u32;
if (other->Is<F32>() || other->Is<U32>() || other->Is<I32>()) {
return ty_.U32();
}
auto* vec_ty = other->As<ast::Vector>();
if (vec_ty) {
return builder_.ty.vec(u32, vec_ty->size());
if (auto* vec_ty = other->As<Vector>()) {
return ty_.Vector(ty_.U32(), vec_ty->size);
}
Fail() << "required numeric scalar or vector, but got " << other->type_name();
Fail() << "required numeric scalar or vector, but got "
<< other->TypeInfo().name;
return nullptr;
}
TypedExpression ParserImpl::RectifyForcedResultType(
TypedExpression expr,
const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type) {
const Type* first_operand_type) {
auto* forced_result_ty = ForcedResultType(inst, first_operand_type);
if ((forced_result_ty == nullptr) ||
AstTypesEquivalent(forced_result_ty, expr.type)) {
if ((!forced_result_ty) || (forced_result_ty == expr.type)) {
return expr;
}
return {expr.type,
create<ast::BitcastExpression>(Source{}, expr.type, expr.expr)};
return {expr.type, create<ast::BitcastExpression>(
Source{}, expr.type->Build(builder_), expr.expr)};
}
TypedExpression ParserImpl::AsUnsigned(TypedExpression expr) {
if (expr.type && expr.type->is_signed_scalar_or_vector()) {
if (expr.type && expr.type->IsSignedScalarOrVector()) {
auto* new_type = GetUnsignedIntMatchingShape(expr.type);
return {new_type,
create<ast::BitcastExpression>(Source{}, new_type, expr.expr)};
return {new_type, create<ast::BitcastExpression>(
Source{}, new_type->Build(builder_), expr.expr)};
}
return expr;
}
TypedExpression ParserImpl::AsSigned(TypedExpression expr) {
if (expr.type && expr.type->is_unsigned_scalar_or_vector()) {
if (expr.type && expr.type->IsUnsignedScalarOrVector()) {
auto* new_type = GetSignedIntMatchingShape(expr.type);
return {new_type,
create<ast::BitcastExpression>(Source{}, new_type, expr.expr)};
return {new_type, create<ast::BitcastExpression>(
Source{}, new_type->Build(builder_), expr.expr)};
}
return expr;
}
@ -1952,7 +1947,7 @@ ParserImpl::GetSpirvTypeForHandleMemoryObjectDeclaration(
return raw_handle_type;
}
ast::Pointer* ParserImpl::GetTypeForHandleVar(
const Pointer* ParserImpl::GetTypeForHandleVar(
const spvtools::opt::Instruction& var) {
auto where = handle_type_.find(&var);
if (where != handle_type_.end()) {
@ -2036,10 +2031,10 @@ ast::Pointer* ParserImpl::GetTypeForHandleVar(
}
// Construct the Tint handle type.
ast::Type* ast_store_type;
const Type* ast_store_type = nullptr;
if (usage.IsSampler()) {
ast_store_type = builder_.ty.sampler(
usage.IsComparisonSampler() ? ast::SamplerKind::kComparisonSampler
ast_store_type = ty_.Sampler(usage.IsComparisonSampler()
? ast::SamplerKind::kComparisonSampler
: ast::SamplerKind::kSampler);
} else if (usage.IsTexture()) {
const spvtools::opt::analysis::Image* image_type =
@ -2069,14 +2064,13 @@ ast::Pointer* ParserImpl::GetTypeForHandleVar(
// OpImage variable with an OpImage*Dref* instruction. In WGSL we must
// treat that as a depth texture.
if (image_type->depth() || usage.IsDepthTexture()) {
ast_store_type = builder_.ty.depth_texture(dim);
ast_store_type = ty_.DepthTexture(dim);
} else if (image_type->is_multisampled()) {
// Multisampled textures are never depth textures.
ast_store_type =
builder_.ty.multisampled_texture(dim, ast_sampled_component_type);
ty_.MultisampledTexture(dim, ast_sampled_component_type);
} else {
ast_store_type =
builder_.ty.sampled_texture(dim, ast_sampled_component_type);
ast_store_type = ty_.SampledTexture(dim, ast_sampled_component_type);
}
} else {
const auto access = usage.IsStorageReadTexture()
@ -2087,7 +2081,7 @@ ast::Pointer* ParserImpl::GetTypeForHandleVar(
return nullptr;
}
ast_store_type =
builder_.ty.access(access, builder_.ty.storage_texture(dim, format));
ty_.AccessControl(ty_.StorageTexture(dim, format), access);
}
} else {
Fail() << "unsupported: UniformConstant variable is not a recognized "
@ -2097,14 +2091,14 @@ ast::Pointer* ParserImpl::GetTypeForHandleVar(
}
// Form the pointer type.
auto result =
builder_.ty.pointer(ast_store_type, ast::StorageClass::kUniformConstant);
auto* result =
ty_.Pointer(ast_store_type, ast::StorageClass::kUniformConstant);
// Remember it for later.
handle_type_[&var] = result;
return result;
}
ast::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
const Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
switch (format) {
case ast::ImageFormat::kR8Uint:
case ast::ImageFormat::kR16Uint:
@ -2115,7 +2109,7 @@ ast::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Uint:
case ast::ImageFormat::kRgba16Uint:
case ast::ImageFormat::kRgba32Uint:
return builder_.ty.u32();
return ty_.U32();
case ast::ImageFormat::kR8Sint:
case ast::ImageFormat::kR16Sint:
@ -2126,7 +2120,7 @@ ast::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Sint:
case ast::ImageFormat::kRgba16Sint:
case ast::ImageFormat::kRgba32Sint:
return builder_.ty.i32();
return ty_.I32();
case ast::ImageFormat::kR8Unorm:
case ast::ImageFormat::kRg8Unorm:
@ -2145,7 +2139,7 @@ ast::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Float:
case ast::ImageFormat::kRgba16Float:
case ast::ImageFormat::kRgba32Float:
return builder_.ty.f32();
return ty_.F32();
default:
break;
}
@ -2153,7 +2147,7 @@ ast::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
return nullptr;
}
ast::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
const Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
auto* component_type = GetComponentTypeForFormat(format);
if (!component_type) {
return nullptr;
@ -2185,7 +2179,7 @@ ast::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg8Uint:
case ast::ImageFormat::kRg8Unorm:
// Two channels
return builder_.ty.vec(component_type, 2);
return ty_.Vector(component_type, 2);
case ast::ImageFormat::kBgra8Unorm:
case ast::ImageFormat::kBgra8UnormSrgb:
@ -2202,7 +2196,7 @@ ast::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRgba8Unorm:
case ast::ImageFormat::kRgba8UnormSrgb:
// Four channels
return builder_.ty.vec(component_type, 4);
return ty_.Vector(component_type, 4);
default:
break;

View File

@ -28,6 +28,7 @@
#include "src/reader/spirv/entry_point_info.h"
#include "src/reader/spirv/enum_converter.h"
#include "src/reader/spirv/namer.h"
#include "src/reader/spirv/parser_type.h"
#include "src/reader/spirv/usage.h"
/// This is the implementation of the SPIR-V parser for Tint.
@ -51,14 +52,6 @@ namespace tint {
namespace reader {
namespace spirv {
/// Returns true of the two input ast types are semantically equivalent
/// @param lhs first type to compare
/// @param rhs other type to compare
/// @returns true if both types are semantically equivalent
inline bool AstTypesEquivalent(ast::Type* lhs, ast::Type* rhs) {
return lhs->type_name() == rhs->type_name();
}
/// The binary representation of a SPIR-V decoration enum followed by its
/// operands, if any.
/// Example: { SpvDecorationBlock }
@ -81,10 +74,10 @@ struct TypedExpression {
/// Constructor
/// @param type_in the type of the expression
/// @param expr_in the expression
TypedExpression(ast::Type* type_in, ast::Expression* expr_in);
TypedExpression(const Type* type_in, ast::Expression* expr_in);
/// The type
ast::Type* type;
Type const* type = nullptr;
/// The expression
ast::Expression* expr = nullptr;
};
@ -110,6 +103,9 @@ class ParserImpl : Reader {
/// program. To be used only for testing.
ProgramBuilder& builder() { return builder_; }
/// @returns the type manager
TypeManager& type_manager() { return ty_; }
/// Logs failure, ands return a failure stream to accumulate diagnostic
/// messages. By convention, a failure should only be logged along with
/// a non-empty string diagnostic.
@ -163,7 +159,7 @@ class ParserImpl : Reader {
/// after the internal representation of the module has been built.
/// @param type_id the SPIR-V ID of a type.
/// @returns a Tint type, or nullptr
ast::Type* ConvertType(uint32_t type_id);
const Type* ConvertType(uint32_t type_id);
/// Emits an alias type declaration for the given type, if necessary, and
/// also updates the mapping of the SPIR-V type ID to the alias type.
@ -176,9 +172,9 @@ class ParserImpl : Reader {
/// @param type the type that might get an alias
/// @param ast_type the ast type that might get an alias
/// @returns an alias type or `ast_type` if no alias was created
ast::Type* MaybeGenerateAlias(uint32_t type_id,
const Type* MaybeGenerateAlias(uint32_t type_id,
const spvtools::opt::analysis::Type* type,
ast::Type* ast_type);
const Type* ast_type);
/// @returns the fail stream object
FailStream& fail_stream() { return fail_stream_; }
@ -328,7 +324,7 @@ class ParserImpl : Reader {
/// in the error case
ast::Variable* MakeVariable(uint32_t id,
ast::StorageClass sc,
ast::Type* type,
const Type* type,
bool is_const,
ast::Expression* constructor,
ast::DecorationList decorations);
@ -341,12 +337,12 @@ class ParserImpl : Reader {
/// Creates an AST expression node for the null value for the given type.
/// @param type the AST type
/// @returns a new expression
ast::Expression* MakeNullValue(ast::Type* type);
ast::Expression* MakeNullValue(const Type* type);
/// Make a typed expression for the null value for the given type.
/// @param type the AST type
/// @returns a new typed expression
TypedExpression MakeNullExpression(ast::Type* type);
TypedExpression MakeNullExpression(const Type* type);
/// Converts a given expression to the signedness demanded for an operand
/// of the given SPIR-V instruction, if required. If the instruction assumes
@ -371,7 +367,7 @@ class ParserImpl : Reader {
/// @returns second_operand_expr, or a cast of it
TypedExpression RectifySecondOperandSignedness(
const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type,
const Type* first_operand_type,
TypedExpression&& second_operand_expr);
/// Returns the "forced" result type for the given SPIR-V instruction.
@ -382,8 +378,8 @@ class ParserImpl : Reader {
/// @param inst the SPIR-V instruction
/// @param first_operand_type the AST type for the first operand.
/// @returns the forced AST result type, or nullptr if no forcing is required.
ast::Type* ForcedResultType(const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type);
const Type* ForcedResultType(const spvtools::opt::Instruction& inst,
const Type* first_operand_type);
/// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a
@ -391,7 +387,7 @@ class ParserImpl : Reader {
/// requirement.
/// @param other the type whose shape must be matched
/// @returns the signed scalar or vector type
ast::Type* GetSignedIntMatchingShape(ast::Type* other);
const Type* GetSignedIntMatchingShape(const Type* other);
/// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a
@ -399,7 +395,7 @@ class ParserImpl : Reader {
/// requirement.
/// @param other the type whose shape must be matched
/// @returns the unsigned scalar or vector type
ast::Type* GetUnsignedIntMatchingShape(ast::Type* other);
const Type* GetUnsignedIntMatchingShape(const Type* other);
/// Wraps the given expression in an as-cast to the given expression's type,
/// when the underlying operation produces a forced result type different
@ -412,18 +408,20 @@ class ParserImpl : Reader {
TypedExpression RectifyForcedResultType(
TypedExpression expr,
const spvtools::opt::Instruction& inst,
ast::Type* first_operand_type);
const Type* first_operand_type);
/// @returns the given expression, but ensuring it's an unsigned type of the
/// same shape as the operand. Wraps the expresion with a bitcast if needed.
/// Returns the given expression, but ensuring it's an unsigned type of the
/// same shape as the operand. Wraps the expression with a bitcast if needed.
/// Assumes the given expresion is a integer scalar or vector.
/// @param expr an integer scalar or integer vector expression.
/// @return the potentially cast TypedExpression
TypedExpression AsUnsigned(TypedExpression expr);
/// @returns the given expression, but ensuring it's a signed type of the
/// same shape as the operand. Wraps the expresion with a bitcast if needed.
/// Returns the given expression, but ensuring it's a signed type of the
/// same shape as the operand. Wraps the expression with a bitcast if needed.
/// Assumes the given expresion is a integer scalar or vector.
/// @param expr an integer scalar or integer vector expression.
/// @return the potentially cast TypedExpression
TypedExpression AsSigned(TypedExpression expr);
/// Bookkeeping used for tracking the "position" builtin variable.
@ -512,18 +510,18 @@ class ParserImpl : Reader {
/// @param var the OpVariable instruction
/// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
/// error
ast::Pointer* GetTypeForHandleVar(const spvtools::opt::Instruction& var);
const Pointer* GetTypeForHandleVar(const spvtools::opt::Instruction& var);
/// Returns the channel component type corresponding to the given image
/// format.
/// @param format image texel format
/// @returns the component type, one of f32, i32, u32
ast::Type* GetComponentTypeForFormat(ast::ImageFormat format);
const Type* GetComponentTypeForFormat(ast::ImageFormat format);
/// Returns texel type corresponding to the given image format.
/// @param format image texel format
/// @returns the texel format
ast::Type* GetTexelTypeForFormat(ast::ImageFormat format);
const Type* GetTexelTypeForFormat(ast::ImageFormat format);
/// Returns the SPIR-V instruction with the given ID, or nullptr.
/// @param id the SPIR-V result ID
@ -561,19 +559,20 @@ class ParserImpl : Reader {
private:
/// Converts a specific SPIR-V type to a Tint type. Integer case
ast::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
const Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
/// Converts a specific SPIR-V type to a Tint type. Float case
ast::Type* ConvertType(const spvtools::opt::analysis::Float* float_ty);
const Type* ConvertType(const spvtools::opt::analysis::Float* float_ty);
/// Converts a specific SPIR-V type to a Tint type. Vector case
ast::Type* ConvertType(const spvtools::opt::analysis::Vector* vec_ty);
const Type* ConvertType(const spvtools::opt::analysis::Vector* vec_ty);
/// Converts a specific SPIR-V type to a Tint type. Matrix case
ast::Type* ConvertType(const spvtools::opt::analysis::Matrix* mat_ty);
const Type* ConvertType(const spvtools::opt::analysis::Matrix* mat_ty);
/// Converts a specific SPIR-V type to a Tint type. RuntimeArray case
/// @param rtarr_ty the Tint type
ast::Type* ConvertType(const spvtools::opt::analysis::RuntimeArray* rtarr_ty);
const Type* ConvertType(
const spvtools::opt::analysis::RuntimeArray* rtarr_ty);
/// Converts a specific SPIR-V type to a Tint type. Array case
/// @param arr_ty the Tint type
ast::Type* ConvertType(const spvtools::opt::analysis::Array* arr_ty);
const Type* ConvertType(const spvtools::opt::analysis::Array* arr_ty);
/// Converts a specific SPIR-V type to a Tint type. Struct case.
/// SPIR-V allows distinct struct type definitions for two OpTypeStruct
/// that otherwise have the same set of members (and struct and member
@ -585,34 +584,34 @@ class ParserImpl : Reader {
/// not significant to the optimizer's module representation.
/// @param type_id the SPIR-V ID for the type.
/// @param struct_ty the Tint type
ast::Type* ConvertType(uint32_t type_id,
const Type* ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Struct* struct_ty);
/// Converts a specific SPIR-V type to a Tint type. Pointer case
/// The pointer to gl_PerVertex maps to nullptr, and instead is recorded
/// in member #builtin_position_.
/// @param type_id the SPIR-V ID for the type.
/// @param ptr_ty the Tint type
ast::Type* ConvertType(uint32_t type_id,
const Type* ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Pointer* ptr_ty);
/// If `type` is a signed integral, or vector of signed integral,
/// returns the unsigned type, otherwise returns `type`.
/// @param type the possibly signed type
/// @returns the unsigned type
ast::Type* UnsignedTypeFor(ast::Type* type);
const Type* UnsignedTypeFor(const Type* type);
/// If `type` is a unsigned integral, or vector of unsigned integral,
/// returns the signed type, otherwise returns `type`.
/// @param type the possibly unsigned type
/// @returns the signed type
ast::Type* SignedTypeFor(ast::Type* type);
const Type* SignedTypeFor(const Type* type);
/// Parses the array or runtime-array decorations.
/// @param spv_type the SPIR-V array or runtime-array type.
/// @param decorations the populated decoration list
/// @param array_stride pointer to the array stride
/// @returns true on success.
bool ParseArrayDecorations(const spvtools::opt::analysis::Type* spv_type,
ast::DecorationList* decorations);
uint32_t* array_stride);
/// Adds `type` as a constructed type if it hasn't been added yet.
/// @param name the type's unique name
@ -633,6 +632,9 @@ class ParserImpl : Reader {
// The program builder.
ProgramBuilder builder_;
// The type manager.
TypeManager ty_;
// Is the parse successful?
bool success_ = true;
// Collector for diagnostic messages.
@ -716,7 +718,7 @@ class ParserImpl : Reader {
// usages implied by usages of the memory-object-declaration.
std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
// The inferred pointer type for the given handle variable.
std::unordered_map<const spvtools::opt::Instruction*, ast::Pointer*>
std::unordered_map<const spvtools::opt::Instruction*, const Pointer*>
handle_type_;
// Set of symbols of constructed types that have been added, used to avoid

View File

@ -75,7 +75,7 @@ TEST_F(SpvParserTest, ConvertType_Void) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<ast::Void>());
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
@ -84,7 +84,7 @@ TEST_F(SpvParserTest, ConvertType_Bool) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(100);
EXPECT_TRUE(type->Is<ast::Bool>());
EXPECT_TRUE(type->Is<Bool>());
EXPECT_TRUE(p->error().empty());
}
@ -93,7 +93,7 @@ TEST_F(SpvParserTest, ConvertType_I32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(2);
EXPECT_TRUE(type->Is<ast::I32>());
EXPECT_TRUE(type->Is<I32>());
EXPECT_TRUE(p->error().empty());
}
@ -102,7 +102,7 @@ TEST_F(SpvParserTest, ConvertType_U32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::U32>());
EXPECT_TRUE(type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
@ -111,7 +111,7 @@ TEST_F(SpvParserTest, ConvertType_F32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(4);
EXPECT_TRUE(type->Is<ast::F32>());
EXPECT_TRUE(type->Is<F32>());
EXPECT_TRUE(p->error().empty());
}
@ -155,19 +155,19 @@ TEST_F(SpvParserTest, ConvertType_VecOverF32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xf32 = p->ConvertType(20);
EXPECT_TRUE(v2xf32->Is<ast::Vector>());
EXPECT_TRUE(v2xf32->As<ast::Vector>()->type()->Is<ast::F32>());
EXPECT_EQ(v2xf32->As<ast::Vector>()->size(), 2u);
EXPECT_TRUE(v2xf32->Is<Vector>());
EXPECT_TRUE(v2xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v2xf32->As<Vector>()->size, 2u);
auto* v3xf32 = p->ConvertType(30);
EXPECT_TRUE(v3xf32->Is<ast::Vector>());
EXPECT_TRUE(v3xf32->As<ast::Vector>()->type()->Is<ast::F32>());
EXPECT_EQ(v3xf32->As<ast::Vector>()->size(), 3u);
EXPECT_TRUE(v3xf32->Is<Vector>());
EXPECT_TRUE(v3xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v3xf32->As<Vector>()->size, 3u);
auto* v4xf32 = p->ConvertType(40);
EXPECT_TRUE(v4xf32->Is<ast::Vector>());
EXPECT_TRUE(v4xf32->As<ast::Vector>()->type()->Is<ast::F32>());
EXPECT_EQ(v4xf32->As<ast::Vector>()->size(), 4u);
EXPECT_TRUE(v4xf32->Is<Vector>());
EXPECT_TRUE(v4xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v4xf32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
@ -182,19 +182,19 @@ TEST_F(SpvParserTest, ConvertType_VecOverI32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xi32 = p->ConvertType(20);
EXPECT_TRUE(v2xi32->Is<ast::Vector>());
EXPECT_TRUE(v2xi32->As<ast::Vector>()->type()->Is<ast::I32>());
EXPECT_EQ(v2xi32->As<ast::Vector>()->size(), 2u);
EXPECT_TRUE(v2xi32->Is<Vector>());
EXPECT_TRUE(v2xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v2xi32->As<Vector>()->size, 2u);
auto* v3xi32 = p->ConvertType(30);
EXPECT_TRUE(v3xi32->Is<ast::Vector>());
EXPECT_TRUE(v3xi32->As<ast::Vector>()->type()->Is<ast::I32>());
EXPECT_EQ(v3xi32->As<ast::Vector>()->size(), 3u);
EXPECT_TRUE(v3xi32->Is<Vector>());
EXPECT_TRUE(v3xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v3xi32->As<Vector>()->size, 3u);
auto* v4xi32 = p->ConvertType(40);
EXPECT_TRUE(v4xi32->Is<ast::Vector>());
EXPECT_TRUE(v4xi32->As<ast::Vector>()->type()->Is<ast::I32>());
EXPECT_EQ(v4xi32->As<ast::Vector>()->size(), 4u);
EXPECT_TRUE(v4xi32->Is<Vector>());
EXPECT_TRUE(v4xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v4xi32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
@ -209,19 +209,19 @@ TEST_F(SpvParserTest, ConvertType_VecOverU32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xu32 = p->ConvertType(20);
EXPECT_TRUE(v2xu32->Is<ast::Vector>());
EXPECT_TRUE(v2xu32->As<ast::Vector>()->type()->Is<ast::U32>());
EXPECT_EQ(v2xu32->As<ast::Vector>()->size(), 2u);
EXPECT_TRUE(v2xu32->Is<Vector>());
EXPECT_TRUE(v2xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v2xu32->As<Vector>()->size, 2u);
auto* v3xu32 = p->ConvertType(30);
EXPECT_TRUE(v3xu32->Is<ast::Vector>());
EXPECT_TRUE(v3xu32->As<ast::Vector>()->type()->Is<ast::U32>());
EXPECT_EQ(v3xu32->As<ast::Vector>()->size(), 3u);
EXPECT_TRUE(v3xu32->Is<Vector>());
EXPECT_TRUE(v3xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v3xu32->As<Vector>()->size, 3u);
auto* v4xu32 = p->ConvertType(40);
EXPECT_TRUE(v4xu32->Is<ast::Vector>());
EXPECT_TRUE(v4xu32->As<ast::Vector>()->type()->Is<ast::U32>());
EXPECT_EQ(v4xu32->As<ast::Vector>()->size(), 4u);
EXPECT_TRUE(v4xu32->Is<Vector>());
EXPECT_TRUE(v4xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v4xu32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
@ -261,58 +261,58 @@ TEST_F(SpvParserTest, ConvertType_MatrixOverF32) {
EXPECT_TRUE(p->BuildInternalModule());
auto* m22 = p->ConvertType(22);
EXPECT_TRUE(m22->Is<ast::Matrix>());
EXPECT_TRUE(m22->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m22->As<ast::Matrix>()->rows(), 2u);
EXPECT_EQ(m22->As<ast::Matrix>()->columns(), 2u);
EXPECT_TRUE(m22->Is<Matrix>());
EXPECT_TRUE(m22->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m22->As<Matrix>()->rows, 2u);
EXPECT_EQ(m22->As<Matrix>()->columns, 2u);
auto* m23 = p->ConvertType(23);
EXPECT_TRUE(m23->Is<ast::Matrix>());
EXPECT_TRUE(m23->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m23->As<ast::Matrix>()->rows(), 2u);
EXPECT_EQ(m23->As<ast::Matrix>()->columns(), 3u);
EXPECT_TRUE(m23->Is<Matrix>());
EXPECT_TRUE(m23->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m23->As<Matrix>()->rows, 2u);
EXPECT_EQ(m23->As<Matrix>()->columns, 3u);
auto* m24 = p->ConvertType(24);
EXPECT_TRUE(m24->Is<ast::Matrix>());
EXPECT_TRUE(m24->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m24->As<ast::Matrix>()->rows(), 2u);
EXPECT_EQ(m24->As<ast::Matrix>()->columns(), 4u);
EXPECT_TRUE(m24->Is<Matrix>());
EXPECT_TRUE(m24->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m24->As<Matrix>()->rows, 2u);
EXPECT_EQ(m24->As<Matrix>()->columns, 4u);
auto* m32 = p->ConvertType(32);
EXPECT_TRUE(m32->Is<ast::Matrix>());
EXPECT_TRUE(m32->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m32->As<ast::Matrix>()->rows(), 3u);
EXPECT_EQ(m32->As<ast::Matrix>()->columns(), 2u);
EXPECT_TRUE(m32->Is<Matrix>());
EXPECT_TRUE(m32->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m32->As<Matrix>()->rows, 3u);
EXPECT_EQ(m32->As<Matrix>()->columns, 2u);
auto* m33 = p->ConvertType(33);
EXPECT_TRUE(m33->Is<ast::Matrix>());
EXPECT_TRUE(m33->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m33->As<ast::Matrix>()->rows(), 3u);
EXPECT_EQ(m33->As<ast::Matrix>()->columns(), 3u);
EXPECT_TRUE(m33->Is<Matrix>());
EXPECT_TRUE(m33->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m33->As<Matrix>()->rows, 3u);
EXPECT_EQ(m33->As<Matrix>()->columns, 3u);
auto* m34 = p->ConvertType(34);
EXPECT_TRUE(m34->Is<ast::Matrix>());
EXPECT_TRUE(m34->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m34->As<ast::Matrix>()->rows(), 3u);
EXPECT_EQ(m34->As<ast::Matrix>()->columns(), 4u);
EXPECT_TRUE(m34->Is<Matrix>());
EXPECT_TRUE(m34->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m34->As<Matrix>()->rows, 3u);
EXPECT_EQ(m34->As<Matrix>()->columns, 4u);
auto* m42 = p->ConvertType(42);
EXPECT_TRUE(m42->Is<ast::Matrix>());
EXPECT_TRUE(m42->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m42->As<ast::Matrix>()->rows(), 4u);
EXPECT_EQ(m42->As<ast::Matrix>()->columns(), 2u);
EXPECT_TRUE(m42->Is<Matrix>());
EXPECT_TRUE(m42->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m42->As<Matrix>()->rows, 4u);
EXPECT_EQ(m42->As<Matrix>()->columns, 2u);
auto* m43 = p->ConvertType(43);
EXPECT_TRUE(m43->Is<ast::Matrix>());
EXPECT_TRUE(m43->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m43->As<ast::Matrix>()->rows(), 4u);
EXPECT_EQ(m43->As<ast::Matrix>()->columns(), 3u);
EXPECT_TRUE(m43->Is<Matrix>());
EXPECT_TRUE(m43->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m43->As<Matrix>()->rows, 4u);
EXPECT_EQ(m43->As<Matrix>()->columns, 3u);
auto* m44 = p->ConvertType(44);
EXPECT_TRUE(m44->Is<ast::Matrix>());
EXPECT_TRUE(m44->As<ast::Matrix>()->type()->Is<ast::F32>());
EXPECT_EQ(m44->As<ast::Matrix>()->rows(), 4u);
EXPECT_EQ(m44->As<ast::Matrix>()->columns(), 4u);
EXPECT_TRUE(m44->Is<Matrix>());
EXPECT_TRUE(m44->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m44->As<Matrix>()->rows, 4u);
EXPECT_EQ(m44->As<Matrix>()->columns, 4u);
EXPECT_TRUE(p->error().empty());
}
@ -326,15 +326,14 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<ast::Array>());
auto* arr_type = type->UnwrapAliasIfNeeded()->As<ast::Array>();
EXPECT_TRUE(arr_type->IsRuntimeArray());
EXPECT_TRUE(type->UnwrapAll()->Is<Array>());
auto* arr_type = type->UnwrapAll()->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size(), 0u);
EXPECT_EQ(arr_type->decorations().size(), 0u);
auto* elem_type = arr_type->type();
EXPECT_EQ(arr_type->size, 0u);
EXPECT_EQ(arr_type->stride, 0u);
auto* elem_type = arr_type->type;
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<ast::U32>());
EXPECT_TRUE(elem_type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
@ -361,14 +360,10 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
auto* arr_type = type->UnwrapAliasIfNeeded()->As<ast::Array>();
EXPECT_TRUE(arr_type->IsRuntimeArray());
auto* arr_type = type->UnwrapAll()->As<Array>();
EXPECT_EQ(arr_type->size, 0u);
ASSERT_NE(arr_type, nullptr);
ASSERT_EQ(arr_type->decorations().size(), 1u);
auto* stride = arr_type->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 64u);
EXPECT_TRUE(p->error().empty());
EXPECT_EQ(arr_type->stride, 64u);
}
TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_ZeroIsError) {
@ -409,15 +404,14 @@ TEST_F(SpvParserTest, ConvertType_Array) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<ast::Array>());
auto* arr_type = type->As<ast::Array>();
EXPECT_FALSE(arr_type->IsRuntimeArray());
EXPECT_TRUE(type->Is<Array>());
auto* arr_type = type->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size(), 42u);
EXPECT_EQ(arr_type->decorations().size(), 0u);
auto* elem_type = arr_type->type();
EXPECT_EQ(arr_type->size, 42u);
EXPECT_EQ(arr_type->stride, 0u);
auto* elem_type = arr_type->type;
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<ast::U32>());
EXPECT_TRUE(elem_type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
@ -496,15 +490,10 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_Valid) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<ast::Array>());
auto* arr_type = type->UnwrapAliasIfNeeded()->As<ast::Array>();
EXPECT_TRUE(type->UnwrapAll()->Is<Array>());
auto* arr_type = type->UnwrapAll()->As<Array>();
ASSERT_NE(arr_type, nullptr);
ASSERT_EQ(arr_type->decorations().size(), 1u);
auto* stride = arr_type->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
ASSERT_EQ(stride->As<ast::StrideDecoration>()->stride(), 8u);
EXPECT_EQ(arr_type->stride, 8u);
EXPECT_TRUE(p->error().empty());
}
@ -550,14 +539,11 @@ TEST_F(SpvParserTest, ConvertType_StructTwoMembers) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<ast::Struct>());
EXPECT_TRUE(type->Is<Struct>());
auto* str = type->Build(p->builder());
Program program = p->program();
EXPECT_THAT(program.str(type->As<ast::Struct>()), Eq(R"(Struct S {
StructMember{field0: __u32}
StructMember{field1: __f32}
}
)"));
EXPECT_THAT(program.str(str), Eq(R"(__type_name_S)"));
}
TEST_F(SpvParserTest, ConvertType_StructWithBlockDecoration) {
@ -571,14 +557,11 @@ TEST_F(SpvParserTest, ConvertType_StructWithBlockDecoration) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<ast::Struct>());
EXPECT_TRUE(type->Is<Struct>());
auto* str = type->Build(p->builder());
Program program = p->program();
EXPECT_THAT(program.str(type->As<ast::Struct>()), Eq(R"(Struct S {
[[block]]
StructMember{field0: __u32}
}
)"));
EXPECT_THAT(program.str(str), Eq(R"(__type_name_S)"));
}
TEST_F(SpvParserTest, ConvertType_StructWithMemberDecorations) {
@ -596,15 +579,11 @@ TEST_F(SpvParserTest, ConvertType_StructWithMemberDecorations) {
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<ast::Struct>());
EXPECT_TRUE(type->Is<Struct>());
auto* str = type->Build(p->builder());
Program program = p->program();
EXPECT_THAT(program.str(type->As<ast::Struct>()), Eq(R"(Struct S {
StructMember{[[ offset 0 ]] field0: __f32}
StructMember{[[ offset 8 ]] field1: __vec_2__f32}
StructMember{[[ offset 16 ]] field2: __mat_2_2__f32}
}
)"));
EXPECT_THAT(program.str(str), Eq(R"(__type_name_S)"));
}
// TODO(dneto): Demonstrate other member decorations. Blocked on
@ -645,11 +624,11 @@ TEST_F(SpvParserTest, ConvertType_PointerInput) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kInput);
EXPECT_TRUE(p->error().empty());
}
@ -661,11 +640,11 @@ TEST_F(SpvParserTest, ConvertType_PointerOutput) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kOutput);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kOutput);
EXPECT_TRUE(p->error().empty());
}
@ -677,11 +656,11 @@ TEST_F(SpvParserTest, ConvertType_PointerUniform) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniform);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kUniform);
EXPECT_TRUE(p->error().empty());
}
@ -693,11 +672,11 @@ TEST_F(SpvParserTest, ConvertType_PointerWorkgroup) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kWorkgroup);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kWorkgroup);
EXPECT_TRUE(p->error().empty());
}
@ -709,11 +688,11 @@ TEST_F(SpvParserTest, ConvertType_PointerUniformConstant) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kUniformConstant);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kUniformConstant);
EXPECT_TRUE(p->error().empty());
}
@ -725,11 +704,11 @@ TEST_F(SpvParserTest, ConvertType_PointerStorageBuffer) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kStorage);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kStorage);
EXPECT_TRUE(p->error().empty());
}
@ -741,11 +720,11 @@ TEST_F(SpvParserTest, ConvertType_PointerImage) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kImage);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kImage);
EXPECT_TRUE(p->error().empty());
}
@ -757,11 +736,11 @@ TEST_F(SpvParserTest, ConvertType_PointerPrivate) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kPrivate);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kPrivate);
EXPECT_TRUE(p->error().empty());
}
@ -773,11 +752,11 @@ TEST_F(SpvParserTest, ConvertType_PointerFunction) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<ast::Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kFunction);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kFunction);
EXPECT_TRUE(p->error().empty());
}
@ -792,17 +771,17 @@ TEST_F(SpvParserTest, ConvertType_PointerToPointer) {
auto* type = p->ConvertType(3);
EXPECT_NE(type, nullptr);
EXPECT_TRUE(type->Is<ast::Pointer>());
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<ast::Pointer>();
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_EQ(ptr_ty->storage_class(), ast::StorageClass::kInput);
EXPECT_TRUE(ptr_ty->type()->Is<ast::Pointer>());
EXPECT_EQ(ptr_ty->storage_class, ast::StorageClass::kInput);
EXPECT_TRUE(ptr_ty->type->Is<Pointer>());
auto* ptr_ptr_ty = ptr_ty->type()->As<ast::Pointer>();
auto* ptr_ptr_ty = ptr_ty->type->As<Pointer>();
EXPECT_NE(ptr_ptr_ty, nullptr);
EXPECT_EQ(ptr_ptr_ty->storage_class(), ast::StorageClass::kOutput);
EXPECT_TRUE(ptr_ptr_ty->type()->Is<ast::F32>());
EXPECT_EQ(ptr_ptr_ty->storage_class, ast::StorageClass::kOutput);
EXPECT_TRUE(ptr_ptr_ty->type->Is<F32>());
EXPECT_TRUE(p->error().empty());
}
@ -815,7 +794,7 @@ TEST_F(SpvParserTest, ConvertType_Sampler_PretendVoid) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<ast::Void>());
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
@ -828,7 +807,7 @@ TEST_F(SpvParserTest, ConvertType_Image_PretendVoid) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<ast::Void>());
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
@ -841,7 +820,7 @@ TEST_F(SpvParserTest, ConvertType_SampledImage_PretendVoid) {
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<ast::Void>());
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}

View File

@ -1772,7 +1772,7 @@ TEST_F(SpvModuleScopeVarParserTest,
[[block]]
StructMember{[[ offset 0 ]] field0: __u32}
StructMember{[[ offset 4 ]] field1: __f32}
StructMember{[[ offset 8 ]] field2: __alias_Arr__array__u32_2_stride_4}
StructMember{[[ offset 8 ]] field2: __type_name_Arr}
}
Variable{
x_1

View File

@ -158,7 +158,7 @@ class ParserImplWrapperForTest {
/// after the internal representation of the module has been built.
/// @param id the SPIR-V ID of a type.
/// @returns a Tint type, or nullptr
ast::Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); }
const Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); }
/// Gets the list of decorations for a SPIR-V result ID. Returns an empty
/// vector if the ID is not a result ID, or if no decorations target that ID.

View File

@ -0,0 +1,514 @@
// Copyright 2021 The Tint Authors.
//
// 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 stateied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/reader/spirv/parser_type.h"
#include <unordered_map>
#include <utility>
#include "src/program_builder.h"
#include "src/utils/get_or_create.h"
#include "src/utils/hash.h"
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Type);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Void);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Bool);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::U32);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::F32);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::I32);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Pointer);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Vector);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Matrix);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Array);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::AccessControl);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Sampler);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Texture);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::DepthTexture);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::MultisampledTexture);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::SampledTexture);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::StorageTexture);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Named);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Alias);
TINT_INSTANTIATE_TYPEINFO(tint::reader::spirv::Struct);
namespace tint {
namespace reader {
namespace spirv {
namespace {
struct PointerHasher {
size_t operator()(const Pointer& t) const {
return utils::Hash(t.type, t.storage_class);
}
};
struct VectorHasher {
size_t operator()(const Vector& t) const {
return utils::Hash(t.type, t.size);
}
};
struct MatrixHasher {
size_t operator()(const Matrix& t) const {
return utils::Hash(t.type, t.columns, t.rows);
}
};
struct ArrayHasher {
size_t operator()(const Array& t) const {
return utils::Hash(t.type, t.size, t.stride);
}
};
struct AccessControlHasher {
size_t operator()(const AccessControl& t) const {
return utils::Hash(t.type, t.access);
}
};
struct MultisampledTextureHasher {
size_t operator()(const MultisampledTexture& t) const {
return utils::Hash(t.dims, t.type);
}
};
struct SampledTextureHasher {
size_t operator()(const SampledTexture& t) const {
return utils::Hash(t.dims, t.type);
}
};
struct StorageTextureHasher {
size_t operator()(const StorageTexture& t) const {
return utils::Hash(t.dims, t.format);
}
};
} // namespace
static bool operator==(const Pointer& a, const Pointer& b) {
return a.type == b.type && a.storage_class == b.storage_class;
}
static bool operator==(const Vector& a, const Vector& b) {
return a.type == b.type && a.size == b.size;
}
static bool operator==(const Matrix& a, const Matrix& b) {
return a.type == b.type && a.columns == b.columns && a.rows == b.rows;
}
static bool operator==(const Array& a, const Array& b) {
return a.type == b.type && a.size == b.size && a.stride == b.stride;
}
static bool operator==(const AccessControl& a, const AccessControl& b) {
return a.type == b.type && a.access == b.access;
}
static bool operator==(const MultisampledTexture& a,
const MultisampledTexture& b) {
return a.dims == b.dims && a.type == b.type;
}
static bool operator==(const SampledTexture& a, const SampledTexture& b) {
return a.dims == b.dims && a.type == b.type;
}
static bool operator==(const StorageTexture& a, const StorageTexture& b) {
return a.dims == b.dims && a.format == b.format;
}
ast::Type* Void::Build(ProgramBuilder& b) const {
return b.ty.void_();
}
ast::Type* Bool::Build(ProgramBuilder& b) const {
return b.ty.bool_();
}
ast::Type* U32::Build(ProgramBuilder& b) const {
return b.ty.u32();
}
ast::Type* F32::Build(ProgramBuilder& b) const {
return b.ty.f32();
}
ast::Type* I32::Build(ProgramBuilder& b) const {
return b.ty.i32();
}
Pointer::Pointer(const Type* t, ast::StorageClass s)
: type(t), storage_class(s) {}
Pointer::Pointer(const Pointer&) = default;
ast::Type* Pointer::Build(ProgramBuilder& b) const {
return b.ty.pointer(type->Build(b), storage_class);
}
Vector::Vector(const Type* t, uint32_t s) : type(t), size(s) {}
Vector::Vector(const Vector&) = default;
ast::Type* Vector::Build(ProgramBuilder& b) const {
return b.ty.vec(type->Build(b), size);
}
Matrix::Matrix(const Type* t, uint32_t c, uint32_t r)
: type(t), columns(c), rows(r) {}
Matrix::Matrix(const Matrix&) = default;
ast::Type* Matrix::Build(ProgramBuilder& b) const {
return b.ty.mat(type->Build(b), columns, rows);
}
Array::Array(const Type* t, uint32_t sz, uint32_t st)
: type(t), size(sz), stride(st) {}
Array::Array(const Array&) = default;
ast::Type* Array::Build(ProgramBuilder& b) const {
return b.ty.array(type->Build(b), size, stride);
}
AccessControl::AccessControl(const Type* t, ast::AccessControl::Access a)
: type(t), access(a) {}
AccessControl::AccessControl(const AccessControl&) = default;
ast::Type* AccessControl::Build(ProgramBuilder& b) const {
return b.ty.access(access, type->Build(b));
}
Sampler::Sampler(ast::SamplerKind k) : kind(k) {}
Sampler::Sampler(const Sampler&) = default;
ast::Type* Sampler::Build(ProgramBuilder& b) const {
return b.ty.sampler(kind);
}
Texture::Texture(ast::TextureDimension d) : dims(d) {}
Texture::Texture(const Texture&) = default;
DepthTexture::DepthTexture(ast::TextureDimension d) : Base(d) {}
DepthTexture::DepthTexture(const DepthTexture&) = default;
ast::Type* DepthTexture::Build(ProgramBuilder& b) const {
return b.ty.depth_texture(dims);
}
MultisampledTexture::MultisampledTexture(ast::TextureDimension d, const Type* t)
: Base(d), type(t) {}
MultisampledTexture::MultisampledTexture(const MultisampledTexture&) = default;
ast::Type* MultisampledTexture::Build(ProgramBuilder& b) const {
return b.ty.multisampled_texture(dims, type->Build(b));
}
SampledTexture::SampledTexture(ast::TextureDimension d, const Type* t)
: Base(d), type(t) {}
SampledTexture::SampledTexture(const SampledTexture&) = default;
ast::Type* SampledTexture::Build(ProgramBuilder& b) const {
return b.ty.sampled_texture(dims, type->Build(b));
}
StorageTexture::StorageTexture(ast::TextureDimension d, ast::ImageFormat f)
: Base(d), format(f) {}
StorageTexture::StorageTexture(const StorageTexture&) = default;
ast::Type* StorageTexture::Build(ProgramBuilder& b) const {
return b.ty.storage_texture(dims, format);
}
Named::Named(Symbol n) : name(n) {}
Named::Named(const Named&) = default;
Named::~Named() = default;
Alias::Alias(Symbol n, const Type* ty) : Base(n), type(ty) {}
Alias::Alias(const Alias&) = default;
ast::Type* Alias::Build(ProgramBuilder& b) const {
return b.ty.type_name(name);
}
Struct::Struct(Symbol n, TypeList m) : Base(n), members(std::move(m)) {}
Struct::Struct(const Struct&) = default;
Struct::~Struct() = default;
ast::Type* Struct::Build(ProgramBuilder& b) const {
return b.ty.type_name(name);
}
/// The PIMPL state of the Types object.
struct TypeManager::State {
/// The allocator of types
BlockAllocator<Type> allocator_;
/// The lazily-created Void type
spirv::Void const* void_ = nullptr;
/// The lazily-created Bool type
spirv::Bool const* bool_ = nullptr;
/// The lazily-created U32 type
spirv::U32 const* u32_ = nullptr;
/// The lazily-created F32 type
spirv::F32 const* f32_ = nullptr;
/// The lazily-created I32 type
spirv::I32 const* i32_ = nullptr;
/// Map of Pointer to the returned Pointer type instance
std::unordered_map<spirv::Pointer, const spirv::Pointer*, PointerHasher>
pointers_;
/// Map of Vector to the returned Vector type instance
std::unordered_map<spirv::Vector, const spirv::Vector*, VectorHasher>
vectors_;
/// Map of Matrix to the returned Matrix type instance
std::unordered_map<spirv::Matrix, const spirv::Matrix*, MatrixHasher>
matrices_;
/// Map of Array to the returned Array type instance
std::unordered_map<spirv::Array, const spirv::Array*, ArrayHasher> arrays_;
/// Map of AccessControl to the returned AccessControl type instance
std::unordered_map<spirv::AccessControl,
const spirv::AccessControl*,
AccessControlHasher>
access_controls_;
/// Map of type name to returned Alias instance
std::unordered_map<Symbol, const spirv::Alias*> aliases_;
/// Map of type name to returned Struct instance
std::unordered_map<Symbol, const spirv::Struct*> structs_;
/// Map of ast::SamplerKind to returned Sampler instance
std::unordered_map<ast::SamplerKind, const spirv::Sampler*> samplers_;
/// Map of ast::TextureDimension to returned DepthTexture instance
std::unordered_map<ast::TextureDimension, const spirv::DepthTexture*>
depth_textures_;
/// Map of MultisampledTexture to the returned MultisampledTexture type
/// instance
std::unordered_map<spirv::MultisampledTexture,
const spirv::MultisampledTexture*,
MultisampledTextureHasher>
multisampled_textures_;
/// Map of SampledTexture to the returned SampledTexture type instance
std::unordered_map<spirv::SampledTexture,
const spirv::SampledTexture*,
SampledTextureHasher>
sampled_textures_;
/// Map of StorageTexture to the returned StorageTexture type instance
std::unordered_map<spirv::StorageTexture,
const spirv::StorageTexture*,
StorageTextureHasher>
storage_textures_;
};
const Type* Type::UnwrapPtrIfNeeded() const {
if (auto* ptr = As<Pointer>()) {
return ptr->type;
}
return this;
}
const Type* Type::UnwrapAliasIfNeeded() const {
const Type* unwrapped = this;
while (auto* ptr = unwrapped->As<Alias>()) {
unwrapped = ptr->type;
}
return unwrapped;
}
const Type* Type::UnwrapIfNeeded() const {
auto* where = this;
while (true) {
if (auto* alias = where->As<Alias>()) {
where = alias->type;
} else if (auto* access = where->As<AccessControl>()) {
where = access->type;
} else {
break;
}
}
return where;
}
const Type* Type::UnwrapAll() const {
return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded();
}
bool Type::IsFloatScalar() const {
return Is<F32>();
}
bool Type::IsFloatScalarOrVector() const {
return IsFloatScalar() || IsFloatVector();
}
bool Type::IsFloatVector() const {
return Is<Vector>([](const Vector* v) { return v->type->IsFloatScalar(); });
}
bool Type::IsIntegerScalar() const {
return IsAnyOf<U32, I32>();
}
bool Type::IsIntegerScalarOrVector() const {
return IsUnsignedScalarOrVector() || IsSignedScalarOrVector();
}
bool Type::IsScalar() const {
return IsAnyOf<F32, U32, I32, Bool>();
}
bool Type::IsSignedIntegerVector() const {
return Is<Vector>([](const Vector* v) { return v->type->Is<I32>(); });
}
bool Type::IsSignedScalarOrVector() const {
return Is<I32>() || IsSignedIntegerVector();
}
bool Type::IsUnsignedIntegerVector() const {
return Is<Vector>([](const Vector* v) { return v->type->Is<U32>(); });
}
bool Type::IsUnsignedScalarOrVector() const {
return Is<U32>() || IsUnsignedIntegerVector();
}
TypeManager::TypeManager() {
state = std::make_unique<State>();
}
TypeManager::~TypeManager() = default;
const spirv::Void* TypeManager::Void() {
if (!state->void_) {
state->void_ = state->allocator_.Create<spirv::Void>();
}
return state->void_;
}
const spirv::Bool* TypeManager::Bool() {
if (!state->bool_) {
state->bool_ = state->allocator_.Create<spirv::Bool>();
}
return state->bool_;
}
const spirv::U32* TypeManager::U32() {
if (!state->u32_) {
state->u32_ = state->allocator_.Create<spirv::U32>();
}
return state->u32_;
}
const spirv::F32* TypeManager::F32() {
if (!state->f32_) {
state->f32_ = state->allocator_.Create<spirv::F32>();
}
return state->f32_;
}
const spirv::I32* TypeManager::I32() {
if (!state->i32_) {
state->i32_ = state->allocator_.Create<spirv::I32>();
}
return state->i32_;
}
const spirv::Pointer* TypeManager::Pointer(const Type* el,
ast::StorageClass sc) {
return utils::GetOrCreate(state->pointers_, spirv::Pointer(el, sc), [&] {
return state->allocator_.Create<spirv::Pointer>(el, sc);
});
}
const spirv::Vector* TypeManager::Vector(const Type* el, uint32_t size) {
return utils::GetOrCreate(state->vectors_, spirv::Vector(el, size), [&] {
return state->allocator_.Create<spirv::Vector>(el, size);
});
}
const spirv::Matrix* TypeManager::Matrix(const Type* el,
uint32_t columns,
uint32_t rows) {
return utils::GetOrCreate(
state->matrices_, spirv::Matrix(el, columns, rows), [&] {
return state->allocator_.Create<spirv::Matrix>(el, columns, rows);
});
}
const spirv::Array* TypeManager::Array(const Type* el,
uint32_t size,
uint32_t stride) {
return utils::GetOrCreate(
state->arrays_, spirv::Array(el, size, stride),
[&] { return state->allocator_.Create<spirv::Array>(el, size, stride); });
}
const spirv::AccessControl* TypeManager::AccessControl(
const Type* ty,
ast::AccessControl::Access ac) {
return utils::GetOrCreate(
state->access_controls_, spirv::AccessControl(ty, ac),
[&] { return state->allocator_.Create<spirv::AccessControl>(ty, ac); });
}
const spirv::Alias* TypeManager::Alias(Symbol name, const Type* ty) {
return utils::GetOrCreate(state->aliases_, name, [&] {
return state->allocator_.Create<spirv::Alias>(name, ty);
});
}
const spirv::Struct* TypeManager::Struct(Symbol name, TypeList members) {
return utils::GetOrCreate(state->structs_, name, [&] {
return state->allocator_.Create<spirv::Struct>(name, std::move(members));
});
}
const spirv::Sampler* TypeManager::Sampler(ast::SamplerKind kind) {
return utils::GetOrCreate(state->samplers_, kind, [&] {
return state->allocator_.Create<spirv::Sampler>(kind);
});
}
const spirv::DepthTexture* TypeManager::DepthTexture(
ast::TextureDimension dims) {
return utils::GetOrCreate(state->depth_textures_, dims, [&] {
return state->allocator_.Create<spirv::DepthTexture>(dims);
});
}
const spirv::MultisampledTexture* TypeManager::MultisampledTexture(
ast::TextureDimension dims,
const Type* ty) {
return utils::GetOrCreate(
state->multisampled_textures_, spirv::MultisampledTexture(dims, ty), [&] {
return state->allocator_.Create<spirv::MultisampledTexture>(dims, ty);
});
}
const spirv::SampledTexture* TypeManager::SampledTexture(
ast::TextureDimension dims,
const Type* ty) {
return utils::GetOrCreate(
state->sampled_textures_, spirv::SampledTexture(dims, ty), [&] {
return state->allocator_.Create<spirv::SampledTexture>(dims, ty);
});
}
const spirv::StorageTexture* TypeManager::StorageTexture(
ast::TextureDimension dims,
ast::ImageFormat fmt) {
return utils::GetOrCreate(
state->storage_textures_, spirv::StorageTexture(dims, fmt), [&] {
return state->allocator_.Create<spirv::StorageTexture>(dims, fmt);
});
}
} // namespace spirv
} // namespace reader
} // namespace tint

View File

@ -0,0 +1,498 @@
// Copyright 2021 The Tint Authors.
//
// 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.
#ifndef SRC_READER_SPIRV_PARSER_TYPE_H_
#define SRC_READER_SPIRV_PARSER_TYPE_H_
#include <memory>
#include <vector>
#include "src/ast/access_control.h"
#include "src/ast/sampler.h"
#include "src/ast/storage_class.h"
#include "src/ast/storage_texture.h"
#include "src/ast/texture.h"
#include "src/block_allocator.h"
#include "src/castable.h"
// Forward declarations
namespace tint {
class ProgramBuilder;
namespace ast {
class Type;
} // namespace ast
} // namespace tint
namespace tint {
namespace reader {
namespace spirv {
/// Type is the base class for all types
class Type : public Castable<Type> {
public:
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
virtual ast::Type* Build(ProgramBuilder& b) const = 0;
/// @returns the pointee type if this is a pointer, `this` otherwise
const Type* UnwrapPtrIfNeeded() const;
/// @returns the most deeply nested aliased type if this is an alias, `this`
/// otherwise
const Type* UnwrapAliasIfNeeded() const;
/// Removes all levels of aliasing and access control.
/// This is just enough to assist with WGSL translation
/// in that you want see through one level of pointer to get from an
/// identifier-like expression as an l-value to its corresponding r-value,
/// plus see through the wrappers on either side.
/// @returns the completely unaliased type.
const Type* UnwrapIfNeeded() const;
/// Returns the type found after:
/// - removing all layers of aliasing and access control if they exist, then
/// - removing the pointer, if it exists, then
/// - removing all further layers of aliasing or access control, if they exist
/// @returns the unwrapped type
const Type* UnwrapAll() const;
/// @returns true if this type is a float scalar
bool IsFloatScalar() const;
/// @returns true if this type is a float scalar or vector
bool IsFloatScalarOrVector() const;
/// @returns true if this type is a float vector
bool IsFloatVector() const;
/// @returns true if this type is an integer scalar
bool IsIntegerScalar() const;
/// @returns true if this type is an integer scalar or vector
bool IsIntegerScalarOrVector() const;
/// @returns true if this type is a scalar
bool IsScalar() const;
/// @returns true if this type is a signed integer vector
bool IsSignedIntegerVector() const;
/// @returns true if this type is a signed scalar or vector
bool IsSignedScalarOrVector() const;
/// @returns true if this type is an unsigned integer vector
bool IsUnsignedIntegerVector() const;
/// @returns true if this type is an unsigned scalar or vector
bool IsUnsignedScalarOrVector() const;
};
using TypeList = std::vector<const Type*>;
/// `void` type
struct Void : public Castable<Void, Type> {
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `bool` type
struct Bool : public Castable<Bool, Type> {
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `u32` type
struct U32 : public Castable<U32, Type> {
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `f32` type
struct F32 : public Castable<F32, Type> {
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `i32` type
struct I32 : public Castable<I32, Type> {
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `ptr<SC, T>` type
struct Pointer : public Castable<Pointer, Type> {
/// Constructor
/// @param ty the pointee type
/// @param sc the pointer storage class
Pointer(const Type* ty, ast::StorageClass sc);
/// Copy constructor
/// @param other the other type to copy
Pointer(const Pointer& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the pointee type
Type const* const type;
/// the pointer storage class
ast::StorageClass const storage_class;
};
/// `vecN<T>` type
struct Vector : public Castable<Vector, Type> {
/// Constructor
/// @param ty the element type
/// @param sz the number of elements in the vector
Vector(const Type* ty, uint32_t sz);
/// Copy constructor
/// @param other the other type to copy
Vector(const Vector& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the element type
Type const* const type;
/// the number of elements in the vector
uint32_t const size;
};
/// `matNxM<T>` type
struct Matrix : public Castable<Matrix, Type> {
/// Constructor
/// @param ty the matrix element type
/// @param c the number of columns in the matrix
/// @param r the number of rows in the matrix
Matrix(const Type* ty, uint32_t c, uint32_t r);
/// Copy constructor
/// @param other the other type to copy
Matrix(const Matrix& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the matrix element type
Type const* const type;
/// the number of columns in the matrix
uint32_t const columns;
/// the number of rows in the matrix
uint32_t const rows;
};
/// `array<T, N>` type
struct Array : public Castable<Array, Type> {
/// Constructor
/// @param el the element type
/// @param sz the number of elements in the array. 0 represents runtime-sized
/// array.
/// @param st the byte stride of the array
Array(const Type* el, uint32_t sz, uint32_t st);
/// Copy constructor
/// @param other the other type to copy
Array(const Array& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the element type
Type const* const type;
/// the number of elements in the array. 0 represents runtime-sized array.
uint32_t const size;
/// the byte stride of the array
uint32_t const stride;
};
/// `[[access]]` type
struct AccessControl : public Castable<AccessControl, Type> {
/// Constructor
/// @param ty the inner type
/// @param ac the access control
AccessControl(const Type* ty, ast::AccessControl::Access ac);
/// Copy constructor
/// @param other the other type to copy
AccessControl(const AccessControl& other);
/// @return the
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the inner type
Type const* const type;
/// the access control
ast::AccessControl::Access const access;
};
/// `sampler` type
struct Sampler : public Castable<Sampler, Type> {
/// Constructor
/// @param k the sampler kind
explicit Sampler(ast::SamplerKind k);
/// Copy constructor
/// @param other the other type to copy
Sampler(const Sampler& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the sampler kind
ast::SamplerKind const kind;
};
/// Base class for texture types
struct Texture : public Castable<Texture, Type> {
/// Constructor
/// @param d the texture dimensions
explicit Texture(ast::TextureDimension d);
/// Copy constructor
/// @param other the other type to copy
Texture(const Texture& other);
/// the texture dimensions
ast::TextureDimension const dims;
};
/// `texture_depth_D` type
struct DepthTexture : public Castable<DepthTexture, Texture> {
/// Constructor
/// @param d the texture dimensions
explicit DepthTexture(ast::TextureDimension d);
/// Copy constructor
/// @param other the other type to copy
DepthTexture(const DepthTexture& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
};
/// `texture_multisampled_D<T>` type
struct MultisampledTexture : public Castable<MultisampledTexture, Texture> {
/// Constructor
/// @param d the texture dimensions
/// @param t the multisampled texture type
MultisampledTexture(ast::TextureDimension d, const Type* t);
/// Copy constructor
/// @param other the other type to copy
MultisampledTexture(const MultisampledTexture& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the multisampled texture type
Type const* const type;
};
/// `texture_D<T>` type
struct SampledTexture : public Castable<SampledTexture, Texture> {
/// Constructor
/// @param d the texture dimensions
/// @param t the sampled texture type
SampledTexture(ast::TextureDimension d, const Type* t);
/// Copy constructor
/// @param other the other type to copy
SampledTexture(const SampledTexture& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the sampled texture type
Type const* const type;
};
/// `texture_storage_D<F>` type
struct StorageTexture : public Castable<StorageTexture, Texture> {
/// Constructor
/// @param d the texture dimensions
/// @param f the storage image format
StorageTexture(ast::TextureDimension d, ast::ImageFormat f);
/// Copy constructor
/// @param other the other type to copy
StorageTexture(const StorageTexture& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the storage image format
ast::ImageFormat const format;
};
/// Base class for named types
struct Named : public Castable<Named, Type> {
/// Constructor
/// @param n the type name
explicit Named(Symbol n);
/// Copy constructor
/// @param other the other type to copy
Named(const Named& other);
/// Destructor
~Named() override;
/// the type name
Symbol const name;
};
/// `type T = N` type
struct Alias : public Castable<Alias, Named> {
/// Constructor
/// @param n the alias name
/// @param t the aliased type
Alias(Symbol n, const Type* t);
/// Copy constructor
/// @param other the other type to copy
Alias(const Alias& other);
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the aliased type
Type const* const type;
};
/// `struct N { ... };` type
struct Struct : public Castable<Struct, Named> {
/// Constructor
/// @param n the struct name
/// @param m the member types
Struct(Symbol n, TypeList m);
/// Copy constructor
/// @param other the other type to copy
Struct(const Struct& other);
/// Destructor
~Struct() override;
/// @param b the ProgramBuilder used to construct the AST types
/// @returns the constructed ast::Type node for the given type
ast::Type* Build(ProgramBuilder& b) const override;
/// the member types
TypeList const members;
};
/// A manager of types
class TypeManager {
public:
/// Constructor
TypeManager();
/// Destructor
~TypeManager();
/// @return a Void type. Repeated calls will return the same pointer.
const spirv::Void* Void();
/// @return a Bool type. Repeated calls will return the same pointer.
const spirv::Bool* Bool();
/// @return a U32 type. Repeated calls will return the same pointer.
const spirv::U32* U32();
/// @return a F32 type. Repeated calls will return the same pointer.
const spirv::F32* F32();
/// @return a I32 type. Repeated calls will return the same pointer.
const spirv::I32* I32();
/// @param ty the pointee type
/// @param sc the pointer storage class
/// @return a Pointer type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Pointer* Pointer(const Type* ty, ast::StorageClass sc);
/// @param ty the element type
/// @param sz the number of elements in the vector
/// @return a Vector type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Vector* Vector(const Type* ty, uint32_t sz);
/// @param ty the matrix element type
/// @param c the number of columns in the matrix
/// @param r the number of rows in the matrix
/// @return a Matrix type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Matrix* Matrix(const Type* ty, uint32_t c, uint32_t r);
/// @param el the element type
/// @param sz the number of elements in the array. 0 represents runtime-sized
/// array.
/// @param st the byte stride of the array
/// @return a Array type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Array* Array(const Type* el, uint32_t sz, uint32_t st);
/// @param ty the inner type
/// @param ac the access control
/// @return a AccessControl type. Repeated calls with the same arguments will
/// return the same pointer.
const spirv::AccessControl* AccessControl(const Type* ty,
ast::AccessControl::Access ac);
/// @param n the alias name
/// @param t the aliased type
/// @return a Alias type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Alias* Alias(Symbol n, const Type* t);
/// @param n the struct name
/// @param m the member types
/// @return a Struct type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Struct* Struct(Symbol n, TypeList m);
/// @param k the sampler kind
/// @return a Sampler type. Repeated calls with the same arguments will return
/// the same pointer.
const spirv::Sampler* Sampler(ast::SamplerKind k);
/// @param d the texture dimensions
/// @return a DepthTexture type. Repeated calls with the same arguments will
/// return the same pointer.
const spirv::DepthTexture* DepthTexture(ast::TextureDimension d);
/// @param d the texture dimensions
/// @param t the multisampled texture type
/// @return a MultisampledTexture type. Repeated calls with the same arguments
/// will return the same pointer.
const spirv::MultisampledTexture* MultisampledTexture(ast::TextureDimension d,
const Type* t);
/// @param d the texture dimensions
/// @param t the sampled texture type
/// @return a SampledTexture type. Repeated calls with the same arguments will
/// return the same pointer.
const spirv::SampledTexture* SampledTexture(ast::TextureDimension d,
const Type* t);
/// @param d the texture dimensions
/// @param f the storage image format
/// @return a StorageTexture type. Repeated calls with the same arguments will
/// return the same pointer.
const spirv::StorageTexture* StorageTexture(ast::TextureDimension d,
ast::ImageFormat f);
private:
struct State;
std::unique_ptr<State> state;
};
} // namespace spirv
} // namespace reader
} // namespace tint
#endif // SRC_READER_SPIRV_PARSER_TYPE_H_

View File

@ -0,0 +1,104 @@
// Copyright 2020 The Tint Authors.
//
// 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 "gtest/gtest.h"
#include "src/reader/spirv/parser_type.h"
namespace tint {
namespace reader {
namespace spirv {
namespace {
TEST(SpvParserTypeTest, SameArgumentsGivesSamePointer) {
Symbol sym(Symbol(1, {}));
TypeManager ty;
EXPECT_EQ(ty.Void(), ty.Void());
EXPECT_EQ(ty.Bool(), ty.Bool());
EXPECT_EQ(ty.U32(), ty.U32());
EXPECT_EQ(ty.F32(), ty.F32());
EXPECT_EQ(ty.I32(), ty.I32());
EXPECT_EQ(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
ty.Pointer(ty.I32(), ast::StorageClass::kNone));
EXPECT_EQ(ty.Vector(ty.I32(), 3), ty.Vector(ty.I32(), 3));
EXPECT_EQ(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.I32(), 3, 2));
EXPECT_EQ(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 2));
EXPECT_EQ(ty.AccessControl(ty.I32(), ast::AccessControl::Access::kReadOnly),
ty.AccessControl(ty.I32(), ast::AccessControl::Access::kReadOnly));
EXPECT_EQ(ty.Alias(sym, ty.I32()), ty.Alias(sym, ty.I32()));
EXPECT_EQ(ty.Struct(sym, {ty.I32()}), ty.Struct(sym, {ty.I32()}));
EXPECT_EQ(ty.Sampler(ast::SamplerKind::kSampler),
ty.Sampler(ast::SamplerKind::kSampler));
EXPECT_EQ(ty.DepthTexture(ast::TextureDimension::k2d),
ty.DepthTexture(ast::TextureDimension::k2d));
EXPECT_EQ(ty.MultisampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.MultisampledTexture(ast::TextureDimension::k2d, ty.I32()));
EXPECT_EQ(ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()));
EXPECT_EQ(
ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
ty.StorageTexture(ast::TextureDimension::k2d,
ast::ImageFormat::kR16Sint));
}
TEST(SpvParserTypeTest, DifferentArgumentsGivesDifferentPointer) {
Symbol sym_a(Symbol(1, {}));
Symbol sym_b(Symbol(2, {}));
TypeManager ty;
EXPECT_NE(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
ty.Pointer(ty.U32(), ast::StorageClass::kNone));
EXPECT_NE(ty.Pointer(ty.I32(), ast::StorageClass::kNone),
ty.Pointer(ty.I32(), ast::StorageClass::kInput));
EXPECT_NE(ty.Vector(ty.I32(), 3), ty.Vector(ty.U32(), 3));
EXPECT_NE(ty.Vector(ty.I32(), 3), ty.Vector(ty.I32(), 2));
EXPECT_NE(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.U32(), 3, 2));
EXPECT_NE(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.I32(), 2, 2));
EXPECT_NE(ty.Matrix(ty.I32(), 3, 2), ty.Matrix(ty.I32(), 3, 3));
EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.U32(), 3, 2));
EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 2, 2));
EXPECT_NE(ty.Array(ty.I32(), 3, 2), ty.Array(ty.I32(), 3, 3));
EXPECT_NE(ty.AccessControl(ty.I32(), ast::AccessControl::Access::kReadOnly),
ty.AccessControl(ty.U32(), ast::AccessControl::Access::kReadOnly));
EXPECT_NE(ty.AccessControl(ty.I32(), ast::AccessControl::Access::kReadOnly),
ty.AccessControl(ty.I32(), ast::AccessControl::Access::kWriteOnly));
EXPECT_NE(ty.Alias(sym_a, ty.I32()), ty.Alias(sym_b, ty.I32()));
EXPECT_NE(ty.Struct(sym_a, {ty.I32()}), ty.Struct(sym_b, {ty.I32()}));
EXPECT_NE(ty.Sampler(ast::SamplerKind::kSampler),
ty.Sampler(ast::SamplerKind::kComparisonSampler));
EXPECT_NE(ty.DepthTexture(ast::TextureDimension::k2d),
ty.DepthTexture(ast::TextureDimension::k1d));
EXPECT_NE(ty.MultisampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.MultisampledTexture(ast::TextureDimension::k3d, ty.I32()));
EXPECT_NE(ty.MultisampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.MultisampledTexture(ast::TextureDimension::k2d, ty.U32()));
EXPECT_NE(ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.SampledTexture(ast::TextureDimension::k3d, ty.I32()));
EXPECT_NE(ty.SampledTexture(ast::TextureDimension::k2d, ty.I32()),
ty.SampledTexture(ast::TextureDimension::k2d, ty.U32()));
EXPECT_NE(
ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
ty.StorageTexture(ast::TextureDimension::k3d,
ast::ImageFormat::kR16Sint));
EXPECT_NE(
ty.StorageTexture(ast::TextureDimension::k2d, ast::ImageFormat::kR16Sint),
ty.StorageTexture(ast::TextureDimension::k2d,
ast::ImageFormat::kR32Sint));
}
} // namespace
} // namespace spirv
} // namespace reader
} // namespace tint

View File

@ -349,6 +349,7 @@ tint_unittests_source_set("tint_unittests_spv_reader_src") {
"../src/reader/spirv/parser_impl_test_helper.cc",
"../src/reader/spirv/parser_impl_test_helper.h",
"../src/reader/spirv/parser_impl_user_name_test.cc",
"../src/reader/spirv/parser_type_test.cc",
"../src/reader/spirv/parser_test.cc",
"../src/reader/spirv/spirv_tools_helpers_test.cc",
"../src/reader/spirv/spirv_tools_helpers_test.h",