Convert IRGenerator::convertPrefixExpr to PrefixExpression::Make.

Surprisingly, this actually improved our error detection slightly.
The expression `- -half4(0)` can now be simplified to `half4(0)` at
IR generation time, which allows the constant-folder to detect a
constant zero (and from there, a division by constant zero).

Change-Id: I8c4f6ab522efab5bf98913f9c6a1487b7af39a99
Bug: skia:11342, skia:11343
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/376842
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index a0e0ae2..2b12e43 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -2129,99 +2129,7 @@
     if (!base) {
         return nullptr;
     }
-    return this->convertPrefixExpression(expression.getOperator(), std::move(base));
-}
-
-std::unique_ptr<Expression> IRGenerator::convertPrefixExpression(Operator op,
-                                                                 std::unique_ptr<Expression> base) {
-    const Type& baseType = base->type();
-    switch (op.kind()) {
-        case Token::Kind::TK_PLUS:
-            if (!baseType.componentType().isNumber()) {
-                this->errorReporter().error(
-                        base->fOffset, "'+' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            return base;
-
-        case Token::Kind::TK_MINUS:
-            if (!baseType.componentType().isNumber()) {
-                this->errorReporter().error(
-                        base->fOffset, "'-' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            if (base->is<IntLiteral>()) {
-                return std::make_unique<IntLiteral>(base->fOffset,
-                                                    -base->as<IntLiteral>().value(),
-                                                    &base->type());
-            }
-            if (base->is<FloatLiteral>()) {
-                return std::make_unique<FloatLiteral>(base->fOffset,
-                                                      -base->as<FloatLiteral>().value(),
-                                                      &base->type());
-            }
-            return std::make_unique<PrefixExpression>(Token::Kind::TK_MINUS, std::move(base));
-
-        case Token::Kind::TK_PLUSPLUS:
-            if (!baseType.isNumber()) {
-                this->errorReporter().error(base->fOffset,
-                                            String("'") + op.operatorName() +
-                                            "' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
-                                              &fContext.fErrors)) {
-                return nullptr;
-            }
-            break;
-        case Token::Kind::TK_MINUSMINUS:
-            if (!baseType.isNumber()) {
-                this->errorReporter().error(base->fOffset,
-                                            String("'") + op.operatorName() +
-                                            "' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
-                                              &fContext.fErrors)) {
-                return nullptr;
-            }
-            break;
-        case Token::Kind::TK_LOGICALNOT:
-            if (!baseType.isBoolean()) {
-                this->errorReporter().error(base->fOffset,
-                                            String("'") + op.operatorName() +
-                                            "' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            if (base->is<BoolLiteral>()) {
-                return std::make_unique<BoolLiteral>(base->fOffset,
-                                                     !base->as<BoolLiteral>().value(),
-                                                     &base->type());
-            }
-            break;
-        case Token::Kind::TK_BITWISENOT:
-            if (this->strictES2Mode()) {
-                // GLSL ES 1.00, Section 5.1
-                this->errorReporter().error(
-                        base->fOffset,
-                        String("operator '") + op.operatorName() + "' is not allowed");
-                return nullptr;
-            }
-            if (!baseType.isInteger()) {
-                this->errorReporter().error(base->fOffset,
-                                            String("'") + op.operatorName() +
-                                            "' cannot operate on '" + baseType.displayName() + "'");
-                return nullptr;
-            }
-            if (baseType.isLiteral()) {
-                // The expression `~123` is no longer a literal; coerce to the actual type.
-                base = this->coerce(std::move(base), baseType.scalarTypeForLiteral());
-            }
-            break;
-        default:
-            SK_ABORT("unsupported prefix operator\n");
-    }
-    return std::make_unique<PrefixExpression>(op, std::move(base));
+    return PrefixExpression::Make(fContext, expression.getOperator(), std::move(base));
 }
 
 std::unique_ptr<Expression> IRGenerator::convertField(std::unique_ptr<Expression> base,
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 06dcc59..00ff1e4 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -224,8 +224,6 @@
     std::unique_ptr<Expression> convertPostfixExpression(std::unique_ptr<Expression> base,
                                                          Operator op);
     std::unique_ptr<Expression> convertPostfixExpression(const ASTNode& expression);
-    std::unique_ptr<Expression> convertPrefixExpression(Operator op,
-                                                        std::unique_ptr<Expression> base);
     std::unique_ptr<Expression> convertScopeExpression(const ASTNode& expression);
     std::unique_ptr<StructDefinition> convertStructDefinition(const ASTNode& expression);
     std::unique_ptr<Expression> convertTypeField(int offset, const Type& type,
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index 7899ca4..4ba9157 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -363,7 +363,7 @@
         }
         case Expression::Kind::kPrefix: {
             const PrefixExpression& p = expression.as<PrefixExpression>();
-            return std::make_unique<PrefixExpression>(p.getOperator(), expr(p.operand()));
+            return PrefixExpression::Make(*fContext, p.getOperator(), expr(p.operand()));
         }
         case Expression::Kind::kPostfix: {
             const PostfixExpression& p = expression.as<PostfixExpression>();
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index 95d9f5d..225ecee 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -508,7 +508,7 @@
         case Rehydrator::kPrefix_Command: {
             Token::Kind op = (Token::Kind) this->readU8();
             std::unique_ptr<Expression> operand = this->expression();
-            return std::make_unique<PrefixExpression>(op, std::move(operand));
+            return PrefixExpression::Make(fContext, op, std::move(operand));
         }
         case Rehydrator::kSetting_Command: {
             StringFragment name = this->readString();
diff --git a/src/sksl/dsl/priv/DSLWriter.cpp b/src/sksl/dsl/priv/DSLWriter.cpp
index 62307b7..f94e941 100644
--- a/src/sksl/dsl/priv/DSLWriter.cpp
+++ b/src/sksl/dsl/priv/DSLWriter.cpp
@@ -16,6 +16,7 @@
 #include "src/sksl/dsl/DSLCore.h"
 #include "src/sksl/dsl/DSLErrorHandling.h"
 #include "src/sksl/ir/SkSLConstructor.h"
+#include "src/sksl/ir/SkSLPrefixExpression.h"
 #include "src/sksl/ir/SkSLSwitchStatement.h"
 
 #if !SKSL_USE_THREAD_LOCAL
@@ -136,7 +137,7 @@
 
 std::unique_ptr<SkSL::Expression> DSLWriter::ConvertPrefix(Operator op,
                                                            std::unique_ptr<Expression> expr) {
-    return IRGenerator().convertPrefixExpression(op, std::move(expr));
+    return PrefixExpression::Make(Context(), op, std::move(expr));
 }
 
 DSLPossibleStatement DSLWriter::ConvertSwitch(std::unique_ptr<Expression> value,
diff --git a/src/sksl/ir/SkSLPrefixExpression.cpp b/src/sksl/ir/SkSLPrefixExpression.cpp
index 62b537c..8e99779 100644
--- a/src/sksl/ir/SkSLPrefixExpression.cpp
+++ b/src/sksl/ir/SkSLPrefixExpression.cpp
@@ -14,25 +14,143 @@
 
 namespace SkSL {
 
-static std::unique_ptr<Expression> negate_operand(const Expression& operand) {
-    switch (operand.kind()) {
+static std::unique_ptr<Expression> negate_operand(const Context& context,
+                                                  std::unique_ptr<Expression> operand) {
+    switch (operand->kind()) {
         case Expression::Kind::kFloatLiteral:
             // Convert -floatLiteral(1) to floatLiteral(-1).
-            return std::make_unique<FloatLiteral>(operand.fOffset,
-                                                  -operand.as<FloatLiteral>().value(),
-                                                  &operand.type());
+            return std::make_unique<FloatLiteral>(operand->fOffset,
+                                                  -operand->as<FloatLiteral>().value(),
+                                                  &operand->type());
 
         case Expression::Kind::kIntLiteral:
             // Convert -intLiteral(1) to intLiteral(-1).
-            return std::make_unique<IntLiteral>(operand.fOffset,
-                                                -operand.as<IntLiteral>().value(),
-                                                &operand.type());
+            return std::make_unique<IntLiteral>(operand->fOffset,
+                                                -operand->as<IntLiteral>().value(),
+                                                &operand->type());
+
+        case Expression::Kind::kConstructor:
+            // To be consistent with prior behavior, the conversion of a negated constructor into a
+            // constructor of negative values is only performed when optimization is on.
+            // Conceptually it's pretty similar to the int/float optimizations above, though.
+            if (context.fConfig->fSettings.fOptimize && operand->isCompileTimeConstant()) {
+                Constructor& ctor = operand->as<Constructor>();
+
+                // We've found a negated constant constructor, e.g.:
+                //     -float4(float3(floatLiteral(1)), floatLiteral(2))
+                // To optimize this, the outer negation is removed and each argument is negated:
+                //     float4(-float3(floatLiteral(1)), floatLiteral(-2))
+                // Recursion will continue to push negation inwards as deeply as possible:
+                //     float4(float3(floatLiteral(-1)), floatLiteral(-2))
+                ExpressionArray args;
+                args.reserve_back(ctor.arguments().size());
+                for (std::unique_ptr<Expression>& arg : ctor.arguments()) {
+                    args.push_back(negate_operand(context, std::move(arg)));
+                }
+                return Constructor::Make(context, ctor.fOffset, ctor.type(), std::move(args));
+            }
+            break;
 
         default:
-            // Convert Expr to Prefix(TK_MINUS, Expr).
-            return std::make_unique<PrefixExpression>(Token::Kind::TK_MINUS,
-                                                      operand.clone());
+            break;
     }
+
+    // No simplified form; convert expression to Prefix(TK_MINUS, expression).
+    return std::make_unique<PrefixExpression>(Token::Kind::TK_MINUS, std::move(operand));
+}
+
+static std::unique_ptr<Expression> logical_not_operand(const Context& context,
+                                                       std::unique_ptr<Expression> operand) {
+    if (operand->is<BoolLiteral>()) {
+        // Convert !boolLiteral(true) to boolLiteral(false).
+        const BoolLiteral& b = operand->as<BoolLiteral>();
+        return std::make_unique<BoolLiteral>(operand->fOffset, !b.value(), &operand->type());
+    }
+
+    // No simplified form; convert expression to Prefix(TK_LOGICALNOT, expression).
+    return std::make_unique<PrefixExpression>(Token::Kind::TK_LOGICALNOT, std::move(operand));
+}
+
+std::unique_ptr<Expression> PrefixExpression::Make(const Context& context, Operator op,
+                                                   std::unique_ptr<Expression> base) {
+    const Type& baseType = base->type();
+    switch (op.kind()) {
+        case Token::Kind::TK_PLUS:
+            if (!baseType.componentType().isNumber()) {
+                context.fErrors.error(base->fOffset,
+                                      "'+' cannot operate on '" + baseType.displayName() + "'");
+                return nullptr;
+            }
+            return base;
+
+        case Token::Kind::TK_MINUS:
+            if (!baseType.componentType().isNumber()) {
+                context.fErrors.error(base->fOffset,
+                                      "'-' cannot operate on '" + baseType.displayName() + "'");
+                return nullptr;
+            }
+            return negate_operand(context, std::move(base));
+
+        case Token::Kind::TK_PLUSPLUS:
+            if (!baseType.isNumber()) {
+                context.fErrors.error(base->fOffset,
+                                      String("'") + op.operatorName() + "' cannot operate on '" +
+                                      baseType.displayName() + "'");
+                return nullptr;
+            }
+            if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
+                                              &context.fErrors)) {
+                return nullptr;
+            }
+            break;
+
+        case Token::Kind::TK_MINUSMINUS:
+            if (!baseType.isNumber()) {
+                context.fErrors.error(base->fOffset,
+                                      String("'") + op.operatorName() + "' cannot operate on '" +
+                                      baseType.displayName() + "'");
+                return nullptr;
+            }
+            if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
+                                              &context.fErrors)) {
+                return nullptr;
+            }
+            break;
+
+        case Token::Kind::TK_LOGICALNOT:
+            if (!baseType.isBoolean()) {
+                context.fErrors.error(base->fOffset,
+                                      String("'") + op.operatorName() + "' cannot operate on '" +
+                                      baseType.displayName() + "'");
+                return nullptr;
+            }
+            return logical_not_operand(context, std::move(base));
+
+        case Token::Kind::TK_BITWISENOT:
+            if (context.fConfig->strictES2Mode()) {
+                // GLSL ES 1.00, Section 5.1
+                context.fErrors.error(
+                        base->fOffset,
+                        String("operator '") + op.operatorName() + "' is not allowed");
+                return nullptr;
+            }
+            if (!baseType.isInteger()) {
+                context.fErrors.error(base->fOffset,
+                                      String("'") + op.operatorName() + "' cannot operate on '" +
+                                      baseType.displayName() + "'");
+                return nullptr;
+            }
+            if (baseType.isLiteral()) {
+                // The expression `~123` is no longer a literal; coerce to the actual type.
+                base = baseType.scalarTypeForLiteral().coerceExpression(std::move(base), context);
+            }
+            break;
+
+        default:
+            SK_ABORT("unsupported prefix operator\n");
+    }
+
+    return std::make_unique<PrefixExpression>(op, std::move(base));
 }
 
 std::unique_ptr<Expression> PrefixExpression::constantPropagate(const IRGenerator& irGenerator,
@@ -43,33 +161,15 @@
             switch (this->operand()->kind()) {
                 case Expression::Kind::kFloatLiteral:
                 case Expression::Kind::kIntLiteral:
-                    return negate_operand(*this->operand());
-
-                case Expression::Kind::kConstructor: {
-                    // We've found a negated constant vector, e.g.:
-                    //     -float4(float3(floatLiteral(1)), floatLiteral(2))
-                    // To optimize this, the outer negation is removed and each argument is negated:
-                    //     float4(-float3(floatLiteral(1)), floatLiteral(-2))
-                    // Steady state is reached after another optimization pass:
-                    //     float4(float3(floatLiteral(-1)), floatLiteral(-2))
-                    const Constructor& constructor = this->operand()->as<Constructor>();
-                    ExpressionArray args;
-                    args.reserve_back(constructor.arguments().size());
-                    for (const std::unique_ptr<Expression>& arg : constructor.arguments()) {
-                        args.push_back(negate_operand(*arg));
-                    }
-                    return Constructor::Make(irGenerator.fContext, constructor.fOffset,
-                                             constructor.type(), std::move(args));
-                }
+                case Expression::Kind::kConstructor:
+                    return negate_operand(irGenerator.fContext, this->operand()->clone());
 
                 default:
                     break;
             }
         } else if (this->getOperator().kind() == Token::Kind::TK_LOGICALNOT) {
             if (this->operand()->is<BoolLiteral>()) {
-                // Convert !boolLiteral(true) to boolLiteral(false).
-                const BoolLiteral& b = this->operand()->as<BoolLiteral>();
-                return std::make_unique<BoolLiteral>(b.fOffset, !b.value(), &b.type());
+                return logical_not_operand(irGenerator.fContext, this->operand()->clone());
             }
         }
     }
diff --git a/src/sksl/ir/SkSLPrefixExpression.h b/src/sksl/ir/SkSLPrefixExpression.h
index 673c789..2f40ef4 100644
--- a/src/sksl/ir/SkSLPrefixExpression.h
+++ b/src/sksl/ir/SkSLPrefixExpression.h
@@ -13,6 +13,8 @@
 #include "src/sksl/SkSLOperators.h"
 #include "src/sksl/ir/SkSLExpression.h"
 
+#include <memory>
+
 namespace SkSL {
 
 /**
@@ -22,11 +24,15 @@
 public:
     static constexpr Kind kExpressionKind = Kind::kPrefix;
 
+    // Use PrefixExpression::Make to automatically simplify various prefix expression types.
     PrefixExpression(Operator op, std::unique_ptr<Expression> operand)
         : INHERITED(operand->fOffset, kExpressionKind, &operand->type())
         , fOperator(op)
         , fOperand(std::move(operand)) {}
 
+    static std::unique_ptr<Expression> Make(const Context& context, Operator op,
+                                            std::unique_ptr<Expression> base);
+
     Operator getOperator() const {
         return fOperator;
     }
@@ -52,8 +58,7 @@
                                                   const DefinitionMap& definitions) override;
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new PrefixExpression(this->getOperator(),
-                                                                this->operand()->clone()));
+        return std::make_unique<PrefixExpression>(this->getOperator(), this->operand()->clone());
     }
 
     String description() const override {
diff --git a/tests/sksl/errors/Ossfuzz27663.glsl b/tests/sksl/errors/Ossfuzz27663.glsl
index b68394b..8cffafe 100644
--- a/tests/sksl/errors/Ossfuzz27663.glsl
+++ b/tests/sksl/errors/Ossfuzz27663.glsl
@@ -1,4 +1,5 @@
 ### Compilation failed:
 
+error: 1: division by zero
 error: 1: type mismatch: '=' cannot operate on 'half4', 'float4'
-1 error
+2 errors