[hlsl-writer] Emit LogicalAnd and LogicalOr operations
This Cl emits the required code to handle LogicalAnd and LogicalOr short
circuting in HLSL.
Bug: tint:192
Change-Id: I30a88c207f7864e0a3c856d2dae1420c7ff35eb4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/27782
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: David Neto <dneto@google.com>
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index a2b15ea..bba6c0f 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -161,7 +161,7 @@
std::string GeneratorImpl::generate_name(const std::string& prefix) {
std::string name = prefix;
uint32_t i = 0;
- while (namer_.IsMapped(name)) {
+ while (namer_.IsMapped(name) || namer_.IsRemapped(name)) {
name = prefix + "_" + std::to_string(i);
++i;
}
@@ -302,8 +302,97 @@
bool GeneratorImpl::EmitBinary(std::ostream& pre,
std::ostream& out,
ast::BinaryExpression* expr) {
- out << "(";
+ if (expr->op() == ast::BinaryOp::kLogicalAnd) {
+ std::ostringstream lhs_out;
+ if (!EmitExpression(pre, lhs_out, expr->lhs())) {
+ return false;
+ }
+ auto name = generate_name(kTempNamePrefix);
+ make_indent(pre);
+ pre << "bool " << name << " = false;" << std::endl;
+ make_indent(pre);
+ pre << "if (" << lhs_out.str() << ") {" << std::endl;
+ {
+ increment_indent();
+
+ std::ostringstream rhs_out;
+ if (!EmitExpression(pre, rhs_out, expr->rhs())) {
+ return false;
+ }
+ make_indent(pre);
+ pre << "if (" << rhs_out.str() << ") {" << std::endl;
+ {
+ increment_indent();
+
+ make_indent(pre);
+ pre << name << " = true;" << std::endl;
+
+ decrement_indent();
+ }
+ make_indent(pre);
+ pre << "}" << std::endl;
+
+ decrement_indent();
+ }
+ make_indent(pre);
+ pre << "}" << std::endl;
+
+ out << "(" << name << ")";
+ return true;
+ }
+ if (expr->op() == ast::BinaryOp::kLogicalOr) {
+ std::ostringstream lhs_out;
+ if (!EmitExpression(pre, lhs_out, expr->lhs())) {
+ return false;
+ }
+
+ auto name = generate_name(kTempNamePrefix);
+ make_indent(pre);
+ pre << "bool " << name << " = false;" << std::endl;
+ make_indent(pre);
+ pre << "if (" << lhs_out.str() << ") {" << std::endl;
+ {
+ increment_indent();
+
+ make_indent(pre);
+ pre << name << " = true;" << std::endl;
+
+ decrement_indent();
+ }
+
+ make_indent(pre);
+ pre << "} else {" << std::endl;
+ {
+ increment_indent();
+
+ std::ostringstream rhs_out;
+ if (!EmitExpression(pre, rhs_out, expr->rhs())) {
+ return false;
+ }
+ make_indent(pre);
+ pre << "if (" << rhs_out.str() << ") {" << std::endl;
+ {
+ increment_indent();
+
+ make_indent(pre);
+ pre << name << " = true;" << std::endl;
+
+ decrement_indent();
+ }
+ make_indent(pre);
+ pre << "}" << std::endl;
+
+ decrement_indent();
+ }
+ make_indent(pre);
+ pre << "}" << std::endl;
+
+ out << "(" << name << ")";
+ return true;
+ }
+
+ out << "(";
if (!EmitExpression(pre, out, expr->lhs())) {
return false;
}
@@ -320,13 +409,11 @@
out << "^";
break;
case ast::BinaryOp::kLogicalAnd:
- // TODO(dsinclair): Implement support ...
- error_ = "&& not supported yet";
+ case ast::BinaryOp::kLogicalOr: {
+ // These are both handled above.
+ assert(false);
return false;
- case ast::BinaryOp::kLogicalOr:
- // TODO(dsinclair): Implement support ...
- error_ = "|| not supported yet";
- return false;
+ }
case ast::BinaryOp::kEqual:
out << "==";
break;
diff --git a/src/writer/hlsl/generator_impl_binary_test.cc b/src/writer/hlsl/generator_impl_binary_test.cc
index 024aa42..c1b04bb 100644
--- a/src/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/writer/hlsl/generator_impl_binary_test.cc
@@ -24,6 +24,8 @@
namespace hlsl {
namespace {
+using HlslGeneratorImplTest_Binary = TestHelper;
+
struct BinaryData {
const char* result;
ast::BinaryOp op;
@@ -52,8 +54,6 @@
BinaryData{"(left & right)", ast::BinaryOp::kAnd},
BinaryData{"(left | right)", ast::BinaryOp::kOr},
BinaryData{"(left ^ right)", ast::BinaryOp::kXor},
- // BinaryData{"(left && right)", ast::BinaryOp::kLogicalAnd},
- // BinaryData{"(left || right)", ast::BinaryOp::kLogicalOr},
BinaryData{"(left == right)", ast::BinaryOp::kEqual},
BinaryData{"(left != right)", ast::BinaryOp::kNotEqual},
BinaryData{"(left < right)", ast::BinaryOp::kLessThan},
@@ -68,6 +68,85 @@
BinaryData{"(left / right)", ast::BinaryOp::kDivide},
BinaryData{"(left % right)", ast::BinaryOp::kModulo}));
+TEST_F(HlslGeneratorImplTest_Binary, Logical_And) {
+ auto left = std::make_unique<ast::IdentifierExpression>("left");
+ auto right = std::make_unique<ast::IdentifierExpression>("right");
+
+ ast::BinaryExpression expr(ast::BinaryOp::kLogicalAnd, std::move(left),
+ std::move(right));
+
+ ASSERT_TRUE(gen().EmitExpression(pre(), out(), &expr)) << gen().error();
+ EXPECT_EQ(result(), "(_tint_tmp)");
+ EXPECT_EQ(pre_result(), R"(bool _tint_tmp = false;
+if (left) {
+ if (right) {
+ _tint_tmp = true;
+ }
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Binary, Logical_Multi) {
+ // (a && b) || (c || d)
+ auto a = std::make_unique<ast::IdentifierExpression>("a");
+ auto b = std::make_unique<ast::IdentifierExpression>("b");
+ auto c = std::make_unique<ast::IdentifierExpression>("c");
+ auto d = std::make_unique<ast::IdentifierExpression>("d");
+
+ ast::BinaryExpression expr(
+ ast::BinaryOp::kLogicalOr,
+ std::make_unique<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
+ std::move(a), std::move(b)),
+ std::make_unique<ast::BinaryExpression>(ast::BinaryOp::kLogicalOr,
+ std::move(c), std::move(d)));
+
+ ASSERT_TRUE(gen().EmitExpression(pre(), out(), &expr)) << gen().error();
+ EXPECT_EQ(result(), "(_tint_tmp_0)");
+ EXPECT_EQ(pre_result(), R"(bool _tint_tmp = false;
+if (a) {
+ if (b) {
+ _tint_tmp = true;
+ }
+}
+bool _tint_tmp_0 = false;
+if ((_tint_tmp)) {
+ _tint_tmp_0 = true;
+} else {
+ bool _tint_tmp_1 = false;
+ if (c) {
+ _tint_tmp_1 = true;
+ } else {
+ if (d) {
+ _tint_tmp_1 = true;
+ }
+ }
+ if ((_tint_tmp_1)) {
+ _tint_tmp_0 = true;
+ }
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Binary, Logical_Or) {
+ auto left = std::make_unique<ast::IdentifierExpression>("left");
+ auto right = std::make_unique<ast::IdentifierExpression>("right");
+
+ ast::BinaryExpression expr(ast::BinaryOp::kLogicalOr, std::move(left),
+ std::move(right));
+
+ ASSERT_TRUE(gen().EmitExpression(pre(), out(), &expr)) << gen().error();
+ EXPECT_EQ(result(), "(_tint_tmp)");
+ EXPECT_EQ(pre_result(), R"(bool _tint_tmp = false;
+if (left) {
+ _tint_tmp = true;
+} else {
+ if (right) {
+ _tint_tmp = true;
+ }
+}
+)");
+}
+
} // namespace
} // namespace hlsl
} // namespace writer
diff --git a/src/writer/hlsl/namer.cc b/src/writer/hlsl/namer.cc
index 8c04e91..a577d63 100644
--- a/src/writer/hlsl/namer.cc
+++ b/src/writer/hlsl/namer.cc
@@ -664,8 +664,7 @@
uint32_t i = 0;
// Make sure the ident name wasn't assigned by a remapping.
while (true) {
- auto remap_it = remapped_names_.find(ret_name);
- if (remap_it == remapped_names_.end()) {
+ if (!IsRemapped(ret_name)) {
break;
}
ret_name = name + "_" + std::to_string(i);
@@ -683,6 +682,11 @@
return it != name_map_.end();
}
+bool Namer::IsRemapped(const std::string& name) {
+ auto it = remapped_names_.find(name);
+ return it != remapped_names_.end();
+}
+
void Namer::RegisterRemappedName(const std::string& name) {
remapped_names_.insert(name);
}
diff --git a/src/writer/hlsl/namer.h b/src/writer/hlsl/namer.h
index be3b521..b5da343 100644
--- a/src/writer/hlsl/namer.h
+++ b/src/writer/hlsl/namer.h
@@ -39,11 +39,16 @@
/// @param name the name to register
void RegisterRemappedName(const std::string& name);
- /// Returns if the given name has been mapped alread
+ /// Returns if the given name has been mapped already
/// @param name the name to check
/// @returns true if the name has been mapped
bool IsMapped(const std::string& name);
+ /// Returns if the given name has been remapped already
+ /// @param name the name to check
+ /// @returns true if the name has been remapped
+ bool IsRemapped(const std::string& name);
+
private:
/// Map of original name to new name. The two names may be the same.
std::unordered_map<std::string, std::string> name_map_;