Import Tint changes from Dawn
Changes:
- 205e16de63a2a642394202ab57a06ff07273064d tint/utils: Add Vector::Sort() by Ben Clayton <bclayton@google.com>
- d205b716cac39c85255739ecae340637578537be tint/resolver: forbid spelling non-instantiable pointer t... by Ben Clayton <bclayton@google.com>
- 31e0850560d3cd937833c3111a41750eb49e585e [ir] Convert instruction to a pointer by dan sinclair <dsinclair@chromium.org>
- e272eaf30f35389e626d91f64a832d1195c379b9 [ir] Split Value into Temp and Constant. by dan sinclair <dsinclair@chromium.org>
- 34afd9b6c6016df4287dc5fe5277000ce56c16f2 tint/resolver: Clean up variable validation by Ben Clayton <bclayton@google.com>
- 75b18674bf9846de04ecd223d7d7fab7066b0c5b tint: Add Symbol inequality operator by Ben Clayton <bclayton@google.com>
- c158e845e65cfccac35146b908b55dd668025741 tint/utils: Add Hashmap equality and hashing by Ben Clayton <bclayton@google.com>
- 2d108ae5ed54fc4d6eefe31bac44cf6d984cd229 [ir] Make Value a pointer stored in the module. by dan sinclair <dsinclair@chromium.org>
- 028092a84345815d2d574e4334bbf4d20b58f8f5 tint/utils: Add Hashmap::Keys(), Hashmap::Values() by Ben Clayton <bclayton@google.com>
- 05f3ebfc6892c758670a53235183015869c8a5f5 tint/utils: Make Hashmap iterator values mutable by Ben Clayton <bclayton@google.com>
- b200e74715e8cac7ec9ac3ca47c1367b859d3503 tint: simplify const eval binary op unit tests by Antonio Maiorano <amaiorano@google.com>
- 775dfbf7a8df0b918ea4d9385b08d14de0518af2 tint: fix const eval of divide to emit errors on dbz and ... by Antonio Maiorano <amaiorano@google.com>
- ec7a1d2a1b276c44b1781b99720992535ca37401 [ir] Remove the VarData from Value. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: 205e16de63a2a642394202ab57a06ff07273064d
Change-Id: Ifac1dd1105497bd7d95f4e023c2f3331d3c8d298
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/112360
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 06aa483..15532b2 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -656,6 +656,8 @@
ir/builder.h
ir/builder_impl.cc
ir/builder_impl.h
+ ir/constant.cc
+ ir/constant.h
ir/debug.cc
ir/debug.h
ir/disassembler.cc
@@ -674,6 +676,8 @@
ir/module.h
ir/switch.cc
ir/switch.h
+ ir/temp.cc
+ ir/temp.h
ir/terminator.cc
ir/terminator.h
ir/value.cc
@@ -1341,9 +1345,10 @@
if (${TINT_BUILD_IR})
list(APPEND TINT_TEST_SRCS
ir/builder_impl_test.cc
+ ir/constant_test.cc
ir/instruction_test.cc
+ ir/temp_test.cc
ir/test_helper.h
- ir/value_test.cc
)
endif()
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index d27fdf4..ac9028b 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -34,7 +34,7 @@
const FlowNode* branch_target = nullptr;
/// The instructions in the block
- utils::Vector<Instruction, 16> instructions;
+ utils::Vector<const Instruction*, 16> instructions;
};
} // namespace tint::ir
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index 0403807..5c55b91 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -93,83 +93,85 @@
to->inbound_branches.Push(from);
}
-Value::Id Builder::AllocateValue() {
- return next_value_id++;
+Temp::Id Builder::AllocateTempId() {
+ return next_temp_id++;
}
-Instruction Builder::CreateInstruction(Instruction::Kind kind, Value lhs, Value rhs) {
- return Instruction(kind, Value(AllocateValue()), lhs, rhs);
+const Instruction* Builder::CreateInstruction(Instruction::Kind kind,
+ const Value* lhs,
+ const Value* rhs) {
+ return ir.instructions.Create<ir::Instruction>(kind, Temp(), lhs, rhs);
}
-Instruction Builder::And(Value lhs, Value rhs) {
+const Instruction* Builder::And(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kAnd, lhs, rhs);
}
-Instruction Builder::Or(Value lhs, Value rhs) {
+const Instruction* Builder::Or(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kOr, lhs, rhs);
}
-Instruction Builder::Xor(Value lhs, Value rhs) {
+const Instruction* Builder::Xor(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kXor, lhs, rhs);
}
-Instruction Builder::LogicalAnd(Value lhs, Value rhs) {
+const Instruction* Builder::LogicalAnd(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kLogicalAnd, lhs, rhs);
}
-Instruction Builder::LogicalOr(Value lhs, Value rhs) {
+const Instruction* Builder::LogicalOr(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kLogicalOr, lhs, rhs);
}
-Instruction Builder::Equal(Value lhs, Value rhs) {
+const Instruction* Builder::Equal(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kEqual, lhs, rhs);
}
-Instruction Builder::NotEqual(Value lhs, Value rhs) {
+const Instruction* Builder::NotEqual(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kNotEqual, lhs, rhs);
}
-Instruction Builder::LessThan(Value lhs, Value rhs) {
+const Instruction* Builder::LessThan(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kLessThan, lhs, rhs);
}
-Instruction Builder::GreaterThan(Value lhs, Value rhs) {
+const Instruction* Builder::GreaterThan(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kGreaterThan, lhs, rhs);
}
-Instruction Builder::LessThanEqual(Value lhs, Value rhs) {
+const Instruction* Builder::LessThanEqual(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kLessThanEqual, lhs, rhs);
}
-Instruction Builder::GreaterThanEqual(Value lhs, Value rhs) {
+const Instruction* Builder::GreaterThanEqual(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kGreaterThanEqual, lhs, rhs);
}
-Instruction Builder::ShiftLeft(Value lhs, Value rhs) {
+const Instruction* Builder::ShiftLeft(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kShiftLeft, lhs, rhs);
}
-Instruction Builder::ShiftRight(Value lhs, Value rhs) {
+const Instruction* Builder::ShiftRight(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kShiftRight, lhs, rhs);
}
-Instruction Builder::Add(Value lhs, Value rhs) {
+const Instruction* Builder::Add(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kAdd, lhs, rhs);
}
-Instruction Builder::Subtract(Value lhs, Value rhs) {
+const Instruction* Builder::Subtract(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kSubtract, lhs, rhs);
}
-Instruction Builder::Multiply(Value lhs, Value rhs) {
+const Instruction* Builder::Multiply(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kMultiply, lhs, rhs);
}
-Instruction Builder::Divide(Value lhs, Value rhs) {
+const Instruction* Builder::Divide(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kDivide, lhs, rhs);
}
-Instruction Builder::Modulo(Value lhs, Value rhs) {
+const Instruction* Builder::Modulo(const Value* lhs, const Value* rhs) {
return CreateInstruction(Instruction::Kind::kModulo, lhs, rhs);
}
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 6776d35..d5065b8 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -15,12 +15,14 @@
#ifndef SRC_TINT_IR_BUILDER_H_
#define SRC_TINT_IR_BUILDER_H_
+#include "src/tint/ir/constant.h"
#include "src/tint/ir/function.h"
#include "src/tint/ir/if.h"
#include "src/tint/ir/instruction.h"
#include "src/tint/ir/loop.h"
#include "src/tint/ir/module.h"
#include "src/tint/ir/switch.h"
+#include "src/tint/ir/temp.h"
#include "src/tint/ir/terminator.h"
#include "src/tint/ir/value.h"
@@ -83,129 +85,143 @@
/// @param to the node to branch too
void Branch(Block* from, FlowNode* to);
+ /// Creates a new Constant
+ /// @param val the constant value
+ /// @returns the new constant
+ template <typename T>
+ const ir::Constant* Constant(T val) {
+ return ir.values.Create<ir::Constant>(val);
+ }
+
+ /// Creates a new Temporary
+ /// @returns the new temporary
+ const ir::Temp* Temp() { return ir.values.Create<ir::Temp>(AllocateTempId()); }
+
/// Creates an op for `lhs kind rhs`
/// @param kind the kind of operation
/// @param lhs the left-hand-side of the operation
/// @param rhs the right-hand-side of the operation
/// @returns the operation
- Instruction CreateInstruction(Instruction::Kind kind, Value lhs, Value rhs);
+ const Instruction* CreateInstruction(Instruction::Kind kind,
+ const Value* lhs,
+ const Value* rhs);
/// Creates an And operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction And(Value lhs, Value rhs);
+ const Instruction* And(const Value* lhs, const Value* rhs);
/// Creates an Or operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Or(Value lhs, Value rhs);
+ const Instruction* Or(const Value* lhs, const Value* rhs);
/// Creates an Xor operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Xor(Value lhs, Value rhs);
+ const Instruction* Xor(const Value* lhs, const Value* rhs);
/// Creates an LogicalAnd operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction LogicalAnd(Value lhs, Value rhs);
+ const Instruction* LogicalAnd(const Value* lhs, const Value* rhs);
/// Creates an LogicalOr operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction LogicalOr(Value lhs, Value rhs);
+ const Instruction* LogicalOr(const Value* lhs, const Value* rhs);
/// Creates an Equal operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Equal(Value lhs, Value rhs);
+ const Instruction* Equal(const Value* lhs, const Value* rhs);
/// Creates an NotEqual operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction NotEqual(Value lhs, Value rhs);
+ const Instruction* NotEqual(const Value* lhs, const Value* rhs);
/// Creates an LessThan operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction LessThan(Value lhs, Value rhs);
+ const Instruction* LessThan(const Value* lhs, const Value* rhs);
/// Creates an GreaterThan operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction GreaterThan(Value lhs, Value rhs);
+ const Instruction* GreaterThan(const Value* lhs, const Value* rhs);
/// Creates an LessThanEqual operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction LessThanEqual(Value lhs, Value rhs);
+ const Instruction* LessThanEqual(const Value* lhs, const Value* rhs);
/// Creates an GreaterThanEqual operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction GreaterThanEqual(Value lhs, Value rhs);
+ const Instruction* GreaterThanEqual(const Value* lhs, const Value* rhs);
/// Creates an ShiftLeft operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction ShiftLeft(Value lhs, Value rhs);
+ const Instruction* ShiftLeft(const Value* lhs, const Value* rhs);
/// Creates an ShiftRight operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction ShiftRight(Value lhs, Value rhs);
+ const Instruction* ShiftRight(const Value* lhs, const Value* rhs);
/// Creates an Add operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Add(Value lhs, Value rhs);
+ const Instruction* Add(const Value* lhs, const Value* rhs);
/// Creates an Subtract operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Subtract(Value lhs, Value rhs);
+ const Instruction* Subtract(const Value* lhs, const Value* rhs);
/// Creates an Multiply operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Multiply(Value lhs, Value rhs);
+ const Instruction* Multiply(const Value* lhs, const Value* rhs);
/// Creates an Divide operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Divide(Value lhs, Value rhs);
+ const Instruction* Divide(const Value* lhs, const Value* rhs);
/// Creates an Modulo operation
/// @param lhs the lhs of the add
/// @param rhs the rhs of the add
/// @returns the operation
- Instruction Modulo(Value lhs, Value rhs);
+ const Instruction* Modulo(const Value* lhs, const Value* rhs);
- /// @returns a unique Value id
- Value::Id AllocateValue();
+ /// @returns a unique temp id
+ Temp::Id AllocateTempId();
/// The IR module.
Module ir;
- /// The next Value number to allocate
- Value::Id next_value_id = 1;
+ /// The next temporary number to allocate
+ Temp::Id next_temp_id = 1;
};
} // namespace tint::ir
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 3af54f9..2286bf4 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -516,7 +516,7 @@
return true;
}
-utils::Result<Value> BuilderImpl::EmitExpression(const ast::Expression* expr) {
+utils::Result<const Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
return tint::Switch(
expr,
// [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
@@ -551,7 +551,7 @@
});
}
-utils::Result<Value> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
+utils::Result<const Value*> BuilderImpl::EmitBinary(const ast::BinaryExpression* expr) {
auto lhs = EmitExpression(expr->lhs);
if (!lhs) {
return utils::Failure;
@@ -562,7 +562,7 @@
return utils::Failure;
}
- Instruction instr;
+ const Instruction* instr = nullptr;
switch (expr->op) {
case ast::BinaryOp::kAnd:
instr = builder.And(lhs.Get(), rhs.Get());
@@ -623,26 +623,28 @@
return utils::Failure;
}
- auto result = instr.Result();
current_flow_block->instructions.Push(instr);
- return result;
+ return utils::Result<const Value*>(instr->Result());
}
-utils::Result<Value> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
+utils::Result<const Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
return tint::Switch( //
lit,
- [&](const ast::BoolLiteralExpression* l) { return utils::Result<Value>{Value(l->value)}; },
+ [&](const ast::BoolLiteralExpression* l) {
+ return utils::Result<const Value*>(builder.Constant(l->value));
+ },
[&](const ast::FloatLiteralExpression* l) {
if (l->suffix == ast::FloatLiteralExpression::Suffix::kF) {
- return utils::Result<Value>{Value(f32(static_cast<float>(l->value)))};
+ return utils::Result<const Value*>(
+ builder.Constant(f32(static_cast<float>(l->value))));
}
- return utils::Result<Value>{Value(f16(static_cast<float>(l->value)))};
+ return utils::Result<const Value*>(builder.Constant(f16(static_cast<float>(l->value))));
},
[&](const ast::IntLiteralExpression* l) {
if (l->suffix == ast::IntLiteralExpression::Suffix::kI) {
- return utils::Result<Value>{Value(i32(l->value))};
+ return utils::Result<const Value*>(builder.Constant(i32(l->value)));
}
- return utils::Result<Value>{Value(u32(l->value))};
+ return utils::Result<const Value*>(builder.Constant(u32(l->value)));
},
[&](Default) {
diagnostics_.add_warning(tint::diag::System::IR,
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 60eda73..5090be7 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -140,7 +140,7 @@
/// Emits an expression
/// @param expr the expression to emit
/// @returns true if successful, false otherwise
- utils::Result<Value> EmitExpression(const ast::Expression* expr);
+ utils::Result<const Value*> EmitExpression(const ast::Expression* expr);
/// Emits a variable
/// @param var the variable to emit
@@ -150,12 +150,12 @@
/// Emits a binary expression
/// @param expr the binary expression
/// @returns the value storing the result if successful, utils::Failure otherwise
- utils::Result<Value> EmitBinary(const ast::BinaryExpression* expr);
+ utils::Result<const Value*> EmitBinary(const ast::BinaryExpression* expr);
/// Emits a literal expression
/// @param lit the literal to emit
/// @returns true if successful, false otherwise
- utils::Result<Value> EmitLiteral(const ast::LiteralExpression* lit);
+ utils::Result<const Value*> EmitLiteral(const ast::LiteralExpression* lit);
/// Emits a type
/// @param ty the type to emit
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index 6c8e496..79e6e3f 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -101,9 +101,10 @@
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
// Check condition
- auto instr = flow->condition;
- ASSERT_TRUE(instr.IsBool());
- EXPECT_TRUE(instr.AsBool());
+ ASSERT_TRUE(flow->condition->Is<Constant>());
+ auto* instr = flow->condition->As<Constant>();
+ ASSERT_TRUE(instr->IsBool());
+ EXPECT_TRUE(instr->AsBool());
}
TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
@@ -502,9 +503,10 @@
EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
// Check condition
- auto instr = if_flow->condition;
- ASSERT_TRUE(instr.IsBool());
- EXPECT_TRUE(instr.AsBool());
+ ASSERT_TRUE(if_flow->condition->Is<Constant>());
+ auto* instr = if_flow->condition->As<Constant>();
+ ASSERT_TRUE(instr->IsBool());
+ EXPECT_TRUE(instr->AsBool());
}
TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
@@ -947,9 +949,10 @@
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
// Check condition
- auto instr = if_flow->condition;
- ASSERT_TRUE(instr.IsBool());
- EXPECT_FALSE(instr.AsBool());
+ ASSERT_TRUE(if_flow->condition->Is<Constant>());
+ auto* instr = if_flow->condition->As<Constant>();
+ ASSERT_TRUE(instr->IsBool());
+ EXPECT_FALSE(instr->AsBool());
}
TEST_F(IR_BuilderImplTest, While_Return) {
@@ -1071,9 +1074,10 @@
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
// Check condition
- auto instr = if_flow->condition;
- ASSERT_TRUE(instr.IsBool());
- EXPECT_FALSE(instr.AsBool());
+ ASSERT_TRUE(if_flow->condition->Is<Constant>());
+ auto* instr = if_flow->condition->As<Constant>();
+ ASSERT_TRUE(instr->IsBool());
+ EXPECT_FALSE(instr->AsBool());
}
TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
@@ -1171,9 +1175,10 @@
EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
// Check condition
- auto instr = flow->condition;
- ASSERT_TRUE(instr.IsI32());
- EXPECT_EQ(1_i, instr.AsI32());
+ ASSERT_TRUE(flow->condition->Is<Constant>());
+ auto* instr = flow->condition->As<Constant>();
+ ASSERT_TRUE(instr->IsI32());
+ EXPECT_EQ(1_i, instr->AsI32());
}
TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
@@ -1341,9 +1346,10 @@
auto r = b.EmitLiteral(Expr(true));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsBool());
- EXPECT_TRUE(reg.AsBool());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsBool());
+ EXPECT_TRUE(val->AsBool());
}
TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_False) {
@@ -1351,9 +1357,10 @@
auto r = b.EmitLiteral(Expr(false));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsBool());
- EXPECT_FALSE(reg.AsBool());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsBool());
+ EXPECT_FALSE(val->AsBool());
}
TEST_F(IR_BuilderImplTest, EmitLiteral_F32) {
@@ -1361,9 +1368,10 @@
auto r = b.EmitLiteral(Expr(1.2_f));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsF32());
- EXPECT_EQ(1.2_f, reg.AsF32());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsF32());
+ EXPECT_EQ(1.2_f, val->AsF32());
}
TEST_F(IR_BuilderImplTest, EmitLiteral_F16) {
@@ -1371,9 +1379,10 @@
auto r = b.EmitLiteral(Expr(1.2_h));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsF16());
- EXPECT_EQ(1.2_h, reg.AsF16());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsF16());
+ EXPECT_EQ(1.2_h, val->AsF16());
}
TEST_F(IR_BuilderImplTest, EmitLiteral_I32) {
@@ -1381,9 +1390,10 @@
auto r = b.EmitLiteral(Expr(-2_i));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsI32());
- EXPECT_EQ(-2_i, reg.AsI32());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsI32());
+ EXPECT_EQ(-2_i, val->AsI32());
}
TEST_F(IR_BuilderImplTest, EmitLiteral_U32) {
@@ -1391,9 +1401,10 @@
auto r = b.EmitLiteral(Expr(2_u));
ASSERT_TRUE(r);
- auto reg = r.Get();
- EXPECT_TRUE(reg.IsU32());
- EXPECT_EQ(2_u, reg.AsU32());
+ ASSERT_TRUE(r.Get()->Is<Constant>());
+ auto* val = r.Get()->As<Constant>();
+ EXPECT_TRUE(val->IsU32());
+ EXPECT_EQ(2_u, val->AsU32());
}
TEST_F(IR_BuilderImplTest, EmitExpression_Binary_Add) {
diff --git a/src/tint/ir/constant.cc b/src/tint/ir/constant.cc
new file mode 100644
index 0000000..78895bf
--- /dev/null
+++ b/src/tint/ir/constant.cc
@@ -0,0 +1,56 @@
+// Copyright 2022 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/tint/ir/constant.h"
+
+#include <string>
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Constant);
+
+namespace tint::ir {
+
+Constant::Constant(f32 f) : kind_(Kind::kF32), data_(f) {}
+
+Constant::Constant(f16 f) : kind_(Kind::kF16), data_(f) {}
+
+Constant::Constant(u32 u) : kind_(Kind::kU32), data_(u) {}
+
+Constant::Constant(i32 i) : kind_(Kind::kI32), data_(i) {}
+
+Constant::Constant(bool b) : kind_(Kind::kBool), data_(b) {}
+
+Constant::~Constant() = default;
+
+std::ostream& operator<<(std::ostream& out, const Constant& r) {
+ switch (r.GetKind()) {
+ case Constant::Kind::kF32:
+ out << std::to_string(r.AsF32().value);
+ break;
+ case Constant::Kind::kF16:
+ out << std::to_string(r.AsF16().value);
+ break;
+ case Constant::Kind::kI32:
+ out << std::to_string(r.AsI32().value);
+ break;
+ case Constant::Kind::kU32:
+ out << std::to_string(r.AsU32().value);
+ break;
+ case Constant::Kind::kBool:
+ out << (r.AsBool() ? "true" : "false");
+ break;
+ }
+ return out;
+}
+
+} // namespace tint::ir
diff --git a/src/tint/ir/constant.h b/src/tint/ir/constant.h
new file mode 100644
index 0000000..c06e121
--- /dev/null
+++ b/src/tint/ir/constant.h
@@ -0,0 +1,114 @@
+// Copyright 2022 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_TINT_IR_CONSTANT_H_
+#define SRC_TINT_IR_CONSTANT_H_
+
+#include <ostream>
+#include <variant>
+
+#include "src/tint/ir/value.h"
+#include "src/tint/number.h"
+
+namespace tint::ir {
+
+/// Constant in the IR. The constant can be one of several types these include, but aren't limited
+/// to, `f32`, `u32`, `bool`. The type of the constant determines the type of data stored.
+class Constant : public Castable<Constant, Value> {
+ public:
+ /// The type of the constant
+ enum class Kind {
+ /// A f32 constant
+ kF32,
+ /// A f16 constant
+ kF16,
+ /// An i32 constant
+ kI32,
+ /// A u32 constant
+ kU32,
+ /// A boolean constant
+ kBool,
+ };
+
+ /// Constructor
+ /// @param b the `bool` constant to store in the constant
+ explicit Constant(bool b);
+
+ /// Constructor
+ /// @param f the `f32` constant to store in the constant
+ explicit Constant(f32 f);
+
+ /// Constructor
+ /// @param f the `f16` constant to store in the constant
+ explicit Constant(f16 f);
+
+ /// Constructor
+ /// @param u the `u32` constant to store in the constant
+ explicit Constant(u32 u);
+
+ /// Constructor
+ /// @param i the `i32` constant to store in the constant
+ explicit Constant(i32 i);
+
+ /// Destructor
+ ~Constant() override;
+
+ Constant(const Constant&) = delete;
+ Constant(Constant&&) = delete;
+
+ Constant& operator=(const Constant&) = delete;
+ Constant& operator=(Constant&&) = delete;
+
+ /// @returns true if this is a f32 constant
+ bool IsF32() const { return kind_ == Kind::kF32; }
+ /// @returns true if this is a f16 constant
+ bool IsF16() const { return kind_ == Kind::kF16; }
+ /// @returns true if this is an i32 constant
+ bool IsI32() const { return kind_ == Kind::kI32; }
+ /// @returns true if this is a u32 constant
+ bool IsU32() const { return kind_ == Kind::kU32; }
+ /// @returns true if this is a bool constant
+ bool IsBool() const { return kind_ == Kind::kBool; }
+
+ /// @returns the kind of constant
+ Kind GetKind() const { return kind_; }
+
+ /// @returns the constant data as a `f32`.
+ /// @note, must only be called if `IsF32()` is true
+ f32 AsF32() const { return std::get<f32>(data_); }
+ /// @returns the constant data as a `f16`.
+ /// @note, must only be called if `IsF16()` is true
+ f16 AsF16() const { return std::get<f16>(data_); }
+ /// @returns the constant data as an `i32`.
+ /// @note, must only be called if `IsI32()` is true
+ i32 AsI32() const { return std::get<i32>(data_); }
+ /// @returns the constant data as a `u32`.
+ /// @note, must only be called if `IsU32()` is true
+ u32 AsU32() const { return std::get<u32>(data_); }
+ /// @returns the constant data as a `bool`.
+ /// @note, must only be called if `IsBool()` is true
+ bool AsBool() const { return std::get<bool>(data_); }
+
+ private:
+ /// The type of data stored in this constant
+ Kind kind_;
+ /// The data stored in the constant
+ std::variant<f32, f16, u32, i32, bool> data_;
+};
+
+std::ostream& operator<<(std::ostream& out, const Constant& r);
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_CONSTANT_H_
diff --git a/src/tint/ir/constant_test.cc b/src/tint/ir/constant_test.cc
new file mode 100644
index 0000000..7b4e224
--- /dev/null
+++ b/src/tint/ir/constant_test.cc
@@ -0,0 +1,125 @@
+// Copyright 2022 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 <sstream>
+
+#include "src/tint/ir/test_helper.h"
+#include "src/tint/ir/value.h"
+
+namespace tint::ir {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+
+using IR_ConstantTest = TestHelper;
+
+TEST_F(IR_ConstantTest, f32) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ auto* val = b.builder.Constant(1.2_f);
+ EXPECT_EQ(1.2_f, val->AsF32());
+
+ str << *val;
+ EXPECT_EQ("1.200000", str.str());
+
+ EXPECT_TRUE(val->IsF32());
+ EXPECT_FALSE(val->IsF16());
+ EXPECT_FALSE(val->IsI32());
+ EXPECT_FALSE(val->IsU32());
+ EXPECT_FALSE(val->IsBool());
+}
+
+TEST_F(IR_ConstantTest, f16) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ auto* val = b.builder.Constant(1.1_h);
+ EXPECT_EQ(1.1_h, val->AsF16());
+
+ str << *val;
+ EXPECT_EQ("1.099609", str.str());
+
+ EXPECT_FALSE(val->IsF32());
+ EXPECT_TRUE(val->IsF16());
+ EXPECT_FALSE(val->IsI32());
+ EXPECT_FALSE(val->IsU32());
+ EXPECT_FALSE(val->IsBool());
+}
+
+TEST_F(IR_ConstantTest, i32) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ auto* val = b.builder.Constant(1_i);
+ EXPECT_EQ(1_i, val->AsI32());
+
+ str << *val;
+ EXPECT_EQ("1", str.str());
+
+ EXPECT_FALSE(val->IsF32());
+ EXPECT_FALSE(val->IsF16());
+ EXPECT_TRUE(val->IsI32());
+ EXPECT_FALSE(val->IsU32());
+ EXPECT_FALSE(val->IsBool());
+}
+
+TEST_F(IR_ConstantTest, u32) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ auto* val = b.builder.Constant(2_u);
+ EXPECT_EQ(2_u, val->AsU32());
+
+ str << *val;
+ EXPECT_EQ("2", str.str());
+
+ EXPECT_FALSE(val->IsF32());
+ EXPECT_FALSE(val->IsF16());
+ EXPECT_FALSE(val->IsI32());
+ EXPECT_TRUE(val->IsU32());
+ EXPECT_FALSE(val->IsBool());
+}
+
+TEST_F(IR_ConstantTest, bool) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ auto* val = b.builder.Constant(false);
+ EXPECT_FALSE(val->AsBool());
+
+ str << *val;
+ EXPECT_EQ("false", str.str());
+
+ str.str("");
+ val = b.builder.Constant(true);
+ EXPECT_TRUE(val->AsBool());
+
+ str << *val;
+ EXPECT_EQ("true", str.str());
+
+ EXPECT_FALSE(val->IsF32());
+ EXPECT_FALSE(val->IsF16());
+ EXPECT_FALSE(val->IsI32());
+ EXPECT_FALSE(val->IsU32());
+ EXPECT_TRUE(val->IsBool());
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index f98950f..e2749a1 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -62,8 +62,8 @@
}
void Disassembler::EmitBlockInstructions(const Block* b) {
- for (const auto& instr : b->instructions) {
- out_ << instr << std::endl;
+ for (const auto* instr : b->instructions) {
+ out_ << *instr << std::endl;
}
}
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 4b2969d..905f311 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -45,7 +45,7 @@
/// branches into it. (e.g. if both branches `return`)
Block* merge_target = nullptr;
/// Value holding the condition result
- Value condition;
+ const Value* condition = nullptr;
};
} // namespace tint::ir
diff --git a/src/tint/ir/instruction.cc b/src/tint/ir/instruction.cc
index 9460f39..6c0f176 100644
--- a/src/tint/ir/instruction.cc
+++ b/src/tint/ir/instruction.cc
@@ -14,11 +14,13 @@
#include "src/tint/ir/instruction.h"
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Instruction);
+
namespace tint::ir {
Instruction::Instruction() {}
-Instruction::Instruction(Kind kind, Value result, Value lhs, Value rhs)
+Instruction::Instruction(Kind kind, const Value* result, const Value* lhs, const Value* rhs)
: kind_(kind), result_(result), args_({lhs, rhs}) {}
Instruction::Instruction(const Instruction&) = default;
@@ -32,9 +34,9 @@
Instruction& Instruction::operator=(Instruction&& instr) = default;
std::ostream& operator<<(std::ostream& out, const Instruction& instr) {
- out << instr.Result() << " = ";
+ out << *(instr.Result()) << " = ";
if (instr.HasLHS()) {
- out << instr.LHS();
+ out << *(instr.LHS());
}
out << " ";
@@ -96,7 +98,7 @@
}
if (instr.HasRHS()) {
- out << " " << instr.RHS();
+ out << " " << *(instr.RHS());
}
return out;
diff --git a/src/tint/ir/instruction.h b/src/tint/ir/instruction.h
index b62300c..461b1f7 100644
--- a/src/tint/ir/instruction.h
+++ b/src/tint/ir/instruction.h
@@ -17,13 +17,15 @@
#include <ostream>
+#include "src/tint/castable.h"
+#include "src/tint/debug.h"
#include "src/tint/ir/value.h"
#include "src/tint/utils/vector.h"
namespace tint::ir {
/// An instruction in the IR.
-class Instruction {
+class Instruction : public Castable<Instruction> {
public:
/// The kind of instruction.
enum class Kind {
@@ -58,7 +60,7 @@
/// @param result the result value
/// @param lhs the lhs of the instruction
/// @param rhs the rhs of the instruction
- Instruction(Kind kind, Value result, Value lhs, Value rhs);
+ Instruction(Kind kind, const Value* result, const Value* lhs, const Value* rhs);
/// Copy constructor
/// @param instr the instruction to copy from
Instruction(const Instruction& instr);
@@ -81,12 +83,12 @@
Kind GetKind() const { return kind_; }
/// @returns the result value for the instruction
- const Value& Result() const { return result_; }
+ const Value* Result() const { return result_; }
/// @returns true if the instruction has a LHS
bool HasLHS() const { return args_.Length() >= 1; }
/// @returns the left-hand-side value for the instruction
- const Value& LHS() const {
+ const Value* LHS() const {
TINT_ASSERT(IR, HasLHS());
return args_[0];
}
@@ -94,7 +96,7 @@
/// @returns true if the instruction has a RHS
bool HasRHS() const { return args_.Length() >= 2; }
/// @returns the right-hand-side value for the instruction
- const Value& RHS() const {
+ const Value* RHS() const {
TINT_ASSERT(IR, HasRHS());
return args_[1];
}
@@ -102,8 +104,8 @@
private:
Kind kind_;
- Value result_;
- utils::Vector<Value, 2> args_;
+ const Value* result_;
+ utils::Vector<const Value*, 2> args_;
};
std::ostream& operator<<(std::ostream& out, const Instruction&);
diff --git a/src/tint/ir/instruction_test.cc b/src/tint/ir/instruction_test.cc
index 8fdd541..0957903 100644
--- a/src/tint/ir/instruction_test.cc
+++ b/src/tint/ir/instruction_test.cc
@@ -25,468 +25,509 @@
TEST_F(IR_InstructionTest, CreateAnd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.And(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.And(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kAnd);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kAnd);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 & 2");
}
TEST_F(IR_InstructionTest, CreateOr) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Or(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Or(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kOr);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kOr);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 | 2");
}
TEST_F(IR_InstructionTest, CreateXor) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Xor(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Xor(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kXor);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kXor);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 ^ 2");
}
TEST_F(IR_InstructionTest, CreateLogicalAnd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.LogicalAnd(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr =
+ b.builder.LogicalAnd(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLogicalAnd);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLogicalAnd);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 && 2");
}
TEST_F(IR_InstructionTest, CreateLogicalOr) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.LogicalOr(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.LogicalOr(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLogicalOr);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLogicalOr);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 || 2");
}
TEST_F(IR_InstructionTest, CreateEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Equal(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Equal(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kEqual);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kEqual);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 == 2");
}
TEST_F(IR_InstructionTest, CreateNotEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.NotEqual(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.NotEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kNotEqual);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kNotEqual);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 != 2");
}
TEST_F(IR_InstructionTest, CreateLessThan) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.LessThan(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.LessThan(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLessThan);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLessThan);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 < 2");
}
TEST_F(IR_InstructionTest, CreateGreaterThan) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.GreaterThan(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr =
+ b.builder.GreaterThan(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kGreaterThan);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kGreaterThan);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 > 2");
}
TEST_F(IR_InstructionTest, CreateLessThanEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.LessThanEqual(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr =
+ b.builder.LessThanEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kLessThanEqual);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kLessThanEqual);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 <= 2");
}
TEST_F(IR_InstructionTest, CreateGreaterThanEqual) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.GreaterThanEqual(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr =
+ b.builder.GreaterThanEqual(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kGreaterThanEqual);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kGreaterThanEqual);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 >= 2");
}
TEST_F(IR_InstructionTest, CreateShiftLeft) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.ShiftLeft(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.ShiftLeft(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kShiftLeft);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kShiftLeft);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 << 2");
}
TEST_F(IR_InstructionTest, CreateShiftRight) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.ShiftRight(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr =
+ b.builder.ShiftRight(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kShiftRight);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kShiftRight);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 >> 2");
}
TEST_F(IR_InstructionTest, CreateAdd) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Add(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Add(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kAdd);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kAdd);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 + 2");
}
TEST_F(IR_InstructionTest, CreateSubtract) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Subtract(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Subtract(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kSubtract);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kSubtract);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 - 2");
}
TEST_F(IR_InstructionTest, CreateMultiply) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Multiply(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Multiply(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kMultiply);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kMultiply);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 * 2");
}
TEST_F(IR_InstructionTest, CreateDivide) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Divide(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Divide(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kDivide);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kDivide);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 / 2");
}
TEST_F(IR_InstructionTest, CreateModulo) {
auto& b = CreateEmptyBuilder();
- b.builder.next_value_id = Value::Id(42);
- auto instr = b.builder.Modulo(Value(i32(4)), Value(i32(2)));
+ b.builder.next_temp_id = Temp::Id(42);
+ const auto* instr = b.builder.Modulo(b.builder.Constant(i32(4)), b.builder.Constant(i32(2)));
- EXPECT_EQ(instr.GetKind(), Instruction::Kind::kModulo);
+ EXPECT_EQ(instr->GetKind(), Instruction::Kind::kModulo);
- ASSERT_TRUE(instr.Result().IsTemp());
- EXPECT_EQ(Value::Id(42), instr.Result().AsId());
+ ASSERT_TRUE(instr->Result()->Is<Temp>());
+ EXPECT_EQ(Temp::Id(42), instr->Result()->As<Temp>()->AsId());
- ASSERT_TRUE(instr.HasLHS());
- auto& lhs = instr.LHS();
- ASSERT_TRUE(lhs.IsI32());
- EXPECT_EQ(i32(4), lhs.AsI32());
+ ASSERT_TRUE(instr->HasLHS());
+ ASSERT_TRUE(instr->LHS()->Is<Constant>());
+ auto lhs = instr->LHS()->As<Constant>();
+ ASSERT_TRUE(lhs->IsI32());
+ EXPECT_EQ(i32(4), lhs->AsI32());
- ASSERT_TRUE(instr.HasRHS());
- auto& rhs = instr.RHS();
- ASSERT_TRUE(rhs.IsI32());
- EXPECT_EQ(i32(2), rhs.AsI32());
+ ASSERT_TRUE(instr->HasRHS());
+ ASSERT_TRUE(instr->RHS()->Is<Constant>());
+ auto rhs = instr->RHS()->As<Constant>();
+ ASSERT_TRUE(rhs->IsI32());
+ EXPECT_EQ(i32(2), rhs->AsI32());
std::stringstream str;
- str << instr;
+ str << *instr;
EXPECT_EQ(str.str(), "%42 = 4 % 2");
}
diff --git a/src/tint/ir/module.h b/src/tint/ir/module.h
index 4d38614..d95a13e 100644
--- a/src/tint/ir/module.h
+++ b/src/tint/ir/module.h
@@ -18,6 +18,8 @@
#include <string>
#include "src/tint/ir/function.h"
+#include "src/tint/ir/instruction.h"
+#include "src/tint/ir/value.h"
#include "src/tint/utils/block_allocator.h"
#include "src/tint/utils/result.h"
#include "src/tint/utils/vector.h"
@@ -66,6 +68,10 @@
/// The flow node allocator
utils::BlockAllocator<FlowNode> flow_nodes;
+ /// The value allocator
+ utils::BlockAllocator<Value> values;
+ /// The instruction allocator
+ utils::BlockAllocator<Instruction> instructions;
/// List of functions in the program
utils::Vector<Function*, 8> functions;
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index e526fe8..3ea9c3f 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -53,7 +53,7 @@
utils::Vector<Case, 4> cases;
/// Value holding the condition result
- Value condition;
+ const Value* condition = nullptr;
};
} // namespace tint::ir
diff --git a/src/tint/ir/temp.cc b/src/tint/ir/temp.cc
new file mode 100644
index 0000000..de227ee
--- /dev/null
+++ b/src/tint/ir/temp.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 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/tint/ir/temp.h"
+
+#include <string>
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Temp);
+
+namespace tint::ir {
+
+Temp::Temp(Id id) : id_(id) {}
+
+Temp::~Temp() = default;
+
+std::ostream& operator<<(std::ostream& out, const Temp& r) {
+ out << "%" << std::to_string(r.AsId());
+ return out;
+}
+
+} // namespace tint::ir
diff --git a/src/tint/ir/temp.h b/src/tint/ir/temp.h
new file mode 100644
index 0000000..b16baf2
--- /dev/null
+++ b/src/tint/ir/temp.h
@@ -0,0 +1,54 @@
+// Copyright 2022 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_TINT_IR_TEMP_H_
+#define SRC_TINT_IR_TEMP_H_
+
+#include <ostream>
+
+#include "src/tint/ir/value.h"
+
+namespace tint::ir {
+
+/// Temporary value in the IR.
+class Temp : public Castable<Temp, Value> {
+ public:
+ /// A value id.
+ using Id = uint32_t;
+
+ /// Constructor
+ /// @param id the id for the value
+ explicit Temp(Id id);
+
+ /// Destructor
+ ~Temp() override;
+
+ Temp(const Temp&) = delete;
+ Temp(Temp&&) = delete;
+
+ Temp& operator=(const Temp&) = delete;
+ Temp& operator=(Temp&&) = delete;
+
+ /// @returns the value data as an `Id`.
+ Id AsId() const { return id_; }
+
+ private:
+ Id id_ = 0;
+};
+
+std::ostream& operator<<(std::ostream& out, const Temp& r);
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_TEMP_H_
diff --git a/src/tint/ir/temp_test.cc b/src/tint/ir/temp_test.cc
new file mode 100644
index 0000000..b5d817b
--- /dev/null
+++ b/src/tint/ir/temp_test.cc
@@ -0,0 +1,41 @@
+// Copyright 2022 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 <sstream>
+
+#include "src/tint/ir/temp.h"
+#include "src/tint/ir/test_helper.h"
+
+namespace tint::ir {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+
+using IR_TempTest = TestHelper;
+
+TEST_F(IR_TempTest, id) {
+ auto& b = CreateEmptyBuilder();
+
+ std::stringstream str;
+
+ b.builder.next_temp_id = Temp::Id(4);
+ auto* val = b.builder.Temp();
+ EXPECT_EQ(4u, val->AsId());
+
+ str << *val;
+ EXPECT_EQ("%4", str.str());
+}
+
+} // namespace
+} // namespace tint::ir
diff --git a/src/tint/ir/value.cc b/src/tint/ir/value.cc
index bd45a98..a1431e3 100644
--- a/src/tint/ir/value.cc
+++ b/src/tint/ir/value.cc
@@ -14,61 +14,26 @@
#include "src/tint/ir/value.h"
+#include "src/tint/ir/constant.h"
+#include "src/tint/ir/temp.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Value);
+
namespace tint::ir {
-Value::Value() : kind_(Kind::kUninitialized), data_(Id(0)) {}
-
-Value::Value(Id id) : kind_(Kind::kTemp), data_(id) {}
-
-Value::Value(f32 f) : kind_(Kind::kF32), data_(f) {}
-
-Value::Value(f16 f) : kind_(Kind::kF16), data_(f) {}
-
-Value::Value(u32 u) : kind_(Kind::kU32), data_(u) {}
-
-Value::Value(i32 i) : kind_(Kind::kI32), data_(i) {}
-
-Value::Value(bool b) : kind_(Kind::kBool), data_(b) {}
-
-Value::Value(Symbol s, Id id) : kind_(Kind::kVar), data_(VarData{s, id}) {}
+Value::Value() = default;
Value::~Value() = default;
-Value::Value(const Value& o) = default;
+std::ostream& operator<<(std::ostream& out, const Value& v) {
+ const auto* ptr = &v;
-Value::Value(Value&& o) = default;
-
-Value& Value::operator=(const Value& o) = default;
-
-Value& Value::operator=(Value&& o) = default;
-
-std::ostream& operator<<(std::ostream& out, const Value& r) {
- switch (r.GetKind()) {
- case Value::Kind::kTemp:
- out << "%" << std::to_string(r.AsId());
- break;
- case Value::Kind::kF32:
- out << std::to_string(r.AsF32().value);
- break;
- case Value::Kind::kF16:
- out << std::to_string(r.AsF16().value);
- break;
- case Value::Kind::kI32:
- out << std::to_string(r.AsI32().value);
- break;
- case Value::Kind::kU32:
- out << std::to_string(r.AsU32().value);
- break;
- // TODO(dsinclair): Emit the symbol instead of v
- case Value::Kind::kVar:
- out << "%v" << std::to_string(r.AsVarData().id);
- break;
- case Value::Kind::kBool:
- out << (r.AsBool() ? "true" : "false");
- break;
- case Value::Kind::kUninitialized:
- out << "unknown value";
- break;
+ if (auto* c = ptr->As<Constant>()) {
+ out << *c;
+ } else if (auto* t = ptr->As<Temp>()) {
+ out << *t;
+ } else {
+ out << "Unknown value";
}
return out;
}
diff --git a/src/tint/ir/value.h b/src/tint/ir/value.h
index 7c9f8b5..326931f 100644
--- a/src/tint/ir/value.h
+++ b/src/tint/ir/value.h
@@ -16,153 +16,29 @@
#define SRC_TINT_IR_VALUE_H_
#include <ostream>
-#include <variant>
-#include "src/tint/number.h"
-#include "src/tint/symbol.h"
+#include "src/tint/castable.h"
namespace tint::ir {
-/// Value in the IR. The value can be one of several types these include, but aren't limited
-/// to, `f32`, `u32`, `temp`, `var`. The type of the value determines the type of data stored
-/// in the value.
-class Value {
+/// Value in the IR.
+class Value : public Castable<Value> {
public:
- /// A value id.
- using Id = uint32_t;
-
- /// The type of the value
- enum class Kind {
- /// A uninitialized value
- kUninitialized,
- /// A temporary allocated value
- kTemp,
- /// A f32 value
- kF32,
- /// A f16 value
- kF16,
- /// An i32 value
- kI32,
- /// A u32 value
- kU32,
- /// A variable value
- kVar,
- /// A boolean value
- kBool,
- };
-
- /// Stores data for a given variable. There will be multiple `VarData` entries for a given `id`.
- /// The `id` acts like a generation number (although they aren't sequential, they are
- /// increasing). As the variable is stored too a new value will be created and the the `id`
- /// will be incremented.
- struct VarData {
- /// The symbol for the variable
- Symbol sym;
- /// The id for the variable.
- Id id;
- // TODO(dsinclair): Should var type data be stored here along side the variable info?
- };
-
- /// Constructor
- /// Creates a uninitialized value
- Value();
-
- /// Constructor
- /// @param id the id for the value
- explicit Value(Id id);
-
- /// Constructor
- /// @param s the symbol for the value
- /// @param id the id for the value
- Value(Symbol s, Id id);
-
- /// Constructor
- /// @param b the `bool` value to store in the value
- explicit Value(bool b);
-
- /// Constructor
- /// @param f the `f32` value to store in the value
- explicit Value(f32 f);
-
- /// Constructor
- /// @param f the `f16` value to store in the value
- explicit Value(f16 f);
-
- /// Constructor
- /// @param u the `u32` value to store in the value
- explicit Value(u32 u);
-
- /// Constructor
- /// @param i the `i32` value to store in the value
- explicit Value(i32 i);
-
/// Destructor
- ~Value();
+ virtual ~Value();
- /// Copy constructor
- /// @param o the value to copy from
- Value(const Value& o);
- /// Move constructor
- /// @param o the value to move from
- Value(Value&& o);
+ Value(const Value&) = delete;
+ Value(Value&&) = delete;
- /// Copy assign
- /// @param o the value to copy from
- /// @returns this
- Value& operator=(const Value& o);
- /// Move assign
- /// @param o the value to move from
- /// @returns this
- Value& operator=(Value&& o);
+ Value& operator=(const Value&) = delete;
+ Value& operator=(Value&&) = delete;
- /// @returns true if this is a temporary value
- bool IsTemp() const { return kind_ == Kind::kTemp; }
- /// @returns true if this is a f32 value
- bool IsF32() const { return kind_ == Kind::kF32; }
- /// @returns true if this is a f16 value
- bool IsF16() const { return kind_ == Kind::kF16; }
- /// @returns true if this is an i32 value
- bool IsI32() const { return kind_ == Kind::kI32; }
- /// @returns true if this is a u32 value
- bool IsU32() const { return kind_ == Kind::kU32; }
- /// @returns true if this is a var value
- bool IsVar() const { return kind_ == Kind::kVar; }
- /// @returns true if this is a bool value
- bool IsBool() const { return kind_ == Kind::kBool; }
-
- /// @returns the kind of value
- Kind GetKind() const { return kind_; }
-
- /// @returns the value data as a `f32`.
- /// @note, must only be called if `IsF32()` is true
- f32 AsF32() const { return std::get<f32>(data_); }
- /// @returns the value data as a `f16`.
- /// @note, must only be called if `IsF16()` is true
- f16 AsF16() const { return std::get<f16>(data_); }
- /// @returns the value data as an `i32`.
- /// @note, must only be called if `IsI32()` is true
- i32 AsI32() const { return std::get<i32>(data_); }
- /// @returns the value data as a `u32`.
- /// @note, must only be called if `IsU32()` is true
- u32 AsU32() const { return std::get<u32>(data_); }
- /// @returns the value data as an `Id`.
- /// @note, must only be called if `IsTemp()` is true
- Id AsId() const { return std::get<Id>(data_); }
- /// @returns the value data as a `VarData` structure.
- /// @note, must only be called if `IsVar()` is true
- VarData AsVarData() const { return std::get<VarData>(data_); }
- /// @returns the value data as a `bool`.
- /// @note, must only be called if `IsBool()` is true
- bool AsBool() const { return std::get<bool>(data_); }
-
- private:
- /// The type of data stored in this value
- Kind kind_;
- /// The data stored in the value
- std::variant<Id, f32, f16, u32, i32, VarData, bool> data_;
+ protected:
+ /// Constructor
+ Value();
};
-std::ostream& operator<<(std::ostream& out, const Value& r);
+std::ostream& operator<<(std::ostream& out, const Value& v);
} // namespace tint::ir
diff --git a/src/tint/ir/value_test.cc b/src/tint/ir/value_test.cc
deleted file mode 100644
index 77ee3e4..0000000
--- a/src/tint/ir/value_test.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2022 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 <sstream>
-
-#include "src/tint/ir/test_helper.h"
-#include "src/tint/ir/value.h"
-
-namespace tint::ir {
-namespace {
-
-using namespace tint::number_suffixes; // NOLINT
-
-using IR_ValueTest = TestHelper;
-
-TEST_F(IR_ValueTest, f32) {
- std::stringstream str;
-
- Value val(1.2_f);
- EXPECT_EQ(1.2_f, val.AsF32());
-
- str << val;
- EXPECT_EQ("1.200000", str.str());
-
- EXPECT_TRUE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, f16) {
- std::stringstream str;
-
- Value val(1.1_h);
- EXPECT_EQ(1.1_h, val.AsF16());
-
- str << val;
- EXPECT_EQ("1.099609", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_TRUE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, i32) {
- std::stringstream str;
-
- Value val(1_i);
- EXPECT_EQ(1_i, val.AsI32());
-
- str << val;
- EXPECT_EQ("1", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_TRUE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, u32) {
- std::stringstream str;
-
- Value val(2_u);
- EXPECT_EQ(2_u, val.AsU32());
-
- str << val;
- EXPECT_EQ("2", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_TRUE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, id) {
- std::stringstream str;
-
- Value val(Value::Id(4));
- EXPECT_EQ(4u, val.AsId());
-
- str << val;
- EXPECT_EQ("%4", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_TRUE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, bool) {
- std::stringstream str;
-
- Value val(false);
- EXPECT_FALSE(val.AsBool());
-
- str << val;
- EXPECT_EQ("false", str.str());
-
- str.str("");
- val = Value(true);
- EXPECT_TRUE(val.AsBool());
-
- str << val;
- EXPECT_EQ("true", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_TRUE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, var) {
- std::stringstream str;
-
- Symbol s;
- Value val(s, 2);
- EXPECT_EQ(2u, val.AsVarData().id);
- EXPECT_EQ(s, val.AsVarData().sym);
-
- str << val;
- EXPECT_EQ("%v2", str.str());
- str.str("");
-
- val = Value(s, 4);
- EXPECT_EQ(4u, val.AsVarData().id);
- EXPECT_EQ(s, val.AsVarData().sym);
-
- str << val;
- EXPECT_EQ("%v4", str.str());
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_TRUE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-TEST_F(IR_ValueTest, uninitialized) {
- Value val;
-
- EXPECT_FALSE(val.IsF32());
- EXPECT_FALSE(val.IsF16());
- EXPECT_FALSE(val.IsI32());
- EXPECT_FALSE(val.IsU32());
- EXPECT_FALSE(val.IsTemp());
- EXPECT_FALSE(val.IsVar());
- EXPECT_FALSE(val.IsBool());
-}
-
-} // namespace
-} // namespace tint::ir
diff --git a/src/tint/resolver/address_space_layout_validation_test.cc b/src/tint/resolver/address_space_layout_validation_test.cc
index b34b889..638df21 100644
--- a/src/tint/resolver/address_space_layout_validation_test.cc
+++ b/src/tint/resolver/address_space_layout_validation_test.cc
@@ -52,7 +52,7 @@
/* offset(5) align(1) size( 4) */ b : f32;
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'S' used in address space 'storage' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
@@ -116,7 +116,7 @@
/* align(4) size(4) */ struct Inner {
/* offset(0) align(4) size(4) */ scalar : i32;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest,
@@ -182,7 +182,7 @@
/* offset( 0) align(4) size( 4) */ scalar : f32;
/* offset( 4) align(4) size(160) */ inner : @stride(16) array<f32, 10>;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
@@ -253,7 +253,7 @@
/* align(1) size(5) */ struct Inner {
/* offset(0) align(1) size(5) */ scalar : i32;
/* */ };
-22:24 note: see declaration of variable)");
+22:24 note: 'Outer' used in address space 'uniform' here)");
}
// See https://crbug.com/tint/1344
@@ -308,7 +308,7 @@
/* offset(12) align(1) size( 5) */ scalar : i32;
/* offset(17) align(1) size( 3) */ // -- implicit struct size padding --;
/* */ };
-22:24 note: see declaration of variable)");
+22:24 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest,
@@ -418,7 +418,7 @@
/* offset( 0) align(4) size(40) */ inner : array<f32, 10>;
/* offset(40) align(4) size( 4) */ scalar : i32;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
@@ -453,7 +453,7 @@
/* offset(80) align(4) size( 4) */ scalar : i32;
/* offset(84) align(1) size( 4) */ // -- implicit struct size padding --;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
@@ -495,7 +495,7 @@
/* offset( 0) align(4) size(80) */ inner : array<ArrayElem, 10>;
/* offset(80) align(4) size( 4) */ scalar : i32;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
@@ -507,7 +507,7 @@
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
+ R"(78:90 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
@@ -534,7 +534,7 @@
/* align(4) size(64) */ struct Outer {
/* offset( 0) align(4) size(64) */ inner : array<array<f32, 4>, 4>;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'Outer' used in address space 'uniform' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
@@ -587,7 +587,7 @@
/* offset(5) align(1) size( 4) */ b : f32;
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
/* */ };
-78:90 note: see declaration of variable)");
+78:90 note: 'S' used in address space 'push_constant' here)");
}
TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_Aligned) {
diff --git a/src/tint/resolver/address_space_validation_test.cc b/src/tint/resolver/address_space_validation_test.cc
index 60e54df..bc6c4a4 100644
--- a/src/tint/resolver/address_space_validation_test.cc
+++ b/src/tint/resolver/address_space_validation_test.cc
@@ -27,7 +27,7 @@
using ResolverAddressSpaceValidationTest = ResolverTest;
-TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableNoAddressSpace_Fail) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NoAddressSpace_Fail) {
// var g : f32;
GlobalVar(Source{{12, 34}}, "g", ty.f32());
@@ -36,8 +36,16 @@
"12:34 error: module-scope 'var' declaration must have a address space");
}
-TEST_F(ResolverAddressSpaceValidationTest, GlobalVariableFunctionAddressSpace_Fail) {
- // var<function> g : f32;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NoAddressSpace_Fail) {
+ // type g = ptr<f32>;
+ Alias("g", ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kUndefined));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: ptr missing address space");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_FunctionAddressSpace_Fail) {
+ // var<private> g : f32;
GlobalVar(Source{{12, 34}}, "g", ty.f32(), ast::AddressSpace::kFunction);
EXPECT_FALSE(r()->Resolve());
@@ -45,301 +53,611 @@
"12:34 error: module-scope 'var' must not use address space 'function'");
}
-TEST_F(ResolverAddressSpaceValidationTest, Private_RuntimeArray) {
- GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kPrivate);
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArray) {
+ // var<private> v : array<i32>;
+ GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, ty.i32()),
+ ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating 'var' v)");
}
-TEST_F(ResolverAddressSpaceValidationTest, Private_RuntimeArrayInStruct) {
- auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
- GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kPrivate);
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArray) {
+ // type t : ptr<private, array<i32>>;
+ Alias("t", ty.pointer(Source{{56, 78}}, ty.array(Source{{12, 34}}, ty.i32()),
+ ast::AddressSpace::kPrivate));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating ptr<private, array<i32>, read_write>)");
}
-TEST_F(ResolverAddressSpaceValidationTest, Workgroup_RuntimeArray) {
- GlobalVar(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::AddressSpace::kWorkgroup);
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArrayInStruct) {
+ // struct S { m : array<i32> };
+ // var<private> v : S;
+ Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+ GlobalVar(Source{{56, 78}}, "v", ty.type_name("S"), ast::AddressSpace::kPrivate);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArrayInStruct) {
+ // struct S { m : array<i32> };
+ // type t = ptr<private, S>;
+ Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+ Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kPrivate));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+note: while instantiating ptr<private, S, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArray) {
+ // var<workgroup> v : array<i32>;
+ GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, ty.i32()),
+ ast::AddressSpace::kWorkgroup);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating 'var' v)");
+56:78 note: while instantiating 'var' v)");
}
-TEST_F(ResolverAddressSpaceValidationTest, Workgroup_RuntimeArrayInStruct) {
- auto* s = Structure("S", utils::Vector{Member("m", ty.array(ty.i32()))});
- GlobalVar(Source{{12, 34}}, "v", ty.Of(s), ast::AddressSpace::kWorkgroup);
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArray) {
+ // type t = ptr<workgroup, array<i32>>;
+ Alias("t", ty.pointer(ty.array(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kWorkgroup));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+note: while instantiating ptr<workgroup, array<i32>, read_write>)");
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArrayInStruct) {
+ // struct S { m : array<i32> };
+ // var<workgroup> v : S;
+ Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+ GlobalVar(Source{{56, 78}}, "v", ty.type_name("S"), ast::AddressSpace::kWorkgroup);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArrayInStruct) {
+ // struct S { m : array<i32> };
+ // type t = ptr<workgroup, S>;
+ Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))});
+ Alias(Source{{56, 78}}, "t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kWorkgroup));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+note: while instantiating ptr<workgroup, S, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Bool) {
// var<storage> g : bool;
- GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kStorage, Binding(0_a),
- Group(0_a));
+ GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), ast::AddressSpace::kStorage,
+ Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_Bool) {
+ // type t = ptr<storage, bool>;
+ Alias(Source{{56, 78}}, "t",
+ ty.pointer(ty.bool_(Source{{12, 34}}), ast::AddressSpace::kStorage));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+note: while instantiating ptr<storage, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_BoolAlias) {
// type a = bool;
- // var<storage, read> g : a;
- auto* a = Alias("a", ty.bool_());
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::AddressSpace::kStorage, Binding(0_a),
- Group(0_a));
-
- ASSERT_FALSE(r()->Resolve());
-
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferPointer) {
- // var<storage> g : ptr<private, f32>;
- GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+ // @binding(0) @group(0) var<storage, read> g : a;
+ Alias("a", ty.bool_());
+ GlobalVar(Source{{56, 78}}, "g", ty.type_name(Source{{12, 34}}, "a"),
ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'storage' as it is non-host-shareable
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_BoolAlias) {
+ // type a = bool;
+ // type t = ptr<storage, a>;
+ Alias("a", ty.bool_());
+ Alias(Source{{56, 78}}, "t",
+ ty.pointer(ty.type_name(Source{{12, 34}}, "a"), ast::AddressSpace::kStorage));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+note: while instantiating ptr<storage, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Pointer) {
+ // var<storage> g : ptr<private, f32>;
+ GlobalVar(Source{{56, 78}}, "g",
+ ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+ ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while instantiating 'var' g)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_Pointer) {
+ // type t = ptr<storage, ptr<private, f32>>;
+ Alias("t", ty.pointer(Source{{56, 78}},
+ ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+ ast::AddressSpace::kStorage));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while instantiating ptr<storage, ptr<private, f32, read_write>, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_IntScalar) {
// var<storage> g : i32;
- GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kStorage, Binding(0_a),
- Group(0_a));
+ GlobalVar("g", ty.i32(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_IntScalar) {
+ // type t = ptr<storage, i32;
+ Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16) {
+ // enable f16;
// var<storage> g : f16;
Enable(ast::Extension::kF16);
- GlobalVar("g", ty.f16(Source{{56, 78}}), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
+ GlobalVar("g", ty.f16(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferF16Alias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16) {
+ // enable f16;
+ // type t = ptr<storage, f16>;
+ Enable(ast::Extension::kF16);
+
+ Alias("t", ty.pointer(ty.f16(), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16Alias) {
+ // enable f16;
// type a = f16;
// var<storage, read> g : a;
Enable(ast::Extension::kF16);
- auto* a = Alias("a", ty.f16());
- GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::AddressSpace::kStorage,
- Binding(0_a), Group(0_a));
+ Alias("a", ty.f16());
+ GlobalVar("g", ty.type_name("a"), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16Alias) {
+ // enable f16;
+ // type a = f16;
+ // type t = ptr<storage, a>;
+ Enable(ast::Extension::kF16);
+
+ Alias("a", ty.f16());
+ Alias("t", ty.pointer(ty.type_name("a"), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF32) {
// var<storage> g : vec4<f32>;
- GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a),
- Group(0_a));
+ GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferVectorF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF32) {
+ // type t = ptr<storage, vec4<f32>>;
+ Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF16) {
// var<storage> g : vec4<f16>;
Enable(ast::Extension::kF16);
- GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::AddressSpace::kStorage,
- Binding(0_a), Group(0_a));
+ GlobalVar("g", ty.vec(ty.f16(), 4u), ast::AddressSpace::kStorage, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF16) {
+ // type t = ptr<storage, vec4<f16>>;
+ Enable(ast::Extension::kF16);
+ Alias("t", ty.pointer(ty.vec(ty.f16(), 4u), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF32) {
+ // struct S{ a : f32 };
// var<storage, read> g : array<S, 3u>;
- auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
- auto* a = ty.array(ty.Of(s), 3_u);
- GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kStorage, ast::Access::kRead,
- Binding(0_a), Group(0_a));
+ Structure("S", utils::Vector{Member("a", ty.f32())});
+ GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+ ast::Access::kRead, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferArrayF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF32) {
+ // struct S{ a : f32 };
+ // type t = ptr<storage, array<S, 3u>>;
+ Structure("S", utils::Vector{Member("a", ty.f32())});
+ Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF16) {
+ // enable f16;
+ // struct S{ a : f16 };
// var<storage, read> g : array<S, 3u>;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member("a", ty.f16())});
- auto* a = ty.array(ty.Of(s), 3_u);
- GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kStorage, ast::Access::kRead,
- Binding(0_a), Group(0_a));
+ Structure("S", utils::Vector{Member("a", ty.f16())});
+ GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+ ast::Access::kRead, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32) {
- // struct S { x : i32 };
- // var<storage, read> g : S;
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
- Binding(0_a), Group(0_a));
-
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructI32Aliases) {
- // struct S { x : i32 };
- // type a1 = S;
- // var<storage, read> g : a1;
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
- auto* a1 = Alias("a1", ty.Of(s));
- auto* a2 = Alias("a2", ty.Of(a1));
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
- Binding(0_a), Group(0_a));
-
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16) {
- // struct S { x : f16 };
- // var<storage, read> g : S;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF16) {
+ // enable f16;
+ // struct S{ a : f16 };
+ // type t = ptr<storage, read, array<S, 3u>>;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
- GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+ Structure("S", utils::Vector{Member("a", ty.f16())});
+ Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kStorage,
+ ast::Access::kRead));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32) {
+ // struct S { x : i32 };
+ // var<storage, read> g : S;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, StorageBufferStructF16Aliases) {
- // struct S { x : f16 };
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32) {
+ // struct S { x : i32 };
+ // type t = ptr<storage, read, S>;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32Aliases) {
+ // struct S { x : i32 };
// type a1 = S;
// var<storage, read> g : a1;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("a1", ty.type_name("S"));
+ Alias("a2", ty.type_name("a1"));
+ GlobalVar("g", ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead,
+ Binding(0_a), Group(0_a));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32Aliases) {
+ // struct S { x : i32 };
+ // type a1 = S;
+ // type t = ptr<storage, read, a1>;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("a1", ty.type_name("S"));
+ Alias("a2", ty.type_name("a1"));
+ Alias("t", ty.pointer(ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16) {
+ // struct S { x : f16 };
+ // var<storage, read> g : S;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member("x", ty.f16(Source{{12, 34}}))});
- auto* a1 = Alias("a1", ty.Of(s));
- auto* a2 = Alias("a2", ty.Of(a1));
- GlobalVar("g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead, Binding(0_a),
Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, NotStorage_AccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16) {
+ // struct S { x : f16 };
+ // type t = ptr<storage, read, S>;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16Aliases) {
+ // struct S { x : f16 };
+ // type a1 = S;
+ // var<storage, read> g : a1;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("a1", ty.type_name("S"));
+ Alias("a2", ty.type_name("a1"));
+ GlobalVar("g", ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead,
+ Binding(0_a), Group(0_a));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16Aliases) {
+ // struct S { x : f16 };
+ // type a1 = S;
+ // type t = ptr<storage, read, a1>;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("a1", ty.type_name("S"));
+ Alias("a2", ty.type_name("a1"));
+ Alias("g", ty.pointer(ty.type_name("a2"), ast::AddressSpace::kStorage, ast::Access::kRead));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NotStorage_AccessMode) {
// var<private, read> g : a;
- GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead);
+ GlobalVar(Source{{12, 34}}, "g", ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead);
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: only variables in <storage> address space may declare an access mode)");
+ R"(12:34 error: only variables in <storage> address space may declare an access mode)");
}
-TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NotStorage_AccessMode) {
+ // type t = ptr<private, read, a>;
+ Alias("t",
+ ty.pointer(Source{{12, 34}}, ty.i32(), ast::AddressSpace::kPrivate, ast::Access::kRead));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: only pointers in <storage> address space may declare an access mode)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadAccessMode) {
// @group(0) @binding(0) var<storage, read> a : i32;
- GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead,
- Group(0_a), Binding(0_a));
+ GlobalVar("a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead, Group(0_a),
+ Binding(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, Storage_ReadWriteAccessMode) {
- // @group(0) @binding(0) var<storage, read_write> a : i32;
- GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite,
- Group(0_a), Binding(0_a));
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadAccessMode) {
+ // type t = ptr<storage, read, i32>;
+ Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage, ast::Access::kRead));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, Storage_WriteAccessMode) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadWriteAccessMode) {
// @group(0) @binding(0) var<storage, read_write> a : i32;
- GlobalVar(Source{{56, 78}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite,
+ GlobalVar("a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
+ Binding(0_a));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadWriteAccessMode) {
+ // type t = ptr<storage, read_write, i32>;
+ Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kStorage, ast::Access::kReadWrite));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_WriteAccessMode) {
+ // @group(0) @binding(0) var<storage, read_write> a : i32;
+ GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite,
Group(0_a), Binding(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
+ R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)");
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBuffer_Struct_Runtime) {
- // struct S { m: array<f32>; };
- // @group(0) @binding(0) var<uniform, > svar : S;
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_WriteAccessMode) {
+ // type t = ptr<storage, read_write, i32>;
+ Alias("t",
+ ty.pointer(Source{{12, 34}}, ty.i32(), ast::AddressSpace::kStorage, ast::Access::kWrite));
- auto* s = Structure(Source{{12, 34}}, "S", utils::Vector{Member("m", ty.array<i32>())});
+ ASSERT_FALSE(r()->Resolve());
- GlobalVar(Source{{56, 78}}, "svar", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBuffer_Struct_Runtime) {
+ // struct S { m: array<f32>; };
+ // @group(0) @binding(0) var<uniform> svar : S;
+
+ Structure("S",
+ utils::Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))});
+
+ GlobalVar(Source{{90, 12}}, "svar", ty.type_name("S"), ast::AddressSpace::kUniform,
+ Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
-note: while analyzing structure member S.m
-56:78 note: while instantiating 'var' svar)");
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
+56:78 note: while analyzing structure member S.m
+90:12 note: while instantiating 'var' svar)");
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferBool) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBuffer_Struct_Runtime) {
+ // struct S { m: array<f32>; };
+ // type t = ptr<uniform, S>;
+
+ Structure("S",
+ utils::Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))});
+
+ Alias("t", ty.pointer(Source{{90, 12}}, ty.type_name("S"), ast::AddressSpace::kUniform));
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.
+note: see layout of struct:
+/* align(4) size(4) */ struct S {
+/* offset(0) align(4) size(4) */ m : array<i32>;
+/* */ };
+90:12 note: 'S' used in address space 'uniform' here)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBool) {
// var<uniform> g : bool;
- GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), ast::AddressSpace::kUniform,
+ Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
+ R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferBoolAlias) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBool) {
+ // type t = ptr<uniform, bool>;
+ Alias("t",
+ ty.pointer(Source{{56, 78}}, ty.bool_(Source{{12, 34}}), ast::AddressSpace::kUniform));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating ptr<uniform, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBoolAlias) {
// type a = bool;
// var<uniform> g : a;
- auto* a = Alias("a", ty.bool_());
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
-
- ASSERT_FALSE(r()->Resolve());
-
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
-56:78 note: while instantiating 'var' g)");
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferPointer) {
- // var<uniform> g : ptr<private, f32>;
- GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+ Alias("a", ty.bool_());
+ GlobalVar(Source{{56, 78}}, "g", ty.type_name(Source{{12, 34}}, "a"),
ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'uniform' as it is non-host-shareable
+ R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBoolAlias) {
+ // type a = bool;
+ // type t = ptr<uniform, a>;
+ Alias("a", ty.bool_());
+ Alias("t", ty.pointer(Source{{56, 78}}, ty.type_name(Source{{12, 34}}, "a"),
+ ast::AddressSpace::kUniform));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating ptr<uniform, bool, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformPointer) {
+ // var<uniform> g : ptr<private, f32>;
+ GlobalVar(Source{{56, 78}}, "g",
+ ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+ ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating 'var' g)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformPointer) {
+ // type t = ptr<uniform, ptr<private, f32>>;
+ Alias("t", ty.pointer(Source{{56, 78}},
+ ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+ ast::AddressSpace::kUniform));
+
+ ASSERT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'uniform' as it is non-host-shareable
+56:78 note: while instantiating ptr<uniform, ptr<private, f32, read_write>, read>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferIntScalar) {
// var<uniform> g : i32;
GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::AddressSpace::kUniform, Binding(0_a),
Group(0_a));
@@ -347,124 +665,241 @@
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferIntScalar) {
+ // type t = ptr<uniform, i32>;
+ Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferF16) {
+ // enable f16;
// var<uniform> g : f16;
Enable(ast::Extension::kF16);
- GlobalVar(Source{{56, 78}}, "g", ty.f16(), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ GlobalVar("g", ty.f16(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferF16) {
+ // enable f16;
+ // type t = ptr<uniform, f16>;
+ Enable(ast::Extension::kF16);
+
+ Alias("t", ty.pointer(ty.f16(), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF32) {
// var<uniform> g : vec4<f32>;
- GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ GlobalVar("g", ty.vec4<f32>(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferVectorF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF32) {
+ // type t = ptr<uniform, vec4<f32>>;
+ Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF16) {
+ // enable f16;
// var<uniform> g : vec4<f16>;
Enable(ast::Extension::kF16);
- GlobalVar(Source{{56, 78}}, "g", ty.vec4<f16>(), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ GlobalVar("g", ty.vec4<f16>(), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF16) {
+ // enable f16;
+ // type t = ptr<uniform, vec4<f16>>;
+ Enable(ast::Extension::kF16);
+
+ Alias("t", ty.pointer(ty.vec4<f16>(), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferArrayF32) {
// struct S {
// @size(16) f : f32;
// }
// var<uniform> g : array<S, 3u>;
- auto* s = Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
- auto* a = ty.array(ty.Of(s), 3_u);
- GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+ Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
+ GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform, Binding(0_a),
+ Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferArrayF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF32) {
+ // struct S {
+ // @size(16) f : f32;
+ // }
+ // type t = ptr<uniform, array<S, 3u>>;
+ Structure("S", utils::Vector{Member("a", ty.f32(), utils::Vector{MemberSize(16_a)})});
+ Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferArrayF16) {
+ // enable f16;
// struct S {
// @size(16) f : f16;
// }
// var<uniform> g : array<S, 3u>;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
- auto* a = ty.array(ty.Of(s), 3_u);
- GlobalVar(Source{{56, 78}}, "g", a, ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
-
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32) {
- // struct S { x : i32 };
- // var<uniform> g : S;
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
+ Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
+ GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform, Binding(0_a),
Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructI32Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF16) {
+ // enable f16;
+ // struct S {
+ // @size(16) f : f16;
+ // }
+ // type t = ptr<uniform, array<S, 3u>>;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("a", ty.f16(), utils::Vector{MemberSize(16_a)})});
+ Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructI32) {
+ // struct S { x : i32 };
+ // var<uniform> g : S;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32) {
+ // struct S { x : i32 };
+ // type t = ptr<uniform, S>;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructI32Aliases) {
// struct S { x : i32 };
// type a1 = S;
// var<uniform> g : a1;
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.i32())});
- auto* a1 = Alias("a1", ty.Of(s));
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("a1", ty.type_name("S"));
+ GlobalVar("g", ty.type_name("a1"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32Aliases) {
+ // struct S { x : i32 };
+ // type a1 = S;
+ // type t = ptr<uniform, a1>;
+ Structure("S", utils::Vector{Member("x", ty.i32())});
+ Alias("a1", ty.type_name("S"));
+ Alias("t", ty.pointer(ty.type_name("a1"), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16) {
+ // enable f16;
// struct S { x : f16 };
- // var<uniform> g : S;
+ // var<uniform> g : S;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ GlobalVar("g", ty.type_name("S"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, UniformBufferStructF16Aliases) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16) {
+ // enable f16;
+ // struct S { x : f16 };
+ // type t = ptr<uniform, S>;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("t", ty.pointer(ty.type_name("S"), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16Aliases) {
+ // enable f16;
// struct S { x : f16 };
// type a1 = S;
// var<uniform> g : a1;
Enable(ast::Extension::kF16);
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.f16())});
- auto* a1 = Alias("a1", ty.Of(s));
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a1), ast::AddressSpace::kUniform, Binding(0_a),
- Group(0_a));
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("a1", ty.type_name("S"));
+ GlobalVar("g", ty.type_name("a1"), ast::AddressSpace::kUniform, Binding(0_a), Group(0_a));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantBool) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16Aliases) {
+ // enable f16;
+ // struct S { x : f16 };
+ // type a1 = S;
+ // type t = ptr<uniform, a1>;
+ Enable(ast::Extension::kF16);
+
+ Structure("S", utils::Vector{Member("x", ty.f16())});
+ Alias("a1", ty.type_name("S"));
+ Alias("t", ty.pointer(ty.type_name("a1"), ast::AddressSpace::kUniform));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantBool) {
// enable chromium_experimental_push_constant;
// var<push_constant> g : bool;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
- GlobalVar(Source{{56, 78}}, "g", ty.bool_(), ast::AddressSpace::kPushConstant);
+ GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), ast::AddressSpace::kPushConstant);
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
+ R"(12:34 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantF16) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantBool) {
// enable chromium_experimental_push_constant;
+ // type t = ptr<push_constant, bool>;
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ Alias(Source{{56, 78}}, "t",
+ ty.pointer(ty.bool_(Source{{12, 34}}), ast::AddressSpace::kPushConstant));
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable
+note: while instantiating ptr<push_constant, bool, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantF16) {
// enable f16;
+ // enable chromium_experimental_push_constant;
// var<push_constant> g : f16;
Enable(ast::Extension::kF16);
Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -472,25 +907,53 @@
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "56:78 error: using f16 types in 'push_constant' address space is not "
- "implemented yet");
+ "error: using f16 types in 'push_constant' address space is not implemented yet");
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantPointer) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantF16) {
+ // enable f16;
+ // enable chromium_experimental_push_constant;
+ // type t = ptr<push_constant, f16>;
+ Enable(ast::Extension::kF16);
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ Alias("t", ty.pointer(ty.f16(Source{{56, 78}}), ast::AddressSpace::kPushConstant));
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: using f16 types in 'push_constant' address space is not implemented yet");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantPointer) {
// enable chromium_experimental_push_constant;
// var<push_constant> g : ptr<private, f32>;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
- GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::AddressSpace::kPrivate),
+ GlobalVar(Source{{56, 78}}, "g",
+ ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
ast::AddressSpace::kPushConstant);
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantIntScalar) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantPointer) {
+ // enable chromium_experimental_push_constant;
+ // type t = ptr<push_constant, ptr<private, f32>>;
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ Alias(Source{{56, 78}}, "t",
+ ty.pointer(ty.pointer(Source{{12, 34}}, ty.f32(), ast::AddressSpace::kPrivate),
+ ast::AddressSpace::kPushConstant));
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable
+note: while instantiating ptr<push_constant, ptr<private, f32, read_write>, read_write>)");
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantIntScalar) {
// enable chromium_experimental_push_constant;
// var<push_constant> g : i32;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -499,7 +962,16 @@
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantVectorF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantIntScalar) {
+ // enable chromium_experimental_push_constant;
+ // type t = ptr<push_constant, i32>;
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ Alias("t", ty.pointer(ty.i32(), ast::AddressSpace::kPushConstant));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantVectorF32) {
// enable chromium_experimental_push_constant;
// var<push_constant> g : vec4<f32>;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
@@ -508,29 +980,35 @@
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantArrayF32) {
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantVectorF32) {
// enable chromium_experimental_push_constant;
- // struct S { a : f32}
- // var<push_constant> g : array<S, 3u>;
+ // var<push_constant> g : vec4<f32>;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
- auto* s = Structure("S", utils::Vector{Member("a", ty.f32())});
- auto* a = ty.array(ty.Of(s), 3_u);
- GlobalVar("g", a, ast::AddressSpace::kPushConstant);
+ Alias("t", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kPushConstant));
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAddressSpaceValidationTest, PushConstantWithInitializer) {
+TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantArrayF32) {
// enable chromium_experimental_push_constant;
- // var<push_constant> a : u32 = 0u;
+ // struct S { a : f32}
+ // var<push_constant> g : array<S, 3u>;
Enable(ast::Extension::kChromiumExperimentalPushConstant);
- GlobalVar(Source{{1u, 2u}}, "a", ty.u32(), ast::AddressSpace::kPushConstant,
- Expr(Source{{3u, 4u}}, u32(0)));
+ Structure("S", utils::Vector{Member("a", ty.f32())});
+ GlobalVar("g", ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kPushConstant);
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:2 error: var of address space 'push_constant' cannot have an initializer. var initializers are only supported for the address spacees 'private' and 'function')");
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantArrayF32) {
+ // enable chromium_experimental_push_constant;
+ // struct S { a : f32}
+ // type t = ptr<push_constant, array<S, 3u>>;
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ Structure("S", utils::Vector{Member("a", ty.f32())});
+ Alias("t", ty.pointer(ty.array(ty.type_name("S"), 3_u), ast::AddressSpace::kPushConstant));
+
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index 08bdf13..8b79941 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -40,7 +40,7 @@
}
TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage_Struct) {
- auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
Binding(0_a));
@@ -55,31 +55,28 @@
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Simple) {
- GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
+ GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: atomic variables must have <storage> or <workgroup> "
- "address space");
+ "12:34 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Array) {
- GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
+ GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: atomic variables must have <storage> or <workgroup> "
- "address space");
+ "12:34 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Struct) {
- auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
+ auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(ty.i32()))});
+ GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
+ "56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
"note: atomic sub-type of 's' is declared here");
}
@@ -91,12 +88,11 @@
auto* Inner =
Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
- GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
+ GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
+ "56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
"note: atomic sub-type of 'Outer' is declared here");
}
@@ -108,13 +104,12 @@
auto* Inner =
Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
- GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
+ GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
- "12:34 note: atomic sub-type of 'Outer' is declared here");
+ R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
+12:34 note: atomic sub-type of 'Outer' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
@@ -127,8 +122,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space");
+ "56:78 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
@@ -137,14 +131,13 @@
// };
// var<private> v: array<S, 5u>;
- auto* s = Structure("S", utils::Vector{Member("m", ty.atomic<u32>())});
+ auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic<u32>())});
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
- "note: atomic sub-type of 'array<S, 5>' is declared here");
+ R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
+12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
@@ -154,16 +147,14 @@
// };
// var<private> v: array<S, 5u>;
- auto* atomic_array =
- Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
- auto* s = Structure("S", utils::Vector{Member("m", ty.Of(atomic_array))});
+ auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
+ auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.Of(atomic_array))});
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
- "note: atomic sub-type of 'array<S, 5>' is declared here");
+ R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
+12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
@@ -181,19 +172,18 @@
// struct S0 { x: S1; };
// var<private> g : S0;
- auto* atomic_array =
- Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
- auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
- Member("y", ty.Of(atomic_array)), //
- Member("z", array_atomic_u32_8)}); //
- auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
- Member("y", ty.Of(s5)), //
- Member("z", array_atomic_i32_4)}); //
+ auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
+ Member(Source{{12, 34}}, "y", ty.Of(atomic_array)), //
+ Member("z", array_atomic_u32_8)}); //
+ auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(s5)), //
+ Member("z", array_atomic_i32_4)}); //
auto* s3 = Structure("S3", utils::Vector{Member("x", ty.Of(s4))});
auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
@@ -202,33 +192,32 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "address space\n"
- "note: atomic sub-type of 'S0' is declared here");
+ R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
+12:34 note: atomic sub-type of 'S0' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
- auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> address space must have read_write "
- "access mode\n"
- "note: atomic sub-type of 's' is declared here");
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
+12:34 note: atomic sub-type of 's' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
- auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> address space must have read_write "
- "access mode\n"
- "note: atomic sub-type of 's' is declared here");
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
+12:34 note: atomic sub-type of 's' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
@@ -237,16 +226,16 @@
// var<storage, read> g : Outer;
auto* Inner =
- Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kStorage, ast::Access::kRead,
Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> address space must have read_write "
- "access mode\n"
- "note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
+12:34 note: atomic sub-type of 'Outer' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
@@ -261,10 +250,10 @@
Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> address space must have "
- "read_write access mode\n"
- "12:34 note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
+12:34 note: atomic sub-type of 'Outer' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
@@ -282,31 +271,30 @@
// struct S0 { x: S1; };
// var<storage, read> g : S0;
- auto* atomic_array =
- Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
- auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
- Member("y", ty.Of(atomic_array)), //
- Member("z", array_atomic_u32_8)}); //
- auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
- Member("y", ty.Of(s5)), //
- Member("z", array_atomic_i32_4)}); //
+ auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
+ Member(Source{{56, 78}}, "y", ty.Of(atomic_array)), //
+ Member("z", array_atomic_u32_8)}); //
+ auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(s5)), //
+ Member("z", array_atomic_i32_4)}); //
auto* s3 = Structure("S3", utils::Vector{Member("x", ty.Of(s4))});
auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::AddressSpace::kStorage, ast::Access::kRead,
+ GlobalVar(Source{{12, 34}}, "g", ty.Of(s0), ast::AddressSpace::kStorage, ast::Access::kRead,
Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> address space must have "
- "read_write access mode\n"
- "note: atomic sub-type of 'S0' is declared here");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: atomic variables in <storage> address space must have read_write access mode
+56:78 note: atomic sub-type of 'S0' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, Local) {
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index b7674dc..7195824 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -779,24 +779,22 @@
}
} else {
using T = UnwrapNumber<NumberT>;
- auto divide_values = [](T lhs, T rhs) {
- if constexpr (std::is_integral_v<T>) {
- // For integers, lhs / 0 returns lhs
- if (rhs == 0) {
- return lhs;
- }
-
- if constexpr (std::is_signed_v<T>) {
- // For signed integers, for lhs / -1, return lhs if lhs is the
- // most negative value
- if (rhs == -1 && lhs == std::numeric_limits<T>::min()) {
- return lhs;
- }
- }
+ auto lhs = a.value;
+ auto rhs = b.value;
+ if (rhs == 0) {
+ // For integers (as for floats), lhs / 0 is an error
+ AddError(OverflowErrorMessage(a, "/", b), source);
+ return utils::Failure;
+ }
+ if constexpr (std::is_signed_v<T>) {
+ // For signed integers, lhs / -1 where lhs is the
+ // most negative value is an error
+ if (rhs == -1 && lhs == std::numeric_limits<T>::min()) {
+ AddError(OverflowErrorMessage(a, "/", b), source);
+ return utils::Failure;
}
- return lhs / rhs;
- };
- result = divide_values(a.value, b.value);
+ }
+ result = lhs / rhs;
}
return result;
}
diff --git a/src/tint/resolver/const_eval_binary_op_test.cc b/src/tint/resolver/const_eval_binary_op_test.cc
index a35214a..2a13360 100644
--- a/src/tint/resolver/const_eval_binary_op_test.cc
+++ b/src/tint/resolver/const_eval_binary_op_test.cc
@@ -419,36 +419,31 @@
template <typename T>
std::vector<Case> OpDivIntCases() {
- std::vector<Case> r = {
- C(Val(T{0}), Val(T{1}), Val(T{0})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{2}), Val(T{1}), Val(T{2})),
- C(Val(T{4}), Val(T{2}), Val(T{2})),
- C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
- C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
- C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
- C(Val(T{0}), Val(T::Highest()), Val(T{0})),
- C(Val(T{0}), Val(T::Lowest()), Val(T{0})),
- };
- ConcatIntoIf<!IsAbstract<T> && IsIntegral<T>>( //
- r, std::vector<Case>{
- // e1, when e2 is zero.
- C(T{123}, T{0}, T{123}),
- });
- ConcatIntoIf<!IsAbstract<T> && IsSignedIntegral<T>>( //
- r, std::vector<Case>{
- // e1, when e1 is the most negative value in T, and e2 is -1.
- C(T::Smallest(), T{-1}, T::Smallest()),
- });
-
auto error_msg = [](auto a, auto b) {
return "12:34 error: " + OverflowErrorMessage(a, "/", b);
};
- ConcatIntoIf<IsAbstract<T>>( //
+
+ std::vector<Case> r = {
+ C(T{0}, T{1}, T{0}),
+ C(T{1}, T{1}, T{1}),
+ C(T{1}, T{1}, T{1}),
+ C(T{2}, T{1}, T{2}),
+ C(T{4}, T{2}, T{2}),
+ C(T::Highest(), T{1}, T::Highest()),
+ C(T::Lowest(), T{1}, T::Lowest()),
+ C(T::Highest(), T::Highest(), T{1}),
+ C(T{0}, T::Highest(), T{0}),
+
+ // Divide by zero
+ E(T{123}, T{0}, error_msg(T{123}, T{0})),
+ E(T::Highest(), T{0}, error_msg(T::Highest(), T{0})),
+ E(T::Lowest(), T{0}, error_msg(T::Lowest(), T{0})),
+ };
+
+ // Error on most negative divided by -1
+ ConcatIntoIf<IsSignedIntegral<T>>( //
r, std::vector<Case>{
- // Most negative value divided by -1
- E(AInt::Lowest(), -1_a, error_msg(AInt::Lowest(), -1_a)),
+ E(T::Lowest(), T{-1}, error_msg(T::Lowest(), T{-1})),
});
return r;
}
@@ -459,16 +454,16 @@
return "12:34 error: " + OverflowErrorMessage(a, "/", b);
};
std::vector<Case> r = {
- C(Val(T{0}), Val(T{1}), Val(T{0})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{1}), Val(T{1}), Val(T{1})),
- C(Val(T{2}), Val(T{1}), Val(T{2})),
- C(Val(T{4}), Val(T{2}), Val(T{2})),
- C(Val(T::Highest()), Val(T{1}), Val(T::Highest())),
- C(Val(T::Lowest()), Val(T{1}), Val(T::Lowest())),
- C(Val(T::Highest()), Val(T::Highest()), Val(T{1})),
- C(Val(T{0}), Val(T::Highest()), Val(T{0})),
- C(Val(T{0}), Val(T::Lowest()), Val(-T{0})),
+ C(T{0}, T{1}, T{0}),
+ C(T{1}, T{1}, T{1}),
+ C(T{1}, T{1}, T{1}),
+ C(T{2}, T{1}, T{2}),
+ C(T{4}, T{2}, T{2}),
+ C(T::Highest(), T{1}, T::Highest()),
+ C(T::Lowest(), T{1}, T::Lowest()),
+ C(T::Highest(), T::Highest(), T{1}),
+ C(T{0}, T::Highest(), T{0}),
+ C(T{0}, T::Lowest(), -T{0}),
// Divide by zero
E(T{123}, T{0}, error_msg(T{123}, T{0})),
@@ -493,10 +488,10 @@
template <typename T, bool equals>
std::vector<Case> OpEqualCases() {
return {
- C(Val(T{0}), Val(T{0}), Val(true == equals)),
- C(Val(T{0}), Val(T{1}), Val(false == equals)),
- C(Val(T{1}), Val(T{0}), Val(false == equals)),
- C(Val(T{1}), Val(T{1}), Val(true == equals)),
+ C(T{0}, T{0}, true == equals),
+ C(T{0}, T{1}, false == equals),
+ C(T{1}, T{0}, false == equals),
+ C(T{1}, T{1}, true == equals),
C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(true == equals, true == equals)),
C(Vec(T{1}, T{0}), Vec(T{0}, T{1}), Vec(false == equals, false == equals)),
C(Vec(T{1}, T{1}), Vec(T{0}, T{1}), Vec(false == equals, true == equals)),
@@ -530,10 +525,10 @@
template <typename T, bool less_than>
std::vector<Case> OpLessThanCases() {
return {
- C(Val(T{0}), Val(T{0}), Val(false == less_than)),
- C(Val(T{0}), Val(T{1}), Val(true == less_than)),
- C(Val(T{1}), Val(T{0}), Val(false == less_than)),
- C(Val(T{1}), Val(T{1}), Val(false == less_than)),
+ C(T{0}, T{0}, false == less_than),
+ C(T{0}, T{1}, true == less_than),
+ C(T{1}, T{0}, false == less_than),
+ C(T{1}, T{1}, false == less_than),
C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(true == less_than, true == less_than)),
C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(false == less_than, false == less_than)),
@@ -566,10 +561,10 @@
template <typename T, bool greater_than>
std::vector<Case> OpGreaterThanCases() {
return {
- C(Val(T{0}), Val(T{0}), Val(false == greater_than)),
- C(Val(T{0}), Val(T{1}), Val(false == greater_than)),
- C(Val(T{1}), Val(T{0}), Val(true == greater_than)),
- C(Val(T{1}), Val(T{1}), Val(false == greater_than)),
+ C(T{0}, T{0}, false == greater_than),
+ C(T{0}, T{1}, false == greater_than),
+ C(T{1}, T{0}, true == greater_than),
+ C(T{1}, T{1}, false == greater_than),
C(Vec(T{0}, T{0}), Vec(T{0}, T{0}), Vec(false == greater_than, false == greater_than)),
C(Vec(T{1}, T{1}), Vec(T{0}, T{0}), Vec(true == greater_than, true == greater_than)),
C(Vec(T{0}, T{0}), Vec(T{1}, T{1}), Vec(false == greater_than, false == greater_than)),
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index 97519cd..325595c 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -26,49 +26,52 @@
using ResolverHostShareableValidationTest = ResolverTest;
TEST_F(ResolverHostShareableValidationTest, BoolMember) {
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.bool_())});
+ auto* s =
+ Structure("S", utils::Vector{Member(Source{{56, 78}}, "x", ty.bool_(Source{{12, 34}}))});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
+ GlobalVar(Source{{90, 12}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
}
TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
+ auto* s = Structure(
+ "S", utils::Vector{Member(Source{{56, 78}}, "x", ty.vec3<bool>(Source{{12, 34}}))});
- GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
+ GlobalVar(Source{{90, 12}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'vec3<bool>' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+ R"(12:34 error: Type 'vec3<bool>' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
}
TEST_F(ResolverHostShareableValidationTest, Aliases) {
- auto* a1 = Alias("a1", ty.bool_());
- auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "x", ty.Of(a1))});
+ Alias("a1", ty.bool_());
+ auto* s = Structure(
+ "S", utils::Vector{Member(Source{{56, 78}}, "x", ty.type_name(Source{{12, 34}}, "a1"))});
auto* a2 = Alias("a2", ty.Of(s));
- GlobalVar(Source{{56, 78}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
+ GlobalVar(Source{{90, 12}}, "g", ty.Of(a2), ast::AddressSpace::kStorage, ast::Access::kRead,
Binding(0_a), Group(0_a));
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
-12:34 note: while analyzing structure member S.x
-56:78 note: while instantiating 'var' g)");
+ R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+56:78 note: while analyzing structure member S.x
+90:12 note: while instantiating 'var' g)");
}
TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
@@ -85,7 +88,7 @@
EXPECT_EQ(
r()->error(),
- R"(9:10 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
+ R"(error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable
1:2 note: while analyzing structure member I1.x
3:4 note: while analyzing structure member I2.y
5:6 note: while analyzing structure member I3.z
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 0fdd78c..cf55a1d 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -103,7 +103,11 @@
const_eval_(*builder),
intrinsic_table_(IntrinsicTable::Create(*builder)),
sem_(builder, dependencies_),
- validator_(builder, sem_) {}
+ validator_(builder,
+ sem_,
+ enabled_extensions_,
+ atomic_composite_info_,
+ valid_type_storage_layouts_) {}
Resolver::~Resolver() = default;
@@ -258,7 +262,18 @@
if (access == ast::Access::kUndefined) {
access = DefaultAccessForAddressSpace(t->address_space);
}
- return builder_->create<sem::Pointer>(el, t->address_space, access);
+ auto ptr = builder_->create<sem::Pointer>(el, t->address_space, access);
+ if (!ptr) {
+ return nullptr;
+ }
+ if (!validator_.Pointer(t, ptr)) {
+ return nullptr;
+ }
+ if (!ApplyAddressSpaceUsageToType(t->address_space, el, t->type->source)) {
+ AddNote("while instantiating " + builder_->FriendlyName(ptr), t->source);
+ return nullptr;
+ }
+ return ptr;
}
return nullptr;
},
@@ -621,7 +636,8 @@
auto* var_ty = builder_->create<sem::Reference>(storage_ty, address_space, access);
- if (!ApplyAddressSpaceUsageToType(address_space, var_ty, var->source)) {
+ if (!ApplyAddressSpaceUsageToType(address_space, var_ty,
+ var->type ? var->type->source : var->source)) {
AddNote("while instantiating 'var' " + builder_->Symbols().NameFor(var->symbol),
var->source);
return nullptr;
@@ -723,7 +739,7 @@
return nullptr;
}
- if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, ty, param->source)) {
+ if (!ApplyAddressSpaceUsageToType(ast::AddressSpace::kNone, ty, param->type->source)) {
add_note();
return nullptr;
}
@@ -891,13 +907,7 @@
return nullptr;
}
- if (!validator_.GlobalVariable(sem, override_ids_, atomic_composite_info_)) {
- return nullptr;
- }
-
- // TODO(bclayton): Call this at the end of resolve on all uniform and storage
- // referenced structs
- if (!validator_.AddressSpaceLayout(sem, enabled_extensions_, valid_type_storage_layouts_)) {
+ if (!validator_.GlobalVariable(sem, override_ids_)) {
return nullptr;
}
@@ -2300,7 +2310,7 @@
current_function_->AddDirectCall(call);
}
- if (!validator_.RequiredExtensionForBuiltinFunction(call, enabled_extensions_)) {
+ if (!validator_.RequiredExtensionForBuiltinFunction(call)) {
return nullptr;
}
@@ -3564,12 +3574,14 @@
str->AddUsage(address_space);
for (auto* member : str->Members()) {
- if (!ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
- usage)) {
+ auto decl = member->Declaration();
+ if (decl &&
+ !ApplyAddressSpaceUsageToType(address_space, const_cast<sem::Type*>(member->Type()),
+ decl->type->source)) {
std::stringstream err;
err << "while analyzing structure member " << sem_.TypeNameOf(str) << "."
- << builder_->Symbols().NameFor(member->Declaration()->symbol);
- AddNote(err.str(), member->Declaration()->source);
+ << builder_->Symbols().NameFor(decl->symbol);
+ AddNote(err.str(), decl->source);
return false;
}
}
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 0b31fe1..abc7633 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -111,8 +111,6 @@
const Validator* GetValidatorForTesting() const { return &validator_; }
private:
- Validator::ValidTypeStorageLayouts valid_type_storage_layouts_;
-
/// Resolves the program, without creating final the semantic nodes.
/// @returns true on success, false on error
bool ResolveInternal();
@@ -471,6 +469,7 @@
sem::CompoundStatement* current_compound_statement_ = nullptr;
uint32_t current_scoping_depth_ = 0;
utils::UniqueVector<const sem::GlobalVariable*, 4>* resolved_overrides_ = nullptr;
+ utils::Hashset<TypeAndAddressSpace, 8> valid_type_storage_layouts_;
};
} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index d44c9d8..68a7017 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -608,7 +608,7 @@
/// @return a new AST alias type
static inline const ast::Type* AST(ProgramBuilder& b) {
return b.create<ast::Pointer>(DataType<T>::AST(b), ast::AddressSpace::kPrivate,
- ast::Access::kReadWrite);
+ ast::Access::kUndefined);
}
/// @param b the ProgramBuilder
/// @return the semantic aliased type
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 59367d0..fe1aa05 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -321,12 +321,11 @@
TEST_F(ResolverTypeValidationTest, ArraySize_OverElementCountLimit) {
// var<private> a : array<f32, 65536>;
- GlobalVar(Source{{1, 2}}, "a",
- ty.array(Source{{12, 34}}, ty.f32(), Expr(Source{{12, 34}}, 65536_a)),
+ GlobalVar(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.f32(), Expr(65536_a)),
ast::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(1:2 error: array count (65536) must be less than 65536
-1:2 note: while instantiating 'var' a)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: array count (65536) must be less than 65536
+56:78 note: while instantiating 'var' a)");
}
TEST_F(ResolverTypeValidationTest, ArraySize_StorageBufferLargeArray) {
@@ -569,7 +568,7 @@
/// @vertex
// fn func() { var a : array<i32>; }
- auto* var = Var(Source{{12, 34}}, "a", ty.array<i32>());
+ auto* var = Var(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
Func("func", utils::Empty, ty.void_(),
utils::Vector{
@@ -582,7 +581,7 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating 'var' a)");
+56:78 note: while instantiating 'var' a)");
}
TEST_F(ResolverTypeValidationTest, Struct_Member_VectorNoType) {
@@ -731,23 +730,24 @@
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsGlobalVariable) {
- GlobalVar(Source{{56, 78}}, "g", ty.array<i32>(), ast::AddressSpace::kPrivate);
+ GlobalVar(Source{{56, 78}}, "g", ty.array(Source{{12, 34}}, ty.i32()),
+ ast::AddressSpace::kPrivate);
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
56:78 note: while instantiating 'var' g)");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsLocalVariable) {
- auto* v = Var(Source{{56, 78}}, "g", ty.array<i32>());
+ auto* v = Var(Source{{56, 78}}, "g", ty.array(Source{{12, 34}}, ty.i32()));
WrapInFunction(v);
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> address space
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
56:78 note: while instantiating 'var' g)");
}
@@ -755,7 +755,7 @@
// fn func(a : array<u32>) {}
// @vertex fn main() {}
- auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
+ auto* param = Param(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
Func("func", utils::Vector{param}, ty.void_(),
utils::Vector{
@@ -773,14 +773,14 @@
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating parameter a)");
+56:78 note: while instantiating parameter a)");
}
-TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsParameter_Fail) {
+TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsPointerParameter_Fail) {
// fn func(a : ptr<workgroup, array<u32>>) {}
- auto* param =
- Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::AddressSpace::kWorkgroup));
+ auto* param = Param("a", ty.pointer(Source{{56, 78}}, ty.array(Source{{12, 34}}, ty.i32()),
+ ast::AddressSpace::kWorkgroup));
Func("func", utils::Vector{param}, ty.void_(),
utils::Vector{
@@ -790,7 +790,23 @@
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-12:34 note: while instantiating parameter a)");
+56:78 note: while instantiating ptr<workgroup, array<i32>, read_write>)");
+}
+
+TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsParameter_Fail) {
+ // fn func(a : ptr<workgroup, array<u32>>) {}
+
+ auto* param = Param(Source{{56, 78}}, "a", ty.array(Source{{12, 34}}, ty.i32()));
+
+ Func("func", utils::Vector{param}, ty.void_(),
+ utils::Vector{
+ Return(),
+ });
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
+56:78 note: while instantiating parameter a)");
}
TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsNotLast_Fail) {
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index ffedaf6..b47be22 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -673,7 +673,7 @@
}
// Set each variable's exit node as its value in the outer scope.
- for (auto& v : info.var_exit_nodes) {
+ for (auto v : info.var_exit_nodes) {
current_function_->variables.Set(v.key, v.value);
}
@@ -726,7 +726,7 @@
cfx->AddEdge(cf);
// Add edges from variable loop input nodes to their values at the end of the loop.
- for (auto& v : info.var_in_nodes) {
+ for (auto v : info.var_in_nodes) {
auto* in_node = v.value;
auto* out_node = current_function_->variables.Get(v.key);
if (out_node != in_node) {
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 70d9f8e..70e1de1 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -153,8 +153,18 @@
} // namespace
-Validator::Validator(ProgramBuilder* builder, SemHelper& sem)
- : symbols_(builder->Symbols()), diagnostics_(builder->Diagnostics()), sem_(sem) {}
+Validator::Validator(
+ ProgramBuilder* builder,
+ SemHelper& sem,
+ const ast::Extensions& enabled_extensions,
+ const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info,
+ utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts)
+ : symbols_(builder->Symbols()),
+ diagnostics_(builder->Diagnostics()),
+ sem_(sem),
+ enabled_extensions_(enabled_extensions),
+ atomic_composite_info_(atomic_composite_info),
+ valid_type_storage_layouts_(valid_type_storage_layouts) {}
Validator::~Validator() = default;
@@ -259,6 +269,28 @@
return true;
}
+bool Validator::Pointer(const ast::Pointer* a, const sem::Pointer* s) const {
+ if (s->AddressSpace() == ast::AddressSpace::kUndefined) {
+ AddError("ptr missing address space", a->source);
+ return false;
+ }
+
+ if (a->access != ast::Access::kUndefined) {
+ // https://www.w3.org/TR/WGSL/#access-mode-defaults
+ // When writing a variable declaration or a pointer type in WGSL source:
+ // * For the storage address space, the access mode is optional, and defaults to read.
+ // * For other address spaces, the access mode must not be written.
+ if (a->address_space != ast::AddressSpace::kStorage) {
+ AddError("only pointers in <storage> address space may declare an access mode",
+ a->source);
+ return false;
+ }
+ }
+
+ return CheckTypeAccessAddressSpace(s->StoreType(), s->Access(), s->AddressSpace(), utils::Empty,
+ a->source);
+}
+
bool Validator::StorageTexture(const ast::StorageTexture* t) const {
switch (t->access) {
case ast::Access::kWrite:
@@ -345,11 +377,10 @@
default:
// https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
// Optionally has an initializer expression, if the variable is in the private or
- // function address spacees.
+ // function address spaces.
AddError("var of address space '" + utils::ToString(address_space) +
- "' cannot have an initializer. var initializers are only "
- "supported for the address spacees "
- "'private' and 'function'",
+ "' cannot have an initializer. var initializers are only supported "
+ "for the address spaces 'private' and 'function'",
v->source);
return false;
}
@@ -360,8 +391,7 @@
bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
ast::AddressSpace address_space,
- Source source,
- ValidTypeStorageLayouts& layouts) const {
+ Source source) const {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
auto is_uniform_struct_or_array = [address_space](const sem::Type* ty) {
@@ -386,8 +416,8 @@
return symbols_.NameFor(sm->Declaration()->symbol);
};
- // Cache result of type + address space pair.
- if (!layouts.emplace(store_ty, address_space).second) {
+ // Only validate the [type + address space] once
+ if (!valid_type_storage_layouts_.Add(TypeAndAddressSpace{store_ty, address_space})) {
return true;
}
@@ -395,6 +425,12 @@
return true;
}
+ auto note_usage = [&] {
+ AddNote("'" + store_ty->FriendlyName(symbols_) + "' used in address space '" +
+ utils::ToString(address_space) + "' here",
+ source);
+ };
+
// Among three host-shareable address spaces, f16 is supported in "uniform" and
// "storage" address space, but not "push_constant" address space yet.
if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty)) &&
@@ -409,10 +445,10 @@
uint32_t required_align = required_alignment_of(m->Type());
// Recurse into the member type.
- if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source,
- layouts)) {
+ if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source)) {
AddNote("see layout of struct:\n" + str->Layout(symbols_),
str->Declaration()->source);
+ note_usage();
return false;
}
@@ -435,6 +471,7 @@
member_str->Declaration()->source);
}
+ note_usage();
return false;
}
@@ -460,6 +497,7 @@
AddNote("and layout of previous member struct:\n" +
prev_member_str->Layout(symbols_),
prev_member_str->Declaration()->source);
+ note_usage();
return false;
}
}
@@ -472,7 +510,7 @@
// TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested element type here, but
// we can't easily get that from the semantic node. We should consider recursing through the
// AST type nodes instead.
- if (!AddressSpaceLayout(arr->ElemType(), address_space, source, layouts)) {
+ if (!AddressSpaceLayout(arr->ElemType(), address_space, source)) {
return false;
}
@@ -484,21 +522,16 @@
// shader author can resolve the issue.
std::string hint;
if (arr->ElemType()->is_scalar()) {
- hint =
- "Consider using a vector or struct as the element type "
- "instead.";
+ hint = "Consider using a vector or struct as the element type instead.";
} else if (auto* vec = arr->ElemType()->As<sem::Vector>();
vec && vec->type()->Size() == 4) {
hint = "Consider using a vec4 instead.";
} else if (arr->ElemType()->Is<sem::Struct>()) {
- hint =
- "Consider using the @size attribute on the last struct "
- "member.";
+ hint = "Consider using the @size attribute on the last struct member.";
} else {
hint =
- "Consider wrapping the element type in a struct and using "
- "the "
- "@size attribute.";
+ "Consider wrapping the element type in a struct and using the @size "
+ "attribute.";
}
AddError(
"uniform storage requires that array elements be aligned to 16 "
@@ -513,42 +546,6 @@
return true;
}
-bool Validator::AddressSpaceLayout(const sem::Variable* var,
- const ast::Extensions& enabled_extensions,
- ValidTypeStorageLayouts& layouts) const {
- if (var->AddressSpace() == ast::AddressSpace::kPushConstant &&
- !enabled_extensions.Contains(ast::Extension::kChromiumExperimentalPushConstant) &&
- IsValidationEnabled(var->Declaration()->attributes,
- ast::DisabledValidation::kIgnoreAddressSpace)) {
- AddError(
- "use of variable address space 'push_constant' requires enabling extension "
- "'chromium_experimental_push_constant'",
- var->Declaration()->source);
- return false;
- }
-
- if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
- // Check the structure has a declaration. Builtins like modf() and frexp() return untypeable
- // structures, and so they have no declaration. Just skip validation for these.
- if (auto* str_decl = str->Declaration()) {
- if (!AddressSpaceLayout(str, var->AddressSpace(), str_decl->source, layouts)) {
- AddNote("see declaration of variable", var->Declaration()->source);
- return false;
- }
- }
- } else {
- Source source = var->Declaration()->source;
- if (var->Declaration()->type) {
- source = var->Declaration()->type->source;
- }
- if (!AddressSpaceLayout(var->Type()->UnwrapRef(), var->AddressSpace(), source, layouts)) {
- return false;
- }
- }
-
- return true;
-}
-
bool Validator::LocalVariable(const sem::Variable* local) const {
auto* decl = local->Declaration();
if (IsArrayWithOverrideCount(local->Type())) {
@@ -581,8 +578,7 @@
bool Validator::GlobalVariable(
const sem::GlobalVariable* global,
- const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids,
- const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
+ const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids) const {
auto* decl = global->Declaration();
if (global->AddressSpace() != ast::AddressSpace::kWorkgroup &&
IsArrayWithOverrideCount(global->Type())) {
@@ -592,7 +588,7 @@
}
bool ok = Switch(
decl, //
- [&](const ast::Var* var) {
+ [&](const ast::Var*) {
if (auto* init = global->Initializer();
init && init->Stage() > sem::EvaluationStage::kOverride) {
AddError("module-scope 'var' initializer must be a constant or override-expression",
@@ -620,29 +616,6 @@
}
}
- // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
- // The access mode always has a default, and except for variables in the storage address
- // space, must not be written.
- if (var->declared_access != ast::Access::kUndefined) {
- if (global->AddressSpace() == ast::AddressSpace::kStorage) {
- // The access mode for the storage address space can only be 'read' or
- // 'read_write'.
- if (var->declared_access == ast::Access::kWrite) {
- AddError("access mode 'write' is not valid for the 'storage' address space",
- decl->source);
- return false;
- }
- } else {
- AddError("only variables in <storage> address space may declare an access mode",
- decl->source);
- return false;
- }
- }
-
- if (!AtomicVariable(global, atomic_composite_info)) {
- return false;
- }
-
return Var(global);
},
[&](const ast::Override*) { return Override(global, override_ids); },
@@ -698,65 +671,41 @@
return true;
}
-// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
-// Atomic types may only be instantiated by variables in the workgroup storage class or by storage
-// buffer variables with a read_write access mode.
-bool Validator::AtomicVariable(
- const sem::Variable* var,
- const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
- auto address_space = var->AddressSpace();
- auto* decl = var->Declaration();
- auto access = var->Access();
- auto* type = var->Type()->UnwrapRef();
- auto source = decl->type ? decl->type->source : decl->source;
-
- if (type->Is<sem::Atomic>()) {
- if (address_space != ast::AddressSpace::kWorkgroup &&
- address_space != ast::AddressSpace::kStorage) {
- AddError("atomic variables must have <storage> or <workgroup> address space", source);
- return false;
- }
- } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
- if (auto found = atomic_composite_info.Find(type)) {
- if (address_space != ast::AddressSpace::kStorage &&
- address_space != ast::AddressSpace::kWorkgroup) {
- AddError("atomic variables must have <storage> or <workgroup> address space",
- source);
- AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
- **found);
- return false;
- } else if (address_space == ast::AddressSpace::kStorage &&
- access != ast::Access::kReadWrite) {
- AddError(
- "atomic variables in <storage> address space must have read_write "
- "access mode",
- source);
- AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
- **found);
- return false;
- }
- }
- }
-
- return true;
-}
-
bool Validator::Var(const sem::Variable* v) const {
auto* var = v->Declaration()->As<ast::Var>();
- auto* storage_ty = v->Type()->UnwrapRef();
+ auto* store_ty = v->Type()->UnwrapRef();
- if (!IsStorable(storage_ty)) {
- AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var", var->source);
+ if (!IsStorable(store_ty)) {
+ AddError(sem_.TypeNameOf(store_ty) + " cannot be used as the type of a var", var->source);
return false;
}
- if (storage_ty->is_handle() && var->declared_address_space != ast::AddressSpace::kNone) {
- // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
- // If the store type is a texture type or a sampler type, then the variable declaration must
- // not have a address space attribute. The address space will always be handle.
- AddError(
- "variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a address space",
- var->source);
+ if (store_ty->is_handle()) {
+ if (var->declared_address_space != ast::AddressSpace::kNone) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
+ // If the store type is a texture type or a sampler type, then the variable declaration
+ // must not have a address space attribute. The address space will always be handle.
+ AddError("variables of type '" + sem_.TypeNameOf(store_ty) +
+ "' must not have a address space",
+ var->source);
+ return false;
+ }
+ }
+
+ if (var->declared_access != ast::Access::kUndefined) {
+ // https://www.w3.org/TR/WGSL/#access-mode-defaults
+ // When writing a variable declaration or a pointer type in WGSL source:
+ // * For the storage address space, the access mode is optional, and defaults to read.
+ // * For other address spaces, the access mode must not be written.
+ if (var->declared_address_space != ast::AddressSpace::kStorage) {
+ AddError("only variables in <storage> address space may declare an access mode",
+ var->source);
+ return false;
+ }
+ }
+
+ if (!CheckTypeAccessAddressSpace(v->Type()->UnwrapRef(), v->Access(), v->AddressSpace(),
+ var->attributes, var->source)) {
return false;
}
@@ -1642,9 +1591,7 @@
check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
}
-bool Validator::RequiredExtensionForBuiltinFunction(
- const sem::Call* call,
- const ast::Extensions& enabled_extensions) const {
+bool Validator::RequiredExtensionForBuiltinFunction(const sem::Call* call) const {
const auto* builtin = call->Target()->As<sem::Builtin>();
if (!builtin) {
return true;
@@ -1655,7 +1602,7 @@
return true;
}
- if (!enabled_extensions.Contains(extension)) {
+ if (!enabled_extensions_.Contains(extension)) {
AddError("cannot call built-in function '" + std::string(builtin->str()) +
"' without extension " + utils::ToString(extension),
call->Declaration()->source);
@@ -2451,4 +2398,69 @@
return vec_type.FriendlyName(symbols_);
}
+bool Validator::CheckTypeAccessAddressSpace(
+ const sem::Type* store_ty,
+ ast::Access access,
+ ast::AddressSpace address_space,
+ const utils::VectorRef<const tint::ast::Attribute*> attributes,
+ const Source& source) const {
+ if (!AddressSpaceLayout(store_ty, address_space, source)) {
+ return false;
+ }
+
+ if (address_space == ast::AddressSpace::kPushConstant &&
+ !enabled_extensions_.Contains(ast::Extension::kChromiumExperimentalPushConstant) &&
+ IsValidationEnabled(attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
+ AddError(
+ "use of variable address space 'push_constant' requires enabling extension "
+ "'chromium_experimental_push_constant'",
+ source);
+ return false;
+ }
+
+ if (address_space == ast::AddressSpace::kStorage && access == ast::Access::kWrite) {
+ // The access mode for the storage address space can only be 'read' or
+ // 'read_write'.
+ AddError("access mode 'write' is not valid for the 'storage' address space", source);
+ return false;
+ }
+
+ auto atomic_error = [&]() -> const char* {
+ if (address_space != ast::AddressSpace::kStorage &&
+ address_space != ast::AddressSpace::kWorkgroup) {
+ return "atomic variables must have <storage> or <workgroup> address space";
+ }
+ if (address_space == ast::AddressSpace::kStorage && access != ast::Access::kReadWrite) {
+ return "atomic variables in <storage> address space must have read_write access "
+ "mode";
+ }
+ return nullptr;
+ };
+
+ auto check_sub_atomics = [&] {
+ if (auto atomic_use = atomic_composite_info_.Get(store_ty)) {
+ if (auto* err = atomic_error()) {
+ AddError(err, source);
+ AddNote("atomic sub-type of '" + sem_.TypeNameOf(store_ty) + "' is declared here",
+ **atomic_use);
+ return false;
+ }
+ }
+ return true;
+ };
+
+ return Switch(
+ store_ty, //
+ [&](const sem::Atomic*) {
+ if (auto* err = atomic_error()) {
+ AddError(err, source);
+ return false;
+ }
+ return true;
+ },
+ [&](const sem::Struct*) { return check_sub_atomics(); }, //
+ [&](const sem::Array*) { return check_sub_atomics(); }, //
+ [&](Default) { return true; });
+}
+
} // namespace tint::resolver
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 500d056..0f183bb 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -24,6 +24,7 @@
#include "src/tint/resolver/sem_helper.h"
#include "src/tint/sem/evaluation_stage.h"
#include "src/tint/source.h"
+#include "src/tint/utils/hash.h"
#include "src/tint/utils/hashmap.h"
#include "src/tint/utils/vector.h"
@@ -66,19 +67,38 @@
namespace tint::resolver {
+/// TypeAndAddressSpace is a pair of type and address space
+struct TypeAndAddressSpace {
+ /// The type
+ const sem::Type* type;
+ /// The address space
+ ast::AddressSpace address_space;
+
+ /// Equality operator
+ /// @param other the other TypeAndAddressSpace to compare this TypeAndAddressSpace to
+ /// @returns true if the type and address space of this TypeAndAddressSpace is equal to @p other
+ bool operator==(const TypeAndAddressSpace& other) const {
+ return type == other.type && address_space == other.address_space;
+ }
+};
+
/// Validation logic for various ast nodes. The validations in general should
/// be shallow and depend on the resolver to call on children. The validations
/// also assume that sem changes have already been made. The validation checks
/// should not alter the AST or SEM trees.
class Validator {
public:
- /// The valid type storage layouts typedef
- using ValidTypeStorageLayouts = std::set<std::pair<const sem::Type*, ast::AddressSpace>>;
-
/// Constructor
/// @param builder the program builder
/// @param helper the SEM helper to validate with
- Validator(ProgramBuilder* builder, SemHelper& helper);
+ /// @param enabled_extensions all the extensions declared in current module
+ /// @param atomic_composite_info atomic composite info of the module
+ /// @param valid_type_storage_layouts a set of validated type layouts by address space
+ Validator(ProgramBuilder* builder,
+ SemHelper& helper,
+ const ast::Extensions& enabled_extensions,
+ const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info,
+ utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts);
~Validator();
/// Adds the given error message to the diagnostics
@@ -143,19 +163,17 @@
uint32_t el_size,
uint32_t el_align) const;
- /// Validates an atomic
- /// @param a the atomic ast node to validate
+ /// Validates an atomic type
+ /// @param a the atomic ast node
/// @param s the atomic sem node
/// @returns true on success, false otherwise.
bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
- /// Validates an atoic variable
- /// @param var the variable to validate
- /// @param atomic_composite_info store atomic information
+ /// Validates a pointer type
+ /// @param a the pointer ast node
+ /// @param s the pointer sem node
/// @returns true on success, false otherwise.
- bool AtomicVariable(
- const sem::Variable* var,
- const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
+ bool Pointer(const ast::Pointer* a, const sem::Pointer* s) const;
/// Validates an assignment
/// @param a the assignment statement
@@ -238,12 +256,10 @@
/// Validates a global variable
/// @param var the global variable to validate
/// @param override_id the set of override ids in the module
- /// @param atomic_composite_info atomic composite info in the module
/// @returns true on success, false otherwise
bool GlobalVariable(
const sem::GlobalVariable* var,
- const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id,
- const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
+ const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id) const;
/// Validates a break-if statement
/// @param stmt the statement to validate
@@ -423,10 +439,8 @@
/// Validates an optional builtin function and its required extension.
/// @param call the builtin call to validate
- /// @param enabled_extensions all the extensions declared in current module
/// @returns true on success, false otherwise
- bool RequiredExtensionForBuiltinFunction(const sem::Call* call,
- const ast::Extensions& enabled_extensions) const;
+ bool RequiredExtensionForBuiltinFunction(const sem::Call* call) const;
/// Validates there are no duplicate attributes
/// @param attributes the list of attributes to validate
@@ -437,21 +451,8 @@
/// @param type the type to validate
/// @param sc the address space
/// @param source the source of the type
- /// @param layouts previously validated storage layouts
/// @returns true on success, false otherwise
- bool AddressSpaceLayout(const sem::Type* type,
- ast::AddressSpace sc,
- Source source,
- ValidTypeStorageLayouts& layouts) const;
-
- /// Validates a address space layout
- /// @param var the variable to validate
- /// @param layouts previously validated storage layouts
- /// @param enabled_extensions all the extensions declared in current module
- /// @returns true on success, false otherwise.
- bool AddressSpaceLayout(const sem::Variable* var,
- const ast::Extensions& enabled_extensions,
- ValidTypeStorageLayouts& layouts) const;
+ bool AddressSpaceLayout(const sem::Type* type, ast::AddressSpace sc, Source source) const;
/// @returns true if the attribute list contains a
/// ast::DisableValidationAttribute with the validation mode equal to
@@ -497,11 +498,41 @@
/// @return pretty string representation
std::string VectorPretty(uint32_t size, const sem::Type* element_type) const;
+ /// Raises an error if combination of @p store_ty, @p access and @p address_space are not valid
+ /// for a `var` or `ptr` declaration.
+ /// @param store_ty the store type of the var or pointer
+ /// @param access the var or pointer access
+ /// @param address_space the var or pointer address space
+ /// @param source the source for the error
+ /// @returns true on success, false if an error was raised.
+ bool CheckTypeAccessAddressSpace(const sem::Type* store_ty,
+ ast::Access access,
+ ast::AddressSpace address_space,
+ const utils::VectorRef<const tint::ast::Attribute*> attributes,
+ const Source& source) const;
SymbolTable& symbols_;
diag::List& diagnostics_;
SemHelper& sem_;
+ const ast::Extensions& enabled_extensions_;
+ const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info_;
+ utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts_;
};
} // namespace tint::resolver
+namespace std {
+
+/// Custom std::hash specialization for tint::resolver::TypeAndAddressSpace.
+template <>
+class hash<tint::resolver::TypeAndAddressSpace> {
+ public:
+ /// @param tas the TypeAndAddressSpace
+ /// @return the hash value
+ inline std::size_t operator()(const tint::resolver::TypeAndAddressSpace& tas) const {
+ return tint::utils::Hash(tas.type, tas.address_space);
+ }
+};
+
+} // namespace std
+
#endif // SRC_TINT_RESOLVER_VALIDATOR_H_
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 964c8d9..cf35e4c 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -321,16 +321,16 @@
TEST_F(ResolverVariableValidationTest, NonConstructibleType_RuntimeArray) {
auto* s = Structure("S", utils::Vector{
- Member(Source{{56, 78}}, "m", ty.array(ty.i32())),
+ Member(Source{{12, 34}}, "m", ty.array<i32>()),
});
- auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
+ auto* v = Var(Source{{56, 78}}, "v", ty.Of(s));
WrapInFunction(v);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space
-56:78 note: while analyzing structure member S.m
-12:34 note: while instantiating 'var' v)");
+ R"(error: runtime-sized arrays can only be used in the <storage> address space
+12:34 note: while analyzing structure member S.m
+56:78 note: while instantiating 'var' v)");
}
TEST_F(ResolverVariableValidationTest, NonConstructibleType_Struct_WithAtomic) {
@@ -365,7 +365,7 @@
EXPECT_EQ(r()->error(),
"12:34 error: var of address space 'workgroup' cannot have "
"an initializer. var initializers are only supported for the "
- "address spacees 'private' and 'function'");
+ "address spaces 'private' and 'function'");
}
TEST_F(ResolverVariableValidationTest, VectorConstNoType) {
@@ -486,5 +486,18 @@
56:78 note: consider changing 'const' to 'let')");
}
+TEST_F(ResolverVariableValidationTest, GlobalVariable_PushConstantWithInitializer) {
+ // enable chromium_experimental_push_constant;
+ // var<push_constant> a : u32 = 0u;
+ Enable(ast::Extension::kChromiumExperimentalPushConstant);
+ GlobalVar(Source{{1u, 2u}}, "a", ty.u32(), ast::AddressSpace::kPushConstant,
+ Expr(Source{{3u, 4u}}, u32(0)));
+
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:2 error: var of address space 'push_constant' cannot have an initializer. var initializers are only supported for the address spaces 'private' and 'function')");
+}
+
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/symbol.cc b/src/tint/symbol.cc
index 9adc1aa..32177f6 100644
--- a/src/tint/symbol.cc
+++ b/src/tint/symbol.cc
@@ -42,6 +42,11 @@
return val_ == other.val_;
}
+bool Symbol::operator!=(const Symbol& other) const {
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Symbol, program_id_, other.program_id_);
+ return val_ != other.val_;
+}
+
bool Symbol::operator<(const Symbol& other) const {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(Symbol, program_id_, other.program_id_);
return val_ < other.val_;
diff --git a/src/tint/symbol.h b/src/tint/symbol.h
index fc0a0df..c093673 100644
--- a/src/tint/symbol.h
+++ b/src/tint/symbol.h
@@ -63,11 +63,16 @@
/// @returns teh symbol after doing the move
Symbol& operator=(Symbol&& o);
- /// Comparison operator
+ /// Equality operator
/// @param o the other symbol
/// @returns true if the symbols are the same
bool operator==(const Symbol& o) const;
+ /// Inequality operator
+ /// @param o the other symbol
+ /// @returns true if the symbols are the different
+ bool operator!=(const Symbol& o) const;
+
/// Less-than operator
/// @param o the other symbol
/// @returns true if this symbol is ordered before symbol `o`
diff --git a/src/tint/symbol_test.cc b/src/tint/symbol_test.cc
index 22135b5..db8e321 100644
--- a/src/tint/symbol_test.cc
+++ b/src/tint/symbol_test.cc
@@ -43,8 +43,11 @@
Symbol sym3(1, program_id);
EXPECT_TRUE(sym1 == sym3);
+ EXPECT_FALSE(sym1 != sym3);
EXPECT_FALSE(sym1 == sym2);
+ EXPECT_TRUE(sym1 != sym2);
EXPECT_FALSE(sym3 == sym2);
+ EXPECT_TRUE(sym3 != sym2);
}
} // namespace
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index 3be550c..944797a 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -466,6 +466,9 @@
const sem::VariableUser* var_user) {
auto address_space = var_user->Variable()->AddressSpace();
auto access = var_user->Variable()->Access();
+ if (address_space != ast::AddressSpace::kStorage) {
+ access = ast::Access::kUndefined;
+ }
return utils::GetOrCreate(
load_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
utils::Vector params{
@@ -562,6 +565,9 @@
const sem::VariableUser* var_user) {
auto address_space = var_user->Variable()->AddressSpace();
auto access = var_user->Variable()->Access();
+ if (address_space != ast::AddressSpace::kStorage) {
+ access = ast::Access::kUndefined;
+ }
return utils::GetOrCreate(
store_funcs, LoadStoreKey{address_space, access, buf_ty, el_ty}, [&] {
utils::Vector params{
@@ -670,7 +676,11 @@
const sem::Builtin* intrinsic,
const sem::VariableUser* var_user) {
auto op = intrinsic->Type();
+ auto address_space = var_user->Variable()->AddressSpace();
auto access = var_user->Variable()->Access();
+ if (address_space != ast::AddressSpace::kStorage) {
+ access = ast::Access::kUndefined;
+ }
return utils::GetOrCreate(atomic_funcs, AtomicKey{access, buf_ty, el_ty, op}, [&] {
// The first parameter to all WGSL atomics is the expression to the
// atomic. This is replaced with two parameters: the buffer and offset.
diff --git a/src/tint/transform/decompose_memory_access_test.cc b/src/tint/transform/decompose_memory_access_test.cc
index ac798e0..4c2cd11 100644
--- a/src/tint/transform/decompose_memory_access_test.cc
+++ b/src/tint/transform/decompose_memory_access_test.cc
@@ -804,126 +804,126 @@
@group(0) @binding(0) var<uniform> ub : UB;
@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f32
@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> i32
@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> u32
@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f16
@internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f32>
@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<i32>
@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<u32>
@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f16>
@internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f32>
@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<i32>
@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<u32>
@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f16>
@internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f32>
@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<i32>
@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<u32>
@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f16>
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f32> {
return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
}
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f32> {
return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
}
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f32> {
return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
}
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f32> {
return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
}
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f32> {
return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
}
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f32> {
return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
}
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f32> {
return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
}
-fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f32> {
return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
}
-fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f32> {
return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
}
-fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f16> {
return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
}
-fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f16> {
return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
}
-fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f16> {
return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
}
-fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f16> {
return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
}
-fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f16> {
return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
}
-fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f16> {
return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
}
-fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f16> {
return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
}
-fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f16> {
return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
}
-fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f16> {
return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
}
-fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<vec3<f32>, 2u> {
var arr : array<vec3<f32>, 2u>;
for(var i = 0u; (i < 2u); i = (i + 1u)) {
arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
@@ -931,7 +931,7 @@
return arr;
}
-fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<mat4x2<f16>, 2u> {
var arr_1 : array<mat4x2<f16>, 2u>;
for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
@@ -1075,126 +1075,126 @@
enable f16;
@internal(intrinsic_load_uniform_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f32
+fn tint_symbol(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f32
@internal(intrinsic_load_uniform_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> i32
+fn tint_symbol_1(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> i32
@internal(intrinsic_load_uniform_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> u32
+fn tint_symbol_2(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> u32
@internal(intrinsic_load_uniform_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> f16
+fn tint_symbol_3(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> f16
@internal(intrinsic_load_uniform_vec2_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f32>
+fn tint_symbol_4(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f32>
@internal(intrinsic_load_uniform_vec2_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<i32>
+fn tint_symbol_5(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<i32>
@internal(intrinsic_load_uniform_vec2_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<u32>
+fn tint_symbol_6(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<u32>
@internal(intrinsic_load_uniform_vec2_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec2<f16>
+fn tint_symbol_7(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec2<f16>
@internal(intrinsic_load_uniform_vec3_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f32>
+fn tint_symbol_8(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f32>
@internal(intrinsic_load_uniform_vec3_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<i32>
+fn tint_symbol_9(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<i32>
@internal(intrinsic_load_uniform_vec3_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<u32>
+fn tint_symbol_10(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<u32>
@internal(intrinsic_load_uniform_vec3_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec3<f16>
+fn tint_symbol_11(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec3<f16>
@internal(intrinsic_load_uniform_vec4_f32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f32>
+fn tint_symbol_12(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f32>
@internal(intrinsic_load_uniform_vec4_i32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<i32>
+fn tint_symbol_13(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<i32>
@internal(intrinsic_load_uniform_vec4_u32) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<u32>
+fn tint_symbol_14(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<u32>
@internal(intrinsic_load_uniform_vec4_f16) @internal(disable_validation__function_has_no_body)
-fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> vec4<f16>
+fn tint_symbol_15(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> vec4<f16>
-fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f32> {
+fn tint_symbol_16(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f32> {
return mat2x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)));
}
-fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f32> {
+fn tint_symbol_17(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f32> {
return mat2x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)));
}
-fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f32> {
+fn tint_symbol_18(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f32> {
return mat2x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)));
}
-fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f32> {
+fn tint_symbol_19(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f32> {
return mat3x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)));
}
-fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f32> {
+fn tint_symbol_20(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f32> {
return mat3x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)));
}
-fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f32> {
+fn tint_symbol_21(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f32> {
return mat3x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)));
}
-fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f32> {
+fn tint_symbol_22(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f32> {
return mat4x2<f32>(tint_symbol_4(buffer, (offset + 0u)), tint_symbol_4(buffer, (offset + 8u)), tint_symbol_4(buffer, (offset + 16u)), tint_symbol_4(buffer, (offset + 24u)));
}
-fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f32> {
+fn tint_symbol_23(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f32> {
return mat4x3<f32>(tint_symbol_8(buffer, (offset + 0u)), tint_symbol_8(buffer, (offset + 16u)), tint_symbol_8(buffer, (offset + 32u)), tint_symbol_8(buffer, (offset + 48u)));
}
-fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f32> {
+fn tint_symbol_24(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f32> {
return mat4x4<f32>(tint_symbol_12(buffer, (offset + 0u)), tint_symbol_12(buffer, (offset + 16u)), tint_symbol_12(buffer, (offset + 32u)), tint_symbol_12(buffer, (offset + 48u)));
}
-fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x2<f16> {
+fn tint_symbol_25(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x2<f16> {
return mat2x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)));
}
-fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x3<f16> {
+fn tint_symbol_26(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x3<f16> {
return mat2x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)));
}
-fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat2x4<f16> {
+fn tint_symbol_27(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat2x4<f16> {
return mat2x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)));
}
-fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x2<f16> {
+fn tint_symbol_28(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x2<f16> {
return mat3x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)));
}
-fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x3<f16> {
+fn tint_symbol_29(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x3<f16> {
return mat3x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)));
}
-fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat3x4<f16> {
+fn tint_symbol_30(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat3x4<f16> {
return mat3x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)));
}
-fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x2<f16> {
+fn tint_symbol_31(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x2<f16> {
return mat4x2<f16>(tint_symbol_7(buffer, (offset + 0u)), tint_symbol_7(buffer, (offset + 4u)), tint_symbol_7(buffer, (offset + 8u)), tint_symbol_7(buffer, (offset + 12u)));
}
-fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x3<f16> {
+fn tint_symbol_32(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x3<f16> {
return mat4x3<f16>(tint_symbol_11(buffer, (offset + 0u)), tint_symbol_11(buffer, (offset + 8u)), tint_symbol_11(buffer, (offset + 16u)), tint_symbol_11(buffer, (offset + 24u)));
}
-fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> mat4x4<f16> {
+fn tint_symbol_33(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> mat4x4<f16> {
return mat4x4<f16>(tint_symbol_15(buffer, (offset + 0u)), tint_symbol_15(buffer, (offset + 8u)), tint_symbol_15(buffer, (offset + 16u)), tint_symbol_15(buffer, (offset + 24u)));
}
-fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<vec3<f32>, 2u> {
+fn tint_symbol_34(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<vec3<f32>, 2u> {
var arr : array<vec3<f32>, 2u>;
for(var i = 0u; (i < 2u); i = (i + 1u)) {
arr[i] = tint_symbol_8(buffer, (offset + (i * 16u)));
@@ -1202,7 +1202,7 @@
return arr;
}
-fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB, read>, offset : u32) -> array<mat4x2<f16>, 2u> {
+fn tint_symbol_35(@internal(disable_validation__function_parameter) buffer : ptr<uniform, UB>, offset : u32) -> array<mat4x2<f16>, 2u> {
var arr_1 : array<mat4x2<f16>, 2u>;
for(var i_1 = 0u; (i_1 < 2u); i_1 = (i_1 + 1u)) {
arr_1[i_1] = tint_symbol_31(buffer, (offset + (i_1 * 16u)));
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param.cc b/src/tint/transform/module_scope_var_to_entry_point_param.cc
index 8a69bd3..9c9c42f 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param.cc
@@ -139,7 +139,7 @@
}
case ast::AddressSpace::kStorage:
case ast::AddressSpace::kUniform: {
- // Variables into the Storage and Uniform address spacees are redeclared as entry
+ // Variables into the Storage and Uniform address spaces are redeclared as entry
// point parameters with a pointer type.
auto attributes = ctx.Clone(var->Declaration()->attributes);
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kEntryPointParameter));
@@ -192,7 +192,7 @@
[[fallthrough]];
}
case ast::AddressSpace::kPrivate: {
- // Variables in the Private and Workgroup address spacees are redeclared at function
+ // Variables in the Private and Workgroup address spaces are redeclared at function
// scope. Disable address space validation on this variable.
auto* disable_validation =
ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace);
@@ -497,7 +497,7 @@
}
}
- // Now remove all module-scope variables with these address spacees.
+ // Now remove all module-scope variables with these address spaces.
for (auto* var_ast : ctx.src->AST().GlobalVariables()) {
auto* var_sem = ctx.src->Sem().Get(var_ast);
if (var_sem->AddressSpace() != ast::AddressSpace::kNone) {
diff --git a/src/tint/utils/hashmap.h b/src/tint/utils/hashmap.h
index 19a5abe..6a5f3c5 100644
--- a/src/tint/utils/hashmap.h
+++ b/src/tint/utils/hashmap.h
@@ -182,6 +182,55 @@
/// @returns a reference to the entry that is equal to the given value.
ConstReference Find(const Key& key) const { return ConstReference(*this, key); }
+ /// @returns the keys of the map as a vector.
+ /// @note the order of the returned vector is non-deterministic between compilers.
+ template <size_t N2 = N>
+ Vector<Key, N2> Keys() const {
+ Vector<Key, N2> out;
+ out.Reserve(this->Count());
+ for (auto it : *this) {
+ out.Push(it.key);
+ }
+ return out;
+ }
+
+ /// @returns the values of the map as a vector
+ /// @note the order of the returned vector is non-deterministic between compilers.
+ template <size_t N2 = N>
+ Vector<Value, N2> Values() const {
+ Vector<Value, N2> out;
+ out.Reserve(this->Count());
+ for (auto it : *this) {
+ out.Push(it.value);
+ }
+ return out;
+ }
+
+ /// Equality operator
+ /// @param other the other Hashmap to compare this Hashmap to
+ /// @returns true if this Hashmap has the same key and value pairs as @p other
+ template <typename K, typename V, size_t N2>
+ bool operator==(const Hashmap<K, V, N2>& other) const {
+ if (this->Count() != other.Count()) {
+ return false;
+ }
+ for (auto it : *this) {
+ auto other_val = other.Find(it.key);
+ if (!other_val || it.value != *other_val) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// Inequality operator
+ /// @param other the other Hashmap to compare this Hashmap to
+ /// @returns false if this Hashmap has the same key and value pairs as @p other
+ template <typename K, typename V, size_t N2>
+ bool operator!=(const Hashmap<K, V, N2>& other) const {
+ return !(*this == other);
+ }
+
private:
Value* Lookup(const Key& key) {
if (auto [found, index] = this->IndexOf(key); found) {
@@ -198,6 +247,22 @@
}
};
+/// Hasher specialization for Hashmap
+template <typename K, typename V, size_t N, typename HASH, typename EQUAL>
+struct Hasher<Hashmap<K, V, N, HASH, EQUAL>> {
+ /// @param map the Hashmap to hash
+ /// @returns a hash of the map
+ size_t operator()(const Hashmap<K, V, N, HASH, EQUAL>& map) const {
+ auto hash = Hash(map.Count());
+ for (auto it : map) {
+ // Use an XOR to ensure that the non-deterministic ordering of the map still produces
+ // the same hash value for the same entries.
+ hash ^= Hash(it.key) * 31 + Hash(it.value);
+ }
+ return hash;
+ }
+};
+
} // namespace tint::utils
#endif // SRC_TINT_UTILS_HASHMAP_H_
diff --git a/src/tint/utils/hashmap_base.h b/src/tint/utils/hashmap_base.h
index ee52dad..cd4ef6a 100644
--- a/src/tint/utils/hashmap_base.h
+++ b/src/tint/utils/hashmap_base.h
@@ -70,6 +70,27 @@
}
};
+/// KeyValueRef is a pair of references to a key and value.
+/// #key is always a const reference.
+/// #value is always a const reference if @tparam VALUE_IS_CONST is true, otherwise a non-const
+/// reference.
+template <typename KEY, typename VALUE, bool VALUE_IS_CONST>
+struct KeyValueRef {
+ /// The reference to key type
+ using KeyRef = const KEY&;
+ /// The reference to value type
+ using ValueRef = std::conditional_t<VALUE_IS_CONST, const VALUE&, VALUE&>;
+
+ /// The reference to the key
+ KeyRef key;
+
+ /// The reference to the value
+ ValueRef value;
+
+ /// @returns a KeyValue<KEY, VALUE> with the referenced key and value
+ operator KeyValue<KEY, VALUE>() const { return {key, value}; }
+};
+
/// Writes the KeyValue to the std::ostream.
/// @param out the std::ostream to write to
/// @param key_value the KeyValue to write
@@ -97,9 +118,19 @@
/// The entry type for the map.
/// This is:
/// - Key when Value is void (used by Hashset)
- /// - KeyValue<Key, Value> when Value is void (used by Hashmap)
+ /// - KeyValue<Key, Value> when Value is not void (used by Hashmap)
using Entry = std::conditional_t<ValueIsVoid, Key, KeyValue<Key, Value>>;
+ /// A reference to an entry in the map.
+ /// This is:
+ /// - const Key& when Value is void (used by Hashset)
+ /// - KeyValueRef<Key, Value> when Value is not void (used by Hashmap)
+ template <bool IS_CONST>
+ using EntryRef = std::conditional_t<
+ ValueIsVoid,
+ const Key&,
+ KeyValueRef<Key, std::conditional_t<ValueIsVoid, bool, Value>, IS_CONST>>;
+
/// STL-friendly alias to Entry. Used by gmock.
using value_type = Entry;
@@ -154,17 +185,25 @@
public:
/// Iterator for entries in the map.
/// Iterators are invalidated if the map is modified.
- class Iterator {
+ template <bool IS_CONST>
+ class IteratorT {
public:
/// @returns the value pointed to by this iterator
- const Entry* operator->() const { return ¤t->entry.value(); }
+ EntryRef<IS_CONST> operator->() const { return *this; }
/// @returns a reference to the value at the iterator
- const Entry& operator*() const { return current->entry.value(); }
+ EntryRef<IS_CONST> operator*() const {
+ auto& ref = current->entry.value();
+ if constexpr (ValueIsVoid) {
+ return ref;
+ } else {
+ return {ref.key, ref.value};
+ }
+ }
/// Increments the iterator
/// @returns this iterator
- Iterator& operator++() {
+ IteratorT& operator++() {
if (current == end) {
return *this;
}
@@ -176,18 +215,20 @@
/// Equality operator
/// @param other the other iterator to compare this iterator to
/// @returns true if this iterator is equal to other
- bool operator==(const Iterator& other) const { return current == other.current; }
+ bool operator==(const IteratorT& other) const { return current == other.current; }
/// Inequality operator
/// @param other the other iterator to compare this iterator to
/// @returns true if this iterator is not equal to other
- bool operator!=(const Iterator& other) const { return current != other.current; }
+ bool operator!=(const IteratorT& other) const { return current != other.current; }
private:
/// Friend class
friend class HashmapBase;
- Iterator(const Slot* c, const Slot* e) : current(c), end(e) { SkipToNextValue(); }
+ using SLOT = std::conditional_t<IS_CONST, const Slot, Slot>;
+
+ IteratorT(SLOT* c, SLOT* e) : current(c), end(e) { SkipToNextValue(); }
/// Moves the iterator forward, stopping at the next slot that is not empty.
void SkipToNextValue() {
@@ -196,10 +237,16 @@
}
}
- const Slot* current; /// The slot the iterator is pointing to
- const Slot* end; /// One past the last slot in the map
+ SLOT* current; /// The slot the iterator is pointing to
+ SLOT* end; /// One past the last slot in the map
};
+ /// An immutable key and mutable value iterator
+ using Iterator = IteratorT</*IS_CONST*/ false>;
+
+ /// An immutable key and value iterator
+ using ConstIterator = IteratorT</*IS_CONST*/ true>;
+
/// Constructor
HashmapBase() { slots_.Resize(kMinSlots); }
@@ -322,11 +369,17 @@
/// @returns a monotonic counter which is incremented whenever the map is mutated.
size_t Generation() const { return generation_; }
+ /// @returns an immutable iterator to the start of the map.
+ ConstIterator begin() const { return ConstIterator{slots_.begin(), slots_.end()}; }
+
+ /// @returns an immutable iterator to the end of the map.
+ ConstIterator end() const { return ConstIterator{slots_.end(), slots_.end()}; }
+
/// @returns an iterator to the start of the map.
- Iterator begin() const { return Iterator{slots_.begin(), slots_.end()}; }
+ Iterator begin() { return Iterator{slots_.begin(), slots_.end()}; }
/// @returns an iterator to the end of the map.
- Iterator end() const { return Iterator{slots_.end(), slots_.end()}; }
+ Iterator end() { return Iterator{slots_.end(), slots_.end()}; }
/// A debug function for checking that the map is in good health.
/// Asserts if the map is corrupted.
diff --git a/src/tint/utils/hashmap_test.cc b/src/tint/utils/hashmap_test.cc
index 34a93e6..5e97aef 100644
--- a/src/tint/utils/hashmap_test.cc
+++ b/src/tint/utils/hashmap_test.cc
@@ -167,6 +167,32 @@
Entry{3, "three"}, Entry{4, "four"}));
}
+TEST(Hashmap, MutableIterator) {
+ using Map = Hashmap<int, std::string, 8>;
+ using Entry = typename Map::Entry;
+ Map map;
+ map.Add(1, "one");
+ map.Add(4, "four");
+ map.Add(3, "three");
+ map.Add(2, "two");
+ for (auto pair : map) {
+ pair.value += "!";
+ }
+ EXPECT_THAT(map, testing::UnorderedElementsAre(Entry{1, "one!"}, Entry{2, "two!"},
+ Entry{3, "three!"}, Entry{4, "four!"}));
+}
+
+TEST(Hashmap, KeysValues) {
+ using Map = Hashmap<int, std::string, 8>;
+ Map map;
+ map.Add(1, "one");
+ map.Add(4, "four");
+ map.Add(3, "three");
+ map.Add(2, "two");
+ EXPECT_THAT(map.Keys(), testing::UnorderedElementsAre(1, 2, 3, 4));
+ EXPECT_THAT(map.Values(), testing::UnorderedElementsAre("one", "two", "three", "four"));
+}
+
TEST(Hashmap, AddMany) {
Hashmap<int, std::string, 8> map;
for (size_t i = 0; i < kPrimes.size(); i++) {
@@ -329,5 +355,61 @@
}
}
+TEST(Hashmap, EqualitySameSize) {
+ Hashmap<int, std::string, 8> a;
+ Hashmap<int, std::string, 8> b;
+ EXPECT_EQ(a, b);
+ a.Add(1, "one");
+ EXPECT_NE(a, b);
+ b.Add(2, "two");
+ EXPECT_NE(a, b);
+ a.Add(2, "two");
+ EXPECT_NE(a, b);
+ b.Add(1, "one");
+ EXPECT_EQ(a, b);
+}
+
+TEST(Hashmap, EqualityDifferentSize) {
+ Hashmap<int, std::string, 8> a;
+ Hashmap<int, std::string, 4> b;
+ EXPECT_EQ(a, b);
+ a.Add(1, "one");
+ EXPECT_NE(a, b);
+ b.Add(2, "two");
+ EXPECT_NE(a, b);
+ a.Add(2, "two");
+ EXPECT_NE(a, b);
+ b.Add(1, "one");
+ EXPECT_EQ(a, b);
+}
+
+TEST(Hashmap, HashSameSize) {
+ Hashmap<int, std::string, 8> a;
+ Hashmap<int, std::string, 8> b;
+ EXPECT_EQ(Hash(a), Hash(b));
+ a.Add(1, "one");
+ EXPECT_NE(Hash(a), Hash(b));
+ b.Add(2, "two");
+ EXPECT_NE(Hash(a), Hash(b));
+ a.Add(2, "two");
+ EXPECT_NE(Hash(a), Hash(b));
+ b.Add(1, "one");
+ EXPECT_EQ(Hash(a), Hash(b));
+}
+
+TEST(Hashmap, HashDifferentSize) {
+ Hashmap<int, std::string, 8> a;
+ Hashmap<int, std::string, 4> b;
+ EXPECT_EQ(Hash(a), Hash(b));
+ a.Add(1, "one");
+ EXPECT_NE(Hash(a), Hash(b));
+ b.Add(2, "two");
+ EXPECT_NE(Hash(a), Hash(b));
+ a.Add(2, "two");
+ EXPECT_NE(Hash(a), Hash(b));
+ b.Add(1, "one");
+ EXPECT_EQ(Hash(a), Hash(b));
+}
+
} // namespace
} // namespace tint::utils
diff --git a/src/tint/utils/vector.h b/src/tint/utils/vector.h
index ecee170..cefd536 100644
--- a/src/tint/utils/vector.h
+++ b/src/tint/utils/vector.h
@@ -423,6 +423,19 @@
return val;
}
+ /// Sort sorts the vector in-place using the predicate function @p pred
+ /// @param pred a function that has the signature `bool(const T& a, const T& b)` which returns
+ /// true if `a` is ordered before `b`.
+ template <typename PREDICATE>
+ void Sort(PREDICATE&& pred) {
+ std::sort(begin(), end(), std::forward<PREDICATE>(pred));
+ }
+
+ /// Sort sorts the vector in-place using `T::operator<()`
+ void Sort() {
+ Sort([](auto& a, auto& b) { return a < b; });
+ }
+
/// @returns true if the vector is empty.
bool IsEmpty() const { return impl_.slice.len == 0; }
diff --git a/src/tint/utils/vector_test.cc b/src/tint/utils/vector_test.cc
index 9a9bb4b..8e6fa7a 100644
--- a/src/tint/utils/vector_test.cc
+++ b/src/tint/utils/vector_test.cc
@@ -17,7 +17,7 @@
#include <string>
#include <tuple>
-#include "gtest/gtest.h"
+#include "gmock/gmock.h"
#include "src/tint/utils/bitcast.h"
@@ -2009,6 +2009,18 @@
EXPECT_EQ(vec_ref[1], "two");
}
+TEST(TintVectorRefTest, Sort) {
+ Vector vec{1, 5, 3, 4, 2};
+ vec.Sort();
+ EXPECT_THAT(vec, testing::ElementsAre(1, 2, 3, 4, 5));
+}
+
+TEST(TintVectorRefTest, SortPredicate) {
+ Vector vec{1, 5, 3, 4, 2};
+ vec.Sort([](int a, int b) { return b < a; });
+ EXPECT_THAT(vec, testing::ElementsAre(5, 4, 3, 2, 1));
+}
+
TEST(TintVectorRefTest, ConstIndex) {
Vector<std::string, 2> vec{"one", "two"};
const VectorRef<std::string> vec_ref(vec);