[ir] Handle IdentifierExpression
This Cl adds a scope stack into the IR builder and uses it to replace IdentifierExpressions with the relevant IDs. If the IdentifierExpression was const-eval'd then it will be replaced by the constant value. Bug: tint:1919 Change-Id: I54e38d56bd24e2ced1818c509115dd5a5149cb40 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/130900 Reviewed-by: James Price <jrprice@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
94bc4cf046
commit
4cadbc4daf
|
@ -1118,6 +1118,7 @@ libtint_source_set("libtint_ir_builder_src") {
|
|||
":libtint_ir_src",
|
||||
":libtint_program_src",
|
||||
":libtint_sem_src",
|
||||
":libtint_symbols_src",
|
||||
":libtint_type_src",
|
||||
":libtint_utils_src",
|
||||
]
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "src/tint/sem/variable.h"
|
||||
#include "src/tint/switch.h"
|
||||
#include "src/tint/type/void.h"
|
||||
#include "src/tint/utils/defer.h"
|
||||
#include "src/tint/utils/scoped_assignment.h"
|
||||
|
||||
namespace tint::ir {
|
||||
|
@ -217,7 +218,7 @@ void BuilderImpl::EmitFunction(const ast::Function* ast_func) {
|
|||
FlowStackScope scope(this, ir_func);
|
||||
|
||||
current_flow_block = ir_func->start_target;
|
||||
EmitStatements(ast_func->body->statements);
|
||||
EmitBlock(ast_func->body);
|
||||
|
||||
// TODO(dsinclair): Store return type and attributes
|
||||
// TODO(dsinclair): Store parameters
|
||||
|
@ -363,6 +364,9 @@ void BuilderImpl::EmitCompoundAssignment(const ast::CompoundAssignmentStatement*
|
|||
}
|
||||
|
||||
void BuilderImpl::EmitBlock(const ast::BlockStatement* block) {
|
||||
scopes_.Push();
|
||||
TINT_DEFER(scopes_.Pop());
|
||||
|
||||
// Note, this doesn't need to emit a Block as the current block flow node should be
|
||||
// sufficient as the blocks all get flattened. Each flow control node will inject the basic
|
||||
// blocks it requires.
|
||||
|
@ -387,7 +391,7 @@ void BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
|
|||
FlowStackScope scope(this, if_node);
|
||||
|
||||
current_flow_block = if_node->true_.target->As<Block>();
|
||||
EmitStatement(stmt->body);
|
||||
EmitBlock(stmt->body);
|
||||
|
||||
// If the true branch did not execute control flow, then go to the merge target
|
||||
BranchToIfNeeded(if_node->merge.target);
|
||||
|
@ -421,14 +425,14 @@ void BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
|
|||
FlowStackScope scope(this, loop_node);
|
||||
|
||||
current_flow_block = loop_node->start.target->As<Block>();
|
||||
EmitStatement(stmt->body);
|
||||
EmitBlock(stmt->body);
|
||||
|
||||
// The current block didn't `break`, `return` or `continue`, go to the continuing block.
|
||||
BranchToIfNeeded(loop_node->continuing.target);
|
||||
|
||||
current_flow_block = loop_node->continuing.target->As<Block>();
|
||||
if (stmt->continuing) {
|
||||
EmitStatement(stmt->continuing);
|
||||
EmitBlock(stmt->continuing);
|
||||
}
|
||||
|
||||
// Branch back to the start node if the continue target didn't branch out already
|
||||
|
@ -477,7 +481,7 @@ void BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
|
|||
BranchTo(if_node);
|
||||
|
||||
current_flow_block = if_node->merge.target->As<Block>();
|
||||
EmitStatement(stmt->body);
|
||||
EmitBlock(stmt->body);
|
||||
|
||||
BranchToIfNeeded(loop_node->continuing.target);
|
||||
}
|
||||
|
@ -492,6 +496,10 @@ void BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
|
|||
builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
|
||||
utils::Empty);
|
||||
|
||||
// Make sure the initializer ends up in a contained scope
|
||||
scopes_.Push();
|
||||
TINT_DEFER(scopes_.Pop());
|
||||
|
||||
if (stmt->initializer) {
|
||||
// Emit the for initializer before branching to the loop
|
||||
EmitStatement(stmt->initializer);
|
||||
|
@ -527,7 +535,7 @@ void BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
|
|||
current_flow_block = if_node->merge.target->As<Block>();
|
||||
}
|
||||
|
||||
EmitStatement(stmt->body);
|
||||
EmitBlock(stmt->body);
|
||||
BranchToIfNeeded(loop_node->continuing.target);
|
||||
|
||||
if (stmt->continuing) {
|
||||
|
@ -535,6 +543,7 @@ void BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
|
|||
EmitStatement(stmt->continuing);
|
||||
}
|
||||
}
|
||||
|
||||
// The while loop always has a path to the merge target as the break statement comes before
|
||||
// anything inside the loop.
|
||||
current_flow_block = loop_node->merge.target->As<Block>();
|
||||
|
@ -569,7 +578,8 @@ void BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
|||
}
|
||||
|
||||
current_flow_block = builder.CreateCase(switch_node, selectors);
|
||||
EmitStatement(c->Body()->Declaration());
|
||||
EmitBlock(c->Body()->Declaration());
|
||||
|
||||
BranchToIfNeeded(switch_node->merge.target);
|
||||
}
|
||||
}
|
||||
|
@ -677,9 +687,10 @@ utils::Result<Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
|
|||
[&](const ast::BinaryExpression* b) { return EmitBinary(b); },
|
||||
[&](const ast::BitcastExpression* b) { return EmitBitcast(b); },
|
||||
[&](const ast::CallExpression* c) { return EmitCall(c); },
|
||||
// [&](const ast::IdentifierExpression* i) {
|
||||
// TODO(dsinclair): Implement
|
||||
// },
|
||||
[&](const ast::IdentifierExpression* i) {
|
||||
auto* v = scopes_.Get(i->identifier->symbol);
|
||||
return utils::Result<Value*>{v};
|
||||
},
|
||||
[&](const ast::LiteralExpression* l) { return EmitLiteral(l); },
|
||||
// [&](const ast::MemberAccessorExpression* m) {
|
||||
// TODO(dsinclair): Implement
|
||||
|
@ -714,7 +725,8 @@ void BuilderImpl::EmitVariable(const ast::Variable* var) {
|
|||
auto* store = builder.Store(val, init.Get());
|
||||
current_flow_block->instructions.Push(store);
|
||||
}
|
||||
// TODO(dsinclair): Store the mapping from the var name to the `Declare` value
|
||||
// Store the declaration so we can get the instruction to store too
|
||||
scopes_.Set(v->name->symbol, val);
|
||||
},
|
||||
[&](const ast::Let* l) {
|
||||
// A `let` doesn't exist as a standalone item in the IR, it's just the result of the
|
||||
|
@ -723,7 +735,9 @@ void BuilderImpl::EmitVariable(const ast::Variable* var) {
|
|||
if (!init) {
|
||||
return;
|
||||
}
|
||||
// TODO(dsinclair): Store the mapping from the let name to the `init` value
|
||||
|
||||
// Store the results of the initialization
|
||||
scopes_.Set(l->name->symbol, init.Get());
|
||||
},
|
||||
[&](const ast::Override*) {
|
||||
add_error(var->source,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "src/tint/ir/flow_node.h"
|
||||
#include "src/tint/ir/module.h"
|
||||
#include "src/tint/ir/value.h"
|
||||
#include "src/tint/scope_stack.h"
|
||||
#include "src/tint/utils/result.h"
|
||||
|
||||
// Forward Declarations
|
||||
|
@ -232,19 +233,17 @@ class BuilderImpl {
|
|||
|
||||
void add_error(const Source& s, const std::string& err);
|
||||
|
||||
const Program* program_ = nullptr;
|
||||
|
||||
Symbol CloneSymbol(Symbol sym) const;
|
||||
|
||||
diag::List diagnostics_;
|
||||
|
||||
const Program* program_ = nullptr;
|
||||
Function* current_function_ = nullptr;
|
||||
ScopeStack<Symbol, Value*> scopes_;
|
||||
constant::CloneContext clone_ctx_;
|
||||
diag::List diagnostics_;
|
||||
|
||||
/// Map from ast nodes to flow nodes, used to retrieve the flow node for a given AST node.
|
||||
/// Used for testing purposes.
|
||||
std::unordered_map<const ast::Node*, const FlowNode*> ast_to_flow_;
|
||||
|
||||
constant::CloneContext clone_ctx_;
|
||||
};
|
||||
|
||||
} // namespace tint::ir
|
||||
|
|
|
@ -2105,58 +2105,68 @@ TEST_F(IR_BuilderImplTest, EmitStatement_UserFunction) {
|
|||
)");
|
||||
}
|
||||
|
||||
// TODO(dsinclair): This needs assignment in order to output correctly. The empty constructor ends
|
||||
// up materializing, so there is no expression to emit until there is a usage. When assigment is
|
||||
// implemented this can be enabled (and the output updated).
|
||||
TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_ConstructEmpty) {
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_ConstructEmpty) {
|
||||
auto* expr = vec3(ty.f32());
|
||||
GlobalVar("i", builtin::AddressSpace::kPrivate, expr);
|
||||
|
||||
auto& b = CreateBuilder();
|
||||
InjectFlowBlock();
|
||||
auto r = b.EmitExpression(expr);
|
||||
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
|
||||
auto r = Build();
|
||||
ASSERT_TRUE(r) << Error();
|
||||
auto m = r.Move();
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d(b.builder.ir);
|
||||
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
|
||||
EXPECT_EQ(d.AsString(), R"(%1(vec3<f32>) = construct
|
||||
EXPECT_EQ(Disassemble(m), R"(%fn0 = block
|
||||
%1(ref<private, vec3<f32>, read_write>) = var private read_write
|
||||
store %1(ref<private, vec3<f32>, read_write>), vec3<f32> 0.0f
|
||||
ret
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
// Requires identifier expressions
|
||||
TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Construct) {
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Construct) {
|
||||
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
|
||||
auto* expr = vec3(ty.f32(), 2_f, 3_f, i);
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto& b = CreateBuilder();
|
||||
InjectFlowBlock();
|
||||
auto r = b.EmitExpression(expr);
|
||||
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
|
||||
auto r = Build();
|
||||
ASSERT_TRUE(r) << Error();
|
||||
auto m = r.Move();
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d(b.builder.ir);
|
||||
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
|
||||
EXPECT_EQ(d.AsString(), R"(%2(vec3<f32>) = construct 2.0f, 3.0f, %1(void)
|
||||
EXPECT_EQ(Disassemble(m), R"(%fn0 = block
|
||||
%1(ref<private, f32, read_write>) = var private read_write
|
||||
store %1(ref<private, f32, read_write>), 1.0f
|
||||
ret
|
||||
|
||||
%fn1 = func test_function
|
||||
%fn2 = block
|
||||
%2(vec3<f32>) = construct 2.0f, 3.0f, %1(ref<private, f32, read_write>)
|
||||
ret
|
||||
func_end
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
// Requires identifier expressions
|
||||
TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Convert) {
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Convert) {
|
||||
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_i));
|
||||
auto* expr = Call(ty.f32(), i);
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto& b = CreateBuilder();
|
||||
InjectFlowBlock();
|
||||
auto r = b.EmitExpression(expr);
|
||||
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
|
||||
auto r = Build();
|
||||
ASSERT_TRUE(r) << Error();
|
||||
auto m = r.Move();
|
||||
ASSERT_TRUE(r);
|
||||
|
||||
Disassembler d(b.builder.ir);
|
||||
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
|
||||
EXPECT_EQ(d.AsString(), R"(%2(f32) = convert i32, %1(void)
|
||||
EXPECT_EQ(Disassemble(m), R"(%fn0 = block
|
||||
%1(ref<private, i32, read_write>) = var private read_write
|
||||
store %1(ref<private, i32, read_write>), 1i
|
||||
ret
|
||||
|
||||
%fn1 = func test_function
|
||||
%fn2 = block
|
||||
%2(f32) = convert i32, %1(ref<private, i32, read_write>)
|
||||
ret
|
||||
func_end
|
||||
|
||||
)");
|
||||
}
|
||||
|
||||
|
@ -2177,21 +2187,26 @@ func_end
|
|||
)");
|
||||
}
|
||||
|
||||
// Requires identifier expressions
|
||||
TEST_F(IR_BuilderImplTest, DISABLED_EmitExpression_Builtin) {
|
||||
TEST_F(IR_BuilderImplTest, EmitExpression_Builtin) {
|
||||
auto i = GlobalVar("i", builtin::AddressSpace::kPrivate, Expr(1_f));
|
||||
auto* expr = Call("asin", i);
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto& b = CreateBuilder();
|
||||
InjectFlowBlock();
|
||||
auto r = b.EmitExpression(expr);
|
||||
ASSERT_THAT(b.Diagnostics(), testing::IsEmpty());
|
||||
ASSERT_TRUE(r);
|
||||
auto r = Build();
|
||||
ASSERT_TRUE(r) << Error();
|
||||
auto m = r.Move();
|
||||
|
||||
EXPECT_EQ(Disassemble(m), R"(%fn0 = block
|
||||
%1(ref<private, f32, read_write>) = var private read_write
|
||||
store %1(ref<private, f32, read_write>), 1.0f
|
||||
ret
|
||||
|
||||
%fn1 = func test_function
|
||||
%fn2 = block
|
||||
%2(f32) = asin %1(ref<private, f32, read_write>)
|
||||
ret
|
||||
func_end
|
||||
|
||||
Disassembler d(b.builder.ir);
|
||||
d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
|
||||
EXPECT_EQ(d.AsString(), R"(%2(f32) = asin %1(void)
|
||||
)");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue