Implement phony assignment
Bug: tint:1213 Change-Id: Ib1ebc4947405c4ada7a9bdbc6bd5a36447bbd234 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67064 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
092326894e
commit
1aa98e62c0
|
@ -463,6 +463,8 @@ libtint_source_set("libtint_core_all_src") {
|
|||
"transform/pad_array_elements.h",
|
||||
"transform/promote_initializers_to_const_var.cc",
|
||||
"transform/promote_initializers_to_const_var.h",
|
||||
"transform/remove_phonies.cc",
|
||||
"transform/remove_phonies.h",
|
||||
"transform/renamer.cc",
|
||||
"transform/renamer.h",
|
||||
"transform/robustness.cc",
|
||||
|
|
|
@ -329,6 +329,8 @@ set(TINT_LIB_SRCS
|
|||
transform/pad_array_elements.h
|
||||
transform/promote_initializers_to_const_var.cc
|
||||
transform/promote_initializers_to_const_var.h
|
||||
transform/remove_phonies.cc
|
||||
transform/remove_phonies.h
|
||||
transform/renamer.cc
|
||||
transform/renamer.h
|
||||
transform/robustness.cc
|
||||
|
@ -955,6 +957,7 @@ if(${TINT_BUILD_TESTS})
|
|||
transform/num_workgroups_from_uniform_test.cc
|
||||
transform/pad_array_elements_test.cc
|
||||
transform/promote_initializers_to_const_var_test.cc
|
||||
transform/remove_phonies_test.cc
|
||||
transform/renamer_test.cc
|
||||
transform/robustness_test.cc
|
||||
transform/simplify_test.cc
|
||||
|
|
|
@ -1762,6 +1762,18 @@ class ProgramBuilder {
|
|||
Expr(std::forward<IDX>(idx)));
|
||||
}
|
||||
|
||||
/// @param source the source information
|
||||
/// @param obj the object for the member accessor expression
|
||||
/// @param idx the index argument for the array accessor expression
|
||||
/// @returns a `ast::MemberAccessorExpression` that indexes `obj` with `idx`
|
||||
template <typename OBJ, typename IDX>
|
||||
const ast::MemberAccessorExpression* MemberAccessor(const Source& source,
|
||||
OBJ&& obj,
|
||||
IDX&& idx) {
|
||||
return create<ast::MemberAccessorExpression>(
|
||||
source, Expr(std::forward<OBJ>(obj)), Expr(std::forward<IDX>(idx)));
|
||||
}
|
||||
|
||||
/// @param obj the object for the member accessor expression
|
||||
/// @param idx the index argument for the array accessor expression
|
||||
/// @returns a `ast::MemberAccessorExpression` that indexes `obj` with `idx`
|
||||
|
|
|
@ -2792,7 +2792,7 @@ Maybe<const ast::Expression*> ParserImpl::logical_or_expression() {
|
|||
}
|
||||
|
||||
// assignment_stmt
|
||||
// : unary_expression EQUAL logical_or_expression
|
||||
// : (unary_expression | underscore) EQUAL logical_or_expression
|
||||
Maybe<const ast::AssignmentStatement*> ParserImpl::assignment_stmt() {
|
||||
auto t = peek();
|
||||
auto source = t.source();
|
||||
|
@ -2806,19 +2806,27 @@ Maybe<const ast::AssignmentStatement*> ParserImpl::assignment_stmt() {
|
|||
}
|
||||
|
||||
auto lhs = unary_expression();
|
||||
if (lhs.errored)
|
||||
if (lhs.errored) {
|
||||
return Failure::kErrored;
|
||||
if (!lhs.matched)
|
||||
return Failure::kNoMatch;
|
||||
}
|
||||
if (!lhs.matched) {
|
||||
if (!match(Token::Type::kUnderscore, &source)) {
|
||||
return Failure::kNoMatch;
|
||||
}
|
||||
lhs = create<ast::PhonyExpression>(source);
|
||||
}
|
||||
|
||||
if (!expect("assignment", Token::Type::kEqual))
|
||||
if (!expect("assignment", Token::Type::kEqual)) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
auto rhs = logical_or_expression();
|
||||
if (rhs.errored)
|
||||
if (rhs.errored) {
|
||||
return Failure::kErrored;
|
||||
if (!rhs.matched)
|
||||
}
|
||||
if (!rhs.matched) {
|
||||
return add_error(peek(), "unable to parse right side of assignment");
|
||||
}
|
||||
|
||||
return create<ast::AssignmentStatement>(source, lhs.value, rhs.value);
|
||||
}
|
||||
|
|
|
@ -98,6 +98,28 @@ TEST_F(ParserImplTest, AssignmentStmt_Parses_ToMember) {
|
|||
EXPECT_EQ(ident->symbol, p->builder().Symbols().Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, AssignmentStmt_Parses_ToPhony) {
|
||||
auto p = parser("_ = 123");
|
||||
auto e = p->assignment_stmt();
|
||||
EXPECT_TRUE(e.matched);
|
||||
EXPECT_FALSE(e.errored);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_NE(e.value, nullptr);
|
||||
|
||||
ASSERT_TRUE(e->Is<ast::AssignmentStatement>());
|
||||
ASSERT_NE(e->lhs, nullptr);
|
||||
ASSERT_NE(e->rhs, nullptr);
|
||||
|
||||
ASSERT_TRUE(e->rhs->Is<ast::ConstructorExpression>());
|
||||
ASSERT_TRUE(e->rhs->Is<ast::ScalarConstructorExpression>());
|
||||
auto* init = e->rhs->As<ast::ScalarConstructorExpression>();
|
||||
ASSERT_NE(init->literal, nullptr);
|
||||
ASSERT_TRUE(init->literal->Is<ast::SintLiteral>());
|
||||
EXPECT_EQ(init->literal->As<ast::SintLiteral>()->value, 123);
|
||||
|
||||
ASSERT_TRUE(e->lhs->Is<ast::PhonyExpression>());
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, AssignmentStmt_MissingEqual) {
|
||||
auto p = parser("a.b.c[2].d 123");
|
||||
auto e = p->assignment_stmt();
|
||||
|
|
|
@ -293,6 +293,111 @@ TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_RuntimeArray) {
|
|||
"56:78 error: storage type of assignment must be constructible");
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest,
|
||||
AssignToPhony_NonConstructableStruct_Fail) {
|
||||
// [[block]]
|
||||
// struct S {
|
||||
// arr: array<i32>;
|
||||
// };
|
||||
// [[group(0), binding(0)]] var<storage, read_write> s : S;
|
||||
// fn f() {
|
||||
// _ = s;
|
||||
// }
|
||||
auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
|
||||
Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
|
||||
|
||||
WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: cannot assign 'S' to '_'. "
|
||||
"'_' can only be assigned a constructable, pointer, texture or "
|
||||
"sampler type");
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
|
||||
// [[block]]
|
||||
// struct S {
|
||||
// arr: array<i32>;
|
||||
// };
|
||||
// [[group(0), binding(0)]] var<storage, read_write> s : S;
|
||||
// fn f() {
|
||||
// _ = s.arr;
|
||||
// }
|
||||
auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
|
||||
Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
|
||||
|
||||
WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: cannot assign 'ref<storage, array<i32>, read>' to '_'. "
|
||||
"'_' can only be assigned a constructable, pointer, texture or sampler "
|
||||
"type");
|
||||
}
|
||||
|
||||
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
|
||||
// [[block]]
|
||||
// struct S {
|
||||
// i: i32;
|
||||
// arr: array<i32>;
|
||||
// };
|
||||
// [[block]]
|
||||
// struct U {
|
||||
// i: i32;
|
||||
// };
|
||||
// [[group(0), binding(0)]] var tex texture_2d;
|
||||
// [[group(0), binding(1)]] var smp sampler;
|
||||
// [[group(0), binding(2)]] var<uniform> u : U;
|
||||
// [[group(0), binding(3)]] var<storage, read_write> s : S;
|
||||
// var<workgroup> wg : array<f32, 10>
|
||||
// fn f() {
|
||||
// _ = 1;
|
||||
// _ = 2u;
|
||||
// _ = 3.0;
|
||||
// _ = vec2<bool>();
|
||||
// _ = tex;
|
||||
// _ = smp;
|
||||
// _ = &s;
|
||||
// _ = s.i;
|
||||
// _ = &s.arr;
|
||||
// _ = u;
|
||||
// _ = u.i;
|
||||
// _ = wg;
|
||||
// _ = wg[3];
|
||||
// }
|
||||
auto* S = Structure("S",
|
||||
{
|
||||
Member("i", ty.i32()),
|
||||
Member("arr", ty.array<i32>()),
|
||||
},
|
||||
{StructBlock()});
|
||||
auto* U = Structure("U", {Member("i", ty.i32())}, {StructBlock()});
|
||||
Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
|
||||
GroupAndBinding(0, 0));
|
||||
Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
|
||||
Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
|
||||
Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
|
||||
Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
|
||||
|
||||
WrapInFunction(Assign(Phony(), 1), //
|
||||
Assign(Phony(), 2), //
|
||||
Assign(Phony(), 3), //
|
||||
Assign(Phony(), vec2<bool>()), //
|
||||
Assign(Phony(), "tex"), //
|
||||
Assign(Phony(), "smp"), //
|
||||
Assign(Phony(), AddressOf("s")), //
|
||||
Assign(Phony(), MemberAccessor("s", "i")), //
|
||||
Assign(Phony(), AddressOf(MemberAccessor("s", "arr"))), //
|
||||
Assign(Phony(), "u"), //
|
||||
Assign(Phony(), MemberAccessor("u", "i")), //
|
||||
Assign(Phony(), "wg"), //
|
||||
Assign(Phony(), IndexAccessor("wg", 3)));
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
||||
|
|
|
@ -2303,6 +2303,8 @@ bool Resolver::Expression(const ast::Expression* root) {
|
|||
ok = MemberAccessor(member);
|
||||
} else if (auto* unary = expr->As<ast::UnaryOpExpression>()) {
|
||||
ok = UnaryOp(unary);
|
||||
} else if (expr->Is<ast::PhonyExpression>()) {
|
||||
ok = true; // No-op
|
||||
} else {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "unhandled expression type: " << expr->TypeInfo().name;
|
||||
|
@ -4393,13 +4395,30 @@ bool Resolver::Assignment(const ast::AssignmentStatement* a) {
|
|||
if (!Expression(a->lhs) || !Expression(a->rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ValidateAssignment(a);
|
||||
}
|
||||
|
||||
bool Resolver::ValidateAssignment(const ast::AssignmentStatement* a) {
|
||||
auto const* rhs_type = TypeOf(a->rhs);
|
||||
|
||||
if (a->lhs->Is<ast::PhonyExpression>()) {
|
||||
// https://www.w3.org/TR/WGSL/#phony-assignment-section
|
||||
auto* ty = rhs_type->UnwrapRef();
|
||||
if (!ty->IsConstructible() &&
|
||||
!ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
|
||||
AddError(
|
||||
"cannot assign '" + TypeNameOf(a->rhs) +
|
||||
"' to '_'. '_' can only be assigned a constructable, pointer, "
|
||||
"texture or sampler type",
|
||||
a->rhs->source);
|
||||
return false;
|
||||
}
|
||||
return true; // RHS can be anything.
|
||||
}
|
||||
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#assignment-statement
|
||||
auto const* lhs_type = TypeOf(a->lhs);
|
||||
auto const* rhs_type = TypeOf(a->rhs);
|
||||
|
||||
if (auto* ident = a->lhs->As<ast::IdentifierExpression>()) {
|
||||
VariableInfo* var;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "src/transform/manager.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/single_entry_point.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
|
@ -58,6 +59,7 @@ Output Glsl::Run(const Program* in, const DataMap& inputs) {
|
|||
}
|
||||
manager.Add<CanonicalizeEntryPointIO>();
|
||||
manager.Add<InlinePointerLets>();
|
||||
manager.Add<RemovePhonies>();
|
||||
// Simplify cleans up messy `*(&(expr))` expressions from InlinePointerLets.
|
||||
manager.Add<Simplify>();
|
||||
manager.Add<CalculateArrayLength>();
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/remove_phonies.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/ast/traverse_expressions.h"
|
||||
#include "src/program_builder.h"
|
||||
#include "src/sem/block_statement.h"
|
||||
#include "src/sem/function.h"
|
||||
#include "src/sem/statement.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/utils/get_or_create.h"
|
||||
#include "src/utils/scoped_assignment.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::transform::RemovePhonies);
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
struct SinkSignature {
|
||||
std::vector<const sem::Type*> types;
|
||||
|
||||
bool operator==(const SinkSignature& other) const {
|
||||
if (types.size() != other.types.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < types.size(); i++) {
|
||||
if (types[i] != other.types[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Hasher {
|
||||
/// @param sig the CallTargetSignature to hash
|
||||
/// @return the hash value
|
||||
std::size_t operator()(const SinkSignature& sig) const {
|
||||
size_t hash = tint::utils::Hash(sig.types.size());
|
||||
for (auto* ty : sig.types) {
|
||||
tint::utils::HashCombine(&hash, ty);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RemovePhonies::RemovePhonies() = default;
|
||||
|
||||
RemovePhonies::~RemovePhonies() = default;
|
||||
|
||||
void RemovePhonies::Run(CloneContext& ctx, const DataMap&, DataMap&) {
|
||||
auto& sem = ctx.src->Sem();
|
||||
|
||||
std::unordered_map<SinkSignature, Symbol, SinkSignature::Hasher> sinks;
|
||||
|
||||
for (auto* node : ctx.src->ASTNodes().Objects()) {
|
||||
if (auto* stmt = node->As<ast::AssignmentStatement>()) {
|
||||
if (stmt->lhs->Is<ast::PhonyExpression>()) {
|
||||
std::vector<const ast::Expression*> side_effects;
|
||||
if (!ast::TraverseExpressions(stmt->rhs, ctx.dst->Diagnostics(),
|
||||
[&](const ast::CallExpression* call) {
|
||||
side_effects.push_back(call);
|
||||
return ast::TraverseAction::Skip;
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (side_effects.empty()) {
|
||||
// Phony assignment with no side effects.
|
||||
// Just remove it.
|
||||
RemoveStatement(ctx, stmt);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (side_effects.size() == 1) {
|
||||
if (auto* call = side_effects[0]->As<ast::CallExpression>()) {
|
||||
// Phony assignment with single call side effect.
|
||||
// Replace phony assignment with call.
|
||||
ctx.Replace(
|
||||
stmt, [&, call] { return ctx.dst->CallStmt(ctx.Clone(call)); });
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Phony assignment with multiple side effects.
|
||||
// Generate a call to a dummy function with the side effects as
|
||||
// arguments.
|
||||
ctx.Replace(stmt, [&, side_effects] {
|
||||
SinkSignature sig;
|
||||
for (auto* arg : side_effects) {
|
||||
sig.types.push_back(sem.Get(arg)->Type()->UnwrapRef());
|
||||
}
|
||||
auto sink = utils::GetOrCreate(sinks, sig, [&] {
|
||||
auto name = ctx.dst->Symbols().New("phony_sink");
|
||||
ast::VariableList params;
|
||||
for (auto* ty : sig.types) {
|
||||
auto* ast_ty = CreateASTTypeFor(ctx, ty);
|
||||
params.push_back(
|
||||
ctx.dst->Param("p" + std::to_string(params.size()), ast_ty));
|
||||
}
|
||||
ctx.dst->Func(name, params, ctx.dst->ty.void_(), {});
|
||||
return name;
|
||||
});
|
||||
ast::ExpressionList args;
|
||||
for (auto* arg : side_effects) {
|
||||
args.push_back(ctx.Clone(arg));
|
||||
}
|
||||
return ctx.dst->CallStmt(ctx.dst->Call(sink, args));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Clone();
|
||||
}
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SRC_TRANSFORM_REMOVE_PHONIES_H_
|
||||
#define SRC_TRANSFORM_REMOVE_PHONIES_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "src/transform/transform.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
|
||||
/// RemovePhonies is a Transform that removes all phony-assignment statements,
|
||||
/// while preserving function call expressions in the RHS of the assignment that
|
||||
/// may have side-effects.
|
||||
class RemovePhonies : public Castable<RemovePhonies, Transform> {
|
||||
public:
|
||||
/// Constructor
|
||||
RemovePhonies();
|
||||
|
||||
/// Destructor
|
||||
~RemovePhonies() override;
|
||||
|
||||
protected:
|
||||
/// Runs the transform using the CloneContext built for transforming a
|
||||
/// program. Run() is responsible for calling Clone() on the CloneContext.
|
||||
/// @param ctx the CloneContext primed with the input program and
|
||||
/// ProgramBuilder
|
||||
/// @param inputs optional extra transform-specific input data
|
||||
/// @param outputs optional extra transform-specific output data
|
||||
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) override;
|
||||
};
|
||||
|
||||
} // namespace transform
|
||||
} // namespace tint
|
||||
|
||||
#endif // SRC_TRANSFORM_REMOVE_PHONIES_H_
|
|
@ -0,0 +1,226 @@
|
|||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "src/transform/remove_phonies.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "src/transform/test_helper.h"
|
||||
|
||||
namespace tint {
|
||||
namespace transform {
|
||||
namespace {
|
||||
|
||||
using RemovePhoniesTest = TransformTest;
|
||||
|
||||
TEST_F(RemovePhoniesTest, EmptyModule) {
|
||||
auto* src = "";
|
||||
auto* expect = "";
|
||||
|
||||
auto got = Run<RemovePhonies>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(RemovePhoniesTest, NoSideEffects) {
|
||||
auto* src = R"(
|
||||
[[group(0), binding(0)]] var t : texture_2d<f32>;
|
||||
|
||||
fn f() {
|
||||
var v : i32;
|
||||
_ = &v;
|
||||
_ = 1;
|
||||
_ = 1 + 2;
|
||||
_ = t;
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[group(0), binding(0)]] var t : texture_2d<f32>;
|
||||
|
||||
fn f() {
|
||||
var v : i32;
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<RemovePhonies>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(RemovePhoniesTest, SingleSideEffects) {
|
||||
auto* src = R"(
|
||||
fn neg(a : i32) -> i32 {
|
||||
return -(a);
|
||||
}
|
||||
|
||||
fn add(a : i32, b : i32) -> i32 {
|
||||
return (a + b);
|
||||
}
|
||||
|
||||
fn f() {
|
||||
_ = neg(1);
|
||||
_ = add(2, 3);
|
||||
_ = add(neg(4), neg(5));
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn neg(a : i32) -> i32 {
|
||||
return -(a);
|
||||
}
|
||||
|
||||
fn add(a : i32, b : i32) -> i32 {
|
||||
return (a + b);
|
||||
}
|
||||
|
||||
fn f() {
|
||||
neg(1);
|
||||
add(2, 3);
|
||||
add(neg(4), neg(5));
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<RemovePhonies>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(RemovePhoniesTest, MultipleSideEffects) {
|
||||
auto* src = R"(
|
||||
fn neg(a : i32) -> i32 {
|
||||
return -(a);
|
||||
}
|
||||
|
||||
fn add(a : i32, b : i32) -> i32 {
|
||||
return (a + b);
|
||||
}
|
||||
|
||||
fn xor(a : u32, b : u32) -> u32 {
|
||||
return (a ^ b);
|
||||
}
|
||||
|
||||
fn f() {
|
||||
_ = (1 + add(2 + add(3, 4), 5)) * add(6, 7) * neg(8);
|
||||
_ = add(9, neg(10)) + neg(11);
|
||||
_ = xor(12u, 13u) + xor(14u, 15u);
|
||||
_ = neg(16) / neg(17) + add(18, 19);
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
fn neg(a : i32) -> i32 {
|
||||
return -(a);
|
||||
}
|
||||
|
||||
fn add(a : i32, b : i32) -> i32 {
|
||||
return (a + b);
|
||||
}
|
||||
|
||||
fn xor(a : u32, b : u32) -> u32 {
|
||||
return (a ^ b);
|
||||
}
|
||||
|
||||
fn phony_sink(p0 : i32, p1 : i32, p2 : i32) {
|
||||
}
|
||||
|
||||
fn phony_sink_1(p0 : i32, p1 : i32) {
|
||||
}
|
||||
|
||||
fn phony_sink_2(p0 : u32, p1 : u32) {
|
||||
}
|
||||
|
||||
fn f() {
|
||||
phony_sink(add((2 + add(3, 4)), 5), add(6, 7), neg(8));
|
||||
phony_sink_1(add(9, neg(10)), neg(11));
|
||||
phony_sink_2(xor(12u, 13u), xor(14u, 15u));
|
||||
phony_sink(neg(16), neg(17), add(18, 19));
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<RemovePhonies>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
TEST_F(RemovePhoniesTest, ForLoop) {
|
||||
auto* src = R"(
|
||||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]] var<storage, read_write> s : S;
|
||||
|
||||
fn x() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn y() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn z() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn f() {
|
||||
for (_ = &s.arr; ;_ = &s.arr) {
|
||||
}
|
||||
for (_ = x(); ;_ = y() + z()) {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto* expect = R"(
|
||||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]] var<storage, read_write> s : S;
|
||||
|
||||
fn x() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn y() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn z() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn phony_sink(p0 : i32, p1 : i32) {
|
||||
}
|
||||
|
||||
fn f() {
|
||||
for(; ; ) {
|
||||
}
|
||||
for(x(); ; phony_sink(y(), z())) {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
auto got = Run<RemovePhonies>(src);
|
||||
|
||||
EXPECT_EQ(expect, str(got));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace transform
|
||||
} // namespace tint
|
|
@ -75,7 +75,7 @@ bool Transform::Requires(CloneContext& ctx,
|
|||
return true;
|
||||
}
|
||||
|
||||
void Transform::RemoveStatement(CloneContext& ctx, ast::Statement* stmt) {
|
||||
void Transform::RemoveStatement(CloneContext& ctx, const ast::Statement* stmt) {
|
||||
auto* sem = ctx.src->Sem().Get(stmt);
|
||||
if (auto* block = tint::As<sem::BlockStatement>(sem->Parent())) {
|
||||
ctx.Remove(block->Declaration()->statements, stmt);
|
||||
|
|
|
@ -190,7 +190,7 @@ class Transform : public Castable<Transform> {
|
|||
/// continuing of for-loops.
|
||||
/// @param ctx the clone context
|
||||
/// @param stmt the statement to remove when the program is cloned
|
||||
static void RemoveStatement(CloneContext& ctx, ast::Statement* stmt);
|
||||
static void RemoveStatement(CloneContext& ctx, const ast::Statement* stmt);
|
||||
|
||||
/// CreateASTTypeFor constructs new ast::Type nodes that reconstructs the
|
||||
/// semantic type `ty`.
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "src/transform/num_workgroups_from_uniform.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
#include "src/utils/defer.h"
|
||||
|
@ -138,10 +139,14 @@ SanitizedResult Sanitize(const Program* in,
|
|||
manager.Add<transform::InlinePointerLets>();
|
||||
// Simplify cleans up messy `*(&(expr))` expressions from InlinePointerLets.
|
||||
manager.Add<transform::Simplify>();
|
||||
// DecomposeMemoryAccess must come after InlinePointerLets as we cannot take
|
||||
// the address of calls to DecomposeMemoryAccess::Intrinsic. Must also come
|
||||
// after Simplify, as we need to fold away the address-of and defers of
|
||||
manager.Add<transform::RemovePhonies>();
|
||||
// DecomposeMemoryAccess must come after:
|
||||
// * InlinePointerLets, as we cannot take the address of calls to
|
||||
// DecomposeMemoryAccess::Intrinsic.
|
||||
// * Simplify, as we need to fold away the address-of and dereferences of
|
||||
// `*(&(intrinsic_load()))` expressions.
|
||||
// * RemovePhonies, as phonies can be assigned a pointer to a
|
||||
// non-constructable buffer, or dynamic array, which DMA cannot cope with.
|
||||
manager.Add<transform::DecomposeMemoryAccess>();
|
||||
// CalculateArrayLength must come after DecomposeMemoryAccess, as
|
||||
// DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "src/transform/module_scope_var_to_entry_point_param.h"
|
||||
#include "src/transform/pad_array_elements.h"
|
||||
#include "src/transform/promote_initializers_to_const_var.h"
|
||||
#include "src/transform/remove_phonies.h"
|
||||
#include "src/transform/simplify.h"
|
||||
#include "src/transform/wrap_arrays_in_structs.h"
|
||||
#include "src/transform/zero_init_workgroup_memory.h"
|
||||
|
@ -145,6 +146,7 @@ SanitizedResult Sanitize(const Program* in,
|
|||
manager.Add<transform::PadArrayElements>();
|
||||
manager.Add<transform::ModuleScopeVarToEntryPointParam>();
|
||||
manager.Add<transform::InlinePointerLets>();
|
||||
manager.Add<transform::RemovePhonies>();
|
||||
manager.Add<transform::Simplify>();
|
||||
// ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
|
||||
// it assumes that the form of the array length argument is &var.array.
|
||||
|
|
|
@ -388,20 +388,28 @@ bool Builder::GenerateLabel(uint32_t id) {
|
|||
}
|
||||
|
||||
bool Builder::GenerateAssignStatement(const ast::AssignmentStatement* assign) {
|
||||
auto lhs_id = GenerateExpression(assign->lhs);
|
||||
if (lhs_id == 0) {
|
||||
return false;
|
||||
}
|
||||
auto rhs_id = GenerateExpression(assign->rhs);
|
||||
if (rhs_id == 0) {
|
||||
return false;
|
||||
}
|
||||
if (assign->lhs->Is<ast::PhonyExpression>()) {
|
||||
auto rhs_id = GenerateExpression(assign->rhs);
|
||||
if (rhs_id == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
auto lhs_id = GenerateExpression(assign->lhs);
|
||||
if (lhs_id == 0) {
|
||||
return false;
|
||||
}
|
||||
auto rhs_id = GenerateExpression(assign->rhs);
|
||||
if (rhs_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the thing we're assigning is a reference then we must load it first.
|
||||
auto* type = TypeOf(assign->rhs);
|
||||
rhs_id = GenerateLoadIfNeeded(type, rhs_id);
|
||||
// If the thing we're assigning is a reference then we must load it first.
|
||||
auto* type = TypeOf(assign->rhs);
|
||||
rhs_id = GenerateLoadIfNeeded(type, rhs_id);
|
||||
|
||||
return GenerateStore(lhs_id, rhs_id);
|
||||
return GenerateStore(lhs_id, rhs_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Builder::GenerateBreakStatement(const ast::BreakStatement*) {
|
||||
|
|
|
@ -137,6 +137,10 @@ bool GeneratorImpl::EmitExpression(std::ostream& out,
|
|||
if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
|
||||
return EmitMemberAccessor(out, m);
|
||||
}
|
||||
if (expr->Is<ast::PhonyExpression>()) {
|
||||
out << "_";
|
||||
return true;
|
||||
}
|
||||
if (auto* u = expr->As<ast::UnaryOpExpression>()) {
|
||||
return EmitUnaryOp(out, u);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = &s;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
RWByteAddressBuffer s : register(u0, space0);
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
struct S {
|
||||
/* 0x0000 */ int arr[1];
|
||||
};
|
||||
|
||||
kernel void tint_symbol() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 11
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %S "S"
|
||||
OpMemberName %S 0 "arr"
|
||||
OpName %s "s"
|
||||
OpName %main "main"
|
||||
OpDecorate %S Block
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpDecorate %_runtimearr_int ArrayStride 4
|
||||
OpDecorate %s Binding 0
|
||||
OpDecorate %s DescriptorSet 0
|
||||
%int = OpTypeInt 32 1
|
||||
%_runtimearr_int = OpTypeRuntimeArray %int
|
||||
%S = OpTypeStruct %_runtimearr_int
|
||||
%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
|
||||
%s = OpVariable %_ptr_StorageBuffer_S StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%main = OpFunction %void None %6
|
||||
%9 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,11 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = &(s);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = &s.arr;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
RWByteAddressBuffer s : register(u0, space0);
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
struct S {
|
||||
/* 0x0000 */ int arr[1];
|
||||
};
|
||||
|
||||
kernel void tint_symbol() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 15
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %S "S"
|
||||
OpMemberName %S 0 "arr"
|
||||
OpName %s "s"
|
||||
OpName %main "main"
|
||||
OpDecorate %S Block
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpDecorate %_runtimearr_int ArrayStride 4
|
||||
OpDecorate %s Binding 0
|
||||
OpDecorate %s DescriptorSet 0
|
||||
%int = OpTypeInt 32 1
|
||||
%_runtimearr_int = OpTypeRuntimeArray %int
|
||||
%S = OpTypeStruct %_runtimearr_int
|
||||
%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
|
||||
%s = OpVariable %_ptr_StorageBuffer_S StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_ptr_StorageBuffer__runtimearr_int = OpTypePointer StorageBuffer %_runtimearr_int
|
||||
%main = OpFunction %void None %6
|
||||
%9 = OpLabel
|
||||
%14 = OpAccessChain %_ptr_StorageBuffer__runtimearr_int %s %uint_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,11 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
arr : array<i32>;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = &(s.arr);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fn f(a: i32, b: i32, c: i32) -> i32 {
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = f(1, 2, 3);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
int f(int a, int b, int c) {
|
||||
return ((a * b) + c);
|
||||
}
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
(void) f(1, 2, 3);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
int f(int a, int b, int c) {
|
||||
return as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) * as_type<uint>(b)))) + as_type<uint>(c)));
|
||||
}
|
||||
|
||||
kernel void tint_symbol() {
|
||||
f(1, 2, 3);
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 18
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %f "f"
|
||||
OpName %a "a"
|
||||
OpName %b "b"
|
||||
OpName %c "c"
|
||||
OpName %main "main"
|
||||
%int = OpTypeInt 32 1
|
||||
%1 = OpTypeFunction %int %int %int %int
|
||||
%void = OpTypeVoid
|
||||
%10 = OpTypeFunction %void
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_3 = OpConstant %int 3
|
||||
%f = OpFunction %int None %1
|
||||
%a = OpFunctionParameter %int
|
||||
%b = OpFunctionParameter %int
|
||||
%c = OpFunctionParameter %int
|
||||
%7 = OpLabel
|
||||
%8 = OpIMul %int %a %b
|
||||
%9 = OpIAdd %int %8 %c
|
||||
OpReturnValue %9
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %10
|
||||
%13 = OpLabel
|
||||
%14 = OpFunctionCall %int %f %int_1 %int_2 %int_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,8 @@
|
|||
fn f(a : i32, b : i32, c : i32) -> i32 {
|
||||
return ((a * b) + c);
|
||||
}
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = f(1, 2, 3);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fn f(a: i32, b: i32, c: i32) -> i32 {
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = f(1, 2, 3) + f(4, 5, 6) * f(7, f(8, 9, 10), 11);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
int f(int a, int b, int c) {
|
||||
return ((a * b) + c);
|
||||
}
|
||||
|
||||
void phony_sink(int p0, int p1, int p2) {
|
||||
}
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
phony_sink(f(1, 2, 3), f(4, 5, 6), f(7, f(8, 9, 10), 11));
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
int f(int a, int b, int c) {
|
||||
return as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(a) * as_type<uint>(b)))) + as_type<uint>(c)));
|
||||
}
|
||||
|
||||
void phony_sink(int p0, int p1, int p2) {
|
||||
}
|
||||
|
||||
kernel void tint_symbol() {
|
||||
phony_sink(f(1, 2, 3), f(4, 5, 6), f(7, f(8, 9, 10), 11));
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 31
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %f "f"
|
||||
OpName %a "a"
|
||||
OpName %b "b"
|
||||
OpName %c "c"
|
||||
OpName %main "main"
|
||||
%int = OpTypeInt 32 1
|
||||
%1 = OpTypeFunction %int %int %int %int
|
||||
%void = OpTypeVoid
|
||||
%10 = OpTypeFunction %void
|
||||
%int_1 = OpConstant %int 1
|
||||
%int_2 = OpConstant %int 2
|
||||
%int_3 = OpConstant %int 3
|
||||
%int_4 = OpConstant %int 4
|
||||
%int_5 = OpConstant %int 5
|
||||
%int_6 = OpConstant %int 6
|
||||
%int_7 = OpConstant %int 7
|
||||
%int_8 = OpConstant %int 8
|
||||
%int_9 = OpConstant %int 9
|
||||
%int_10 = OpConstant %int 10
|
||||
%int_11 = OpConstant %int 11
|
||||
%f = OpFunction %int None %1
|
||||
%a = OpFunctionParameter %int
|
||||
%b = OpFunctionParameter %int
|
||||
%c = OpFunctionParameter %int
|
||||
%7 = OpLabel
|
||||
%8 = OpIMul %int %a %b
|
||||
%9 = OpIAdd %int %8 %c
|
||||
OpReturnValue %9
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %10
|
||||
%13 = OpLabel
|
||||
%14 = OpFunctionCall %int %f %int_1 %int_2 %int_3
|
||||
%18 = OpFunctionCall %int %f %int_4 %int_5 %int_6
|
||||
%24 = OpFunctionCall %int %f %int_8 %int_9 %int_10
|
||||
%22 = OpFunctionCall %int %f %int_7 %24 %int_11
|
||||
%29 = OpIMul %int %18 %22
|
||||
%30 = OpIAdd %int %14 %29
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,8 @@
|
|||
fn f(a : i32, b : i32, c : i32) -> i32 {
|
||||
return ((a * b) + c);
|
||||
}
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = (f(1, 2, 3) + (f(4, 5, 6) * f(7, f(8, 9, 10), 11)));
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
i : i32;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = s;
|
||||
_ = s.i;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
RWByteAddressBuffer s : register(u0, space0);
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
struct S {
|
||||
/* 0x0000 */ int i;
|
||||
};
|
||||
|
||||
kernel void tint_symbol() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 13
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %S "S"
|
||||
OpMemberName %S 0 "i"
|
||||
OpName %s "s"
|
||||
OpName %main "main"
|
||||
OpDecorate %S Block
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpDecorate %s Binding 0
|
||||
OpDecorate %s DescriptorSet 0
|
||||
%int = OpTypeInt 32 1
|
||||
%S = OpTypeStruct %int
|
||||
%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
|
||||
%s = OpVariable %_ptr_StorageBuffer_S StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%5 = OpTypeFunction %void
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
|
||||
%main = OpFunction %void None %5
|
||||
%8 = OpLabel
|
||||
%12 = OpAccessChain %_ptr_StorageBuffer_int %s %uint_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,12 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
i : i32;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<storage, read_write> s : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = s;
|
||||
_ = s.i;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
i : i32;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<uniform> u : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = u;
|
||||
_ = u.i;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
cbuffer cbuffer_u : register(b0, space0) {
|
||||
uint4 u[1];
|
||||
};
|
||||
|
||||
[numthreads(1, 1, 1)]
|
||||
void main() {
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
struct S {
|
||||
/* 0x0000 */ int i;
|
||||
};
|
||||
|
||||
kernel void tint_symbol() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 13
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %S "S"
|
||||
OpMemberName %S 0 "i"
|
||||
OpName %u "u"
|
||||
OpName %main "main"
|
||||
OpDecorate %S Block
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpDecorate %u NonWritable
|
||||
OpDecorate %u Binding 0
|
||||
OpDecorate %u DescriptorSet 0
|
||||
%int = OpTypeInt 32 1
|
||||
%S = OpTypeStruct %int
|
||||
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||
%u = OpVariable %_ptr_Uniform_S Uniform
|
||||
%void = OpTypeVoid
|
||||
%5 = OpTypeFunction %void
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_0 = OpConstant %uint 0
|
||||
%_ptr_Uniform_int = OpTypePointer Uniform %int
|
||||
%main = OpFunction %void None %5
|
||||
%8 = OpLabel
|
||||
%12 = OpAccessChain %_ptr_Uniform_int %u %uint_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,12 @@
|
|||
[[block]]
|
||||
struct S {
|
||||
i : i32;
|
||||
};
|
||||
|
||||
[[binding(0), group(0)]] var<uniform> u : S;
|
||||
|
||||
[[stage(compute), workgroup_size(1)]]
|
||||
fn main() {
|
||||
_ = u;
|
||||
_ = u.i;
|
||||
}
|
Loading…
Reference in New Issue