Add arrayLength() intrinsic that accepts a pointer argument

The old non-pointer argument overload remains, but is now deprecated.

Bug: tint:806
Change-Id: Ic917ccec0f162414ce1b03df49e332ad84d8060f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54061
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton
2021-06-10 18:43:04 +00:00
committed by Tint LUCI CQ
parent 0aa7edbbd5
commit aba42ed362
46 changed files with 2298 additions and 1215 deletions

View File

@@ -119,20 +119,29 @@ Output CalculateArrayLength::Run(const Program* in, const DataMap&) {
if (intrinsic->Type() == sem::IntrinsicType::kArrayLength) {
// We're dealing with an arrayLength() call
// https://gpuweb.github.io/gpuweb/wgsl.html#array-types states:
// https://gpuweb.github.io/gpuweb/wgsl/#array-types states:
//
// * The last member of the structure type defining the store type for
// a variable in the storage storage class may be a runtime-sized
// array.
// * A runtime-sized array must not be used as the store type or
// contained within a store type in any other cases.
// * The type of an expression must not be a runtime-sized array type.
// arrayLength()
// * An expression must not evaluate to a runtime-sized array type.
//
// We can assume that the arrayLength() call has a single argument of
// the form: arrayLength(X.Y) where X is an expression that resolves
// to the storage buffer structure, and Y is the runtime sized array.
// the form: arrayLength(&X.Y) or the now deprecated form
// arrayLength(X.Y) where X is an expression that resolves to the
// storage buffer structure, and Y is the runtime sized array.
auto* array_expr = call_expr->params()[0];
// TODO(crbug.com/tint/806): Once the deprecated arrayLength()
// overload is removed, this can safely assume a pointer arg.
if (auto* address_of = array_expr->As<ast::UnaryOpExpression>()) {
if (address_of->op() == ast::UnaryOp::kAddressOf) {
array_expr = address_of->expr();
}
}
auto* accessor = array_expr->As<ast::MemberAccessorExpression>();
if (!accessor) {
TINT_ICE(ctx.dst->Diagnostics())

View File

@@ -22,7 +22,8 @@ namespace {
using CalculateArrayLengthTest = TransformTest;
TEST_F(CalculateArrayLengthTest, Basic) {
// TODO(crbug.com/tint/806): Remove
TEST_F(CalculateArrayLengthTest, Basic_DEPRECATED) {
auto* src = R"(
[[block]]
struct SB {
@@ -64,7 +65,8 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, InSameBlock) {
// TODO(crbug.com/tint/806): Remove
TEST_F(CalculateArrayLengthTest, InSameBlock_DEPRECATED) {
auto* src = R"(
[[block]]
struct SB {
@@ -110,7 +112,8 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, WithStride) {
// TODO(crbug.com/tint/806): Remove
TEST_F(CalculateArrayLengthTest, WithStride_DEPRECATED) {
auto* src = R"(
[[block]]
struct SB {
@@ -154,7 +157,8 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, Nested) {
// TODO(crbug.com/tint/806): Remove
TEST_F(CalculateArrayLengthTest, Nested_DEPRECATED) {
auto* src = R"(
[[block]]
struct SB {
@@ -211,7 +215,8 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, MultipleStorageBuffers) {
// TODO(crbug.com/tint/806): Remove
TEST_F(CalculateArrayLengthTest, MultipleStorageBuffers_DEPRECATED) {
auto* src = R"(
[[block]]
struct SB1 {
@@ -279,6 +284,263 @@ fn main() {
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, Basic) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var len : u32 = arrayLength(&sb.arr);
}
)";
auto* expect = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol(buffer : SB, result : ptr<function, u32>)
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var tint_symbol_1 : u32 = 0u;
tint_symbol(sb, &(tint_symbol_1));
let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
var len : u32 = tint_symbol_2;
}
)";
auto got = Run<CalculateArrayLength>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, InSameBlock) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var a : u32 = arrayLength(&sb.arr);
var b : u32 = arrayLength(&sb.arr);
var c : u32 = arrayLength(&sb.arr);
}
)";
auto* expect = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol(buffer : SB, result : ptr<function, u32>)
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var tint_symbol_1 : u32 = 0u;
tint_symbol(sb, &(tint_symbol_1));
let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
var a : u32 = tint_symbol_2;
var b : u32 = tint_symbol_2;
var c : u32 = tint_symbol_2;
}
)";
auto got = Run<CalculateArrayLength>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, WithStride) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
y : f32;
arr : [[stride(64)]] array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var len : u32 = arrayLength(&sb.arr);
}
)";
auto* expect = R"(
[[block]]
struct SB {
x : i32;
y : f32;
arr : [[stride(64)]] array<i32>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol(buffer : SB, result : ptr<function, u32>)
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var tint_symbol_1 : u32 = 0u;
tint_symbol(sb, &(tint_symbol_1));
let tint_symbol_2 : u32 = ((tint_symbol_1 - 8u) / 64u);
var len : u32 = tint_symbol_2;
}
)";
auto got = Run<CalculateArrayLength>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, Nested) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
if (true) {
var len : u32 = arrayLength(&sb.arr);
} else {
if (true) {
var len : u32 = arrayLength(&sb.arr);
}
}
}
)";
auto* expect = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol(buffer : SB, result : ptr<function, u32>)
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
if (true) {
var tint_symbol_1 : u32 = 0u;
tint_symbol(sb, &(tint_symbol_1));
let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
var len : u32 = tint_symbol_2;
} else {
if (true) {
var tint_symbol_3 : u32 = 0u;
tint_symbol(sb, &(tint_symbol_3));
let tint_symbol_4 : u32 = ((tint_symbol_3 - 4u) / 4u);
var len : u32 = tint_symbol_4;
}
}
}
)";
auto got = Run<CalculateArrayLength>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(CalculateArrayLengthTest, MultipleStorageBuffers) {
auto* src = R"(
[[block]]
struct SB1 {
x : i32;
arr1 : array<i32>;
};
[[block]]
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
};
[[group(0), binding(0)]] var<storage, read> sb1 : SB1;
[[group(0), binding(1)]] var<storage, read> sb2 : SB2;
[[stage(compute)]]
fn main() {
var len1 : u32 = arrayLength(&(sb1.arr1));
var len2 : u32 = arrayLength(&(sb2.arr2));
var x : u32 = (len1 + len2);
}
)";
auto* expect = R"(
[[block]]
struct SB1 {
x : i32;
arr1 : array<i32>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol(buffer : SB1, result : ptr<function, u32>)
[[block]]
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
};
[[internal(intrinsic_buffer_size)]]
fn tint_symbol_3(buffer : SB2, result : ptr<function, u32>)
[[group(0), binding(0)]] var<storage, read> sb1 : SB1;
[[group(0), binding(1)]] var<storage, read> sb2 : SB2;
[[stage(compute)]]
fn main() {
var tint_symbol_1 : u32 = 0u;
tint_symbol(sb1, &(tint_symbol_1));
let tint_symbol_2 : u32 = ((tint_symbol_1 - 4u) / 4u);
var tint_symbol_4 : u32 = 0u;
tint_symbol_3(sb2, &(tint_symbol_4));
let tint_symbol_5 : u32 = ((tint_symbol_4 - 16u) / 16u);
var len1 : u32 = tint_symbol_2;
var len2 : u32 = tint_symbol_5;
var x : u32 = (len1 + len2);
}
)";
auto got = Run<CalculateArrayLength>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint

View File

@@ -30,6 +30,7 @@
#include "src/sem/call.h"
#include "src/sem/member_accessor_expression.h"
#include "src/sem/reference_type.h"
#include "src/sem/statement.h"
#include "src/sem/struct.h"
#include "src/sem/variable.h"
#include "src/utils/get_or_create.h"
@@ -742,7 +743,14 @@ Output DecomposeStorageAccess::Run(const Program* in, const DataMap&) {
// arrayLength(X)
// Don't convert X into a load, this actually requires the real
// reference.
state.TakeAccess(call_expr->params()[0]);
auto* arg = call_expr->params()[0];
// TODO(crbug.com/tint/806): Once the deprecated arrayLength()
// overload is removed, this can safely assume a pointer arg.
if (auto* address_of = arg->As<ast::UnaryOpExpression>()) {
arg = address_of->expr();
}
state.TakeAccess(arg);
}
}
}

View File

@@ -44,6 +44,9 @@ Output Hlsl::Run(const Program* in, const DataMap&) {
// after Simplify, as we need to fold away the address-of and defers of
// `*(&(intrinsic_load()))` expressions.
manager.Add<DecomposeStorageAccess>();
// CalculateArrayLength must come after DecomposeStorageAccess, as
// DecomposeStorageAccess special-cases the arrayLength() intrinsic, which
// will be transformed by CalculateArrayLength
manager.Add<CalculateArrayLength>();
manager.Add<ExternalTextureTransform>();
manager.Add<PromoteInitializersToConstVar>();