tint: Fix HLSL emission for out-of-order storage / uniform buffers

Recent changes to DecomposeMemoryAccess meant we lost the dependency information between the user of a module-scope variable of the storage / uniform address space and the variable.

Add dependency information to ast::InternalAttribute so this can be tracked.
This change also means that symbol renaming after the DecomposeMemoryAccess should work.

Fixed: tint:1860
Change-Id: Icfa2925f95c2ac50702522df514cd11bde727546
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/122660
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2023-03-06 15:43:16 +00:00 committed by Dawn LUCI CQ
parent fd387a37c3
commit 63d0fabeb1
32 changed files with 325 additions and 90 deletions

View File

@ -23,7 +23,7 @@ namespace tint::ast {
DisableValidationAttribute::DisableValidationAttribute(ProgramID pid, DisableValidationAttribute::DisableValidationAttribute(ProgramID pid,
NodeID nid, NodeID nid,
DisabledValidation val) DisabledValidation val)
: Base(pid, nid), validation(val) {} : Base(pid, nid, utils::Empty), validation(val) {}
DisableValidationAttribute::~DisableValidationAttribute() = default; DisableValidationAttribute::~DisableValidationAttribute() = default;

View File

@ -14,11 +14,16 @@
#include "src/tint/ast/internal_attribute.h" #include "src/tint/ast/internal_attribute.h"
#include <utility>
TINT_INSTANTIATE_TYPEINFO(tint::ast::InternalAttribute); TINT_INSTANTIATE_TYPEINFO(tint::ast::InternalAttribute);
namespace tint::ast { namespace tint::ast {
InternalAttribute::InternalAttribute(ProgramID pid, NodeID nid) : Base(pid, nid, Source{}) {} InternalAttribute::InternalAttribute(ProgramID pid,
NodeID nid,
utils::VectorRef<const IdentifierExpression*> deps)
: Base(pid, nid, Source{}), dependencies(std::move(deps)) {}
InternalAttribute::~InternalAttribute() = default; InternalAttribute::~InternalAttribute() = default;

View File

@ -18,6 +18,12 @@
#include <string> #include <string>
#include "src/tint/ast/attribute.h" #include "src/tint/ast/attribute.h"
#include "src/tint/utils/vector.h"
// Forward declarations
namespace tint::ast {
class IdentifierExpression;
} // namespace tint::ast
namespace tint::ast { namespace tint::ast {
@ -29,7 +35,10 @@ class InternalAttribute : public Castable<InternalAttribute, Attribute> {
/// Constructor /// Constructor
/// @param program_id the identifier of the program that owns this node /// @param program_id the identifier of the program that owns this node
/// @param nid the unique node identifier /// @param nid the unique node identifier
explicit InternalAttribute(ProgramID program_id, NodeID nid); /// @param deps a list of identifiers that this attribute is dependent on
InternalAttribute(ProgramID program_id,
NodeID nid,
utils::VectorRef<const IdentifierExpression*> deps);
/// Destructor /// Destructor
~InternalAttribute() override; ~InternalAttribute() override;
@ -40,6 +49,9 @@ class InternalAttribute : public Castable<InternalAttribute, Attribute> {
/// @returns the WGSL name for the attribute /// @returns the WGSL name for the attribute
std::string Name() const override; std::string Name() const override;
/// A list of identifiers that this attribute is dependent on
const utils::Vector<const IdentifierExpression*, 1> dependencies;
}; };
} // namespace tint::ast } // namespace tint::ast

View File

@ -1977,4 +1977,40 @@ TEST_F(MustUseAttributeTest, UsedOnFnWithNoReturnValue) {
} // namespace } // namespace
} // namespace MustUseTests } // namespace MustUseTests
namespace InternalAttributeDeps {
namespace {
class TestAttribute : public Castable<TestAttribute, ast::InternalAttribute> {
public:
TestAttribute(ProgramID pid, ast::NodeID nid, const ast::IdentifierExpression* dep)
: Base(pid, nid, utils::Vector{dep}) {}
std::string InternalName() const override { return "test_attribute"; }
const Cloneable* Clone(CloneContext*) const override { return nullptr; }
};
using InternalAttributeDepsTest = ResolverTest;
TEST_F(InternalAttributeDepsTest, Dependency) {
auto* ident = Expr("v");
auto* attr = ASTNodes().Create<TestAttribute>(ID(), AllocateNodeID(), ident);
auto* f = Func("f", utils::Empty, ty.void_(), utils::Empty, utils::Vector{attr});
auto* v = GlobalVar("v", ty.i32(), builtin::AddressSpace::kPrivate);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* user = As<sem::VariableUser>(Sem().Get(ident));
ASSERT_NE(user, nullptr);
auto* var = Sem().Get(v);
EXPECT_EQ(user->Variable(), var);
auto* fn = Sem().Get(f);
EXPECT_THAT(fn->DirectlyReferencedGlobals(), testing::ElementsAre(var));
EXPECT_THAT(fn->TransitivelyReferencedGlobals(), testing::ElementsAre(var));
}
} // namespace
} // namespace InternalAttributeDeps
} // namespace tint::resolver } // namespace tint::resolver
TINT_INSTANTIATE_TYPEINFO(tint::resolver::InternalAttributeDeps::TestAttribute);

View File

@ -414,12 +414,18 @@ class DependencyScanner {
TraverseExpression(wg->y); TraverseExpression(wg->y);
TraverseExpression(wg->z); TraverseExpression(wg->z);
return true; return true;
},
[&](const ast::InternalAttribute* i) {
for (auto* dep : i->dependencies) {
TraverseExpression(dep);
}
return true;
}); });
if (handled) { if (handled) {
return; return;
} }
if (attr->IsAnyOf<ast::BuiltinAttribute, ast::DiagnosticAttribute, ast::InternalAttribute, if (attr->IsAnyOf<ast::BuiltinAttribute, ast::DiagnosticAttribute,
ast::InterpolateAttribute, ast::InvariantAttribute, ast::MustUseAttribute, ast::InterpolateAttribute, ast::InvariantAttribute, ast::MustUseAttribute,
ast::StageAttribute, ast::StrideAttribute, ast::StageAttribute, ast::StrideAttribute,
ast::StructMemberOffsetAttribute>()) { ast::StructMemberOffsetAttribute>()) {

View File

@ -1495,7 +1495,7 @@ IntrinsicTable::CtorOrConv Impl::Lookup(CtorConvIntrinsic type,
// Was this overload a constructor or conversion? // Was this overload a constructor or conversion?
if (match.overload->flags.Contains(OverloadFlag::kIsConstructor)) { if (match.overload->flags.Contains(OverloadFlag::kIsConstructor)) {
utils::Vector<const sem::Parameter*, 8> params; utils::Vector<sem::Parameter*, 8> params;
params.Reserve(match.parameters.Length()); params.Reserve(match.parameters.Length());
for (auto& p : match.parameters) { for (auto& p : match.parameters) {
params.Push(builder.create<sem::Parameter>( params.Push(builder.create<sem::Parameter>(

View File

@ -856,9 +856,9 @@ sem::Statement* Resolver::ConstAssert(const ast::ConstAssert* assertion) {
sem::Function* Resolver::Function(const ast::Function* decl) { sem::Function* Resolver::Function(const ast::Function* decl) {
Mark(decl->name); Mark(decl->name);
uint32_t parameter_index = 0; auto* func = builder_->create<sem::Function>(decl);
utils::Hashmap<Symbol, Source, 8> parameter_names; builder_->Sem().Add(decl, func);
utils::Vector<sem::Parameter*, 8> parameters; TINT_SCOPED_ASSIGNMENT(current_function_, func);
validator_.DiagnosticFilters().Push(); validator_.DiagnosticFilters().Push();
TINT_DEFER(validator_.DiagnosticFilters().Pop()); TINT_DEFER(validator_.DiagnosticFilters().Pop());
@ -872,6 +872,8 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
} }
// Resolve all the parameters // Resolve all the parameters
uint32_t parameter_index = 0;
utils::Hashmap<Symbol, Source, 8> parameter_names;
for (auto* param : decl->params) { for (auto* param : decl->params) {
Mark(param); Mark(param);
@ -893,7 +895,7 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
return nullptr; return nullptr;
} }
parameters.Push(p); func->AddParameter(p);
auto* p_ty = const_cast<type::Type*>(p->Type()); auto* p_ty = const_cast<type::Type*>(p->Type());
if (auto* str = p_ty->As<sem::Struct>()) { if (auto* str = p_ty->As<sem::Struct>()) {
@ -923,9 +925,9 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
} else { } else {
return_type = builder_->create<type::Void>(); return_type = builder_->create<type::Void>();
} }
func->SetReturnType(return_type);
// Determine if the return type has a location // Determine if the return type has a location
std::optional<uint32_t> return_location;
for (auto* attr : decl->return_type_attributes) { for (auto* attr : decl->return_type_attributes) {
if (!Attribute(attr)) { if (!Attribute(attr)) {
return nullptr; return nullptr;
@ -936,7 +938,7 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
if (!value) { if (!value) {
return nullptr; return nullptr;
} }
return_location = value.Get(); func->SetReturnLocation(value.Get());
} }
} }
@ -963,12 +965,7 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
} }
} }
auto* func =
builder_->create<sem::Function>(decl, return_type, return_location, std::move(parameters));
ApplyDiagnosticSeverities(func); ApplyDiagnosticSeverities(func);
builder_->Sem().Add(decl, func);
TINT_SCOPED_ASSIGNMENT(current_function_, func);
if (!WorkgroupSize(decl)) { if (!WorkgroupSize(decl)) {
return nullptr; return nullptr;
@ -2089,7 +2086,7 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
auto* call_target = struct_ctors_.GetOrCreate( auto* call_target = struct_ctors_.GetOrCreate(
StructConstructorSig{{str, args.Length(), args_stage}}, StructConstructorSig{{str, args.Length(), args_stage}},
[&]() -> sem::ValueConstructor* { [&]() -> sem::ValueConstructor* {
utils::Vector<const sem::Parameter*, 8> params; utils::Vector<sem::Parameter*, 8> params;
params.Resize(std::min(args.Length(), str->Members().Length())); params.Resize(std::min(args.Length(), str->Members().Length()));
for (size_t i = 0, n = params.Length(); i < n; i++) { for (size_t i = 0, n = params.Length(); i < n; i++) {
params[i] = builder_->create<sem::Parameter>( params[i] = builder_->create<sem::Parameter>(
@ -3436,6 +3433,7 @@ bool Resolver::Attribute(const ast::Attribute* attr) {
[&](const ast::BuiltinAttribute* b) { return BuiltinAttribute(b); }, [&](const ast::BuiltinAttribute* b) { return BuiltinAttribute(b); },
[&](const ast::DiagnosticAttribute* d) { return DiagnosticControl(d->control); }, [&](const ast::DiagnosticAttribute* d) { return DiagnosticControl(d->control); },
[&](const ast::InterpolateAttribute* i) { return InterpolateAttribute(i); }, [&](const ast::InterpolateAttribute* i) { return InterpolateAttribute(i); },
[&](const ast::InternalAttribute* i) { return InternalAttribute(i); },
[&](Default) { return true; }); [&](Default) { return true; });
} }
@ -3460,6 +3458,15 @@ bool Resolver::InterpolateAttribute(const ast::InterpolateAttribute* attr) {
return true; return true;
} }
bool Resolver::InternalAttribute(const ast::InternalAttribute* attr) {
for (auto* dep : attr->dependencies) {
if (!Expression(dep)) {
return false;
}
}
return true;
}
bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) { bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
Mark(control.rule_name); Mark(control.rule_name);

View File

@ -321,6 +321,10 @@ class Resolver {
/// @returns true on success, false on failure /// @returns true on success, false on failure
bool InterpolateAttribute(const ast::InterpolateAttribute* attr); bool InterpolateAttribute(const ast::InterpolateAttribute* attr);
/// Resolves the internal attribute @p attr
/// @returns true on success, false on failure
bool InternalAttribute(const ast::InternalAttribute* attr);
/// @param control the diagnostic control /// @param control the diagnostic control
/// @returns true on success, false on failure /// @returns true on success, false on failure
bool DiagnosticControl(const ast::DiagnosticControl& control); bool DiagnosticControl(const ast::DiagnosticControl& control);

View File

@ -25,17 +25,6 @@
TINT_INSTANTIATE_TYPEINFO(tint::sem::Builtin); TINT_INSTANTIATE_TYPEINFO(tint::sem::Builtin);
namespace tint::sem { namespace tint::sem {
namespace {
utils::VectorRef<const Parameter*> SetOwner(utils::VectorRef<Parameter*> parameters,
const tint::sem::CallTarget* owner) {
for (auto* parameter : parameters) {
parameter->SetOwner(owner);
}
return parameters;
}
} // namespace
const char* Builtin::str() const { const char* Builtin::str() const {
return sem::str(type_); return sem::str(type_);
@ -112,7 +101,7 @@ Builtin::Builtin(BuiltinType type,
PipelineStageSet supported_stages, PipelineStageSet supported_stages,
bool is_deprecated, bool is_deprecated,
bool must_use) bool must_use)
: Base(return_type, SetOwner(std::move(parameters), this), eval_stage, must_use), : Base(return_type, std::move(parameters), eval_stage, must_use),
type_(type), type_(type),
supported_stages_(supported_stages), supported_stages_(supported_stages),
is_deprecated_(is_deprecated) {} is_deprecated_(is_deprecated) {}

View File

@ -23,17 +23,25 @@ TINT_INSTANTIATE_TYPEINFO(tint::sem::CallTarget);
namespace tint::sem { namespace tint::sem {
CallTarget::CallTarget(EvaluationStage stage, bool must_use) : stage_(stage), must_use_(must_use) {}
CallTarget::CallTarget(const type::Type* return_type, CallTarget::CallTarget(const type::Type* return_type,
utils::VectorRef<const Parameter*> parameters, utils::VectorRef<Parameter*> parameters,
EvaluationStage stage, EvaluationStage stage,
bool must_use) bool must_use)
: signature_{return_type, std::move(parameters)}, stage_(stage), must_use_(must_use) { : stage_(stage), must_use_(must_use) {
SetReturnType(return_type);
for (auto* param : parameters) {
AddParameter(param);
}
TINT_ASSERT(Semantic, return_type); TINT_ASSERT(Semantic, return_type);
} }
CallTarget::CallTarget(const CallTarget&) = default; CallTarget::CallTarget(const CallTarget&) = default;
CallTarget::~CallTarget() = default; CallTarget::~CallTarget() = default;
CallTargetSignature::CallTargetSignature() = default;
CallTargetSignature::CallTargetSignature(const type::Type* ret_ty, CallTargetSignature::CallTargetSignature(const type::Type* ret_ty,
utils::VectorRef<const sem::Parameter*> params) utils::VectorRef<const sem::Parameter*> params)
: return_type(ret_ty), parameters(std::move(params)) {} : return_type(ret_ty), parameters(std::move(params)) {}

View File

@ -27,6 +27,9 @@ namespace tint::sem {
/// CallTargetSignature holds the return type and parameters for a call target /// CallTargetSignature holds the return type and parameters for a call target
struct CallTargetSignature { struct CallTargetSignature {
/// Constructor
CallTargetSignature();
/// Constructor /// Constructor
/// @param ret_ty the call target return type /// @param ret_ty the call target return type
/// @param params the call target parameters /// @param params the call target parameters
@ -39,9 +42,9 @@ struct CallTargetSignature {
~CallTargetSignature(); ~CallTargetSignature();
/// The type of the call target return value /// The type of the call target return value
const type::Type* const return_type = nullptr; const type::Type* return_type = nullptr;
/// The parameters of the call target /// The parameters of the call target
const utils::Vector<const sem::Parameter*, 8> parameters; utils::Vector<const sem::Parameter*, 8> parameters;
/// Equality operator /// Equality operator
/// @param other the signature to compare this to /// @param other the signature to compare this to
@ -66,6 +69,12 @@ struct CallTargetSignature {
/// conversions. /// conversions.
class CallTarget : public Castable<CallTarget, Node> { class CallTarget : public Castable<CallTarget, Node> {
public: public:
/// Constructor
/// @param stage the earliest evaluation stage for a call to this target
/// @param must_use the result of the call target must be used, i.e. it cannot be used as a call
/// statement.
CallTarget(EvaluationStage stage, bool must_use);
/// Constructor /// Constructor
/// @param return_type the return type of the call target /// @param return_type the return type of the call target
/// @param parameters the parameters for the call target /// @param parameters the parameters for the call target
@ -73,7 +82,7 @@ class CallTarget : public Castable<CallTarget, Node> {
/// @param must_use the result of the call target must be used, i.e. it cannot be used as a call /// @param must_use the result of the call target must be used, i.e. it cannot be used as a call
/// statement. /// statement.
CallTarget(const type::Type* return_type, CallTarget(const type::Type* return_type,
utils::VectorRef<const Parameter*> parameters, utils::VectorRef<Parameter*> parameters,
EvaluationStage stage, EvaluationStage stage,
bool must_use); bool must_use);
@ -83,9 +92,20 @@ class CallTarget : public Castable<CallTarget, Node> {
/// Destructor /// Destructor
~CallTarget() override; ~CallTarget() override;
/// Sets the call target's return type
/// @param ty the parameter
void SetReturnType(const type::Type* ty) { signature_.return_type = ty; }
/// @return the return type of the call target /// @return the return type of the call target
const type::Type* ReturnType() const { return signature_.return_type; } const type::Type* ReturnType() const { return signature_.return_type; }
/// Adds a parameter to the call target
/// @param parameter the parameter
void AddParameter(Parameter* parameter) {
parameter->SetOwner(this);
signature_.parameters.Push(parameter);
}
/// @return the parameters of the call target /// @return the parameters of the call target
auto& Parameters() const { return signature_.parameters; } auto& Parameters() const { return signature_.parameters; }

View File

@ -28,29 +28,12 @@
TINT_INSTANTIATE_TYPEINFO(tint::sem::Function); TINT_INSTANTIATE_TYPEINFO(tint::sem::Function);
namespace tint::sem { namespace tint::sem {
namespace {
utils::VectorRef<const Parameter*> SetOwner(utils::VectorRef<Parameter*> parameters, Function::Function(const ast::Function* declaration)
const tint::sem::CallTarget* owner) { : Base(EvaluationStage::kRuntime,
for (auto* parameter : parameters) {
parameter->SetOwner(owner);
}
return parameters;
}
} // namespace
Function::Function(const ast::Function* declaration,
type::Type* return_type,
std::optional<uint32_t> return_location,
utils::VectorRef<Parameter*> parameters)
: Base(return_type,
SetOwner(std::move(parameters), this),
EvaluationStage::kRuntime,
ast::HasAttribute<ast::MustUseAttribute>(declaration->attributes)), ast::HasAttribute<ast::MustUseAttribute>(declaration->attributes)),
declaration_(declaration), declaration_(declaration),
workgroup_size_{1, 1, 1}, workgroup_size_{1, 1, 1} {}
return_location_(return_location) {}
Function::~Function() = default; Function::~Function() = default;

View File

@ -54,17 +54,15 @@ class Function final : public Castable<Function, CallTarget> {
/// Constructor /// Constructor
/// @param declaration the ast::Function /// @param declaration the ast::Function
/// @param return_type the return type of the function explicit Function(const ast::Function* declaration);
/// @param return_location the location value for the return, if provided
/// @param parameters the parameters to the function
Function(const ast::Function* declaration,
type::Type* return_type,
std::optional<uint32_t> return_location,
utils::VectorRef<Parameter*> parameters);
/// Destructor /// Destructor
~Function() override; ~Function() override;
/// Sets the function's return location
/// @param return_location the location value
void SetReturnLocation(uint32_t return_location) { return_location_ = return_location; }
/// @returns the ast::Function declaration /// @returns the ast::Function declaration
const ast::Function* Declaration() const { return declaration_; } const ast::Function* Declaration() const { return declaration_; }

View File

@ -21,7 +21,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::sem::ValueConstructor);
namespace tint::sem { namespace tint::sem {
ValueConstructor::ValueConstructor(const type::Type* type, ValueConstructor::ValueConstructor(const type::Type* type,
utils::VectorRef<const Parameter*> parameters, utils::VectorRef<Parameter*> parameters,
EvaluationStage stage) EvaluationStage stage)
: Base(type, std::move(parameters), stage, /* must_use */ true) {} : Base(type, std::move(parameters), stage, /* must_use */ true) {}

View File

@ -28,7 +28,7 @@ class ValueConstructor final : public Castable<ValueConstructor, CallTarget> {
/// @param parameters the constructor parameters /// @param parameters the constructor parameters
/// @param stage the earliest evaluation stage for the expression /// @param stage the earliest evaluation stage for the expression
ValueConstructor(const type::Type* type, ValueConstructor(const type::Type* type,
utils::VectorRef<const Parameter*> parameters, utils::VectorRef<Parameter*> parameters,
EvaluationStage stage); EvaluationStage stage);
/// Destructor /// Destructor

View File

@ -19,9 +19,9 @@ TINT_INSTANTIATE_TYPEINFO(tint::sem::ValueConversion);
namespace tint::sem { namespace tint::sem {
ValueConversion::ValueConversion(const type::Type* type, ValueConversion::ValueConversion(const type::Type* type,
const sem::Parameter* parameter, sem::Parameter* parameter,
EvaluationStage stage) EvaluationStage stage)
: Base(type, utils::Vector<const sem::Parameter*, 1>{parameter}, stage, /* must_use */ true) {} : Base(type, utils::Vector<sem::Parameter*, 1>{parameter}, stage, /* must_use */ true) {}
ValueConversion::~ValueConversion() = default; ValueConversion::~ValueConversion() = default;

View File

@ -26,7 +26,7 @@ class ValueConversion final : public Castable<ValueConversion, CallTarget> {
/// @param type the target type of the cast /// @param type the target type of the cast
/// @param parameter the type cast parameter /// @param parameter the type cast parameter
/// @param stage the earliest evaluation stage for the expression /// @param stage the earliest evaluation stage for the expression
ValueConversion(const type::Type* type, const sem::Parameter* parameter, EvaluationStage stage); ValueConversion(const type::Type* type, sem::Parameter* parameter, EvaluationStage stage);
/// Destructor /// Destructor
~ValueConversion() override; ~ValueConversion() override;

View File

@ -102,7 +102,7 @@ Transform::ApplyResult AddBlockAttribute::Apply(const Program* src,
} }
AddBlockAttribute::BlockAttribute::BlockAttribute(ProgramID pid, ast::NodeID nid) AddBlockAttribute::BlockAttribute::BlockAttribute(ProgramID pid, ast::NodeID nid)
: Base(pid, nid) {} : Base(pid, nid, utils::Empty) {}
AddBlockAttribute::BlockAttribute::~BlockAttribute() = default; AddBlockAttribute::BlockAttribute::~BlockAttribute() = default;
std::string AddBlockAttribute::BlockAttribute::InternalName() const { std::string AddBlockAttribute::BlockAttribute::InternalName() const {
return "block"; return "block";

View File

@ -71,7 +71,7 @@ struct ArrayUsage {
} // namespace } // namespace
CalculateArrayLength::BufferSizeIntrinsic::BufferSizeIntrinsic(ProgramID pid, ast::NodeID nid) CalculateArrayLength::BufferSizeIntrinsic::BufferSizeIntrinsic(ProgramID pid, ast::NodeID nid)
: Base(pid, nid) {} : Base(pid, nid, utils::Empty) {}
CalculateArrayLength::BufferSizeIntrinsic::~BufferSizeIntrinsic() = default; CalculateArrayLength::BufferSizeIntrinsic::~BufferSizeIntrinsic() = default;
std::string CalculateArrayLength::BufferSizeIntrinsic::InternalName() const { std::string CalculateArrayLength::BufferSizeIntrinsic::InternalName() const {
return "intrinsic_buffer_size"; return "intrinsic_buffer_size";

View File

@ -228,7 +228,7 @@ DecomposeMemoryAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder,
} }
return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>( return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kLoad, type, builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kLoad, type,
address_space, buffer); address_space, builder->Expr(buffer));
} }
/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function to /// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function to
@ -242,7 +242,7 @@ DecomposeMemoryAccess::Intrinsic* IntrinsicStoreFor(ProgramBuilder* builder,
} }
return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>( return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kStore, builder->ID(), builder->AllocateNodeID(), DecomposeMemoryAccess::Intrinsic::Op::kStore,
type, builtin::AddressSpace::kStorage, buffer); type, builtin::AddressSpace::kStorage, builder->Expr(buffer));
} }
/// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function for /// @returns a DecomposeMemoryAccess::Intrinsic attribute that can be applied to a stub function for
@ -299,7 +299,7 @@ DecomposeMemoryAccess::Intrinsic* IntrinsicAtomicFor(ProgramBuilder* builder,
} }
return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>( return builder->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
builder->ID(), builder->AllocateNodeID(), op, type, builtin::AddressSpace::kStorage, builder->ID(), builder->AllocateNodeID(), op, type, builtin::AddressSpace::kStorage,
buffer); builder->Expr(buffer));
} }
/// BufferAccess describes a single storage or uniform buffer access /// BufferAccess describes a single storage or uniform buffer access
@ -692,8 +692,8 @@ DecomposeMemoryAccess::Intrinsic::Intrinsic(ProgramID pid,
Op o, Op o,
DataType ty, DataType ty,
builtin::AddressSpace as, builtin::AddressSpace as,
const Symbol& buf) const ast::IdentifierExpression* buf)
: Base(pid, nid), op(o), type(ty), address_space(as), buffer(buf) {} : Base(pid, nid, utils::Vector{buf}), op(o), type(ty), address_space(as) {}
DecomposeMemoryAccess::Intrinsic::~Intrinsic() = default; DecomposeMemoryAccess::Intrinsic::~Intrinsic() = default;
std::string DecomposeMemoryAccess::Intrinsic::InternalName() const { std::string DecomposeMemoryAccess::Intrinsic::InternalName() const {
utils::StringStream ss; utils::StringStream ss;
@ -794,7 +794,7 @@ std::string DecomposeMemoryAccess::Intrinsic::InternalName() const {
const DecomposeMemoryAccess::Intrinsic* DecomposeMemoryAccess::Intrinsic::Clone( const DecomposeMemoryAccess::Intrinsic* DecomposeMemoryAccess::Intrinsic::Clone(
CloneContext* ctx) const { CloneContext* ctx) const {
auto buf = ctx->Clone(buffer); auto buf = ctx->Clone(Buffer());
return ctx->dst->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>( return ctx->dst->ASTNodes().Create<DecomposeMemoryAccess::Intrinsic>(
ctx->dst->ID(), ctx->dst->AllocateNodeID(), op, type, address_space, buf); ctx->dst->ID(), ctx->dst->AllocateNodeID(), op, type, address_space, buf);
} }
@ -803,6 +803,10 @@ bool DecomposeMemoryAccess::Intrinsic::IsAtomic() const {
return op != Op::kLoad && op != Op::kStore; return op != Op::kLoad && op != Op::kStore;
} }
const ast::IdentifierExpression* DecomposeMemoryAccess::Intrinsic::Buffer() const {
return dependencies[0];
}
DecomposeMemoryAccess::DecomposeMemoryAccess() = default; DecomposeMemoryAccess::DecomposeMemoryAccess() = default;
DecomposeMemoryAccess::~DecomposeMemoryAccess() = default; DecomposeMemoryAccess::~DecomposeMemoryAccess() = default;

View File

@ -80,13 +80,13 @@ class DecomposeMemoryAccess final : public Castable<DecomposeMemoryAccess, Trans
/// @param o the op of the intrinsic /// @param o the op of the intrinsic
/// @param type the data type of the intrinsic /// @param type the data type of the intrinsic
/// @param address_space the address space of the buffer /// @param address_space the address space of the buffer
/// @param buffer the storage or uniform buffer name /// @param buffer the storage or uniform buffer identifier
Intrinsic(ProgramID pid, Intrinsic(ProgramID pid,
ast::NodeID nid, ast::NodeID nid,
Op o, Op o,
DataType type, DataType type,
builtin::AddressSpace address_space, builtin::AddressSpace address_space,
const Symbol& buffer); const ast::IdentifierExpression* buffer);
/// Destructor /// Destructor
~Intrinsic() override; ~Intrinsic() override;
@ -102,6 +102,9 @@ class DecomposeMemoryAccess final : public Castable<DecomposeMemoryAccess, Trans
/// @return true if op is atomic /// @return true if op is atomic
bool IsAtomic() const; bool IsAtomic() const;
/// @return the buffer that this intrinsic operates on
const ast::IdentifierExpression* Buffer() const;
/// The op of the intrinsic /// The op of the intrinsic
const Op op; const Op op;
@ -110,9 +113,6 @@ class DecomposeMemoryAccess final : public Castable<DecomposeMemoryAccess, Trans
/// The address space of the buffer this intrinsic operates on /// The address space of the buffer this intrinsic operates on
const builtin::AddressSpace address_space; const builtin::AddressSpace address_space;
/// The buffer name
const Symbol buffer;
}; };
/// Constructor /// Constructor

View File

@ -294,7 +294,7 @@ SpirvAtomic::SpirvAtomic() = default;
SpirvAtomic::~SpirvAtomic() = default; SpirvAtomic::~SpirvAtomic() = default;
SpirvAtomic::Stub::Stub(ProgramID pid, ast::NodeID nid, sem::BuiltinType b) SpirvAtomic::Stub::Stub(ProgramID pid, ast::NodeID nid, sem::BuiltinType b)
: Base(pid, nid), builtin(b) {} : Base(pid, nid, utils::Empty), builtin(b) {}
SpirvAtomic::Stub::~Stub() = default; SpirvAtomic::Stub::~Stub() = default;
std::string SpirvAtomic::Stub::InternalName() const { std::string SpirvAtomic::Stub::InternalName() const {
return "@internal(spirv-atomic " + std::string(sem::str(builtin)) + ")"; return "@internal(spirv-atomic " + std::string(sem::str(builtin)) + ")";

View File

@ -30,6 +30,9 @@ namespace tint::utils {
/// Attempting to add a duplicate is a no-op. /// Attempting to add a duplicate is a no-op.
template <typename T, size_t N, typename HASH = std::hash<T>, typename EQUAL = std::equal_to<T>> template <typename T, size_t N, typename HASH = std::hash<T>, typename EQUAL = std::equal_to<T>>
struct UniqueVector { struct UniqueVector {
/// STL-friendly alias to T. Used by gmock.
using value_type = T;
/// Constructor /// Constructor
UniqueVector() = default; UniqueVector() = default;

View File

@ -156,9 +156,8 @@ const sem::Call* AppendVector(ProgramBuilder* b,
})); }));
auto* ctor_target = b->create<sem::ValueConstructor>( auto* ctor_target = b->create<sem::ValueConstructor>(
packed_sem_ty, packed_sem_ty,
utils::Transform( utils::Transform(packed,
packed, [&](const tint::sem::ValueExpression* arg, size_t i) {
[&](const tint::sem::ValueExpression* arg, size_t i) -> const sem::Parameter* {
return b->create<sem::Parameter>( return b->create<sem::Parameter>(
nullptr, static_cast<uint32_t>(i), arg->Type()->UnwrapRef(), nullptr, static_cast<uint32_t>(i), arg->Type()->UnwrapRef(),
builtin::AddressSpace::kUndefined, builtin::Access::kUndefined); builtin::AddressSpace::kUndefined, builtin::Access::kUndefined);

View File

@ -1125,7 +1125,7 @@ bool GeneratorImpl::EmitUniformBufferAccess(
utils::StringStream& out, utils::StringStream& out,
const ast::CallExpression* expr, const ast::CallExpression* expr,
const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) { const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
auto const buffer = program_->Symbols().NameFor(intrinsic->buffer); auto const buffer = program_->Symbols().NameFor(intrinsic->Buffer()->identifier->symbol);
auto* const offset = expr->args[0]; auto* const offset = expr->args[0];
// offset in bytes // offset in bytes
@ -1413,7 +1413,7 @@ bool GeneratorImpl::EmitStorageBufferAccess(
utils::StringStream& out, utils::StringStream& out,
const ast::CallExpression* expr, const ast::CallExpression* expr,
const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) { const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
auto const buffer = program_->Symbols().NameFor(intrinsic->buffer); auto const buffer = program_->Symbols().NameFor(intrinsic->Buffer()->identifier->symbol);
auto* const offset = expr->args[0]; auto* const offset = expr->args[0];
auto* const value = expr->args[1]; auto* const value = expr->args[1];
@ -1581,7 +1581,7 @@ bool GeneratorImpl::EmitStorageAtomicIntrinsic(
const auto name = builder_.Symbols().NameFor(func->name->symbol); const auto name = builder_.Symbols().NameFor(func->name->symbol);
auto& buf = *current_buffer_; auto& buf = *current_buffer_;
auto const buffer = program_->Symbols().NameFor(intrinsic->buffer); auto const buffer = program_->Symbols().NameFor(intrinsic->Buffer()->identifier->symbol);
auto rmw = [&](const char* hlsl) -> bool { auto rmw = [&](const char* hlsl) -> bool {
{ {

View File

@ -0,0 +1,10 @@
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4(declared_after_usage.f);
}
struct DeclaredAfterUsage {
f : f32,
}
@group(0) @binding(0) var <uniform> declared_after_usage : DeclaredAfterUsage;

View File

@ -0,0 +1,18 @@
struct tint_symbol {
float4 value : SV_Position;
};
cbuffer cbuffer_declared_after_usage : register(b0, space0) {
uint4 declared_after_usage[1];
};
float4 main_inner() {
return float4((asfloat(declared_after_usage[0].x)).xxxx);
}
tint_symbol main() {
const float4 inner_result = main_inner();
tint_symbol wrapper_result = (tint_symbol)0;
wrapper_result.value = inner_result;
return wrapper_result;
}

View File

@ -0,0 +1,18 @@
struct tint_symbol {
float4 value : SV_Position;
};
cbuffer cbuffer_declared_after_usage : register(b0, space0) {
uint4 declared_after_usage[1];
};
float4 main_inner() {
return float4((asfloat(declared_after_usage[0].x)).xxxx);
}
tint_symbol main() {
const float4 inner_result = main_inner();
tint_symbol wrapper_result = (tint_symbol)0;
wrapper_result.value = inner_result;
return wrapper_result;
}

View File

@ -0,0 +1,25 @@
#version 310 es
struct DeclaredAfterUsage {
float f;
uint pad;
uint pad_1;
uint pad_2;
};
layout(binding = 0, std140) uniform declared_after_usage_block_ubo {
DeclaredAfterUsage inner;
} declared_after_usage;
vec4 tint_symbol() {
return vec4(declared_after_usage.inner.f);
}
void main() {
gl_PointSize = 1.0;
vec4 inner_result = tint_symbol();
gl_Position = inner_result;
gl_Position.y = -(gl_Position.y);
gl_Position.z = ((2.0f * gl_Position.z) - gl_Position.w);
return;
}

View File

@ -0,0 +1,22 @@
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_1 {
float4 value [[position]];
};
struct DeclaredAfterUsage {
/* 0x0000 */ float f;
};
float4 tint_symbol_inner(const constant DeclaredAfterUsage* const tint_symbol_2) {
return float4((*(tint_symbol_2)).f);
}
vertex tint_symbol_1 tint_symbol(const constant DeclaredAfterUsage* tint_symbol_3 [[buffer(0)]]) {
float4 const inner_result = tint_symbol_inner(tint_symbol_3);
tint_symbol_1 wrapper_result = {};
wrapper_result.value = inner_result;
return wrapper_result;
}

View File

@ -0,0 +1,58 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 28
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %value %vertex_point_size
OpName %value "value"
OpName %vertex_point_size "vertex_point_size"
OpName %declared_after_usage_block "declared_after_usage_block"
OpMemberName %declared_after_usage_block 0 "inner"
OpName %DeclaredAfterUsage "DeclaredAfterUsage"
OpMemberName %DeclaredAfterUsage 0 "f"
OpName %declared_after_usage "declared_after_usage"
OpName %main_inner "main_inner"
OpName %main "main"
OpDecorate %value BuiltIn Position
OpDecorate %vertex_point_size BuiltIn PointSize
OpDecorate %declared_after_usage_block Block
OpMemberDecorate %declared_after_usage_block 0 Offset 0
OpMemberDecorate %DeclaredAfterUsage 0 Offset 0
OpDecorate %declared_after_usage NonWritable
OpDecorate %declared_after_usage DescriptorSet 0
OpDecorate %declared_after_usage Binding 0
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%5 = OpConstantNull %v4float
%value = OpVariable %_ptr_Output_v4float Output %5
%_ptr_Output_float = OpTypePointer Output %float
%8 = OpConstantNull %float
%vertex_point_size = OpVariable %_ptr_Output_float Output %8
%DeclaredAfterUsage = OpTypeStruct %float
%declared_after_usage_block = OpTypeStruct %DeclaredAfterUsage
%_ptr_Uniform_declared_after_usage_block = OpTypePointer Uniform %declared_after_usage_block
%declared_after_usage = OpVariable %_ptr_Uniform_declared_after_usage_block Uniform
%13 = OpTypeFunction %v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%void = OpTypeVoid
%22 = OpTypeFunction %void
%float_1 = OpConstant %float 1
%main_inner = OpFunction %v4float None %13
%15 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_float %declared_after_usage %uint_0 %uint_0
%20 = OpLoad %float %19
%21 = OpCompositeConstruct %v4float %20 %20 %20 %20
OpReturnValue %21
OpFunctionEnd
%main = OpFunction %void None %22
%25 = OpLabel
%26 = OpFunctionCall %v4float %main_inner
OpStore %value %26
OpStore %vertex_point_size %float_1
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,10 @@
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4(declared_after_usage.f);
}
struct DeclaredAfterUsage {
f : f32,
}
@group(0) @binding(0) var<uniform> declared_after_usage : DeclaredAfterUsage;