spirv parser: create ast types along with sem types

The spirv parser now creates ast types along with sem types via
typ::Type. All sem::Type* were replaced with typ::Type, and its `ast`
member is used over the `sem` member to make it easier to migrate to
ast-only.

The parser was written to take advantage of the fact that types were
resolved to semantic types during parsing. For instance, a mapping of
spirv typeid to sem::Type* was used throughout (`id_to_type_`) to
resolve types once, and to support type aliasing. Since the goal is to
only create AST types, and to resolve only in the Resolver, I made many
changes to remove this dependency on semantic types. For instance, we
now always call ConvertType(typeid) instead of looking up via
id_to_type. Similarly, the `signed_type_for_` and `unsigned_type_for_`
maps were replaced with `UnsignedTypeFor` and `SignedTypeFor` functions.

Bug: tint:724
Change-Id: I3aee3928834febd71b473d6a8d8cb77b1ac94e21
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49542
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Antonio Maiorano 2021-05-03 20:49:40 +00:00 committed by Commit Bot service account
parent b6831c3395
commit 1b898d56b4
7 changed files with 499 additions and 371 deletions

View File

@ -46,6 +46,12 @@ class Type : public Castable<Type, Node> {
/// @returns the pointee type if this is a pointer, `this` otherwise /// @returns the pointee type if this is a pointer, `this` otherwise
Type* UnwrapPtrIfNeeded(); Type* UnwrapPtrIfNeeded();
/// @returns the most deeply nested aliased type if this is an alias, `this`
/// otherwise
const Type* UnwrapAliasIfNeeded() const {
return const_cast<Type*>(this)->UnwrapAliasIfNeeded();
}
/// @returns the most deeply nested aliased type if this is an alias, `this` /// @returns the most deeply nested aliased type if this is an alias, `this`
/// otherwise /// otherwise
Type* UnwrapAliasIfNeeded(); Type* UnwrapAliasIfNeeded();
@ -58,6 +64,16 @@ class Type : public Castable<Type, Node> {
/// @returns the completely unaliased type. /// @returns the completely unaliased type.
Type* UnwrapIfNeeded(); Type* UnwrapIfNeeded();
/// 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 {
return const_cast<Type*>(this)->UnwrapIfNeeded();
}
/// Returns the type found after: /// Returns the type found after:
/// - removing all layers of aliasing and access control if they exist, then /// - removing all layers of aliasing and access control if they exist, then
/// - removing the pointer, if it exists, then /// - removing the pointer, if it exists, then
@ -65,6 +81,13 @@ class Type : public Castable<Type, Node> {
/// @returns the unwrapped type /// @returns the unwrapped type
Type* UnwrapAll(); Type* UnwrapAll();
/// 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 { return const_cast<Type*>(this)->UnwrapAll(); }
/// @returns true if this type is a scalar /// @returns true if this type is a scalar
bool is_scalar() const; bool is_scalar() const;
/// @returns true if this type is a float scalar /// @returns true if this type is a float scalar

View File

@ -690,6 +690,13 @@ struct LoopStatementBuilder
ast::BlockStatement* continuing = nullptr; ast::BlockStatement* continuing = nullptr;
}; };
// Forwards UnwrapAll to both the ast and sem types of the TypePair
// @param tp the type pair
// @returns the unwrapped type pair
typ::Type UnwrapAll(typ::Type tp) {
return typ::Type{tp.ast->UnwrapAll(), tp.sem->UnwrapAll()};
}
} // namespace } // namespace
BlockInfo::BlockInfo(const spvtools::opt::BasicBlock& bb) BlockInfo::BlockInfo(const spvtools::opt::BasicBlock& bb)
@ -726,8 +733,6 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi,
fail_stream_(pi->fail_stream()), fail_stream_(pi->fail_stream()),
namer_(pi->namer()), namer_(pi->namer()),
function_(function), function_(function),
i32_(builder_.create<sem::I32>()),
u32_(builder_.create<sem::U32>()),
sample_mask_in_id(0u), sample_mask_in_id(0u),
sample_mask_out_id(0u), sample_mask_out_id(0u),
ep_info_(ep_info) { ep_info_(ep_info) {
@ -890,7 +895,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
// Surprisingly, the "type id" on an OpFunction is the result type of the // Surprisingly, the "type id" on an OpFunction is the result type of the
// function, not the type of the function. This is the one exceptional case // function, not the type of the function. This is the one exceptional case
// in SPIR-V where the type ID is not the type of the result ID. // in SPIR-V where the type ID is not the type of the result ID.
auto* ret_ty = parser_impl_.ConvertType(function_.type_id()); auto ret_ty = parser_impl_.ConvertType(function_.type_id());
if (failed()) { if (failed()) {
return false; return false;
} }
@ -903,7 +908,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
ast::VariableList ast_params; ast::VariableList ast_params;
function_.ForEachParam( function_.ForEachParam(
[this, &ast_params](const spvtools::opt::Instruction* param) { [this, &ast_params](const spvtools::opt::Instruction* param) {
auto* ast_type = parser_impl_.ConvertType(param->type_id()); auto ast_type = parser_impl_.ConvertType(param->type_id());
if (ast_type != nullptr) { if (ast_type != nullptr) {
auto* ast_param = parser_impl_.MakeVariable( auto* ast_param = parser_impl_.MakeVariable(
param->result_id(), ast::StorageClass::kNone, ast_type, true, param->result_id(), ast::StorageClass::kNone, ast_type, true,
@ -933,7 +938,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
return success(); return success();
} }
sem::Type* FunctionEmitter::GetVariableStoreType( typ::Type FunctionEmitter::GetVariableStoreType(
const spvtools::opt::Instruction& var_decl_inst) { const spvtools::opt::Instruction& var_decl_inst) {
const auto type_id = var_decl_inst.type_id(); const auto type_id = var_decl_inst.type_id();
auto* var_ref_type = type_mgr_->GetType(type_id); auto* var_ref_type = type_mgr_->GetType(type_id);
@ -1996,7 +2001,7 @@ bool FunctionEmitter::EmitFunctionVariables() {
if (inst.opcode() != SpvOpVariable) { if (inst.opcode() != SpvOpVariable) {
continue; continue;
} }
auto* var_store_type = GetVariableStoreType(inst); auto var_store_type = GetVariableStoreType(inst);
if (failed()) { if (failed()) {
return false; return false;
} }
@ -2062,7 +2067,7 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
case SkipReason::kSampleMaskOutBuiltinPointer: case SkipReason::kSampleMaskOutBuiltinPointer:
// The result type is always u32. // The result type is always u32.
auto name = namer_.Name(sample_mask_out_id); auto name = namer_.Name(sample_mask_out_id);
return TypedExpression{u32_, return TypedExpression{builder_.ty.u32(),
create<ast::IdentifierExpression>( create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name))}; Source{}, builder_.Symbols().Register(name))};
} }
@ -2342,7 +2347,7 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
Source{}, // source Source{}, // source
builder_.Symbols().Register(guard_name), // symbol builder_.Symbols().Register(guard_name), // symbol
ast::StorageClass::kFunction, // storage_class ast::StorageClass::kFunction, // storage_class
parser_impl_.Bool(), // type builder_.ty.bool_(), // type
false, // is_const false, // is_const
MakeTrue(Source{}), // constructor MakeTrue(Source{}), // constructor
ast::DecorationList{}); // decorations ast::DecorationList{}); // decorations
@ -2643,7 +2648,7 @@ bool FunctionEmitter::EmitNormalTerminator(const BlockInfo& block_info) {
if (result_type->AsVoid() != nullptr) { if (result_type->AsVoid() != nullptr) {
AddStatement(create<ast::ReturnStatement>(Source{})); AddStatement(create<ast::ReturnStatement>(Source{}));
} else { } else {
auto* ast_type = parser_impl_.ConvertType(function_.type_id()); auto ast_type = parser_impl_.ConvertType(function_.type_id());
AddStatement(create<ast::ReturnStatement>( AddStatement(create<ast::ReturnStatement>(
Source{}, parser_impl_.MakeNullValue(ast_type))); Source{}, parser_impl_.MakeNullValue(ast_type)));
} }
@ -2888,7 +2893,7 @@ bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
for (auto id : sorted_by_index(block_info.hoisted_ids)) { for (auto id : sorted_by_index(block_info.hoisted_ids)) {
const auto* def_inst = def_use_mgr_->GetDef(id); const auto* def_inst = def_use_mgr_->GetDef(id);
TINT_ASSERT(def_inst); TINT_ASSERT(def_inst);
auto* ast_type = auto ast_type =
RemapStorageClass(parser_impl_.ConvertType(def_inst->type_id()), id); RemapStorageClass(parser_impl_.ConvertType(def_inst->type_id()), id);
AddStatement(create<ast::VariableDeclStatement>( AddStatement(create<ast::VariableDeclStatement>(
Source{}, Source{},
@ -3092,11 +3097,12 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
case SkipReason::kSampleMaskOutBuiltinPointer: case SkipReason::kSampleMaskOutBuiltinPointer:
ptr_id = sample_mask_out_id; ptr_id = sample_mask_out_id;
if (rhs.type != u32_) { if (rhs.type != builder_.ty.u32()) {
// WGSL requires sample_mask_out to be signed. // WGSL requires sample_mask_out to be signed.
rhs = TypedExpression{ rhs = TypedExpression{builder_.ty.u32(),
u32_, create<ast::TypeConstructorExpression>( create<ast::TypeConstructorExpression>(
Source{}, u32_, ast::ExpressionList{rhs.expr})}; Source{}, builder_.ty.u32(),
ast::ExpressionList{rhs.expr})};
} }
break; break;
default: default:
@ -3137,20 +3143,21 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
ast::Expression* id_expr = create<ast::IdentifierExpression>( ast::Expression* id_expr = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name)); Source{}, builder_.Symbols().Register(name));
auto expr = TypedExpression{ auto expr = TypedExpression{
i32_, create<ast::TypeConstructorExpression>( builder_.ty.i32(),
Source{}, i32_, ast::ExpressionList{id_expr})}; create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.i32(), ast::ExpressionList{id_expr})};
return EmitConstDefinition(inst, expr); return EmitConstDefinition(inst, expr);
} }
case SkipReason::kSampleMaskInBuiltinPointer: { case SkipReason::kSampleMaskInBuiltinPointer: {
auto name = namer_.Name(sample_mask_in_id); auto name = namer_.Name(sample_mask_in_id);
ast::Expression* id_expr = create<ast::IdentifierExpression>( ast::Expression* id_expr = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name)); Source{}, builder_.Symbols().Register(name));
auto* load_result_type = parser_impl_.ConvertType(inst.type_id()); auto load_result_type = parser_impl_.ConvertType(inst.type_id());
ast::Expression* ast_expr = nullptr; ast::Expression* ast_expr = nullptr;
if (load_result_type == i32_) { if (load_result_type == builder_.ty.i32()) {
ast_expr = create<ast::TypeConstructorExpression>( ast_expr = create<ast::TypeConstructorExpression>(
Source{}, i32_, ast::ExpressionList{id_expr}); Source{}, builder_.ty.i32(), ast::ExpressionList{id_expr});
} else if (load_result_type == u32_) { } else if (load_result_type == builder_.ty.u32()) {
ast_expr = id_expr; ast_expr = id_expr;
} else { } else {
return Fail() << "loading the whole SampleMask input array is not " return Fail() << "loading the whole SampleMask input array is not "
@ -3169,8 +3176,8 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
} }
// The load result type is the pointee type of its operand. // The load result type is the pointee type of its operand.
TINT_ASSERT(expr.type->Is<sem::Pointer>()); TINT_ASSERT(expr.type.ast->Is<ast::Pointer>());
expr.type = expr.type->As<sem::Pointer>()->type(); expr.type = typ::Call_type(typ::As<typ::Pointer>(expr.type));
return EmitConstDefOrWriteToHoistedVar(inst, expr); return EmitConstDefOrWriteToHoistedVar(inst, expr);
} }
@ -3272,7 +3279,7 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
const auto opcode = inst.opcode(); const auto opcode = inst.opcode();
sem::Type* ast_type = typ::Type ast_type =
inst.type_id() != 0 ? parser_impl_.ConvertType(inst.type_id()) : nullptr; inst.type_id() != 0 ? parser_impl_.ConvertType(inst.type_id()) : nullptr;
auto binary_op = ConvertBinaryOp(opcode); auto binary_op = ConvertBinaryOp(opcode);
@ -3416,7 +3423,7 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst(
auto* func = create<ast::IdentifierExpression>( auto* func = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name)); Source{}, builder_.Symbols().Register(name));
ast::ExpressionList operands; ast::ExpressionList operands;
sem::Type* first_operand_type = nullptr; typ::Type first_operand_type = nullptr;
// All parameters to GLSL.std.450 extended instructions are IDs. // All parameters to GLSL.std.450 extended instructions are IDs.
for (uint32_t iarg = 2; iarg < inst.NumInOperands(); ++iarg) { for (uint32_t iarg = 2; iarg < inst.NumInOperands(); ++iarg) {
TypedExpression operand = MakeOperand(inst, iarg); TypedExpression operand = MakeOperand(inst, iarg);
@ -3425,7 +3432,7 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst(
} }
operands.emplace_back(operand.expr); operands.emplace_back(operand.expr);
} }
auto* ast_type = parser_impl_.ConvertType(inst.type_id()); auto ast_type = parser_impl_.ConvertType(inst.type_id());
auto* call = create<ast::CallExpression>(Source{}, func, std::move(operands)); auto* call = create<ast::CallExpression>(Source{}, func, std::move(operands));
TypedExpression call_expr{ast_type, call}; TypedExpression call_expr{ast_type, call};
return parser_impl_.RectifyForcedResultType(call_expr, inst, return parser_impl_.RectifyForcedResultType(call_expr, inst,
@ -3660,9 +3667,9 @@ TypedExpression FunctionEmitter::MakeAccessChain(
} }
const auto pointer_type_id = const auto pointer_type_id =
type_mgr_->FindPointerToType(pointee_type_id, storage_class); type_mgr_->FindPointerToType(pointee_type_id, storage_class);
auto* ast_pointer_type = parser_impl_.ConvertType(pointer_type_id); auto ast_pointer_type = parser_impl_.ConvertType(pointer_type_id);
TINT_ASSERT(ast_pointer_type); TINT_ASSERT(ast_pointer_type);
TINT_ASSERT(ast_pointer_type->Is<sem::Pointer>()); TINT_ASSERT(ast_pointer_type.ast->Is<ast::Pointer>());
current_expr = TypedExpression{ast_pointer_type, next_expr}; current_expr = TypedExpression{ast_pointer_type, next_expr};
} }
return current_expr; return current_expr;
@ -3826,7 +3833,6 @@ ast::Expression* FunctionEmitter::MakeTrue(const Source& source) const {
} }
ast::Expression* FunctionEmitter::MakeFalse(const Source& source) const { ast::Expression* FunctionEmitter::MakeFalse(const Source& source) const {
sem::Bool bool_type;
return create<ast::ScalarConstructorExpression>( return create<ast::ScalarConstructorExpression>(
source, create<ast::BoolLiteral>(source, false)); source, create<ast::BoolLiteral>(source, false));
} }
@ -3847,8 +3853,8 @@ TypedExpression FunctionEmitter::MakeVectorShuffle(
// Generate an ast::TypeConstructor expression. // Generate an ast::TypeConstructor expression.
// Assume the literal indices are valid, and there is a valid number of them. // Assume the literal indices are valid, and there is a valid number of them.
auto source = GetSourceForInst(inst); auto source = GetSourceForInst(inst);
sem::Vector* result_type = typ::Vector result_type =
parser_impl_.ConvertType(inst.type_id())->As<sem::Vector>(); typ::As<typ::Vector>(parser_impl_.ConvertType(inst.type_id()));
ast::ExpressionList values; ast::ExpressionList values;
for (uint32_t i = 2; i < inst.NumInOperands(); ++i) { for (uint32_t i = 2; i < inst.NumInOperands(); ++i) {
const auto index = inst.GetSingleWordInOperand(i); const auto index = inst.GetSingleWordInOperand(i);
@ -3870,7 +3876,8 @@ TypedExpression FunctionEmitter::MakeVectorShuffle(
source, expr.expr, Swizzle(sub_index))); source, expr.expr, Swizzle(sub_index)));
} else if (index == 0xFFFFFFFF) { } else if (index == 0xFFFFFFFF) {
// By rule, this maps to OpUndef. Instead, make it zero. // 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(typ::Call_type(result_type)));
} else { } else {
Fail() << "invalid vectorshuffle ID %" << inst.result_id() Fail() << "invalid vectorshuffle ID %" << inst.result_id()
<< ": index too large: " << index; << ": index too large: " << index;
@ -3946,8 +3953,8 @@ bool FunctionEmitter::RegisterLocallyDefinedValues() {
const auto* type = type_mgr_->GetType(inst.type_id()); const auto* type = type_mgr_->GetType(inst.type_id());
if (type) { if (type) {
if (type->AsPointer()) { if (type->AsPointer()) {
if (const auto* ast_type = parser_impl_.ConvertType(inst.type_id())) { if (auto ast_type = parser_impl_.ConvertType(inst.type_id())) {
if (auto* ptr = ast_type->As<sem::Pointer>()) { if (auto* ptr = ast_type.ast->As<ast::Pointer>()) {
info->storage_class = ptr->storage_class(); info->storage_class = ptr->storage_class();
} }
} }
@ -3991,22 +3998,22 @@ ast::StorageClass FunctionEmitter::GetStorageClassForPointerValue(uint32_t id) {
} }
const auto type_id = def_use_mgr_->GetDef(id)->type_id(); const auto type_id = def_use_mgr_->GetDef(id)->type_id();
if (type_id) { if (type_id) {
auto* ast_type = parser_impl_.ConvertType(type_id); auto ast_type = parser_impl_.ConvertType(type_id);
if (ast_type && ast_type->Is<sem::Pointer>()) { if (auto ptr = typ::As<typ::Pointer>(ast_type)) {
return ast_type->As<sem::Pointer>()->storage_class(); return ptr.ast->storage_class();
} }
} }
return ast::StorageClass::kNone; return ast::StorageClass::kNone;
} }
sem::Type* FunctionEmitter::RemapStorageClass(sem::Type* type, typ::Type FunctionEmitter::RemapStorageClass(typ::Type type,
uint32_t result_id) { uint32_t result_id) {
if (const auto* ast_ptr_type = type->As<sem::Pointer>()) { if (auto ast_ptr_type = typ::As<typ::Pointer>(type)) {
// Remap an old-style storage buffer pointer to a new-style storage // Remap an old-style storage buffer pointer to a new-style storage
// buffer pointer. // buffer pointer.
const auto sc = GetStorageClassForPointerValue(result_id); const auto sc = GetStorageClassForPointerValue(result_id);
if (ast_ptr_type->storage_class() != sc) { if (ast_ptr_type.ast->storage_class() != sc) {
return builder_.create<sem::Pointer>(ast_ptr_type->type(), sc); return builder_.ty.pointer(typ::Call_type(ast_ptr_type), sc);
} }
} }
return type; return type;
@ -4183,13 +4190,13 @@ const Construct* FunctionEmitter::GetEnclosingScope(uint32_t first_pos,
TypedExpression FunctionEmitter::MakeNumericConversion( TypedExpression FunctionEmitter::MakeNumericConversion(
const spvtools::opt::Instruction& inst) { const spvtools::opt::Instruction& inst) {
const auto opcode = inst.opcode(); const auto opcode = inst.opcode();
auto* requested_type = parser_impl_.ConvertType(inst.type_id()); auto requested_type = parser_impl_.ConvertType(inst.type_id());
auto arg_expr = MakeOperand(inst, 0); auto arg_expr = MakeOperand(inst, 0);
if (!arg_expr.expr || !arg_expr.type) { if (!arg_expr.expr || !arg_expr.type) {
return {}; return {};
} }
sem::Type* expr_type = nullptr; typ::Type expr_type = nullptr;
if ((opcode == SpvOpConvertSToF) || (opcode == SpvOpConvertUToF)) { if ((opcode == SpvOpConvertSToF) || (opcode == SpvOpConvertUToF)) {
if (arg_expr.type->is_integer_scalar_or_vector()) { if (arg_expr.type->is_integer_scalar_or_vector()) {
expr_type = requested_type; expr_type = requested_type;
@ -4248,13 +4255,13 @@ bool FunctionEmitter::EmitFunctionCall(const spvtools::opt::Instruction& inst) {
} }
auto* call_expr = auto* call_expr =
create<ast::CallExpression>(Source{}, function, std::move(params)); create<ast::CallExpression>(Source{}, function, std::move(params));
auto* result_type = parser_impl_.ConvertType(inst.type_id()); auto result_type = parser_impl_.ConvertType(inst.type_id());
if (!result_type) { if (!result_type) {
return Fail() << "internal error: no mapped type result of call: " return Fail() << "internal error: no mapped type result of call: "
<< inst.PrettyPrint(); << inst.PrettyPrint();
} }
if (result_type->Is<sem::Void>()) { if (result_type.ast->Is<ast::Void>()) {
return nullptr != return nullptr !=
AddStatement(create<ast::CallStatement>(Source{}, call_expr)); AddStatement(create<ast::CallStatement>(Source{}, call_expr));
} }
@ -4317,7 +4324,7 @@ TypedExpression FunctionEmitter::MakeIntrinsicCall(
Source{}, builder_.Symbols().Register(name)); Source{}, builder_.Symbols().Register(name));
ast::ExpressionList params; ast::ExpressionList params;
sem::Type* first_operand_type = nullptr; typ::Type first_operand_type = nullptr;
for (uint32_t iarg = 0; iarg < inst.NumInOperands(); ++iarg) { for (uint32_t iarg = 0; iarg < inst.NumInOperands(); ++iarg) {
TypedExpression operand = MakeOperand(inst, iarg); TypedExpression operand = MakeOperand(inst, iarg);
if (first_operand_type == nullptr) { if (first_operand_type == nullptr) {
@ -4327,7 +4334,7 @@ TypedExpression FunctionEmitter::MakeIntrinsicCall(
} }
auto* call_expr = auto* call_expr =
create<ast::CallExpression>(Source{}, ident, std::move(params)); create<ast::CallExpression>(Source{}, ident, std::move(params));
auto* result_type = parser_impl_.ConvertType(inst.type_id()); auto result_type = parser_impl_.ConvertType(inst.type_id());
if (!result_type) { if (!result_type) {
Fail() << "internal error: no mapped type result of call: " Fail() << "internal error: no mapped type result of call: "
<< inst.PrettyPrint(); << inst.PrettyPrint();
@ -4348,9 +4355,9 @@ TypedExpression FunctionEmitter::MakeSimpleSelect(
// - operand1, operand2, and result type to match. // - operand1, operand2, and result type to match.
// - you can't select over pointers or pointer vectors, unless you also have // - you can't select over pointers or pointer vectors, unless you also have
// a VariablePointers* capability, which is not allowed in by WebGPU. // a VariablePointers* capability, which is not allowed in by WebGPU.
auto* op_ty = operand1.type; auto* op_ty = operand1.type.ast;
if (op_ty->Is<sem::Vector>() || op_ty->is_float_scalar() || if (op_ty->Is<ast::Vector>() || op_ty->is_float_scalar() ||
op_ty->is_integer_scalar() || op_ty->Is<sem::Bool>()) { op_ty->is_integer_scalar() || op_ty->Is<ast::Bool>()) {
ast::ExpressionList params; ast::ExpressionList params;
params.push_back(operand1.expr); params.push_back(operand1.expr);
params.push_back(operand2.expr); params.push_back(operand2.expr);
@ -4388,18 +4395,23 @@ const spvtools::opt::Instruction* FunctionEmitter::GetImage(
return image; return image;
} }
sem::Texture* FunctionEmitter::GetImageType( typ::Texture FunctionEmitter::GetImageType(
const spvtools::opt::Instruction& image) { const spvtools::opt::Instruction& image) {
sem::Pointer* ptr_type = parser_impl_.GetTypeForHandleVar(image); typ::Pointer ptr_type = parser_impl_.GetTypeForHandleVar(image);
if (!parser_impl_.success()) { if (!parser_impl_.success()) {
Fail(); Fail();
return nullptr; return {};
} }
if (!ptr_type || !ptr_type->type()->UnwrapAll()->Is<sem::Texture>()) { if (!ptr_type) {
Fail() << "invalid texture type for " << image.PrettyPrint(); Fail() << "invalid texture type for " << image.PrettyPrint();
return nullptr; return {};
} }
return As<sem::Texture>(ptr_type->type()->UnwrapAll()); auto result = typ::As<typ::Texture>(UnwrapAll(typ::Call_type(ptr_type)));
if (!result) {
Fail() << "invalid texture type for " << image.PrettyPrint();
return {};
}
return result;
} }
ast::Expression* FunctionEmitter::GetImageExpression( ast::Expression* FunctionEmitter::GetImageExpression(
@ -4449,12 +4461,13 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
} }
} }
sem::Pointer* texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image); typ::Pointer texture_ptr_type = parser_impl_.GetTypeForHandleVar(*image);
if (!texture_ptr_type) { if (!texture_ptr_type) {
return Fail(); return Fail();
} }
sem::Texture* texture_type = typ::Texture texture_type =
texture_ptr_type->type()->UnwrapAll()->As<sem::Texture>(); typ::As<typ::Texture>(UnwrapAll(typ::Call_type(texture_ptr_type)));
if (!texture_type) { if (!texture_type) {
return Fail(); return Fail();
} }
@ -4556,7 +4569,7 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
} }
TypedExpression lod = MakeOperand(inst, arg_index); TypedExpression lod = MakeOperand(inst, arg_index);
// When sampling from a depth texture, the Lod operand must be an I32. // When sampling from a depth texture, the Lod operand must be an I32.
if (texture_type->Is<sem::DepthTexture>()) { if (texture_type.ast->Is<ast::DepthTexture>()) {
// Convert it to a signed integer type. // Convert it to a signed integer type.
lod = ToI32(lod); lod = ToI32(lod);
} }
@ -4564,11 +4577,11 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
image_operands_mask ^= SpvImageOperandsLodMask; image_operands_mask ^= SpvImageOperandsLodMask;
arg_index++; arg_index++;
} else if ((opcode == SpvOpImageFetch) && } else if ((opcode == SpvOpImageFetch) &&
(texture_type->Is<sem::SampledTexture>() || (texture_type.ast->Is<ast::SampledTexture>() ||
texture_type->Is<sem::DepthTexture>())) { texture_type.ast->Is<ast::DepthTexture>())) {
// textureLoad on sampled texture and depth texture requires an explicit // textureLoad on sampled texture and depth texture requires an explicit
// level-of-detail parameter. // level-of-detail parameter.
params.push_back(parser_impl_.MakeNullValue(i32_)); params.push_back(parser_impl_.MakeNullValue(builder_.ty.i32()));
} }
if (arg_index + 1 < num_args && if (arg_index + 1 < num_args &&
(image_operands_mask & SpvImageOperandsGradMask)) { (image_operands_mask & SpvImageOperandsGradMask)) {
@ -4626,10 +4639,10 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
ast::Expression* value = call_expr; ast::Expression* value = call_expr;
// The result type, derived from the SPIR-V instruction. // The result type, derived from the SPIR-V instruction.
auto* result_type = parser_impl_.ConvertType(inst.type_id()); auto result_type = parser_impl_.ConvertType(inst.type_id());
auto* result_component_type = result_type; auto result_component_type = result_type;
if (auto* result_vector_type = result_type->As<sem::Vector>()) { if (auto result_vector_type = typ::As<typ::Vector>(result_type)) {
result_component_type = result_vector_type->type(); result_component_type = typ::Call_type(result_vector_type);
} }
// For depth textures, the arity might mot match WGSL: // For depth textures, the arity might mot match WGSL:
@ -4643,7 +4656,7 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
// dref gather vec4 ImageFetch vec4 TODO(dneto) // dref gather vec4 ImageFetch vec4 TODO(dneto)
// Construct a 4-element vector with the result from the builtin in the // Construct a 4-element vector with the result from the builtin in the
// first component. // first component.
if (texture_type->Is<sem::DepthTexture>()) { if (texture_type.ast->Is<ast::DepthTexture>()) {
if (is_non_dref_sample || (opcode == SpvOpImageFetch)) { if (is_non_dref_sample || (opcode == SpvOpImageFetch)) {
value = create<ast::TypeConstructorExpression>( value = create<ast::TypeConstructorExpression>(
Source{}, Source{},
@ -4664,14 +4677,14 @@ bool FunctionEmitter::EmitImageAccess(const spvtools::opt::Instruction& inst) {
return Fail() << "invalid image type for image memory object declaration " return Fail() << "invalid image type for image memory object declaration "
<< image->PrettyPrint(); << image->PrettyPrint();
} }
auto* expected_component_type = auto expected_component_type =
parser_impl_.ConvertType(spirv_image_type->GetSingleWordInOperand(0)); parser_impl_.ConvertType(spirv_image_type->GetSingleWordInOperand(0));
if (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, // This occurs if one is signed integer and the other is unsigned integer,
// or vice versa. Perform a bitcast. // or vice versa. Perform a bitcast.
value = create<ast::BitcastExpression>(Source{}, result_type, call_expr); value = create<ast::BitcastExpression>(Source{}, result_type, call_expr);
} }
if (!expected_component_type->Is<sem::F32>() && if (!expected_component_type.ast->Is<ast::F32>() &&
IsSampledImageAccess(opcode)) { IsSampledImageAccess(opcode)) {
// WGSL permits sampled image access only on float textures. // WGSL permits sampled image access only on float textures.
// Reject this case in the SPIR-V reader, at least until SPIR-V validation // Reject this case in the SPIR-V reader, at least until SPIR-V validation
@ -4694,7 +4707,7 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
if (!image) { if (!image) {
return false; return false;
} }
auto* texture_type = GetImageType(*image); auto texture_type = GetImageType(*image);
if (!texture_type) { if (!texture_type) {
return false; return false;
} }
@ -4722,7 +4735,7 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
Source{}, layers_ident, Source{}, layers_ident,
ast::ExpressionList{GetImageExpression(inst)})); ast::ExpressionList{GetImageExpression(inst)}));
} }
auto* result_type = parser_impl_.ConvertType(inst.type_id()); auto result_type = parser_impl_.ConvertType(inst.type_id());
TypedExpression expr = { TypedExpression expr = {
result_type, result_type,
create<ast::TypeConstructorExpression>(Source{}, result_type, exprs)}; create<ast::TypeConstructorExpression>(Source{}, result_type, exprs)};
@ -4742,10 +4755,10 @@ bool FunctionEmitter::EmitImageQuery(const spvtools::opt::Instruction& inst) {
ast::Expression* ast_expr = create<ast::CallExpression>( ast::Expression* ast_expr = create<ast::CallExpression>(
Source{}, levels_ident, Source{}, levels_ident,
ast::ExpressionList{GetImageExpression(inst)}); ast::ExpressionList{GetImageExpression(inst)});
auto* result_type = parser_impl_.ConvertType(inst.type_id()); auto result_type = parser_impl_.ConvertType(inst.type_id());
// The SPIR-V result type must be integer scalar. The WGSL bulitin // The SPIR-V result type must be integer scalar. The WGSL bulitin
// returns i32. If they aren't the same then convert the result. // returns i32. If they aren't the same then convert the result.
if (result_type != i32_) { if (result_type != builder_.ty.i32()) {
ast_expr = create<ast::TypeConstructorExpression>( ast_expr = create<ast::TypeConstructorExpression>(
Source{}, result_type, ast::ExpressionList{ast_expr}); Source{}, result_type, ast::ExpressionList{ast_expr});
} }
@ -4790,7 +4803,7 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
if (!raw_coords.type) { if (!raw_coords.type) {
return {}; return {};
} }
sem::Texture* texture_type = GetImageType(*image); typ::Texture texture_type = GetImageType(*image);
if (!texture_type) { if (!texture_type) {
return {}; return {};
} }
@ -4805,12 +4818,12 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
} }
const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0); const auto num_coords_required = num_axes + (is_arrayed ? 1 : 0);
uint32_t num_coords_supplied = 0; uint32_t num_coords_supplied = 0;
auto* component_type = raw_coords.type; auto component_type = raw_coords.type;
if (component_type->is_float_scalar() || if (component_type->is_float_scalar() ||
component_type->is_integer_scalar()) { component_type->is_integer_scalar()) {
num_coords_supplied = 1; num_coords_supplied = 1;
} else if (auto* vec_type = raw_coords.type->As<sem::Vector>()) { } else if (auto vec_type = typ::As<typ::Vector>(raw_coords.type)) {
component_type = vec_type->type(); component_type = typ::Call_type(vec_type);
num_coords_supplied = vec_type->size(); num_coords_supplied = vec_type->size();
} }
if (num_coords_supplied == 0) { if (num_coords_supplied == 0) {
@ -4834,9 +4847,9 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
// will actually use them. // will actually use them.
auto prefix_swizzle_expr = [this, num_axes, component_type, auto prefix_swizzle_expr = [this, num_axes, component_type,
raw_coords]() -> ast::Expression* { raw_coords]() -> ast::Expression* {
auto* swizzle_type = (num_axes == 1) auto swizzle_type =
? component_type (num_axes == 1) ? component_type
: create<sem::Vector>(component_type, num_axes); : typ::Type{builder_.ty.vec(component_type, num_axes)};
auto* swizzle = create<ast::MemberAccessorExpression>( auto* swizzle = create<ast::MemberAccessorExpression>(
Source{}, raw_coords.expr, PrefixSwizzle(num_axes)); Source{}, raw_coords.expr, PrefixSwizzle(num_axes));
return ToSignedIfUnsigned({swizzle_type, swizzle}).expr; return ToSignedIfUnsigned({swizzle_type, swizzle}).expr;
@ -4870,15 +4883,15 @@ ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
ast::Expression* FunctionEmitter::ConvertTexelForStorage( ast::Expression* FunctionEmitter::ConvertTexelForStorage(
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
TypedExpression texel, TypedExpression texel,
sem::Texture* texture_type) { typ::Texture texture_type) {
auto* storage_texture_type = texture_type->As<sem::StorageTexture>(); auto storage_texture_type = typ::As<typ::StorageTexture>(texture_type);
auto* src_type = texel.type; auto src_type = texel.type;
if (!storage_texture_type) { if (!storage_texture_type) {
Fail() << "writing to other than storage texture: " << inst.PrettyPrint(); Fail() << "writing to other than storage texture: " << inst.PrettyPrint();
return nullptr; return nullptr;
} }
const auto format = storage_texture_type->image_format(); const auto format = storage_texture_type->image_format();
auto* dest_type = parser_impl_.GetTexelTypeForFormat(format); auto dest_type = parser_impl_.GetTexelTypeForFormat(format);
if (!dest_type) { if (!dest_type) {
Fail(); Fail();
return nullptr; return nullptr;
@ -4888,14 +4901,14 @@ ast::Expression* FunctionEmitter::ConvertTexelForStorage(
} }
const uint32_t dest_count = const uint32_t dest_count =
dest_type->is_scalar() ? 1 : dest_type->As<sem::Vector>()->size(); dest_type->is_scalar() ? 1 : dest_type.ast->As<ast::Vector>()->size();
if (dest_count == 3) { if (dest_count == 3) {
Fail() << "3-channel storage textures are not supported: " Fail() << "3-channel storage textures are not supported: "
<< inst.PrettyPrint(); << inst.PrettyPrint();
return nullptr; return nullptr;
} }
const uint32_t src_count = const uint32_t src_count =
src_type->is_scalar() ? 1 : src_type->As<sem::Vector>()->size(); src_type->is_scalar() ? 1 : src_type.ast->As<ast::Vector>()->size();
if (src_count < dest_count) { if (src_count < dest_count) {
Fail() << "texel has too few components for storage texture: " << src_count Fail() << "texel has too few components for storage texture: " << src_count
<< " provided but " << dest_count << " provided but " << dest_count
@ -4954,21 +4967,22 @@ ast::Expression* FunctionEmitter::ConvertTexelForStorage(
} }
TypedExpression FunctionEmitter::ToI32(TypedExpression value) { TypedExpression FunctionEmitter::ToI32(TypedExpression value) {
if (!value.type || value.type == i32_) { if (!value.type || value.type == builder_.ty.i32()) {
return value; return value;
} }
return {i32_, create<ast::TypeConstructorExpression>( return {builder_.ty.i32(),
Source{}, i32_, ast::ExpressionList{value.expr})}; create<ast::TypeConstructorExpression>(
Source{}, builder_.ty.i32(), ast::ExpressionList{value.expr})};
} }
TypedExpression FunctionEmitter::ToSignedIfUnsigned(TypedExpression value) { TypedExpression FunctionEmitter::ToSignedIfUnsigned(TypedExpression value) {
if (!value.type || !value.type->is_unsigned_scalar_or_vector()) { if (!value.type || !value.type->is_unsigned_scalar_or_vector()) {
return value; return value;
} }
if (auto* vec_type = value.type->As<sem::Vector>()) { if (auto* vec_type = value.type.ast->As<ast::Vector>()) {
auto* new_type = create<sem::Vector>(i32_, vec_type->size()); auto new_type = builder_.ty.vec(builder_.ty.i32(), vec_type->size());
return {new_type, create<ast::TypeConstructorExpression>( return {new_type,
Source{}, new_type, ast::ExpressionList{value.expr})}; builder_.Construct(new_type, ast::ExpressionList{value.expr})};
} }
return ToI32(value); return ToI32(value);
} }
@ -5021,9 +5035,10 @@ TypedExpression FunctionEmitter::MakeOuterProduct(
// Synthesize the result. // Synthesize the result.
auto col = MakeOperand(inst, 0); auto col = MakeOperand(inst, 0);
auto row = MakeOperand(inst, 1); auto row = MakeOperand(inst, 1);
auto* col_ty = col.type->As<sem::Vector>(); auto col_ty = typ::As<typ::Vector>(col.type);
auto* row_ty = row.type->As<sem::Vector>(); auto row_ty = typ::As<typ::Vector>(row.type);
auto* result_ty = parser_impl_.ConvertType(inst.type_id())->As<sem::Matrix>(); auto result_ty =
typ::As<typ::Matrix>(parser_impl_.ConvertType(inst.type_id()));
if (!col_ty || !col_ty || !result_ty || result_ty->type() != col_ty->type() || if (!col_ty || !col_ty || !result_ty || result_ty->type() != col_ty->type() ||
result_ty->type() != row_ty->type() || result_ty->type() != row_ty->type() ||
result_ty->columns() != row_ty->size() || result_ty->columns() != row_ty->size() ||
@ -5074,7 +5089,7 @@ bool FunctionEmitter::MakeVectorInsertDynamic(
// Then use result everywhere the original SPIR-V id is used. Using a const // Then use result everywhere the original SPIR-V id is used. Using a const
// like this avoids constantly reloading the value many times. // like this avoids constantly reloading the value many times.
auto* ast_type = parser_impl_.ConvertType(inst.type_id()); auto ast_type = parser_impl_.ConvertType(inst.type_id());
auto src_vector = MakeOperand(inst, 0); auto src_vector = MakeOperand(inst, 0);
auto component = MakeOperand(inst, 1); auto component = MakeOperand(inst, 1);
auto index = MakeOperand(inst, 2); auto index = MakeOperand(inst, 2);
@ -5121,7 +5136,7 @@ bool FunctionEmitter::MakeCompositeInsert(
// - building up an access-chain like access like for CompositeExtract, but // - building up an access-chain like access like for CompositeExtract, but
// on the left-hand side of the assignment. // on the left-hand side of the assignment.
auto* ast_type = parser_impl_.ConvertType(inst.type_id()); auto ast_type = parser_impl_.ConvertType(inst.type_id());
auto component = MakeOperand(inst, 0); auto component = MakeOperand(inst, 0);
auto src_composite = MakeOperand(inst, 1); auto src_composite = MakeOperand(inst, 1);

View File

@ -25,6 +25,7 @@
#include "src/program_builder.h" #include "src/program_builder.h"
#include "src/reader/spirv/construct.h" #include "src/reader/spirv/construct.h"
#include "src/reader/spirv/parser_impl.h" #include "src/reader/spirv/parser_impl.h"
#include "src/typepair.h"
namespace tint { namespace tint {
namespace reader { namespace reader {
@ -512,7 +513,7 @@ class FunctionEmitter {
/// @param type the AST type /// @param type the AST type
/// @param result_id the SPIR-V ID for the locally defined value /// @param result_id the SPIR-V ID for the locally defined value
/// @returns an possibly updated type /// @returns an possibly updated type
sem::Type* RemapStorageClass(sem::Type* type, uint32_t result_id); typ::Type RemapStorageClass(typ::Type type, uint32_t result_id);
/// Marks locally defined values when they should get a 'const' /// Marks locally defined values when they should get a 'const'
/// definition in WGSL, or a 'var' definition at an outer scope. /// definition in WGSL, or a 'var' definition at an outer scope.
@ -853,7 +854,7 @@ class FunctionEmitter {
/// Function parameters /// Function parameters
ast::VariableList params; ast::VariableList params;
/// Function return type /// Function return type
sem::Type* return_type; typ::Type return_type;
/// Function decorations /// Function decorations
ast::DecorationList decorations; ast::DecorationList decorations;
}; };
@ -866,7 +867,7 @@ class FunctionEmitter {
/// @returns the store type for the OpVariable instruction, or /// @returns the store type for the OpVariable instruction, or
/// null on failure. /// null on failure.
sem::Type* GetVariableStoreType( typ::Type GetVariableStoreType(
const spvtools::opt::Instruction& var_decl_inst); const spvtools::opt::Instruction& var_decl_inst);
/// Returns an expression for an instruction operand. Signedness conversion is /// Returns an expression for an instruction operand. Signedness conversion is
@ -934,7 +935,7 @@ class FunctionEmitter {
/// Get the AST texture the SPIR-V image memory object declaration. /// Get the AST texture the SPIR-V image memory object declaration.
/// @param inst the SPIR-V memory object declaration for the image. /// @param inst the SPIR-V memory object declaration for the image.
/// @returns a texture type, or null on error /// @returns a texture type, or null on error
sem::Texture* GetImageType(const spvtools::opt::Instruction& inst); typ::Texture GetImageType(const spvtools::opt::Instruction& inst);
/// Get the expression for the image operand from the first operand to the /// Get the expression for the image operand from the first operand to the
/// given instruction. /// given instruction.
@ -971,7 +972,7 @@ class FunctionEmitter {
ast::Expression* ConvertTexelForStorage( ast::Expression* ConvertTexelForStorage(
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
TypedExpression texel, TypedExpression texel,
sem::Texture* texture_type); typ::Texture texture_type);
/// Returns an expression for an OpSelect, if its operands are scalars /// Returns an expression for an OpSelect, if its operands are scalars
/// or vectors. These translate directly to WGSL select. Otherwise, return /// or vectors. These translate directly to WGSL select. Otherwise, return
@ -1133,8 +1134,6 @@ class FunctionEmitter {
FailStream& fail_stream_; FailStream& fail_stream_;
Namer& namer_; Namer& namer_;
const spvtools::opt::Function& function_; const spvtools::opt::Function& function_;
sem::I32* const i32_; // The unique I32 type object.
sem::U32* const u32_; // The unique U32 type object.
// The SPIR-V ID for the SampleMask input variable. // The SPIR-V ID for the SampleMask input variable.
uint32_t sample_mask_in_id; uint32_t sample_mask_in_id;

View File

@ -23,6 +23,7 @@
#include "src/ast/bitcast_expression.h" #include "src/ast/bitcast_expression.h"
#include "src/ast/override_decoration.h" #include "src/ast/override_decoration.h"
#include "src/ast/struct_block_decoration.h" #include "src/ast/struct_block_decoration.h"
#include "src/ast/type_name.h"
#include "src/reader/spirv/function.h" #include "src/reader/spirv/function.h"
#include "src/sem/access_control_type.h" #include "src/sem/access_control_type.h"
#include "src/sem/depth_texture_type.h" #include "src/sem/depth_texture_type.h"
@ -228,13 +229,28 @@ bool AssumesResultSignednessMatchesFirstOperand(GLSLstd450 extended_opcode) {
return false; return false;
} }
// Forwards UnwrapIfNeeded to both the ast and sem types of the TypePair
// @param tp the type pair
// @returns the unwrapped type pair
typ::Type UnwrapIfNeeded(typ::Type tp) {
return typ::Type{tp.ast->UnwrapIfNeeded(), tp.sem->UnwrapIfNeeded()};
}
} // namespace } // namespace
TypedExpression::TypedExpression() = default;
TypedExpression::TypedExpression(const TypedExpression&) = default;
TypedExpression& TypedExpression::operator=(const TypedExpression&) = default;
TypedExpression::TypedExpression(typ::Type type_in, ast::Expression* expr_in)
: type(type_in), expr(expr_in) {}
ParserImpl::ParserImpl(const std::vector<uint32_t>& spv_binary) ParserImpl::ParserImpl(const std::vector<uint32_t>& spv_binary)
: Reader(), : Reader(),
spv_binary_(spv_binary), spv_binary_(spv_binary),
fail_stream_(&success_, &errors_), fail_stream_(&success_, &errors_),
bool_type_(builder_.create<sem::Bool>()),
namer_(fail_stream_), namer_(fail_stream_),
enum_converter_(fail_stream_), enum_converter_(fail_stream_),
tools_context_(kInputEnv) { tools_context_(kInputEnv) {
@ -292,7 +308,7 @@ Program ParserImpl::program() {
return tint::Program(std::move(builder_)); return tint::Program(std::move(builder_));
} }
sem::Type* ParserImpl::ConvertType(uint32_t type_id) { typ::Type ParserImpl::ConvertType(uint32_t type_id) {
if (!success_) { if (!success_) {
return nullptr; return nullptr;
} }
@ -302,46 +318,42 @@ sem::Type* ParserImpl::ConvertType(uint32_t type_id) {
return nullptr; return nullptr;
} }
auto where = id_to_type_.find(type_id);
if (where != id_to_type_.end()) {
return where->second;
}
auto* spirv_type = type_mgr_->GetType(type_id); auto* spirv_type = type_mgr_->GetType(type_id);
if (spirv_type == nullptr) { if (spirv_type == nullptr) {
Fail() << "ID is not a SPIR-V type: " << type_id; Fail() << "ID is not a SPIR-V type: " << type_id;
return nullptr; return nullptr;
} }
auto save = [this, type_id, spirv_type](sem::Type* type) { auto maybe_generate_alias = [this, type_id,
spirv_type](typ::Type type) -> typ::Type {
if (type != nullptr) { if (type != nullptr) {
id_to_type_[type_id] = type; return MaybeGenerateAlias(type_id, spirv_type, type);
MaybeGenerateAlias(type_id, spirv_type);
} }
return type; return {};
}; };
switch (spirv_type->kind()) { switch (spirv_type->kind()) {
case spvtools::opt::analysis::Type::kVoid: case spvtools::opt::analysis::Type::kVoid:
return save(builder_.create<sem::Void>()); return maybe_generate_alias(builder_.ty.void_());
case spvtools::opt::analysis::Type::kBool: case spvtools::opt::analysis::Type::kBool:
return save(bool_type_); return maybe_generate_alias(builder_.ty.bool_());
case spvtools::opt::analysis::Type::kInteger: case spvtools::opt::analysis::Type::kInteger:
return save(ConvertType(spirv_type->AsInteger())); return maybe_generate_alias(ConvertType(spirv_type->AsInteger()));
case spvtools::opt::analysis::Type::kFloat: case spvtools::opt::analysis::Type::kFloat:
return save(ConvertType(spirv_type->AsFloat())); return maybe_generate_alias(ConvertType(spirv_type->AsFloat()));
case spvtools::opt::analysis::Type::kVector: case spvtools::opt::analysis::Type::kVector:
return save(ConvertType(spirv_type->AsVector())); return maybe_generate_alias(ConvertType(spirv_type->AsVector()));
case spvtools::opt::analysis::Type::kMatrix: case spvtools::opt::analysis::Type::kMatrix:
return save(ConvertType(spirv_type->AsMatrix())); return maybe_generate_alias(ConvertType(spirv_type->AsMatrix()));
case spvtools::opt::analysis::Type::kRuntimeArray: case spvtools::opt::analysis::Type::kRuntimeArray:
return save(ConvertType(spirv_type->AsRuntimeArray())); return maybe_generate_alias(ConvertType(spirv_type->AsRuntimeArray()));
case spvtools::opt::analysis::Type::kArray: case spvtools::opt::analysis::Type::kArray:
return save(ConvertType(spirv_type->AsArray())); return maybe_generate_alias(ConvertType(spirv_type->AsArray()));
case spvtools::opt::analysis::Type::kStruct: case spvtools::opt::analysis::Type::kStruct:
return save(ConvertType(type_id, spirv_type->AsStruct())); return maybe_generate_alias(ConvertType(type_id, spirv_type->AsStruct()));
case spvtools::opt::analysis::Type::kPointer: case spvtools::opt::analysis::Type::kPointer:
return save(ConvertType(type_id, spirv_type->AsPointer())); return maybe_generate_alias(
ConvertType(type_id, spirv_type->AsPointer()));
case spvtools::opt::analysis::Type::kFunction: case spvtools::opt::analysis::Type::kFunction:
// Tint doesn't have a Function type. // Tint doesn't have a Function type.
// We need to convert the result type and parameter types. // We need to convert the result type and parameter types.
@ -353,7 +365,7 @@ sem::Type* ParserImpl::ConvertType(uint32_t type_id) {
case spvtools::opt::analysis::Type::kImage: case spvtools::opt::analysis::Type::kImage:
// Fake it for sampler and texture types. These are handled in an // Fake it for sampler and texture types. These are handled in an
// entirely different way. // entirely different way.
return save(builder_.create<sem::Void>()); return maybe_generate_alias(builder_.ty.void_());
default: default:
break; break;
} }
@ -765,67 +777,51 @@ bool ParserImpl::RegisterEntryPoints() {
return success_; return success_;
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::Integer* int_ty) { const spvtools::opt::analysis::Integer* int_ty) {
if (int_ty->width() == 32) { if (int_ty->width() == 32) {
sem::Type* signed_ty = builder_.create<sem::I32>(); return int_ty->IsSigned() ? typ::Type{builder_.ty.i32()}
sem::Type* unsigned_ty = builder_.create<sem::U32>(); : typ::Type{builder_.ty.u32()};
signed_type_for_[unsigned_ty] = signed_ty;
unsigned_type_for_[signed_ty] = unsigned_ty;
return int_ty->IsSigned() ? signed_ty : unsigned_ty;
} }
Fail() << "unhandled integer width: " << int_ty->width(); Fail() << "unhandled integer width: " << int_ty->width();
return nullptr; return nullptr;
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::Float* float_ty) { const spvtools::opt::analysis::Float* float_ty) {
if (float_ty->width() == 32) { if (float_ty->width() == 32) {
return builder_.create<sem::F32>(); return builder_.ty.f32();
} }
Fail() << "unhandled float width: " << float_ty->width(); Fail() << "unhandled float width: " << float_ty->width();
return nullptr; return nullptr;
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::Vector* vec_ty) { const spvtools::opt::analysis::Vector* vec_ty) {
const auto num_elem = vec_ty->element_count(); const auto num_elem = vec_ty->element_count();
auto* ast_elem_ty = ConvertType(type_mgr_->GetId(vec_ty->element_type())); auto ast_elem_ty = ConvertType(type_mgr_->GetId(vec_ty->element_type()));
if (ast_elem_ty == nullptr) { if (ast_elem_ty == nullptr) {
return nullptr; return nullptr;
} }
auto* this_ty = builder_.create<sem::Vector>(ast_elem_ty, num_elem); return builder_.ty.vec(ast_elem_ty, num_elem);
// Generate the opposite-signedness vector type, if this type is integral.
if (unsigned_type_for_.count(ast_elem_ty)) {
auto* other_ty =
builder_.create<sem::Vector>(unsigned_type_for_[ast_elem_ty], num_elem);
signed_type_for_[other_ty] = this_ty;
unsigned_type_for_[this_ty] = other_ty;
} else if (signed_type_for_.count(ast_elem_ty)) {
auto* other_ty =
builder_.create<sem::Vector>(signed_type_for_[ast_elem_ty], num_elem);
unsigned_type_for_[other_ty] = this_ty;
signed_type_for_[this_ty] = other_ty;
}
return this_ty;
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::Matrix* mat_ty) { const spvtools::opt::analysis::Matrix* mat_ty) {
const auto* vec_ty = mat_ty->element_type()->AsVector(); const auto* vec_ty = mat_ty->element_type()->AsVector();
const auto* scalar_ty = vec_ty->element_type(); const auto* scalar_ty = vec_ty->element_type();
const auto num_rows = vec_ty->element_count(); const auto num_rows = vec_ty->element_count();
const auto num_columns = mat_ty->element_count(); const auto num_columns = mat_ty->element_count();
auto* ast_scalar_ty = ConvertType(type_mgr_->GetId(scalar_ty)); auto ast_scalar_ty = ConvertType(type_mgr_->GetId(scalar_ty));
if (ast_scalar_ty == nullptr) { if (ast_scalar_ty == nullptr) {
return nullptr; return nullptr;
} }
return builder_.create<sem::Matrix>(ast_scalar_ty, num_rows, num_columns); return builder_.ty.mat(ast_scalar_ty, num_columns, num_rows);
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::RuntimeArray* rtarr_ty) { const spvtools::opt::analysis::RuntimeArray* rtarr_ty) {
auto* ast_elem_ty = ConvertType(type_mgr_->GetId(rtarr_ty->element_type())); auto ast_elem_ty = ConvertType(type_mgr_->GetId(rtarr_ty->element_type()));
if (ast_elem_ty == nullptr) { if (ast_elem_ty == nullptr) {
return nullptr; return nullptr;
} }
@ -833,13 +829,13 @@ sem::Type* ParserImpl::ConvertType(
if (!ParseArrayDecorations(rtarr_ty, &decorations)) { if (!ParseArrayDecorations(rtarr_ty, &decorations)) {
return nullptr; return nullptr;
} }
return create<sem::ArrayType>(ast_elem_ty, 0, std::move(decorations)); return builder_.ty.array(ast_elem_ty, 0, std::move(decorations));
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
const spvtools::opt::analysis::Array* arr_ty) { const spvtools::opt::analysis::Array* arr_ty) {
const auto elem_type_id = type_mgr_->GetId(arr_ty->element_type()); const auto elem_type_id = type_mgr_->GetId(arr_ty->element_type());
auto* ast_elem_ty = ConvertType(elem_type_id); auto ast_elem_ty = ConvertType(elem_type_id);
if (ast_elem_ty == nullptr) { if (ast_elem_ty == nullptr) {
return nullptr; return nullptr;
} }
@ -878,8 +874,8 @@ sem::Type* ParserImpl::ConvertType(
if (remap_buffer_block_type_.count(elem_type_id)) { if (remap_buffer_block_type_.count(elem_type_id)) {
remap_buffer_block_type_.insert(type_mgr_->GetId(arr_ty)); remap_buffer_block_type_.insert(type_mgr_->GetId(arr_ty));
} }
return create<sem::ArrayType>(ast_elem_ty, static_cast<uint32_t>(num_elem), return builder_.ty.array(ast_elem_ty, static_cast<uint32_t>(num_elem),
std::move(decorations)); std::move(decorations));
} }
bool ParserImpl::ParseArrayDecorations( bool ParserImpl::ParseArrayDecorations(
@ -911,7 +907,7 @@ bool ParserImpl::ParseArrayDecorations(
return true; return true;
} }
sem::Type* ParserImpl::ConvertType( typ::Type ParserImpl::ConvertType(
uint32_t type_id, uint32_t type_id,
const spvtools::opt::analysis::Struct* struct_ty) { const spvtools::opt::analysis::Struct* struct_ty) {
// Compute the struct decoration. // Compute the struct decoration.
@ -941,7 +937,7 @@ sem::Type* ParserImpl::ConvertType(
for (uint32_t member_index = 0; member_index < members.size(); for (uint32_t member_index = 0; member_index < members.size();
++member_index) { ++member_index) {
const auto member_type_id = type_mgr_->GetId(members[member_index]); const auto member_type_id = type_mgr_->GetId(members[member_index]);
auto* ast_member_ty = ConvertType(member_type_id); auto ast_member_ty = ConvertType(member_type_id);
if (ast_member_ty == nullptr) { if (ast_member_ty == nullptr) {
// Already emitted diagnostics. // Already emitted diagnostics.
return nullptr; return nullptr;
@ -1033,17 +1029,16 @@ sem::Type* ParserImpl::ConvertType(
} }
auto* ast_struct = create<ast::Struct>(Source{}, sym, std::move(ast_members), auto* ast_struct = create<ast::Struct>(Source{}, sym, std::move(ast_members),
std::move(ast_struct_decorations)); std::move(ast_struct_decorations));
auto* result = builder_.create<sem::StructType>(ast_struct); auto result = builder_.ty.struct_(ast_struct);
id_to_type_[type_id] = result;
if (num_non_writable_members == members.size()) { if (num_non_writable_members == members.size()) {
read_only_struct_types_.insert(result); read_only_struct_types_.insert(result.ast->name());
} }
builder_.AST().AddConstructedType(result); builder_.AST().AddConstructedType(result);
return result; return result;
} }
sem::Type* ParserImpl::ConvertType(uint32_t type_id, typ::Type ParserImpl::ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Pointer*) { const spvtools::opt::analysis::Pointer*) {
const auto* inst = def_use_mgr_->GetDef(type_id); const auto* inst = def_use_mgr_->GetDef(type_id);
const auto pointee_type_id = inst->GetSingleWordInOperand(1); const auto pointee_type_id = inst->GetSingleWordInOperand(1);
const auto storage_class = SpvStorageClass(inst->GetSingleWordInOperand(0)); const auto storage_class = SpvStorageClass(inst->GetSingleWordInOperand(0));
@ -1053,7 +1048,7 @@ sem::Type* ParserImpl::ConvertType(uint32_t type_id,
builtin_position_.storage_class = storage_class; builtin_position_.storage_class = storage_class;
return nullptr; return nullptr;
} }
auto* ast_elem_ty = ConvertType(pointee_type_id); auto ast_elem_ty = ConvertType(pointee_type_id);
if (ast_elem_ty == nullptr) { if (ast_elem_ty == nullptr) {
Fail() << "SPIR-V pointer type with ID " << type_id Fail() << "SPIR-V pointer type with ID " << type_id
<< " has invalid pointee type " << pointee_type_id; << " has invalid pointee type " << pointee_type_id;
@ -1082,7 +1077,8 @@ sem::Type* ParserImpl::ConvertType(uint32_t type_id,
} }
} }
return builder_.create<sem::Pointer>(ast_elem_ty, ast_storage_class); ast_elem_ty = builder_.ty.MaybeCreateTypename(ast_elem_ty);
return builder_.ty.pointer(ast_elem_ty, ast_storage_class);
} }
bool ParserImpl::RegisterTypes() { bool ParserImpl::RegisterTypes() {
@ -1115,7 +1111,7 @@ bool ParserImpl::EmitScalarSpecConstants() {
// that is OpSpecConstantTrue, OpSpecConstantFalse, or OpSpecConstant. // that is OpSpecConstantTrue, OpSpecConstantFalse, or OpSpecConstant.
for (auto& inst : module_->types_values()) { for (auto& inst : module_->types_values()) {
// These will be populated for a valid scalar spec constant. // These will be populated for a valid scalar spec constant.
sem::Type* ast_type = nullptr; typ::Type ast_type;
ast::ScalarConstructorExpression* ast_expr = nullptr; ast::ScalarConstructorExpression* ast_expr = nullptr;
switch (inst.opcode()) { switch (inst.opcode()) {
@ -1130,15 +1126,15 @@ bool ParserImpl::EmitScalarSpecConstants() {
case SpvOpSpecConstant: { case SpvOpSpecConstant: {
ast_type = ConvertType(inst.type_id()); ast_type = ConvertType(inst.type_id());
const uint32_t literal_value = inst.GetSingleWordInOperand(0); const uint32_t literal_value = inst.GetSingleWordInOperand(0);
if (ast_type->Is<sem::I32>()) { if (ast_type.ast->Is<ast::I32>()) {
ast_expr = create<ast::ScalarConstructorExpression>( ast_expr = create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>( Source{}, create<ast::SintLiteral>(
Source{}, static_cast<int32_t>(literal_value))); Source{}, static_cast<int32_t>(literal_value)));
} else if (ast_type->Is<sem::U32>()) { } else if (ast_type.ast->Is<ast::U32>()) {
ast_expr = create<ast::ScalarConstructorExpression>( ast_expr = create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>( Source{}, create<ast::UintLiteral>(
Source{}, static_cast<uint32_t>(literal_value))); Source{}, static_cast<uint32_t>(literal_value)));
} else if (ast_type->Is<sem::F32>()) { } else if (ast_type.ast->Is<ast::F32>()) {
float float_value; float float_value;
// Copy the bits so we can read them as a float. // Copy the bits so we can read them as a float.
std::memcpy(&float_value, &literal_value, sizeof(float_value)); std::memcpy(&float_value, &literal_value, sizeof(float_value));
@ -1174,10 +1170,12 @@ bool ParserImpl::EmitScalarSpecConstants() {
return success_; return success_;
} }
void ParserImpl::MaybeGenerateAlias(uint32_t type_id, typ::Type ParserImpl::MaybeGenerateAlias(
const spvtools::opt::analysis::Type* type) { uint32_t type_id,
const spvtools::opt::analysis::Type* type,
typ::Type ast_type) {
if (!success_) { if (!success_) {
return; return {};
} }
// We only care about arrays, and runtime arrays. // We only care about arrays, and runtime arrays.
@ -1190,25 +1188,27 @@ void ParserImpl::MaybeGenerateAlias(uint32_t type_id,
case spvtools::opt::analysis::Type::kArray: case spvtools::opt::analysis::Type::kArray:
// Only make a type aliase for arrays with decorations. // Only make a type aliase for arrays with decorations.
if (GetDecorationsFor(type_id).empty()) { if (GetDecorationsFor(type_id).empty()) {
return; return ast_type;
} }
namer_.SuggestSanitizedName(type_id, "Arr"); namer_.SuggestSanitizedName(type_id, "Arr");
break; break;
default: default:
// Ignore constants, and any other types. // Ignore constants, and any other types.
return; return ast_type;
} }
auto* ast_underlying_type = id_to_type_[type_id]; auto ast_underlying_type = ast_type;
if (ast_underlying_type == nullptr) { if (ast_underlying_type == nullptr) {
Fail() << "internal error: no type registered for SPIR-V ID: " << type_id; Fail() << "internal error: no type registered for SPIR-V ID: " << type_id;
return; return {};
} }
const auto name = namer_.GetName(type_id); const auto name = namer_.GetName(type_id);
auto* ast_alias_type = builder_.create<sem::Alias>( auto ast_alias_type =
builder_.Symbols().Register(name), ast_underlying_type); builder_.ty.alias(builder_.Symbols().Register(name), ast_underlying_type);
// Record this new alias as the AST type for this SPIR-V ID. // Record this new alias as the AST type for this SPIR-V ID.
id_to_type_[type_id] = ast_alias_type;
builder_.AST().AddConstructedType(ast_alias_type); builder_.AST().AddConstructedType(ast_alias_type);
return ast_alias_type;
} }
bool ParserImpl::EmitModuleScopeVariables() { bool ParserImpl::EmitModuleScopeVariables() {
@ -1249,7 +1249,7 @@ bool ParserImpl::EmitModuleScopeVariables() {
if (!success_) { if (!success_) {
return false; return false;
} }
sem::Type* ast_type = nullptr; typ::Type ast_type;
if (spirv_storage_class == SpvStorageClassUniformConstant) { if (spirv_storage_class == SpvStorageClassUniformConstant) {
// These are opaque handles: samplers or textures // These are opaque handles: samplers or textures
ast_type = GetTypeForHandleVar(var); ast_type = GetTypeForHandleVar(var);
@ -1257,20 +1257,20 @@ bool ParserImpl::EmitModuleScopeVariables() {
return false; return false;
} }
} else { } else {
ast_type = id_to_type_[type_id]; ast_type = ConvertType(type_id);
if (ast_type == nullptr) { if (ast_type == nullptr) {
return Fail() << "internal error: failed to register Tint AST type for " return Fail() << "internal error: failed to register Tint AST type for "
"SPIR-V type with ID: " "SPIR-V type with ID: "
<< var.type_id(); << var.type_id();
} }
if (!ast_type->Is<sem::Pointer>()) { if (!ast_type.ast->Is<ast::Pointer>()) {
return Fail() << "variable with ID " << var.result_id() return Fail() << "variable with ID " << var.result_id()
<< " has non-pointer type " << var.type_id(); << " has non-pointer type " << var.type_id();
} }
} }
auto* ast_store_type = ast_type->As<sem::Pointer>()->type(); auto ast_store_type = typ::Call_type(typ::As<typ::Pointer>(ast_type));
auto ast_storage_class = ast_type->As<sem::Pointer>()->storage_class(); auto ast_storage_class = ast_type.ast->As<ast::Pointer>()->storage_class();
ast::Expression* ast_constructor = nullptr; ast::Expression* ast_constructor = nullptr;
if (var.NumInOperands() > 1) { if (var.NumInOperands() > 1) {
// SPIR-V initializers are always constants. // SPIR-V initializers are always constants.
@ -1333,7 +1333,7 @@ const spvtools::opt::analysis::IntConstant* ParserImpl::GetArraySize(
ast::Variable* ParserImpl::MakeVariable(uint32_t id, ast::Variable* ParserImpl::MakeVariable(uint32_t id,
ast::StorageClass sc, ast::StorageClass sc,
sem::Type* type, typ::Type type,
bool is_const, bool is_const,
ast::Expression* constructor, ast::Expression* constructor,
ast::DecorationList decorations) { ast::DecorationList decorations) {
@ -1343,11 +1343,15 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
} }
if (sc == ast::StorageClass::kStorage) { if (sc == ast::StorageClass::kStorage) {
bool read_only = false;
if (auto* tn = type.ast->As<ast::TypeName>()) {
read_only = read_only_struct_types_.count(tn->name()) > 0;
}
// Apply the access(read) or access(read_write) modifier. // Apply the access(read) or access(read_write) modifier.
auto access = read_only_struct_types_.count(type) auto access = read_only ? ast::AccessControl::kReadOnly
? ast::AccessControl::kReadOnly : ast::AccessControl::kReadWrite;
: ast::AccessControl::kReadWrite; type = builder_.ty.access(access, type);
type = builder_.create<sem::AccessControl>(access, type);
} }
for (auto& deco : GetDecorationsFor(id)) { for (auto& deco : GetDecorationsFor(id)) {
@ -1372,7 +1376,7 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
// The SPIR-V variable is likely to be signed (because GLSL // The SPIR-V variable is likely to be signed (because GLSL
// requires signed), but WGSL requires unsigned. Handle specially // requires signed), but WGSL requires unsigned. Handle specially
// so we always perform the conversion at load and store. // so we always perform the conversion at load and store.
if (auto* forced_type = unsigned_type_for_[type]) { if (auto forced_type = UnsignedTypeFor(type)) {
// Requires conversion and special handling in code generation. // Requires conversion and special handling in code generation.
special_builtins_[id] = spv_builtin; special_builtins_[id] = spv_builtin;
type = forced_type; type = forced_type;
@ -1389,7 +1393,7 @@ ast::Variable* ParserImpl::MakeVariable(uint32_t id,
"SampleMask must be an array of 1 element."; "SampleMask must be an array of 1 element.";
} }
special_builtins_[id] = spv_builtin; special_builtins_[id] = spv_builtin;
type = builder_.create<sem::U32>(); type = builder_.ty.u32();
break; break;
} }
default: default:
@ -1449,7 +1453,7 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
Fail() << "ID " << id << " is not a registered instruction"; Fail() << "ID " << id << " is not a registered instruction";
return {}; return {};
} }
auto* original_ast_type = ConvertType(inst->type_id()); auto original_ast_type = ConvertType(inst->type_id());
if (original_ast_type == nullptr) { if (original_ast_type == nullptr) {
return {}; return {};
} }
@ -1467,28 +1471,28 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
} }
auto source = GetSourceForInst(inst); auto source = GetSourceForInst(inst);
auto* ast_type = original_ast_type->UnwrapIfNeeded(); auto ast_type = UnwrapIfNeeded(original_ast_type);
// TODO(dneto): Note: NullConstant for int, uint, float map to a regular 0. // TODO(dneto): Note: NullConstant for int, uint, float map to a regular 0.
// So canonicalization should map that way too. // So canonicalization should map that way too.
// Currently "null<type>" is missing from the WGSL parser. // Currently "null<type>" is missing from the WGSL parser.
// See https://bugs.chromium.org/p/tint/issues/detail?id=34 // See https://bugs.chromium.org/p/tint/issues/detail?id=34
if (ast_type->Is<sem::U32>()) { if (ast_type.ast->Is<ast::U32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>( return {ast_type, create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>( Source{}, create<ast::UintLiteral>(
source, spirv_const->GetU32()))}; source, spirv_const->GetU32()))};
} }
if (ast_type->Is<sem::I32>()) { if (ast_type.ast->Is<ast::I32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>( return {ast_type, create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>( Source{}, create<ast::SintLiteral>(
source, spirv_const->GetS32()))}; source, spirv_const->GetS32()))};
} }
if (ast_type->Is<sem::F32>()) { if (ast_type.ast->Is<ast::F32>()) {
return {ast_type, create<ast::ScalarConstructorExpression>( return {ast_type, create<ast::ScalarConstructorExpression>(
Source{}, create<ast::FloatLiteral>( Source{}, create<ast::FloatLiteral>(
source, spirv_const->GetFloat()))}; source, spirv_const->GetFloat()))};
} }
if (ast_type->Is<sem::Bool>()) { if (ast_type.ast->Is<ast::Bool>()) {
const bool value = spirv_const->AsNullConstant() const bool value = spirv_const->AsNullConstant()
? false ? false
: spirv_const->AsBoolConstant()->value(); : spirv_const->AsBoolConstant()->value();
@ -1531,7 +1535,7 @@ TypedExpression ParserImpl::MakeConstantExpression(uint32_t id) {
return {}; return {};
} }
ast::Expression* ParserImpl::MakeNullValue(sem::Type* type) { ast::Expression* ParserImpl::MakeNullValue(typ::Type type) {
// TODO(dneto): Use the no-operands constructor syntax when it becomes // TODO(dneto): Use the no-operands constructor syntax when it becomes
// available in Tint. // available in Tint.
// https://github.com/gpuweb/gpuweb/issues/685 // https://github.com/gpuweb/gpuweb/issues/685
@ -1542,37 +1546,42 @@ ast::Expression* ParserImpl::MakeNullValue(sem::Type* type) {
return nullptr; return nullptr;
} }
auto* original_type = type; auto original_type = type;
type = type->UnwrapIfNeeded(); type = UnwrapIfNeeded(type);
if (type->Is<sem::Bool>()) { if (type.ast->Is<ast::Bool>()) {
return create<ast::ScalarConstructorExpression>( return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::BoolLiteral>(Source{}, false)); Source{}, create<ast::BoolLiteral>(Source{}, false));
} }
if (type->Is<sem::U32>()) { if (type.ast->Is<ast::U32>()) {
return create<ast::ScalarConstructorExpression>( return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::UintLiteral>(Source{}, 0u)); Source{}, create<ast::UintLiteral>(Source{}, 0u));
} }
if (type->Is<sem::I32>()) { if (type.ast->Is<ast::I32>()) {
return create<ast::ScalarConstructorExpression>( return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::SintLiteral>(Source{}, 0)); Source{}, create<ast::SintLiteral>(Source{}, 0));
} }
if (type->Is<sem::F32>()) { if (type.ast->Is<ast::F32>()) {
return create<ast::ScalarConstructorExpression>( return create<ast::ScalarConstructorExpression>(
Source{}, create<ast::FloatLiteral>(Source{}, 0.0f)); Source{}, create<ast::FloatLiteral>(Source{}, 0.0f));
} }
if (const auto* vec_ty = type->As<sem::Vector>()) { if (type.ast->Is<ast::TypeName>()) {
// TODO(amaiorano): No type constructor for TypeName (yet?)
ast::ExpressionList ast_components;
return create<ast::TypeConstructorExpression>(Source{}, original_type,
std::move(ast_components));
}
if (auto vec_ty = typ::As<typ::Vector>(type)) {
ast::ExpressionList ast_components; ast::ExpressionList ast_components;
for (size_t i = 0; i < vec_ty->size(); ++i) { for (size_t i = 0; i < vec_ty->size(); ++i) {
ast_components.emplace_back(MakeNullValue(vec_ty->type())); ast_components.emplace_back(MakeNullValue(typ::Call_type(vec_ty)));
} }
return create<ast::TypeConstructorExpression>(Source{}, type, return create<ast::TypeConstructorExpression>(Source{}, type,
std::move(ast_components)); std::move(ast_components));
} }
if (const auto* mat_ty = type->As<sem::Matrix>()) { if (auto mat_ty = typ::As<typ::Matrix>(type)) {
// Matrix components are columns // Matrix components are columns
auto* column_ty = auto column_ty = builder_.ty.vec(typ::Call_type(mat_ty), mat_ty->rows());
builder_.create<sem::Vector>(mat_ty->type(), mat_ty->rows());
ast::ExpressionList ast_components; 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)); ast_components.emplace_back(MakeNullValue(column_ty));
@ -1580,17 +1589,17 @@ ast::Expression* ParserImpl::MakeNullValue(sem::Type* type) {
return create<ast::TypeConstructorExpression>(Source{}, type, return create<ast::TypeConstructorExpression>(Source{}, type,
std::move(ast_components)); std::move(ast_components));
} }
if (auto* arr_ty = type->As<sem::ArrayType>()) { if (auto arr_ty = typ::As<typ::Array>(type)) {
ast::ExpressionList ast_components; ast::ExpressionList ast_components;
for (size_t i = 0; i < arr_ty->size(); ++i) { for (size_t i = 0; i < arr_ty->size(); ++i) {
ast_components.emplace_back(MakeNullValue(arr_ty->type())); ast_components.emplace_back(MakeNullValue(typ::Call_type(arr_ty)));
} }
return create<ast::TypeConstructorExpression>(Source{}, original_type, return create<ast::TypeConstructorExpression>(Source{}, original_type,
std::move(ast_components)); std::move(ast_components));
} }
if (auto* struct_ty = type->As<sem::StructType>()) { if (auto struct_ty = typ::As<typ::Struct>(type)) {
ast::ExpressionList ast_components; ast::ExpressionList ast_components;
for (auto* member : struct_ty->impl()->members()) { for (auto* member : struct_ty.ast->members()) {
ast_components.emplace_back(MakeNullValue(member->type())); ast_components.emplace_back(MakeNullValue(member->type()));
} }
return create<ast::TypeConstructorExpression>(Source{}, original_type, return create<ast::TypeConstructorExpression>(Source{}, original_type,
@ -1600,10 +1609,34 @@ ast::Expression* ParserImpl::MakeNullValue(sem::Type* type) {
return nullptr; return nullptr;
} }
TypedExpression ParserImpl::MakeNullExpression(sem::Type* type) { TypedExpression ParserImpl::MakeNullExpression(typ::Type type) {
return {type, MakeNullValue(type)}; return {type, MakeNullValue(type)};
} }
typ::Type ParserImpl::UnsignedTypeFor(typ::Type type) {
if (type.ast->Is<ast::I32>()) {
return builder_.ty.u32();
}
if (auto* v = type.ast->As<ast::Vector>()) {
if (v->type()->Is<ast::I32>()) {
return builder_.ty.vec(builder_.ty.u32(), v->size());
}
}
return {};
}
typ::Type ParserImpl::SignedTypeFor(typ::Type type) {
if (type.ast->Is<ast::U32>()) {
return builder_.ty.i32();
}
if (auto* v = type.ast->As<ast::Vector>()) {
if (v->type()->Is<ast::U32>()) {
return builder_.ty.vec(builder_.ty.i32(), v->size());
}
}
return {};
}
TypedExpression ParserImpl::RectifyOperandSignedness( TypedExpression ParserImpl::RectifyOperandSignedness(
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
TypedExpression&& expr) { TypedExpression&& expr) {
@ -1627,22 +1660,20 @@ TypedExpression ParserImpl::RectifyOperandSignedness(
Fail() << "internal error: RectifyOperandSignedness given a null expr\n"; Fail() << "internal error: RectifyOperandSignedness given a null expr\n";
return {}; return {};
} }
auto* type = expr.type; auto type = expr.type;
if (!type) { if (!type) {
Fail() << "internal error: unmapped type for: " << builder_.str(expr.expr) Fail() << "internal error: unmapped type for: " << builder_.str(expr.expr)
<< "\n"; << "\n";
return {}; return {};
} }
if (requires_unsigned) { if (requires_unsigned) {
auto* unsigned_ty = unsigned_type_for_[type]; if (auto unsigned_ty = UnsignedTypeFor(type)) {
if (unsigned_ty != nullptr) {
// Conversion is required. // Conversion is required.
return {unsigned_ty, return {unsigned_ty,
create<ast::BitcastExpression>(Source{}, unsigned_ty, expr.expr)}; create<ast::BitcastExpression>(Source{}, unsigned_ty, expr.expr)};
} }
} else if (requires_signed) { } else if (requires_signed) {
auto* signed_ty = signed_type_for_[type]; if (auto signed_ty = SignedTypeFor(type)) {
if (signed_ty != nullptr) {
// Conversion is required. // Conversion is required.
return {signed_ty, return {signed_ty,
create<ast::BitcastExpression>(Source{}, signed_ty, expr.expr)}; create<ast::BitcastExpression>(Source{}, signed_ty, expr.expr)};
@ -1654,7 +1685,7 @@ TypedExpression ParserImpl::RectifyOperandSignedness(
TypedExpression ParserImpl::RectifySecondOperandSignedness( TypedExpression ParserImpl::RectifySecondOperandSignedness(
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type, typ::Type first_operand_type,
TypedExpression&& second_operand_expr) { TypedExpression&& second_operand_expr) {
if ((first_operand_type != second_operand_expr.type) && if ((first_operand_type != second_operand_expr.type) &&
AssumesSecondOperandSignednessMatchesFirstOperand(inst.opcode())) { AssumesSecondOperandSignednessMatchesFirstOperand(inst.opcode())) {
@ -1667,8 +1698,8 @@ TypedExpression ParserImpl::RectifySecondOperandSignedness(
return std::move(second_operand_expr); return std::move(second_operand_expr);
} }
sem::Type* ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst, typ::Type ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type) { typ::Type first_operand_type) {
const auto opcode = inst.opcode(); const auto opcode = inst.opcode();
if (AssumesResultSignednessMatchesFirstOperand(opcode)) { if (AssumesResultSignednessMatchesFirstOperand(opcode)) {
return first_operand_type; return first_operand_type;
@ -1683,34 +1714,36 @@ sem::Type* ParserImpl::ForcedResultType(const spvtools::opt::Instruction& inst,
return nullptr; return nullptr;
} }
sem::Type* ParserImpl::GetSignedIntMatchingShape(sem::Type* other) { typ::Type ParserImpl::GetSignedIntMatchingShape(typ::Type other) {
if (other == nullptr) { if (other == nullptr) {
Fail() << "no type provided"; Fail() << "no type provided";
} }
auto* i32 = builder_.create<sem::I32>(); auto i32 = builder_.ty.i32();
if (other->Is<sem::F32>() || other->Is<sem::U32>() || other->Is<sem::I32>()) { if (other.ast->Is<ast::F32>() || other.ast->Is<ast::U32>() ||
other.ast->Is<ast::I32>()) {
return i32; return i32;
} }
auto* vec_ty = other->As<sem::Vector>(); auto* vec_ty = other.ast->As<ast::Vector>();
if (vec_ty) { if (vec_ty) {
return builder_.create<sem::Vector>(i32, vec_ty->size()); return builder_.ty.vec(i32, vec_ty->size());
} }
Fail() << "required numeric scalar or vector, but got " << other->type_name(); Fail() << "required numeric scalar or vector, but got " << other->type_name();
return nullptr; return nullptr;
} }
sem::Type* ParserImpl::GetUnsignedIntMatchingShape(sem::Type* other) { typ::Type ParserImpl::GetUnsignedIntMatchingShape(typ::Type other) {
if (other == nullptr) { if (other == nullptr) {
Fail() << "no type provided"; Fail() << "no type provided";
return nullptr; return nullptr;
} }
auto* u32 = builder_.create<sem::U32>(); auto u32 = builder_.ty.u32();
if (other->Is<sem::F32>() || other->Is<sem::U32>() || other->Is<sem::I32>()) { if (other.ast->Is<ast::F32>() || other.ast->Is<ast::U32>() ||
other.ast->Is<ast::I32>()) {
return u32; return u32;
} }
auto* vec_ty = other->As<sem::Vector>(); auto* vec_ty = other.ast->As<ast::Vector>();
if (vec_ty) { if (vec_ty) {
return builder_.create<sem::Vector>(u32, vec_ty->size()); return builder_.ty.vec(u32, vec_ty->size());
} }
Fail() << "required numeric scalar or vector, but got " << other->type_name(); Fail() << "required numeric scalar or vector, but got " << other->type_name();
return nullptr; return nullptr;
@ -1719,8 +1752,8 @@ sem::Type* ParserImpl::GetUnsignedIntMatchingShape(sem::Type* other) {
TypedExpression ParserImpl::RectifyForcedResultType( TypedExpression ParserImpl::RectifyForcedResultType(
TypedExpression expr, TypedExpression expr,
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type) { typ::Type first_operand_type) {
auto* forced_result_ty = ForcedResultType(inst, first_operand_type); auto forced_result_ty = ForcedResultType(inst, first_operand_type);
if ((forced_result_ty == nullptr) || (forced_result_ty == expr.type)) { if ((forced_result_ty == nullptr) || (forced_result_ty == expr.type)) {
return expr; return expr;
} }
@ -1898,7 +1931,7 @@ ParserImpl::GetSpirvTypeForHandleMemoryObjectDeclaration(
return raw_handle_type; return raw_handle_type;
} }
sem::Pointer* ParserImpl::GetTypeForHandleVar( typ::Pointer ParserImpl::GetTypeForHandleVar(
const spvtools::opt::Instruction& var) { const spvtools::opt::Instruction& var) {
auto where = handle_type_.find(&var); auto where = handle_type_.find(&var);
if (where != handle_type_.end()) { if (where != handle_type_.end()) {
@ -1982,7 +2015,7 @@ sem::Pointer* ParserImpl::GetTypeForHandleVar(
} }
// Construct the Tint handle type. // Construct the Tint handle type.
sem::Type* ast_store_type = nullptr; typ::Type ast_store_type;
if (usage.IsSampler()) { if (usage.IsSampler()) {
ast_store_type = builder_.ty.sampler( ast_store_type = builder_.ty.sampler(
usage.IsComparisonSampler() ? ast::SamplerKind::kComparisonSampler usage.IsComparisonSampler() ? ast::SamplerKind::kComparisonSampler
@ -2007,7 +2040,7 @@ sem::Pointer* ParserImpl::GetTypeForHandleVar(
if (usage.IsSampledTexture() || if (usage.IsSampledTexture() ||
(image_type->format() == SpvImageFormatUnknown)) { (image_type->format() == SpvImageFormatUnknown)) {
// Make a sampled texture type. // Make a sampled texture type.
auto* ast_sampled_component_type = auto ast_sampled_component_type =
ConvertType(raw_handle_type->GetSingleWordInOperand(0)); ConvertType(raw_handle_type->GetSingleWordInOperand(0));
// Vulkan ignores the depth parameter on OpImage, so pay attention to the // Vulkan ignores the depth parameter on OpImage, so pay attention to the
@ -2015,14 +2048,14 @@ sem::Pointer* ParserImpl::GetTypeForHandleVar(
// OpImage variable with an OpImage*Dref* instruction. In WGSL we must // OpImage variable with an OpImage*Dref* instruction. In WGSL we must
// treat that as a depth texture. // treat that as a depth texture.
if (image_type->depth() || usage.IsDepthTexture()) { if (image_type->depth() || usage.IsDepthTexture()) {
ast_store_type = builder_.create<sem::DepthTexture>(dim); ast_store_type = builder_.ty.depth_texture(dim);
} else if (image_type->is_multisampled()) { } else if (image_type->is_multisampled()) {
// Multisampled textures are never depth textures. // Multisampled textures are never depth textures.
ast_store_type = builder_.create<sem::MultisampledTexture>( ast_store_type =
dim, ast_sampled_component_type); builder_.ty.multisampled_texture(dim, ast_sampled_component_type);
} else { } else {
ast_store_type = builder_.create<sem::SampledTexture>( ast_store_type =
dim, ast_sampled_component_type); builder_.ty.sampled_texture(dim, ast_sampled_component_type);
} }
} else { } else {
const auto access = usage.IsStorageReadTexture() const auto access = usage.IsStorageReadTexture()
@ -2032,9 +2065,8 @@ sem::Pointer* ParserImpl::GetTypeForHandleVar(
if (format == ast::ImageFormat::kNone) { if (format == ast::ImageFormat::kNone) {
return nullptr; return nullptr;
} }
auto* subtype = sem::StorageTexture::SubtypeFor(format, builder_.Types()); ast_store_type =
ast_store_type = builder_.create<sem::AccessControl>( builder_.ty.access(access, builder_.ty.storage_texture(dim, format));
access, builder_.create<sem::StorageTexture>(dim, format, subtype));
} }
} else { } else {
Fail() << "unsupported: UniformConstant variable is not a recognized " Fail() << "unsupported: UniformConstant variable is not a recognized "
@ -2044,14 +2076,14 @@ sem::Pointer* ParserImpl::GetTypeForHandleVar(
} }
// Form the pointer type. // Form the pointer type.
auto* result = builder_.create<sem::Pointer>( auto result =
ast_store_type, ast::StorageClass::kUniformConstant); builder_.ty.pointer(ast_store_type, ast::StorageClass::kUniformConstant);
// Remember it for later. // Remember it for later.
handle_type_[&var] = result; handle_type_[&var] = result;
return result; return result;
} }
sem::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) { typ::Type ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
switch (format) { switch (format) {
case ast::ImageFormat::kR8Uint: case ast::ImageFormat::kR8Uint:
case ast::ImageFormat::kR16Uint: case ast::ImageFormat::kR16Uint:
@ -2062,7 +2094,7 @@ sem::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Uint: case ast::ImageFormat::kRg32Uint:
case ast::ImageFormat::kRgba16Uint: case ast::ImageFormat::kRgba16Uint:
case ast::ImageFormat::kRgba32Uint: case ast::ImageFormat::kRgba32Uint:
return builder_.create<sem::U32>(); return builder_.ty.u32();
case ast::ImageFormat::kR8Sint: case ast::ImageFormat::kR8Sint:
case ast::ImageFormat::kR16Sint: case ast::ImageFormat::kR16Sint:
@ -2073,7 +2105,7 @@ sem::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Sint: case ast::ImageFormat::kRg32Sint:
case ast::ImageFormat::kRgba16Sint: case ast::ImageFormat::kRgba16Sint:
case ast::ImageFormat::kRgba32Sint: case ast::ImageFormat::kRgba32Sint:
return builder_.create<sem::I32>(); return builder_.ty.i32();
case ast::ImageFormat::kR8Unorm: case ast::ImageFormat::kR8Unorm:
case ast::ImageFormat::kRg8Unorm: case ast::ImageFormat::kRg8Unorm:
@ -2092,7 +2124,7 @@ sem::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg32Float: case ast::ImageFormat::kRg32Float:
case ast::ImageFormat::kRgba16Float: case ast::ImageFormat::kRgba16Float:
case ast::ImageFormat::kRgba32Float: case ast::ImageFormat::kRgba32Float:
return builder_.create<sem::F32>(); return builder_.ty.f32();
default: default:
break; break;
} }
@ -2100,8 +2132,8 @@ sem::Type* ParserImpl::GetComponentTypeForFormat(ast::ImageFormat format) {
return nullptr; return nullptr;
} }
sem::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) { typ::Type ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
auto* component_type = GetComponentTypeForFormat(format); auto component_type = GetComponentTypeForFormat(format);
if (!component_type) { if (!component_type) {
return nullptr; return nullptr;
} }
@ -2132,7 +2164,7 @@ sem::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRg8Uint: case ast::ImageFormat::kRg8Uint:
case ast::ImageFormat::kRg8Unorm: case ast::ImageFormat::kRg8Unorm:
// Two channels // Two channels
return builder_.create<sem::Vector>(component_type, 2); return builder_.ty.vec(component_type, 2);
case ast::ImageFormat::kBgra8Unorm: case ast::ImageFormat::kBgra8Unorm:
case ast::ImageFormat::kBgra8UnormSrgb: case ast::ImageFormat::kBgra8UnormSrgb:
@ -2149,7 +2181,7 @@ sem::Type* ParserImpl::GetTexelTypeForFormat(ast::ImageFormat format) {
case ast::ImageFormat::kRgba8Unorm: case ast::ImageFormat::kRgba8Unorm:
case ast::ImageFormat::kRgba8UnormSrgb: case ast::ImageFormat::kRgba8UnormSrgb:
// Four channels // Four channels
return builder_.create<sem::Vector>(component_type, 4); return builder_.ty.vec(component_type, 4);
default: default:
break; break;

View File

@ -29,6 +29,7 @@
#include "src/reader/spirv/enum_converter.h" #include "src/reader/spirv/enum_converter.h"
#include "src/reader/spirv/namer.h" #include "src/reader/spirv/namer.h"
#include "src/reader/spirv/usage.h" #include "src/reader/spirv/usage.h"
#include "src/typepair.h"
/// This is the implementation of the SPIR-V parser for Tint. /// This is the implementation of the SPIR-V parser for Tint.
@ -60,8 +61,22 @@ using DecorationList = std::vector<Decoration>;
/// An AST expression with its type. /// An AST expression with its type.
struct TypedExpression { struct TypedExpression {
/// Constructor
TypedExpression();
/// Copy constructor
TypedExpression(const TypedExpression&);
/// Assignment operator
TypedExpression& operator=(const TypedExpression&);
/// Constructor
/// @param type_in the type of the expression
/// @param expr_in the expression
TypedExpression(typ::Type type_in, ast::Expression* expr_in);
/// The type /// The type
sem::Type* type = nullptr; typ::Type type;
/// The expression /// The expression
ast::Expression* expr = nullptr; ast::Expression* expr = nullptr;
}; };
@ -140,7 +155,7 @@ class ParserImpl : Reader {
/// after the internal representation of the module has been built. /// after the internal representation of the module has been built.
/// @param type_id the SPIR-V ID of a type. /// @param type_id the SPIR-V ID of a type.
/// @returns a Tint type, or nullptr /// @returns a Tint type, or nullptr
sem::Type* ConvertType(uint32_t type_id); typ::Type ConvertType(uint32_t type_id);
/// Emits an alias type declaration for the given type, if necessary, and /// 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. /// also updates the mapping of the SPIR-V type ID to the alias type.
@ -151,8 +166,11 @@ class ParserImpl : Reader {
/// This is a no-op if the parser has already failed. /// This is a no-op if the parser has already failed.
/// @param type_id the SPIR-V ID for the type /// @param type_id the SPIR-V ID for the type
/// @param type the type that might get an alias /// @param type the type that might get an alias
void MaybeGenerateAlias(uint32_t type_id, /// @param ast_type the ast type that might get an alias
const spvtools::opt::analysis::Type* type); /// @returns an alias type or `ast_type` if no alias was created
typ::Type MaybeGenerateAlias(uint32_t type_id,
const spvtools::opt::analysis::Type* type,
typ::Type ast_type);
/// @returns the fail stream object /// @returns the fail stream object
FailStream& fail_stream() { return fail_stream_; } FailStream& fail_stream() { return fail_stream_; }
@ -302,7 +320,7 @@ class ParserImpl : Reader {
/// in the error case /// in the error case
ast::Variable* MakeVariable(uint32_t id, ast::Variable* MakeVariable(uint32_t id,
ast::StorageClass sc, ast::StorageClass sc,
sem::Type* type, typ::Type type,
bool is_const, bool is_const,
ast::Expression* constructor, ast::Expression* constructor,
ast::DecorationList decorations); ast::DecorationList decorations);
@ -315,12 +333,12 @@ class ParserImpl : Reader {
/// Creates an AST expression node for the null value for the given type. /// Creates an AST expression node for the null value for the given type.
/// @param type the AST type /// @param type the AST type
/// @returns a new expression /// @returns a new expression
ast::Expression* MakeNullValue(sem::Type* type); ast::Expression* MakeNullValue(typ::Type type);
/// Make a typed expression for the null value for the given type. /// Make a typed expression for the null value for the given type.
/// @param type the AST type /// @param type the AST type
/// @returns a new typed expression /// @returns a new typed expression
TypedExpression MakeNullExpression(sem::Type* type); TypedExpression MakeNullExpression(typ::Type type);
/// Converts a given expression to the signedness demanded for an operand /// Converts a given expression to the signedness demanded for an operand
/// of the given SPIR-V instruction, if required. If the instruction assumes /// of the given SPIR-V instruction, if required. If the instruction assumes
@ -345,7 +363,7 @@ class ParserImpl : Reader {
/// @returns second_operand_expr, or a cast of it /// @returns second_operand_expr, or a cast of it
TypedExpression RectifySecondOperandSignedness( TypedExpression RectifySecondOperandSignedness(
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type, typ::Type first_operand_type,
TypedExpression&& second_operand_expr); TypedExpression&& second_operand_expr);
/// Returns the "forced" result type for the given SPIR-V instruction. /// Returns the "forced" result type for the given SPIR-V instruction.
@ -356,8 +374,8 @@ class ParserImpl : Reader {
/// @param inst the SPIR-V instruction /// @param inst the SPIR-V instruction
/// @param first_operand_type the AST type for the first operand. /// @param first_operand_type the AST type for the first operand.
/// @returns the forced AST result type, or nullptr if no forcing is required. /// @returns the forced AST result type, or nullptr if no forcing is required.
sem::Type* ForcedResultType(const spvtools::opt::Instruction& inst, typ::Type ForcedResultType(const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type); typ::Type first_operand_type);
/// Returns a signed integer scalar or vector type matching the shape (scalar, /// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a /// vector, and component bit width) of another type, which itself is a
@ -365,7 +383,7 @@ class ParserImpl : Reader {
/// requirement. /// requirement.
/// @param other the type whose shape must be matched /// @param other the type whose shape must be matched
/// @returns the signed scalar or vector type /// @returns the signed scalar or vector type
sem::Type* GetSignedIntMatchingShape(sem::Type* other); typ::Type GetSignedIntMatchingShape(typ::Type other);
/// Returns a signed integer scalar or vector type matching the shape (scalar, /// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a /// vector, and component bit width) of another type, which itself is a
@ -373,7 +391,7 @@ class ParserImpl : Reader {
/// requirement. /// requirement.
/// @param other the type whose shape must be matched /// @param other the type whose shape must be matched
/// @returns the unsigned scalar or vector type /// @returns the unsigned scalar or vector type
sem::Type* GetUnsignedIntMatchingShape(sem::Type* other); typ::Type GetUnsignedIntMatchingShape(typ::Type other);
/// Wraps the given expression in an as-cast to the given expression's type, /// Wraps the given expression in an as-cast to the given expression's type,
/// when the underlying operation produces a forced result type different /// when the underlying operation produces a forced result type different
@ -386,10 +404,7 @@ class ParserImpl : Reader {
TypedExpression RectifyForcedResultType( TypedExpression RectifyForcedResultType(
TypedExpression expr, TypedExpression expr,
const spvtools::opt::Instruction& inst, const spvtools::opt::Instruction& inst,
sem::Type* first_operand_type); typ::Type first_operand_type);
/// @returns the registered boolean type.
sem::Type* Bool() const { return bool_type_; }
/// Bookkeeping used for tracking the "position" builtin variable. /// Bookkeeping used for tracking the "position" builtin variable.
struct BuiltInPositionInfo { struct BuiltInPositionInfo {
@ -477,18 +492,18 @@ class ParserImpl : Reader {
/// @param var the OpVariable instruction /// @param var the OpVariable instruction
/// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on /// @returns the Tint AST type for the poiner-to-{sampler|texture} or null on
/// error /// error
sem::Pointer* GetTypeForHandleVar(const spvtools::opt::Instruction& var); typ::Pointer GetTypeForHandleVar(const spvtools::opt::Instruction& var);
/// Returns the channel component type corresponding to the given image /// Returns the channel component type corresponding to the given image
/// format. /// format.
/// @param format image texel format /// @param format image texel format
/// @returns the component type, one of f32, i32, u32 /// @returns the component type, one of f32, i32, u32
sem::Type* GetComponentTypeForFormat(ast::ImageFormat format); typ::Type GetComponentTypeForFormat(ast::ImageFormat format);
/// Returns texel type corresponding to the given image format. /// Returns texel type corresponding to the given image format.
/// @param format image texel format /// @param format image texel format
/// @returns the texel format /// @returns the texel format
sem::Type* GetTexelTypeForFormat(ast::ImageFormat format); typ::Type GetTexelTypeForFormat(ast::ImageFormat format);
/// Returns the SPIR-V instruction with the given ID, or nullptr. /// Returns the SPIR-V instruction with the given ID, or nullptr.
/// @param id the SPIR-V result ID /// @param id the SPIR-V result ID
@ -523,19 +538,19 @@ class ParserImpl : Reader {
private: private:
/// Converts a specific SPIR-V type to a Tint type. Integer case /// Converts a specific SPIR-V type to a Tint type. Integer case
sem::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty); typ::Type ConvertType(const spvtools::opt::analysis::Integer* int_ty);
/// Converts a specific SPIR-V type to a Tint type. Float case /// Converts a specific SPIR-V type to a Tint type. Float case
sem::Type* ConvertType(const spvtools::opt::analysis::Float* float_ty); typ::Type ConvertType(const spvtools::opt::analysis::Float* float_ty);
/// Converts a specific SPIR-V type to a Tint type. Vector case /// Converts a specific SPIR-V type to a Tint type. Vector case
sem::Type* ConvertType(const spvtools::opt::analysis::Vector* vec_ty); typ::Type ConvertType(const spvtools::opt::analysis::Vector* vec_ty);
/// Converts a specific SPIR-V type to a Tint type. Matrix case /// Converts a specific SPIR-V type to a Tint type. Matrix case
sem::Type* ConvertType(const spvtools::opt::analysis::Matrix* mat_ty); typ::Type ConvertType(const spvtools::opt::analysis::Matrix* mat_ty);
/// Converts a specific SPIR-V type to a Tint type. RuntimeArray case /// Converts a specific SPIR-V type to a Tint type. RuntimeArray case
/// @param rtarr_ty the Tint type /// @param rtarr_ty the Tint type
sem::Type* ConvertType(const spvtools::opt::analysis::RuntimeArray* rtarr_ty); typ::Type ConvertType(const spvtools::opt::analysis::RuntimeArray* rtarr_ty);
/// Converts a specific SPIR-V type to a Tint type. Array case /// Converts a specific SPIR-V type to a Tint type. Array case
/// @param arr_ty the Tint type /// @param arr_ty the Tint type
sem::Type* ConvertType(const spvtools::opt::analysis::Array* arr_ty); typ::Type ConvertType(const spvtools::opt::analysis::Array* arr_ty);
/// Converts a specific SPIR-V type to a Tint type. Struct case. /// Converts a specific SPIR-V type to a Tint type. Struct case.
/// SPIR-V allows distinct struct type definitions for two OpTypeStruct /// SPIR-V allows distinct struct type definitions for two OpTypeStruct
/// that otherwise have the same set of members (and struct and member /// that otherwise have the same set of members (and struct and member
@ -547,15 +562,27 @@ class ParserImpl : Reader {
/// not significant to the optimizer's module representation. /// not significant to the optimizer's module representation.
/// @param type_id the SPIR-V ID for the type. /// @param type_id the SPIR-V ID for the type.
/// @param struct_ty the Tint type /// @param struct_ty the Tint type
sem::Type* ConvertType(uint32_t type_id, typ::Type ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Struct* struct_ty); const spvtools::opt::analysis::Struct* struct_ty);
/// Converts a specific SPIR-V type to a Tint type. Pointer case /// Converts a specific SPIR-V type to a Tint type. Pointer case
/// The pointer to gl_PerVertex maps to nullptr, and instead is recorded /// The pointer to gl_PerVertex maps to nullptr, and instead is recorded
/// in member #builtin_position_. /// in member #builtin_position_.
/// @param type_id the SPIR-V ID for the type. /// @param type_id the SPIR-V ID for the type.
/// @param ptr_ty the Tint type /// @param ptr_ty the Tint type
sem::Type* ConvertType(uint32_t type_id, typ::Type ConvertType(uint32_t type_id,
const spvtools::opt::analysis::Pointer* ptr_ty); 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
typ::Type UnsignedTypeFor(typ::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
typ::Type SignedTypeFor(typ::Type type);
/// Parses the array or runtime-array decorations. /// Parses the array or runtime-array decorations.
/// @param spv_type the SPIR-V array or runtime-array type. /// @param spv_type the SPIR-V array or runtime-array type.
@ -585,9 +612,6 @@ class ParserImpl : Reader {
FailStream fail_stream_; FailStream fail_stream_;
spvtools::MessageConsumer message_consumer_; spvtools::MessageConsumer message_consumer_;
// The registered boolean type.
sem::Type* bool_type_;
// An object used to store and generate names for SPIR-V objects. // An object used to store and generate names for SPIR-V objects.
Namer namer_; Namer namer_;
// An object used to convert SPIR-V enums to Tint enums // An object used to convert SPIR-V enums to Tint enums
@ -621,14 +645,6 @@ class ParserImpl : Reader {
// "NonSemanticInfo." import is ignored. // "NonSemanticInfo." import is ignored.
std::unordered_set<uint32_t> ignored_imports_; std::unordered_set<uint32_t> ignored_imports_;
// Maps a SPIR-V type ID to the corresponding Tint type.
std::unordered_map<uint32_t, sem::Type*> id_to_type_;
// Maps an unsigned type corresponding to the given signed type.
std::unordered_map<sem::Type*, sem::Type*> signed_type_for_;
// Maps an signed type corresponding to the given unsigned type.
std::unordered_map<sem::Type*, sem::Type*> unsigned_type_for_;
// Bookkeeping for the gl_Position builtin. // Bookkeeping for the gl_Position builtin.
// In Vulkan SPIR-V, it's the 0 member of the gl_PerVertex structure. // In Vulkan SPIR-V, it's the 0 member of the gl_PerVertex structure.
// But in WGSL we make a module-scope variable: // But in WGSL we make a module-scope variable:
@ -646,8 +662,8 @@ class ParserImpl : Reader {
// and Block decoration. // and Block decoration.
std::unordered_set<uint32_t> remap_buffer_block_type_; std::unordered_set<uint32_t> remap_buffer_block_type_;
// The struct types with only read-only members. // The ast::Struct type names with only read-only members.
std::unordered_set<sem::Type*> read_only_struct_types_; std::unordered_set<Symbol> read_only_struct_types_;
// The IDs of scalar spec constants // The IDs of scalar spec constants
std::unordered_set<uint32_t> scalar_spec_constants_; std::unordered_set<uint32_t> scalar_spec_constants_;
@ -672,7 +688,7 @@ class ParserImpl : Reader {
// usages implied by usages of the memory-object-declaration. // usages implied by usages of the memory-object-declaration.
std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_; std::unordered_map<const spvtools::opt::Instruction*, Usage> handle_usage_;
// The inferred pointer type for the given handle variable. // The inferred pointer type for the given handle variable.
std::unordered_map<const spvtools::opt::Instruction*, sem::Pointer*> std::unordered_map<const spvtools::opt::Instruction*, typ::Pointer>
handle_type_; handle_type_;
/// Maps the SPIR-V ID of a module-scope builtin variable that should be /// Maps the SPIR-V ID of a module-scope builtin variable that should be

View File

@ -26,14 +26,14 @@ using ::testing::Eq;
TEST_F(SpvParserTest, ConvertType_PreservesExistingFailure) { TEST_F(SpvParserTest, ConvertType_PreservesExistingFailure) {
auto p = parser(std::vector<uint32_t>{}); auto p = parser(std::vector<uint32_t>{});
p->Fail() << "boing"; p->Fail() << "boing";
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("boing")); EXPECT_THAT(p->error(), Eq("boing"));
} }
TEST_F(SpvParserTest, ConvertType_RequiresInternalRepresntation) { TEST_F(SpvParserTest, ConvertType_RequiresInternalRepresntation) {
auto p = parser(std::vector<uint32_t>{}); auto p = parser(std::vector<uint32_t>{});
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT( EXPECT_THAT(
p->error(), p->error(),
@ -44,7 +44,7 @@ TEST_F(SpvParserTest, ConvertType_NotAnId) {
auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\"")); auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_EQ(nullptr, type); EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 10")); EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 10"));
@ -54,7 +54,7 @@ TEST_F(SpvParserTest, ConvertType_IdExistsButIsNotAType) {
auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\"")); auto p = parser(test::Assemble("%1 = OpExtInstImport \"GLSL.std.450\""));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1); auto type = p->ConvertType(1);
EXPECT_EQ(nullptr, type); EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 1")); EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 1"));
} }
@ -64,7 +64,7 @@ TEST_F(SpvParserTest, ConvertType_UnhandledType) {
auto p = parser(test::Assemble("%70 = OpTypePipe WriteOnly")); auto p = parser(test::Assemble("%70 = OpTypePipe WriteOnly"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(70); auto type = p->ConvertType(70);
EXPECT_EQ(nullptr, type); EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("unknown SPIR-V type with ID 70: %70 = OpTypePipe WriteOnly")); Eq("unknown SPIR-V type with ID 70: %70 = OpTypePipe WriteOnly"));
@ -74,7 +74,7 @@ TEST_F(SpvParserTest, ConvertType_Void) {
auto p = parser(test::Assemble("%1 = OpTypeVoid")); auto p = parser(test::Assemble("%1 = OpTypeVoid"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1); auto type = p->ConvertType(1);
EXPECT_TRUE(type->Is<sem::Void>()); EXPECT_TRUE(type->Is<sem::Void>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -83,7 +83,7 @@ TEST_F(SpvParserTest, ConvertType_Bool) {
auto p = parser(test::Assemble("%100 = OpTypeBool")); auto p = parser(test::Assemble("%100 = OpTypeBool"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(100); auto type = p->ConvertType(100);
EXPECT_TRUE(type->Is<sem::Bool>()); EXPECT_TRUE(type->Is<sem::Bool>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -92,7 +92,7 @@ TEST_F(SpvParserTest, ConvertType_I32) {
auto p = parser(test::Assemble("%2 = OpTypeInt 32 1")); auto p = parser(test::Assemble("%2 = OpTypeInt 32 1"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(2); auto type = p->ConvertType(2);
EXPECT_TRUE(type->Is<sem::I32>()); EXPECT_TRUE(type->Is<sem::I32>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -101,7 +101,7 @@ TEST_F(SpvParserTest, ConvertType_U32) {
auto p = parser(test::Assemble("%3 = OpTypeInt 32 0")); auto p = parser(test::Assemble("%3 = OpTypeInt 32 0"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::U32>()); EXPECT_TRUE(type->Is<sem::U32>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -110,7 +110,7 @@ TEST_F(SpvParserTest, ConvertType_F32) {
auto p = parser(test::Assemble("%4 = OpTypeFloat 32")); auto p = parser(test::Assemble("%4 = OpTypeFloat 32"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(4); auto type = p->ConvertType(4);
EXPECT_TRUE(type->Is<sem::F32>()); EXPECT_TRUE(type->Is<sem::F32>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -119,7 +119,7 @@ TEST_F(SpvParserTest, ConvertType_BadIntWidth) {
auto p = parser(test::Assemble("%5 = OpTypeInt 17 1")); auto p = parser(test::Assemble("%5 = OpTypeInt 17 1"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(5); auto type = p->ConvertType(5);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unhandled integer width: 17")); EXPECT_THAT(p->error(), Eq("unhandled integer width: 17"));
} }
@ -128,7 +128,7 @@ TEST_F(SpvParserTest, ConvertType_BadFloatWidth) {
auto p = parser(test::Assemble("%6 = OpTypeFloat 19")); auto p = parser(test::Assemble("%6 = OpTypeFloat 19"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(6); auto type = p->ConvertType(6);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unhandled float width: 19")); EXPECT_THAT(p->error(), Eq("unhandled float width: 19"));
} }
@ -140,7 +140,7 @@ TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidVectorElement) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(20); auto type = p->ConvertType(20);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5")); EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
} }
@ -154,17 +154,17 @@ TEST_F(SpvParserTest, ConvertType_VecOverF32) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* v2xf32 = p->ConvertType(20); auto v2xf32 = p->ConvertType(20);
EXPECT_TRUE(v2xf32->Is<sem::Vector>()); EXPECT_TRUE(v2xf32->Is<sem::Vector>());
EXPECT_TRUE(v2xf32->As<sem::Vector>()->type()->Is<sem::F32>()); EXPECT_TRUE(v2xf32->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(v2xf32->As<sem::Vector>()->size(), 2u); EXPECT_EQ(v2xf32->As<sem::Vector>()->size(), 2u);
auto* v3xf32 = p->ConvertType(30); auto v3xf32 = p->ConvertType(30);
EXPECT_TRUE(v3xf32->Is<sem::Vector>()); EXPECT_TRUE(v3xf32->Is<sem::Vector>());
EXPECT_TRUE(v3xf32->As<sem::Vector>()->type()->Is<sem::F32>()); EXPECT_TRUE(v3xf32->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(v3xf32->As<sem::Vector>()->size(), 3u); EXPECT_EQ(v3xf32->As<sem::Vector>()->size(), 3u);
auto* v4xf32 = p->ConvertType(40); auto v4xf32 = p->ConvertType(40);
EXPECT_TRUE(v4xf32->Is<sem::Vector>()); EXPECT_TRUE(v4xf32->Is<sem::Vector>());
EXPECT_TRUE(v4xf32->As<sem::Vector>()->type()->Is<sem::F32>()); EXPECT_TRUE(v4xf32->As<sem::Vector>()->type()->Is<sem::F32>());
EXPECT_EQ(v4xf32->As<sem::Vector>()->size(), 4u); EXPECT_EQ(v4xf32->As<sem::Vector>()->size(), 4u);
@ -181,17 +181,17 @@ TEST_F(SpvParserTest, ConvertType_VecOverI32) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* v2xi32 = p->ConvertType(20); auto v2xi32 = p->ConvertType(20);
EXPECT_TRUE(v2xi32->Is<sem::Vector>()); EXPECT_TRUE(v2xi32->Is<sem::Vector>());
EXPECT_TRUE(v2xi32->As<sem::Vector>()->type()->Is<sem::I32>()); EXPECT_TRUE(v2xi32->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(v2xi32->As<sem::Vector>()->size(), 2u); EXPECT_EQ(v2xi32->As<sem::Vector>()->size(), 2u);
auto* v3xi32 = p->ConvertType(30); auto v3xi32 = p->ConvertType(30);
EXPECT_TRUE(v3xi32->Is<sem::Vector>()); EXPECT_TRUE(v3xi32->Is<sem::Vector>());
EXPECT_TRUE(v3xi32->As<sem::Vector>()->type()->Is<sem::I32>()); EXPECT_TRUE(v3xi32->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(v3xi32->As<sem::Vector>()->size(), 3u); EXPECT_EQ(v3xi32->As<sem::Vector>()->size(), 3u);
auto* v4xi32 = p->ConvertType(40); auto v4xi32 = p->ConvertType(40);
EXPECT_TRUE(v4xi32->Is<sem::Vector>()); EXPECT_TRUE(v4xi32->Is<sem::Vector>());
EXPECT_TRUE(v4xi32->As<sem::Vector>()->type()->Is<sem::I32>()); EXPECT_TRUE(v4xi32->As<sem::Vector>()->type()->Is<sem::I32>());
EXPECT_EQ(v4xi32->As<sem::Vector>()->size(), 4u); EXPECT_EQ(v4xi32->As<sem::Vector>()->size(), 4u);
@ -208,17 +208,17 @@ TEST_F(SpvParserTest, ConvertType_VecOverU32) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* v2xu32 = p->ConvertType(20); auto v2xu32 = p->ConvertType(20);
EXPECT_TRUE(v2xu32->Is<sem::Vector>()); EXPECT_TRUE(v2xu32->Is<sem::Vector>());
EXPECT_TRUE(v2xu32->As<sem::Vector>()->type()->Is<sem::U32>()); EXPECT_TRUE(v2xu32->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(v2xu32->As<sem::Vector>()->size(), 2u); EXPECT_EQ(v2xu32->As<sem::Vector>()->size(), 2u);
auto* v3xu32 = p->ConvertType(30); auto v3xu32 = p->ConvertType(30);
EXPECT_TRUE(v3xu32->Is<sem::Vector>()); EXPECT_TRUE(v3xu32->Is<sem::Vector>());
EXPECT_TRUE(v3xu32->As<sem::Vector>()->type()->Is<sem::U32>()); EXPECT_TRUE(v3xu32->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(v3xu32->As<sem::Vector>()->size(), 3u); EXPECT_EQ(v3xu32->As<sem::Vector>()->size(), 3u);
auto* v4xu32 = p->ConvertType(40); auto v4xu32 = p->ConvertType(40);
EXPECT_TRUE(v4xu32->Is<sem::Vector>()); EXPECT_TRUE(v4xu32->Is<sem::Vector>());
EXPECT_TRUE(v4xu32->As<sem::Vector>()->type()->Is<sem::U32>()); EXPECT_TRUE(v4xu32->As<sem::Vector>()->type()->Is<sem::U32>());
EXPECT_EQ(v4xu32->As<sem::Vector>()->size(), 4u); EXPECT_EQ(v4xu32->As<sem::Vector>()->size(), 4u);
@ -234,7 +234,7 @@ TEST_F(SpvParserTest, DISABLED_ConvertType_InvalidMatrixElement) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(20); auto type = p->ConvertType(20);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5")); EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
} }
@ -260,55 +260,55 @@ TEST_F(SpvParserTest, ConvertType_MatrixOverF32) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* m22 = p->ConvertType(22); auto m22 = p->ConvertType(22);
EXPECT_TRUE(m22->Is<sem::Matrix>()); EXPECT_TRUE(m22->Is<sem::Matrix>());
EXPECT_TRUE(m22->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m22->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m22->As<sem::Matrix>()->rows(), 2u); EXPECT_EQ(m22->As<sem::Matrix>()->rows(), 2u);
EXPECT_EQ(m22->As<sem::Matrix>()->columns(), 2u); EXPECT_EQ(m22->As<sem::Matrix>()->columns(), 2u);
auto* m23 = p->ConvertType(23); auto m23 = p->ConvertType(23);
EXPECT_TRUE(m23->Is<sem::Matrix>()); EXPECT_TRUE(m23->Is<sem::Matrix>());
EXPECT_TRUE(m23->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m23->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m23->As<sem::Matrix>()->rows(), 2u); EXPECT_EQ(m23->As<sem::Matrix>()->rows(), 2u);
EXPECT_EQ(m23->As<sem::Matrix>()->columns(), 3u); EXPECT_EQ(m23->As<sem::Matrix>()->columns(), 3u);
auto* m24 = p->ConvertType(24); auto m24 = p->ConvertType(24);
EXPECT_TRUE(m24->Is<sem::Matrix>()); EXPECT_TRUE(m24->Is<sem::Matrix>());
EXPECT_TRUE(m24->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m24->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m24->As<sem::Matrix>()->rows(), 2u); EXPECT_EQ(m24->As<sem::Matrix>()->rows(), 2u);
EXPECT_EQ(m24->As<sem::Matrix>()->columns(), 4u); EXPECT_EQ(m24->As<sem::Matrix>()->columns(), 4u);
auto* m32 = p->ConvertType(32); auto m32 = p->ConvertType(32);
EXPECT_TRUE(m32->Is<sem::Matrix>()); EXPECT_TRUE(m32->Is<sem::Matrix>());
EXPECT_TRUE(m32->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m32->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m32->As<sem::Matrix>()->rows(), 3u); EXPECT_EQ(m32->As<sem::Matrix>()->rows(), 3u);
EXPECT_EQ(m32->As<sem::Matrix>()->columns(), 2u); EXPECT_EQ(m32->As<sem::Matrix>()->columns(), 2u);
auto* m33 = p->ConvertType(33); auto m33 = p->ConvertType(33);
EXPECT_TRUE(m33->Is<sem::Matrix>()); EXPECT_TRUE(m33->Is<sem::Matrix>());
EXPECT_TRUE(m33->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m33->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m33->As<sem::Matrix>()->rows(), 3u); EXPECT_EQ(m33->As<sem::Matrix>()->rows(), 3u);
EXPECT_EQ(m33->As<sem::Matrix>()->columns(), 3u); EXPECT_EQ(m33->As<sem::Matrix>()->columns(), 3u);
auto* m34 = p->ConvertType(34); auto m34 = p->ConvertType(34);
EXPECT_TRUE(m34->Is<sem::Matrix>()); EXPECT_TRUE(m34->Is<sem::Matrix>());
EXPECT_TRUE(m34->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m34->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m34->As<sem::Matrix>()->rows(), 3u); EXPECT_EQ(m34->As<sem::Matrix>()->rows(), 3u);
EXPECT_EQ(m34->As<sem::Matrix>()->columns(), 4u); EXPECT_EQ(m34->As<sem::Matrix>()->columns(), 4u);
auto* m42 = p->ConvertType(42); auto m42 = p->ConvertType(42);
EXPECT_TRUE(m42->Is<sem::Matrix>()); EXPECT_TRUE(m42->Is<sem::Matrix>());
EXPECT_TRUE(m42->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m42->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m42->As<sem::Matrix>()->rows(), 4u); EXPECT_EQ(m42->As<sem::Matrix>()->rows(), 4u);
EXPECT_EQ(m42->As<sem::Matrix>()->columns(), 2u); EXPECT_EQ(m42->As<sem::Matrix>()->columns(), 2u);
auto* m43 = p->ConvertType(43); auto m43 = p->ConvertType(43);
EXPECT_TRUE(m43->Is<sem::Matrix>()); EXPECT_TRUE(m43->Is<sem::Matrix>());
EXPECT_TRUE(m43->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m43->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m43->As<sem::Matrix>()->rows(), 4u); EXPECT_EQ(m43->As<sem::Matrix>()->rows(), 4u);
EXPECT_EQ(m43->As<sem::Matrix>()->columns(), 3u); EXPECT_EQ(m43->As<sem::Matrix>()->columns(), 3u);
auto* m44 = p->ConvertType(44); auto m44 = p->ConvertType(44);
EXPECT_TRUE(m44->Is<sem::Matrix>()); EXPECT_TRUE(m44->Is<sem::Matrix>());
EXPECT_TRUE(m44->As<sem::Matrix>()->type()->Is<sem::F32>()); EXPECT_TRUE(m44->As<sem::Matrix>()->type()->Is<sem::F32>());
EXPECT_EQ(m44->As<sem::Matrix>()->rows(), 4u); EXPECT_EQ(m44->As<sem::Matrix>()->rows(), 4u);
@ -324,10 +324,10 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::ArrayType>()); EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<sem::ArrayType>());
auto* arr_type = type->As<sem::ArrayType>(); auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
EXPECT_TRUE(arr_type->IsRuntimeArray()); EXPECT_TRUE(arr_type->IsRuntimeArray());
ASSERT_NE(arr_type, nullptr); ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size(), 0u); EXPECT_EQ(arr_type->size(), 0u);
@ -345,7 +345,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_InvalidDecoration) {
%10 = OpTypeRuntimeArray %uint %10 = OpTypeRuntimeArray %uint
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT( EXPECT_THAT(
p->error(), p->error(),
@ -359,9 +359,9 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) {
%10 = OpTypeRuntimeArray %uint %10 = OpTypeRuntimeArray %uint
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
auto* arr_type = type->As<sem::ArrayType>(); auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
EXPECT_TRUE(arr_type->IsRuntimeArray()); EXPECT_TRUE(arr_type->IsRuntimeArray());
ASSERT_NE(arr_type, nullptr); ASSERT_NE(arr_type, nullptr);
ASSERT_EQ(arr_type->decorations().size(), 1u); ASSERT_EQ(arr_type->decorations().size(), 1u);
@ -378,7 +378,7 @@ TEST_F(SpvParserTest, ConvertType_RuntimeArray_ArrayStride_ZeroIsError) {
%10 = OpTypeRuntimeArray %uint %10 = OpTypeRuntimeArray %uint
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: ArrayStride can't be 0")); Eq("invalid array type ID 10: ArrayStride can't be 0"));
@ -393,7 +393,7 @@ TEST_F(SpvParserTest,
%10 = OpTypeRuntimeArray %uint %10 = OpTypeRuntimeArray %uint
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: multiple ArrayStride decorations")); Eq("invalid array type ID 10: multiple ArrayStride decorations"));
@ -407,7 +407,7 @@ TEST_F(SpvParserTest, ConvertType_Array) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::ArrayType>()); EXPECT_TRUE(type->Is<sem::ArrayType>());
auto* arr_type = type->As<sem::ArrayType>(); auto* arr_type = type->As<sem::ArrayType>();
@ -430,7 +430,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantValue) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_EQ(type, nullptr); ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("Array type 10 length is a specialization constant")); Eq("Array type 10 length is a specialization constant"));
@ -445,7 +445,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadLengthIsSpecConstantExpr) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_EQ(type, nullptr); ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("Array type 10 length is a specialization constant")); Eq("Array type 10 length is a specialization constant"));
@ -463,7 +463,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayBadTooBig) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_EQ(type, nullptr); ASSERT_EQ(type, nullptr);
// TODO(dneto): Right now it's rejected earlier in the flow because // TODO(dneto): Right now it's rejected earlier in the flow because
// we can't even utter the uint64 type. // we can't even utter the uint64 type.
@ -478,7 +478,7 @@ TEST_F(SpvParserTest, ConvertType_Array_InvalidDecoration) {
%10 = OpTypeArray %uint %uint_5 %10 = OpTypeArray %uint %uint_5
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT( EXPECT_THAT(
p->error(), p->error(),
@ -494,10 +494,10 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_Valid) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::ArrayType>()); EXPECT_TRUE(type->UnwrapAliasIfNeeded()->Is<sem::ArrayType>());
auto* arr_type = type->As<sem::ArrayType>(); auto* arr_type = type->UnwrapAliasIfNeeded()->As<sem::ArrayType>();
ASSERT_NE(arr_type, nullptr); ASSERT_NE(arr_type, nullptr);
ASSERT_EQ(arr_type->decorations().size(), 1u); ASSERT_EQ(arr_type->decorations().size(), 1u);
@ -517,7 +517,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_ZeroIsError) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_EQ(type, nullptr); ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: ArrayStride can't be 0")); Eq("invalid array type ID 10: ArrayStride can't be 0"));
@ -533,7 +533,7 @@ TEST_F(SpvParserTest, ConvertType_ArrayStride_SpecifiedTwiceIsError) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_EQ(type, nullptr); ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: multiple ArrayStride decorations")); Eq("invalid array type ID 10: multiple ArrayStride decorations"));
@ -548,7 +548,7 @@ TEST_F(SpvParserTest, ConvertType_StructTwoMembers) {
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::StructType>()); EXPECT_TRUE(type->Is<sem::StructType>());
@ -569,7 +569,7 @@ TEST_F(SpvParserTest, ConvertType_StructWithBlockDecoration) {
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::StructType>()); EXPECT_TRUE(type->Is<sem::StructType>());
@ -594,7 +594,7 @@ TEST_F(SpvParserTest, ConvertType_StructWithMemberDecorations) {
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames()); EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10); auto type = p->ConvertType(10);
ASSERT_NE(type, nullptr); ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::StructType>()); EXPECT_TRUE(type->Is<sem::StructType>());
@ -621,7 +621,7 @@ TEST_F(SpvParserTest, ConvertType_InvalidPointeetype) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()) << p->error(); EXPECT_TRUE(p->BuildInternalModule()) << p->error();
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_EQ(type, nullptr); EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), EXPECT_THAT(p->error(),
Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42")); Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42"));
@ -644,7 +644,7 @@ TEST_F(SpvParserTest, ConvertType_PointerInput) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -660,7 +660,7 @@ TEST_F(SpvParserTest, ConvertType_PointerOutput) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -676,7 +676,7 @@ TEST_F(SpvParserTest, ConvertType_PointerUniform) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -692,7 +692,7 @@ TEST_F(SpvParserTest, ConvertType_PointerWorkgroup) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -708,7 +708,7 @@ TEST_F(SpvParserTest, ConvertType_PointerUniformConstant) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -724,7 +724,7 @@ TEST_F(SpvParserTest, ConvertType_PointerStorageBuffer) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -740,7 +740,7 @@ TEST_F(SpvParserTest, ConvertType_PointerImage) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -756,7 +756,7 @@ TEST_F(SpvParserTest, ConvertType_PointerPrivate) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -772,7 +772,7 @@ TEST_F(SpvParserTest, ConvertType_PointerFunction) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
auto* ptr_ty = type->As<sem::Pointer>(); auto* ptr_ty = type->As<sem::Pointer>();
EXPECT_NE(ptr_ty, nullptr); EXPECT_NE(ptr_ty, nullptr);
@ -790,7 +790,7 @@ TEST_F(SpvParserTest, ConvertType_PointerToPointer) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3); auto type = p->ConvertType(3);
EXPECT_NE(type, nullptr); EXPECT_NE(type, nullptr);
EXPECT_TRUE(type->Is<sem::Pointer>()); EXPECT_TRUE(type->Is<sem::Pointer>());
@ -814,7 +814,7 @@ TEST_F(SpvParserTest, ConvertType_Sampler_PretendVoid) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1); auto type = p->ConvertType(1);
EXPECT_TRUE(type->Is<sem::Void>()); EXPECT_TRUE(type->Is<sem::Void>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -827,7 +827,7 @@ TEST_F(SpvParserTest, ConvertType_Image_PretendVoid) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1); auto type = p->ConvertType(1);
EXPECT_TRUE(type->Is<sem::Void>()); EXPECT_TRUE(type->Is<sem::Void>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -840,7 +840,7 @@ TEST_F(SpvParserTest, ConvertType_SampledImage_PretendVoid) {
)")); )"));
EXPECT_TRUE(p->BuildInternalModule()); EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1); auto type = p->ConvertType(1);
EXPECT_TRUE(type->Is<sem::Void>()); EXPECT_TRUE(type->Is<sem::Void>());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }

View File

@ -20,6 +20,8 @@
#define SRC_TYPEPAIR_H_ #define SRC_TYPEPAIR_H_
#include <cstddef> #include <cstddef>
#include <type_traits>
#include <utility>
// X11 likes to #define Bool leading to confusing error messages. // X11 likes to #define Bool leading to confusing error messages.
// If its defined, undefine it. // If its defined, undefine it.
@ -45,6 +47,7 @@ class Sampler;
class SampledTexture; class SampledTexture;
class StorageTexture; class StorageTexture;
class Struct; class Struct;
class Texture;
class Type; class Type;
class U32; class U32;
class Vector; class Vector;
@ -67,6 +70,7 @@ class Sampler;
class SampledTexture; class SampledTexture;
class StorageTexture; class StorageTexture;
class StructType; class StructType;
class Texture;
class Type; class Type;
class U32; class U32;
class Vector; class Vector;
@ -122,6 +126,11 @@ struct Ptr {
/// will switch to returning the ast::Type pointer. /// will switch to returning the ast::Type pointer.
template <typename AST, typename SEM> template <typename AST, typename SEM>
struct TypePair { struct TypePair {
/// Alias of the `AST` template type parameter
using AST_TYPE = AST;
/// Alias of the `SEM` template type parameter
using SEM_TYPE = SEM;
/// The ast::Type pointer /// The ast::Type pointer
AST const* ast = nullptr; AST const* ast = nullptr;
/// The sem::Type pointer /// The sem::Type pointer
@ -246,10 +255,44 @@ using Sampler = TypePair<ast::Sampler, sem::Sampler>;
using SampledTexture = TypePair<ast::SampledTexture, sem::SampledTexture>; using SampledTexture = TypePair<ast::SampledTexture, sem::SampledTexture>;
using StorageTexture = TypePair<ast::StorageTexture, sem::StorageTexture>; using StorageTexture = TypePair<ast::StorageTexture, sem::StorageTexture>;
using Struct = TypePair<ast::Struct, sem::StructType>; using Struct = TypePair<ast::Struct, sem::StructType>;
using Texture = TypePair<ast::Texture, sem::Texture>;
using U32 = TypePair<ast::U32, sem::U32>; using U32 = TypePair<ast::U32, sem::U32>;
using Vector = TypePair<ast::Vector, sem::Vector>; using Vector = TypePair<ast::Vector, sem::Vector>;
using Void = TypePair<ast::Void, sem::Void>; using Void = TypePair<ast::Void, sem::Void>;
// Helpers
/// Makes a type pair, deducing the return type from input args
/// @parm ast the ast node
/// @param sem the sem node
/// @returns a type pair
template <typename AST, typename SEM>
inline auto MakeTypePair(AST* ast, SEM* sem) {
return TypePair<AST, SEM>{ast, sem};
}
/// Performs an As operation on the `ast` and `sem` members of the input type
/// pair, deducing the mapped type from typ::* to ast::* and sem::*
/// respectively.
/// @param tp the type pair to call As on
/// @returns a new type pair after As has been called on each of `sem` and `ast`
template <typename TargetTYP, typename AST, typename SEM>
auto As(TypePair<AST, SEM> tp)
-> TypePair<typename TargetTYP::AST_TYPE, typename TargetTYP::SEM_TYPE> {
return MakeTypePair(
tp.ast ? tp.ast->template As<typename TargetTYP::AST_TYPE>() : nullptr,
tp.sem ? tp.sem->template As<typename TargetTYP::SEM_TYPE>() : nullptr);
}
/// Invokes the `type()` member function on each of `ast` and `sem` of the input
/// type pair
/// @param tp the type pair
/// @returns a type pair with the result of calling `type()` on `ast` and `sem`
template <typename AST, typename SEM>
TypePair<AST, SEM> Call_type(TypePair<AST, SEM> tp) {
return MakeTypePair(tp.ast->type(), tp.sem->type());
}
} // namespace typ } // namespace typ
} // namespace tint } // namespace tint