intrinsics: Add scalar overload of all() & any()

Fixed: tint:1253
Change-Id: I0bdc865a9df9e0171c09daa9918b25bba033ba3b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/67061
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Ben Clayton
2021-10-21 09:39:13 +00:00
committed by Tint LUCI CQ
parent 347c74e671
commit 8cab28c9f9
16 changed files with 2077 additions and 1605 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -267,7 +267,9 @@ fn abs<T: fiu32>(T) -> T
fn abs<N: num, T: fiu32>(vec<N, T>) -> vec<N, T>
fn acos(f32) -> f32
fn acos<N: num>(vec<N, f32>) -> vec<N, f32>
fn all(bool) -> bool
fn all<N: num>(vec<N, bool>) -> bool
fn any(bool) -> bool
fn any<N: num>(vec<N, bool>) -> bool
fn arrayLength<T, A: access>(ptr<storage, array<T>, A>) -> u32
fn asin(f32) -> f32

View File

@@ -107,8 +107,21 @@ INSTANTIATE_TEST_SUITE_P(ResolverTest,
"fwidthCoarse",
"fwidthFine"));
using ResolverIntrinsic = ResolverTestWithParam<std::string>;
TEST_P(ResolverIntrinsic, Test) {
using ResolverIntrinsicTest_BoolMethod = ResolverTestWithParam<std::string>;
TEST_P(ResolverIntrinsicTest_BoolMethod, Scalar) {
auto name = GetParam();
Global("my_var", ty.bool_(), ast::StorageClass::kPrivate);
auto* expr = Call(name, "my_var");
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(expr), nullptr);
EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
}
TEST_P(ResolverIntrinsicTest_BoolMethod, Vector) {
auto name = GetParam();
Global("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
@@ -122,7 +135,7 @@ TEST_P(ResolverIntrinsic, Test) {
EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
ResolverIntrinsic,
ResolverIntrinsicTest_BoolMethod,
testing::Values("any", "all"));
using ResolverIntrinsicTest_FloatMethod = ResolverTestWithParam<std::string>;

View File

@@ -2307,10 +2307,10 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
return result_id;
}
// Generates the SPIR-V ID for the expression for the indexed call parameter,
// Generates the SPIR-V ID for the expression for the indexed call argument,
// and loads it if necessary. Returns 0 on error.
auto get_param_as_value_id = [&](size_t i,
bool generate_load = true) -> uint32_t {
auto get_arg_as_value_id = [&](size_t i,
bool generate_load = true) -> uint32_t {
auto* arg = call->args[i];
auto* param = intrinsic->Parameters()[i];
auto val_id = GenerateExpression(arg);
@@ -2327,7 +2327,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
OperandList params = {Operand::Int(result_type_id), result};
spv::Op op = spv::Op::OpNop;
// Pushes the parameters for a GlslStd450 extended instruction, and sets op
// Pushes the arguments for a GlslStd450 extended instruction, and sets op
// to OpExtInst.
auto glsl_std450 = [&](uint32_t inst_id) {
auto set_id = GetGLSLstd450Import();
@@ -2338,9 +2338,17 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
switch (intrinsic->Type()) {
case IntrinsicType::kAny:
if (intrinsic->Parameters()[0]->Type()->Is<sem::Bool>()) {
// any(v: bool) just resolves to v.
return get_arg_as_value_id(0);
}
op = spv::Op::OpAny;
break;
case IntrinsicType::kAll:
if (intrinsic->Parameters()[0]->Type()->Is<sem::Bool>()) {
// all(v: bool) just resolves to v.
return get_arg_as_value_id(0);
}
op = spv::Op::OpAll;
break;
case IntrinsicType::kArrayLength: {
@@ -2424,7 +2432,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
// Evaluate the single argument, return the non-zero result_id which isn't
// associated with any op (ignore returns void, so this cannot be used in
// an expression).
if (!get_param_as_value_id(0, false)) {
if (!get_arg_as_value_id(0, false)) {
return 0;
}
return result_id;
@@ -2436,7 +2444,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
break;
case IntrinsicType::kIsFinite: {
// Implemented as: not(IsInf or IsNan)
auto val_id = get_param_as_value_id(0);
auto val_id = get_arg_as_value_id(0);
if (!val_id) {
return 0;
}
@@ -2468,7 +2476,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
// clamped = uclamp(1,254,exponent_bits);
// result = (clamped == exponent_bits);
//
auto val_id = get_param_as_value_id(0);
auto val_id = get_arg_as_value_id(0);
if (!val_id) {
return 0;
}
@@ -2541,9 +2549,9 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
case IntrinsicType::kMix: {
auto std450 = Operand::Int(GetGLSLstd450Import());
auto a_id = get_param_as_value_id(0);
auto b_id = get_param_as_value_id(1);
auto f_id = get_param_as_value_id(2);
auto a_id = get_arg_as_value_id(0);
auto b_id = get_arg_as_value_id(1);
auto f_id = get_arg_as_value_id(2);
if (!a_id || !b_id || !f_id) {
return 0;
}
@@ -2572,9 +2580,9 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
break;
case IntrinsicType::kSelect: {
// Note: Argument order is different in WGSL and SPIR-V
auto cond_id = get_param_as_value_id(2);
auto true_id = get_param_as_value_id(1);
auto false_id = get_param_as_value_id(0);
auto cond_id = get_arg_as_value_id(2);
auto true_id = get_arg_as_value_id(1);
auto false_id = get_arg_as_value_id(0);
if (!cond_id || !true_id || !false_id) {
return 0;
}
@@ -2611,7 +2619,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
if (intrinsic->ReturnType()->is_unsigned_scalar_or_vector()) {
// abs() only operates on *signed* integers.
// This is a no-op for unsigned integers.
return get_param_as_value_id(0);
return get_arg_as_value_id(0);
}
if (intrinsic->ReturnType()->is_float_scalar_or_vector()) {
glsl_std450(GLSLstd450FAbs);
@@ -2637,7 +2645,7 @@ uint32_t Builder::GenerateIntrinsic(const ast::CallExpression* call,
}
for (size_t i = 0; i < call->args.size(); i++) {
if (auto val_id = get_param_as_value_id(i)) {
if (auto val_id = get_arg_as_value_id(i)) {
params.emplace_back(Operand::Int(val_id));
} else {
return 0;

View File

@@ -39,7 +39,32 @@ inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
}
using IntrinsicBoolTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
TEST_P(IntrinsicBoolTest, Call_Bool) {
TEST_P(IntrinsicBoolTest, Call_Bool_Scalar) {
auto param = GetParam();
auto* var = Global("v", ty.bool_(), ast::StorageClass::kPrivate);
auto* expr = Call(param.name, "v");
WrapInFunction(expr);
spirv::Builder& b = Build();
b.push_function(Function{});
ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeBool
%2 = OpTypePointer Private %3
%4 = OpConstantNull %3
%1 = OpVariable %2 Private %4
)");
// both any and all are 'passthrough' for scalar booleans
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
"%6 = OpLoad %3 %1\n");
}
TEST_P(IntrinsicBoolTest, Call_Bool_Vector) {
auto param = GetParam();
auto* var = Global("v", ty.vec3<bool>(), ast::StorageClass::kPrivate);