hlsl: Pointer support
Add `transform::InlinePointerLets` - a Transform that moves all usage of function-scope `let` statements of a pointer type into their places of usage. Make the HLSL writer transform pointer parameters to `inout`. Fixed: tint:183 Change-Id: I0a7552fa6cd31c7b7691e64feae3170a81cc6c49 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51281 Commit-Queue: Ben Clayton <bclayton@google.com> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
c9dc951e9e
commit
06aa88aa97
|
@ -513,6 +513,8 @@ libtint_source_set("libtint_core_all_src") {
|
||||||
"transform/external_texture_transform.h",
|
"transform/external_texture_transform.h",
|
||||||
"transform/first_index_offset.cc",
|
"transform/first_index_offset.cc",
|
||||||
"transform/first_index_offset.h",
|
"transform/first_index_offset.h",
|
||||||
|
"transform/inline_pointer_lets.cc",
|
||||||
|
"transform/inline_pointer_lets.h",
|
||||||
"transform/manager.cc",
|
"transform/manager.cc",
|
||||||
"transform/manager.h",
|
"transform/manager.h",
|
||||||
"transform/renamer.cc",
|
"transform/renamer.cc",
|
||||||
|
|
|
@ -277,6 +277,8 @@ set(TINT_LIB_SRCS
|
||||||
transform/external_texture_transform.h
|
transform/external_texture_transform.h
|
||||||
transform/first_index_offset.cc
|
transform/first_index_offset.cc
|
||||||
transform/first_index_offset.h
|
transform/first_index_offset.h
|
||||||
|
transform/inline_pointer_lets.cc
|
||||||
|
transform/inline_pointer_lets.h
|
||||||
transform/manager.cc
|
transform/manager.cc
|
||||||
transform/manager.h
|
transform/manager.h
|
||||||
transform/renamer.cc
|
transform/renamer.cc
|
||||||
|
@ -804,6 +806,7 @@ if(${TINT_BUILD_TESTS})
|
||||||
transform/decompose_storage_access_test.cc
|
transform/decompose_storage_access_test.cc
|
||||||
transform/external_texture_transform_test.cc
|
transform/external_texture_transform_test.cc
|
||||||
transform/first_index_offset_test.cc
|
transform/first_index_offset_test.cc
|
||||||
|
transform/inline_pointer_lets_test.cc
|
||||||
transform/renamer_test.cc
|
transform/renamer_test.cc
|
||||||
transform/single_entry_point.cc
|
transform/single_entry_point.cc
|
||||||
transform/test_helper.h
|
transform/test_helper.h
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "src/transform/canonicalize_entry_point_io.h"
|
#include "src/transform/canonicalize_entry_point_io.h"
|
||||||
#include "src/transform/decompose_storage_access.h"
|
#include "src/transform/decompose_storage_access.h"
|
||||||
#include "src/transform/external_texture_transform.h"
|
#include "src/transform/external_texture_transform.h"
|
||||||
|
#include "src/transform/inline_pointer_lets.h"
|
||||||
#include "src/transform/manager.h"
|
#include "src/transform/manager.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
@ -41,6 +42,7 @@ Output Hlsl::Run(const Program* in, const DataMap& data) {
|
||||||
manager.Add<DecomposeStorageAccess>();
|
manager.Add<DecomposeStorageAccess>();
|
||||||
manager.Add<CalculateArrayLength>();
|
manager.Add<CalculateArrayLength>();
|
||||||
manager.Add<ExternalTextureTransform>();
|
manager.Add<ExternalTextureTransform>();
|
||||||
|
manager.Add<InlinePointerLets>();
|
||||||
auto out = manager.Run(in, data);
|
auto out = manager.Run(in, data);
|
||||||
if (!out.program.IsValid()) {
|
if (!out.program.IsValid()) {
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
// 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/inline_pointer_lets.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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/scoped_assignment.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// Traverses the expression `expr` looking for non-literal array indexing
|
||||||
|
/// expressions that would affect the computed address of a pointer expression.
|
||||||
|
/// The function-like argument `cb` is called for each found.
|
||||||
|
/// @param program the program that owns all the expression nodes
|
||||||
|
/// @param expr the expression to traverse
|
||||||
|
/// @param cb a function-like object with the signature
|
||||||
|
/// `void(const ast::Expression*)`, which is called for each array index
|
||||||
|
/// expression
|
||||||
|
template <typename F>
|
||||||
|
void CollectSavedArrayIndices(const Program* program,
|
||||||
|
ast::Expression* expr,
|
||||||
|
F&& cb) {
|
||||||
|
if (auto* a = expr->As<ast::ArrayAccessorExpression>()) {
|
||||||
|
CollectSavedArrayIndices(program, a->array(), cb);
|
||||||
|
|
||||||
|
if (!a->idx_expr()->Is<ast::ScalarConstructorExpression>()) {
|
||||||
|
cb(a->idx_expr());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
|
||||||
|
CollectSavedArrayIndices(program, m->structure(), cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto* u = expr->As<ast::UnaryOpExpression>()) {
|
||||||
|
CollectSavedArrayIndices(program, u->expr(), cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Other ast::Expression types can be safely ignored as they cannot be
|
||||||
|
// used to generate a reference or pointer.
|
||||||
|
// See https://gpuweb.github.io/gpuweb/wgsl/#forming-references-and-pointers
|
||||||
|
}
|
||||||
|
|
||||||
|
// PtrLet represents a `let` declaration of a pointer type.
|
||||||
|
struct PtrLet {
|
||||||
|
// A map of ptr-let initializer sub-expression to the name of generated
|
||||||
|
// variable that holds the saved value of this sub-expression, when resolved
|
||||||
|
// at the point of the ptr-let declaration.
|
||||||
|
std::unordered_map<const ast::Expression*, Symbol> saved_vars;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
InlinePointerLets::InlinePointerLets() = default;
|
||||||
|
|
||||||
|
InlinePointerLets::~InlinePointerLets() = default;
|
||||||
|
|
||||||
|
Output InlinePointerLets::Run(const Program* in, const DataMap&) {
|
||||||
|
ProgramBuilder out;
|
||||||
|
CloneContext ctx(&out, in);
|
||||||
|
|
||||||
|
// If not null, current_ptr_let is the current PtrLet being operated on.
|
||||||
|
PtrLet* current_ptr_let = nullptr;
|
||||||
|
// A map of the AST `let` variable to the PtrLet
|
||||||
|
std::unordered_map<const ast::Variable*, std::unique_ptr<PtrLet>> ptr_lets;
|
||||||
|
|
||||||
|
// Register the ast::Expression transform handler.
|
||||||
|
// This performs two different transformations:
|
||||||
|
// * Identifiers that resolve to the pointer-typed `let` declarations are
|
||||||
|
// replaced with the inlined (and recursively transformed) initializer
|
||||||
|
// expression for the `let` declaration.
|
||||||
|
// * Sub-expressions inside the pointer-typed `let` initializer expression
|
||||||
|
// that have been hoisted to a saved variable are replaced with the saved
|
||||||
|
// variable identifier.
|
||||||
|
ctx.ReplaceAll([&](ast::Expression* expr) -> ast::Expression* {
|
||||||
|
if (current_ptr_let) {
|
||||||
|
// We're currently processing the initializer expression of a
|
||||||
|
// pointer-typed `let` declaration. Look to see if we need to swap this
|
||||||
|
// Expression with a saved variable.
|
||||||
|
auto it = current_ptr_let->saved_vars.find(expr);
|
||||||
|
if (it != current_ptr_let->saved_vars.end()) {
|
||||||
|
return ctx.dst->Expr(it->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto* ident = expr->As<ast::IdentifierExpression>()) {
|
||||||
|
if (auto* vu = in->Sem().Get<sem::VariableUser>(ident)) {
|
||||||
|
auto* var = vu->Variable()->Declaration();
|
||||||
|
auto it = ptr_lets.find(var);
|
||||||
|
if (it != ptr_lets.end()) {
|
||||||
|
// We've found an identifier that resolves to a `let` declaration.
|
||||||
|
// We need to replace this identifier with the initializer expression
|
||||||
|
// of the `let` declaration. Clone the initializer expression to make
|
||||||
|
// a copy. Note that this will call back into this ReplaceAll()
|
||||||
|
// handler for sub-expressions of the initializer.
|
||||||
|
auto* ptr_let = it->second.get();
|
||||||
|
// TINT_SCOPED_ASSIGNMENT provides a stack of PtrLet*, this is
|
||||||
|
// required to handle the 'chaining' of inlined `let`s.
|
||||||
|
TINT_SCOPED_ASSIGNMENT(current_ptr_let, ptr_let);
|
||||||
|
return ctx.Clone(var->constructor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find all the pointer-typed `let` declarations.
|
||||||
|
// Note that these must be function-scoped, as module-scoped `let`s are not
|
||||||
|
// permitted.
|
||||||
|
for (auto* node : in->ASTNodes().Objects()) {
|
||||||
|
if (auto* let = node->As<ast::VariableDeclStatement>()) {
|
||||||
|
if (!let->variable()->is_const()) {
|
||||||
|
continue; // Not a `let` declaration. Ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* var = in->Sem().Get(let->variable());
|
||||||
|
if (!var->Type()->Is<sem::Pointer>()) {
|
||||||
|
continue; // Not a pointer type. Ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're dealing with a pointer-typed `let` declaration.
|
||||||
|
auto ptr_let = std::make_unique<PtrLet>();
|
||||||
|
TINT_SCOPED_ASSIGNMENT(current_ptr_let, ptr_let.get());
|
||||||
|
|
||||||
|
auto* block = ctx.src->Sem().Get(let)->Block()->Declaration();
|
||||||
|
|
||||||
|
// Scan the initializer expression for array index expressions that need
|
||||||
|
// to be hoist to temporary "saved" variables.
|
||||||
|
CollectSavedArrayIndices(
|
||||||
|
ctx.src, var->Declaration()->constructor(),
|
||||||
|
[&](ast::Expression* idx_expr) {
|
||||||
|
// We have a sub-expression that needs to be saved.
|
||||||
|
// Create a new variable
|
||||||
|
auto saved_name = ctx.dst->Symbols().New(
|
||||||
|
ctx.src->Symbols().NameFor(var->Declaration()->symbol()) +
|
||||||
|
"_save");
|
||||||
|
auto* saved = ctx.dst->Decl(
|
||||||
|
ctx.dst->Const(saved_name, nullptr, ctx.Clone(idx_expr)));
|
||||||
|
// Place this variable after the pointer typed let. Order here is
|
||||||
|
// important as order-of-operations needs to be preserved.
|
||||||
|
// CollectSavedArrayIndices() visits the LHS of an array accessor
|
||||||
|
// before the index expression.
|
||||||
|
// Note that repeated calls to InsertAfter() with the same `after`
|
||||||
|
// argument will result in nodes to inserted in the order the calls
|
||||||
|
// are made (last call is inserted last).
|
||||||
|
ctx.InsertAfter(block->statements(), let, saved);
|
||||||
|
// Record the substitution of `idx_expr` to the saved variable with
|
||||||
|
// the symbol `saved_name`. This will be used by the ReplaceAll()
|
||||||
|
// handler above.
|
||||||
|
ptr_let->saved_vars.emplace(idx_expr, saved_name);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Record the pointer-typed `let` declaration.
|
||||||
|
// This will be used by the ReplaceAll() handler above.
|
||||||
|
ptr_lets.emplace(let->variable(), std::move(ptr_let));
|
||||||
|
// As the original `let` declaration will be fully inlined, there's no
|
||||||
|
// need for the original declaration to exist. Remove it.
|
||||||
|
ctx.Remove(block->statements(), let);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Clone();
|
||||||
|
|
||||||
|
return Output(Program(std::move(out)));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -0,0 +1,52 @@
|
||||||
|
// 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_INLINE_POINTER_LETS_H_
|
||||||
|
#define SRC_TRANSFORM_INLINE_POINTER_LETS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "src/transform/transform.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
|
||||||
|
/// InlinePointerLets is a Transform that moves all usage of function-scope
|
||||||
|
/// `let` statements of a pointer type into their places of usage.
|
||||||
|
///
|
||||||
|
/// Parameters of a pointer type are not adjusted.
|
||||||
|
///
|
||||||
|
/// Note: InlinePointerLets does not operate on module-scope `let`s, as these
|
||||||
|
/// cannot be pointers: https://gpuweb.github.io/gpuweb/wgsl/#module-constants
|
||||||
|
/// `A module-scope let-declared constant must be of atomic-free plain type.`
|
||||||
|
class InlinePointerLets : public Transform {
|
||||||
|
public:
|
||||||
|
/// Constructor
|
||||||
|
InlinePointerLets();
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~InlinePointerLets() override;
|
||||||
|
|
||||||
|
/// Runs the transform on `program`, returning the transformation result.
|
||||||
|
/// @param program the source program to transform
|
||||||
|
/// @param data optional extra transform-specific input data
|
||||||
|
/// @returns the transformation result
|
||||||
|
Output Run(const Program* program, const DataMap& data = {}) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
||||||
|
|
||||||
|
#endif // SRC_TRANSFORM_INLINE_POINTER_LETS_H_
|
|
@ -0,0 +1,323 @@
|
||||||
|
// 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/inline_pointer_lets.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/transform/test_helper.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace transform {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using InlinePointerLetsTest = TransformTest;
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, EmptyModule) {
|
||||||
|
auto* src = "";
|
||||||
|
auto* expect = "";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, Basic) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var v : i32;
|
||||||
|
let p : ptr<function, i32> = &v;
|
||||||
|
let x : i32 = *p;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var v : i32;
|
||||||
|
let x : i32 = *(&(v));
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, ComplexChain) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var m : mat4x4<f32>;
|
||||||
|
let mp : ptr<function, mat4x4<f32>> = &m;
|
||||||
|
let vp : ptr<function, vec4<f32>> = &(*mp)[2];
|
||||||
|
let fp : ptr<function, f32> = &(*vp)[1];
|
||||||
|
let f : f32 = *fp;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var m : mat4x4<f32>;
|
||||||
|
let f : f32 = *(&(*(&(*(&(m))[2]))[1]));
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, Param) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn x(p : ptr<function, i32>) -> i32 {
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var v : i32;
|
||||||
|
let p : ptr<function, i32> = &v;
|
||||||
|
var r : i32 = x(p);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn x(p : ptr<function, i32>) -> i32 {
|
||||||
|
return *(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var v : i32;
|
||||||
|
var r : i32 = x(&(v));
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, SavedVars) {
|
||||||
|
auto* src = R"(
|
||||||
|
struct S {
|
||||||
|
i : i32;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn arr() {
|
||||||
|
var a : array<S, 2>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p : ptr<function, i32> = &a[i + j].i;
|
||||||
|
i = 2;
|
||||||
|
*p = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec() {
|
||||||
|
var v : vec3<f32>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p : ptr<function, f32> = &v[i + j];
|
||||||
|
i = 2;
|
||||||
|
*p = 4.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mat() {
|
||||||
|
var m : mat3x3<f32>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p : ptr<function, vec3<f32>> = &m[i + j];
|
||||||
|
i = 2;
|
||||||
|
*p = vec3<f32>(4.0, 5.0, 6.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
struct S {
|
||||||
|
i : i32;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn arr() {
|
||||||
|
var a : array<S, 2>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p_save = (i + j);
|
||||||
|
i = 2;
|
||||||
|
*(&(a[p_save].i)) = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec() {
|
||||||
|
var v : vec3<f32>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p_save_1 = (i + j);
|
||||||
|
i = 2;
|
||||||
|
*(&(v[p_save_1])) = 4.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mat() {
|
||||||
|
var m : mat3x3<f32>;
|
||||||
|
var i : i32 = 0;
|
||||||
|
var j : i32 = 0;
|
||||||
|
let p_save_2 = (i + j);
|
||||||
|
i = 2;
|
||||||
|
*(&(m[p_save_2])) = vec3<f32>(4.0, 5.0, 6.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, DontSaveLiterals) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var arr : array<i32, 2>;
|
||||||
|
let p1 : ptr<function, i32> = &arr[1];
|
||||||
|
*p1 = 4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var arr : array<i32, 2>;
|
||||||
|
*(&(arr[1])) = 4;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, SavedVarsChain) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn f() {
|
||||||
|
var arr : array<array<i32, 2>, 2>;
|
||||||
|
let i : i32 = 0;
|
||||||
|
let j : i32 = 1;
|
||||||
|
let p : ptr<function, array<i32, 2>> = &arr[i];
|
||||||
|
let q : ptr<function, i32> = &(*p)[j];
|
||||||
|
*q = 12;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn f() {
|
||||||
|
var arr : array<array<i32, 2>, 2>;
|
||||||
|
let i : i32 = 0;
|
||||||
|
let j : i32 = 1;
|
||||||
|
let p_save = i;
|
||||||
|
let q_save = j;
|
||||||
|
*(&(*(&(arr[p_save]))[q_save])) = 12;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InlinePointerLetsTest, MultiSavedVarsInSinglePtrLetExpr) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn x() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn z() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
a : array<i32, 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Outer {
|
||||||
|
a : array<Inner, 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var arr : array<Outer, 2>;
|
||||||
|
let p : ptr<function, i32> = &arr[x()].a[y()].a[z()];
|
||||||
|
*p = 1;
|
||||||
|
*p = 2;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(
|
||||||
|
fn x() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn z() -> i32 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
a : array<i32, 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Outer {
|
||||||
|
a : array<Inner, 2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var arr : array<Outer, 2>;
|
||||||
|
let p_save = x();
|
||||||
|
let p_save_1 = y();
|
||||||
|
let p_save_2 = z();
|
||||||
|
*(&(arr[p_save].a[p_save_1].a[p_save_2])) = 1;
|
||||||
|
*(&(arr[p_save].a[p_save_1].a[p_save_2])) = 2;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(crbug.com/tint/819): Enable when we support inter-scope shadowing.
|
||||||
|
TEST_F(InlinePointerLetsTest, DISABLED_ModificationAfterInline) {
|
||||||
|
auto* src = R"(
|
||||||
|
fn x(p : ptr<function, i32>) -> i32 {
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
var i : i32 = 1;
|
||||||
|
let p : ptr<function, i32> = &i;
|
||||||
|
if (true) {
|
||||||
|
var i : i32 = 2;
|
||||||
|
x(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto* expect = R"(<TODO>)";
|
||||||
|
|
||||||
|
auto got = Run<InlinePointerLets>(src);
|
||||||
|
|
||||||
|
EXPECT_EQ(expect, str(got));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
} // namespace transform
|
||||||
|
} // namespace tint
|
|
@ -1625,7 +1625,16 @@ bool GeneratorImpl::EmitFunctionInternal(std::ostream& out,
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
auto* type = v->Type();
|
auto const* type = v->Type();
|
||||||
|
|
||||||
|
if (auto* ptr = type->As<sem::Pointer>()) {
|
||||||
|
// Transform pointer parameters in to `inout` parameters.
|
||||||
|
// The WGSL spec is highly restrictive in what can be passed in pointer
|
||||||
|
// parameters, which allows for this transformation. See:
|
||||||
|
// https://gpuweb.github.io/gpuweb/wgsl/#function-restriction
|
||||||
|
out << "inout ";
|
||||||
|
type = ptr->StoreType();
|
||||||
|
}
|
||||||
|
|
||||||
// Note: WGSL only allows for StorageClass::kNone on parameters, however the
|
// Note: WGSL only allows for StorageClass::kNone on parameters, however the
|
||||||
// sanitizer transforms generates load / store functions for storage
|
// sanitizer transforms generates load / store functions for storage
|
||||||
|
@ -2458,9 +2467,9 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
|
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
|
||||||
out << mat->columns() << "x" << mat->rows();
|
out << mat->columns() << "x" << mat->rows();
|
||||||
} else if (type->Is<sem::Pointer>()) {
|
} else if (type->Is<sem::Pointer>()) {
|
||||||
// TODO(dsinclair): What do we do with pointers in HLSL?
|
TINT_ICE(diagnostics_)
|
||||||
// https://bugs.chromium.org/p/tint/issues/detail?id=183
|
<< "Attempting to emit pointer type. These should have been removed "
|
||||||
diagnostics_.add_error("pointers not supported in HLSL");
|
"with the InlinePointerLets transform";
|
||||||
return false;
|
return false;
|
||||||
} else if (auto* sampler = type->As<sem::Sampler>()) {
|
} else if (auto* sampler = type->As<sem::Sampler>()) {
|
||||||
out << "Sampler";
|
out << "Sampler";
|
||||||
|
@ -2634,7 +2643,6 @@ bool GeneratorImpl::EmitUnaryOp(std::ostream& pre,
|
||||||
switch (expr->op()) {
|
switch (expr->op()) {
|
||||||
case ast::UnaryOp::kIndirection:
|
case ast::UnaryOp::kIndirection:
|
||||||
case ast::UnaryOp::kAddressOf:
|
case ast::UnaryOp::kAddressOf:
|
||||||
// TODO(crbug.com/tint/183) - support pointers
|
|
||||||
return EmitExpression(pre, out, expr->expr());
|
return EmitExpression(pre, out, expr->expr());
|
||||||
case ast::UnaryOp::kNot:
|
case ast::UnaryOp::kNot:
|
||||||
out << "!";
|
out << "!";
|
||||||
|
|
|
@ -100,6 +100,25 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||||
Validate();
|
Validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest_Function, PtrParameter) {
|
||||||
|
// fn f(foo : ptr<function, f32>) -> f32 {
|
||||||
|
// return *foo;
|
||||||
|
// }
|
||||||
|
Func("f", {Param("foo", ty.pointer<f32>(ast::StorageClass::kFunction))},
|
||||||
|
ty.f32(), {Return(Deref("foo"))});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = SanitizeAndBuild();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||||
|
EXPECT_THAT(result(), HasSubstr(R"(float f(inout float foo) {
|
||||||
|
return foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
)"));
|
||||||
|
|
||||||
|
Validate();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Function,
|
TEST_F(HlslGeneratorImplTest_Function,
|
||||||
Emit_Decoration_EntryPoint_WithInOutVars) {
|
Emit_Decoration_EntryPoint_WithInOutVars) {
|
||||||
// fn frag_main([[location(0)]] foo : f32) -> [[location(1)]] f32 {
|
// fn frag_main([[location(0)]] foo : f32) -> [[location(1)]] f32 {
|
||||||
|
|
|
@ -36,17 +36,17 @@ TEST_F(HlslSanitizerTest, ArrayLength) {
|
||||||
auto* ac_ty = ty.access(ast::AccessControl::kReadOnly, sb_ty);
|
auto* ac_ty = ty.access(ast::AccessControl::kReadOnly, sb_ty);
|
||||||
|
|
||||||
Global("sb", ac_ty, ast::StorageClass::kStorage, nullptr,
|
Global("sb", ac_ty, ast::StorageClass::kStorage, nullptr,
|
||||||
ast::DecorationList{
|
{
|
||||||
create<ast::BindingDecoration>(0),
|
create<ast::BindingDecoration>(0),
|
||||||
create<ast::GroupDecoration>(1),
|
create<ast::GroupDecoration>(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(),
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
|
Decl(Var("len", ty.u32(), ast::StorageClass::kNone,
|
||||||
Call("arrayLength", MemberAccessor("sb", "arr")))),
|
Call("arrayLength", MemberAccessor("sb", "arr")))),
|
||||||
},
|
},
|
||||||
ast::DecorationList{
|
{
|
||||||
Stage(ast::PipelineStage::kFragment),
|
Stage(ast::PipelineStage::kFragment),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,10 +76,10 @@ TEST_F(HlslSanitizerTest, PromoteArrayInitializerToConstVar) {
|
||||||
auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
|
auto* pos = Var("pos", ty.i32(), ast::StorageClass::kNone, array_index);
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(),
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(pos),
|
Decl(pos),
|
||||||
},
|
},
|
||||||
ast::DecorationList{
|
{
|
||||||
Stage(ast::PipelineStage::kFragment),
|
Stage(ast::PipelineStage::kFragment),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -110,10 +110,10 @@ TEST_F(HlslSanitizerTest, PromoteStructInitializerToConstVar) {
|
||||||
Var("pos", ty.vec3<f32>(), ast::StorageClass::kNone, struct_access);
|
Var("pos", ty.vec3<f32>(), ast::StorageClass::kNone, struct_access);
|
||||||
|
|
||||||
Func("main", ast::VariableList{}, ty.void_(),
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
ast::StatementList{
|
{
|
||||||
Decl(pos),
|
Decl(pos),
|
||||||
},
|
},
|
||||||
ast::DecorationList{
|
{
|
||||||
Stage(ast::PipelineStage::kFragment),
|
Stage(ast::PipelineStage::kFragment),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -137,6 +137,134 @@ void main() {
|
||||||
)";
|
)";
|
||||||
EXPECT_EQ(expect, got);
|
EXPECT_EQ(expect, got);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslSanitizerTest, InlinePtrLetsBasic) {
|
||||||
|
// var v : i32;
|
||||||
|
// let p : ptr<function, i32> = &v;
|
||||||
|
// let x : i32 = *p;
|
||||||
|
auto* v = Var("v", ty.i32());
|
||||||
|
auto* p =
|
||||||
|
Const("p", ty.pointer<i32>(ast::StorageClass::kFunction), AddressOf(v));
|
||||||
|
auto* x = Var("x", ty.i32(), ast::StorageClass::kNone, Deref(p));
|
||||||
|
|
||||||
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
|
{
|
||||||
|
Decl(v),
|
||||||
|
Decl(p),
|
||||||
|
Decl(x),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Stage(ast::PipelineStage::kFragment),
|
||||||
|
});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = SanitizeAndBuild();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||||
|
|
||||||
|
auto got = result();
|
||||||
|
auto* expect = R"(void main() {
|
||||||
|
int v = 0;
|
||||||
|
int x = v;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
)";
|
||||||
|
EXPECT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslSanitizerTest, InlinePtrLetsComplexChain) {
|
||||||
|
// var m : mat4x4<f32>;
|
||||||
|
// let mp : ptr<function, mat4x4<f32>> = &m;
|
||||||
|
// let vp : ptr<function, vec4<f32>> = &(*mp)[2];
|
||||||
|
// let fp : ptr<function, f32> = &(*vp)[1];
|
||||||
|
// let f : f32 = *fp;
|
||||||
|
auto* m = Var("m", ty.mat4x4<f32>());
|
||||||
|
auto* mp =
|
||||||
|
Const("mp", ty.pointer(ty.mat4x4<f32>(), ast::StorageClass::kFunction),
|
||||||
|
AddressOf(m));
|
||||||
|
auto* vp =
|
||||||
|
Const("vp", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction),
|
||||||
|
AddressOf(IndexAccessor(Deref(mp), 2)));
|
||||||
|
auto* fp = Const("fp", ty.pointer<f32>(ast::StorageClass::kFunction),
|
||||||
|
AddressOf(IndexAccessor(Deref(vp), 1)));
|
||||||
|
auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, Deref(fp));
|
||||||
|
|
||||||
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
|
{
|
||||||
|
Decl(m),
|
||||||
|
Decl(mp),
|
||||||
|
Decl(vp),
|
||||||
|
Decl(fp),
|
||||||
|
Decl(f),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Stage(ast::PipelineStage::kFragment),
|
||||||
|
});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = SanitizeAndBuild();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||||
|
|
||||||
|
auto got = result();
|
||||||
|
auto* expect = R"(void main() {
|
||||||
|
float4x4 m = float4x4(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
float f = m[2][1];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
)";
|
||||||
|
EXPECT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslSanitizerTest, InlineParam) {
|
||||||
|
// fn x(p : ptr<function, i32>) -> i32 {
|
||||||
|
// return *p;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [[stage(fragment)]]
|
||||||
|
// fn main() {
|
||||||
|
// var v : i32;
|
||||||
|
// let p : ptr<function, i32> = &v;
|
||||||
|
// var r : i32 = x(p);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Func("x", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
|
||||||
|
ty.i32(), {Return(Deref("p"))});
|
||||||
|
|
||||||
|
auto* v = Var("v", ty.i32());
|
||||||
|
auto* p = Const("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction),
|
||||||
|
AddressOf(v));
|
||||||
|
auto* r = Var("r", ty.i32(), ast::StorageClass::kNone, Call("x", p));
|
||||||
|
|
||||||
|
Func("main", ast::VariableList{}, ty.void_(),
|
||||||
|
{
|
||||||
|
Decl(v),
|
||||||
|
Decl(p),
|
||||||
|
Decl(r),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Stage(ast::PipelineStage::kFragment),
|
||||||
|
});
|
||||||
|
|
||||||
|
GeneratorImpl& gen = SanitizeAndBuild();
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen.Generate(out)) << gen.error();
|
||||||
|
|
||||||
|
auto got = result();
|
||||||
|
auto* expect = R"(int x(inout int p) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
int v = 0;
|
||||||
|
int r = x(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
)";
|
||||||
|
EXPECT_EQ(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace hlsl
|
} // namespace hlsl
|
||||||
} // namespace writer
|
} // namespace writer
|
||||||
|
|
|
@ -301,6 +301,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
|
||||||
"../src/transform/decompose_storage_access_test.cc",
|
"../src/transform/decompose_storage_access_test.cc",
|
||||||
"../src/transform/external_texture_transform_test.cc",
|
"../src/transform/external_texture_transform_test.cc",
|
||||||
"../src/transform/first_index_offset_test.cc",
|
"../src/transform/first_index_offset_test.cc",
|
||||||
|
"../src/transform/inline_pointer_lets_test.cc",
|
||||||
"../src/transform/renamer_test.cc",
|
"../src/transform/renamer_test.cc",
|
||||||
"../src/transform/single_entry_point_test.cc",
|
"../src/transform/single_entry_point_test.cc",
|
||||||
"../src/transform/transform_test.cc",
|
"../src/transform/transform_test.cc",
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
SKIP: Failed to generate: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
float3x3 m = float3x3(float3(0.0f, 0.0f, 0.0f), float3(0.0f, 0.0f, 0.0f), float3(0.0f, 0.0f, 0.0f));
|
||||||
|
m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
|
||||||
|
m[1] = float3(5.0f, 5.0f, 5.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
SKIP: Failed to generate: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
|
||||||
|
m[1] = float3(5.0f, 5.0f, 5.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
SKIP: Failed to generate: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
float3 v = float3(0.0f, 0.0f, 0.0f);
|
||||||
|
v = float3(1.0f, 2.0f, 3.0f);
|
||||||
|
v.y = 5.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
SKIP: Failed to generate: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
float3 v = float3(1.0f, 2.0f, 3.0f);
|
||||||
|
v.y = 5.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
SKIP: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
uint x_10 = 0u;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,14 @@
|
||||||
SKIP: error: pointers not supported in HLSL
|
int func(int value, inout int pointer) {
|
||||||
|
const int x_9 = pointer;
|
||||||
|
return (value + x_9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
int i = 0;
|
||||||
|
i = 123;
|
||||||
|
const int x_19 = i;
|
||||||
|
const int x_18 = func(x_19, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
SKIP: error: pointers not supported in HLSL
|
int func(int value, inout int pointer) {
|
||||||
|
return (value + pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
int i = 123;
|
||||||
|
const int r = func(i, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
SKIP: Failed to generate: error: pointers not supported in HLSL
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
int i = 123;
|
||||||
|
i = 123;
|
||||||
|
i = ((100 + 20) + 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,13 @@
|
||||||
SKIP: error: pointers not supported in HLSL
|
void func(int value, inout int pointer) {
|
||||||
|
pointer = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
int i = 0;
|
||||||
|
i = 123;
|
||||||
|
func(123, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
SKIP: error: pointers not supported in HLSL
|
void func(int value, inout int pointer) {
|
||||||
|
pointer = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[numthreads(1, 1, 1)]
|
||||||
|
void main() {
|
||||||
|
int i = 123;
|
||||||
|
func(123, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue