Revert "Revert "moved BinaryExpression's data into IRNode""

This reverts commit b61c3a9a01c44840eaa35b28cae0a4b358727f3c.

Change-Id: I42d93bdc6455c8ef941a6cbe1339df2ba916bb3c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/318697
Auto-Submit: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 3bfa0c6..9a6c286 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -68,6 +68,7 @@
   "$_src/sksl/ir/SkSLFunctionCall.h",
   "$_src/sksl/ir/SkSLFunctionDefinition.h",
   "$_src/sksl/ir/SkSLFunctionReference.h",
+  "$_src/sksl/ir/SkSLIRNode.cpp",
   "$_src/sksl/ir/SkSLIRNode.h",
   "$_src/sksl/ir/SkSLIfStatement.h",
   "$_src/sksl/ir/SkSLIndexExpression.h",
diff --git a/src/sksl/SkSLASTNode.h b/src/sksl/SkSLASTNode.h
index c65429c..795acbb 100644
--- a/src/sksl/SkSLASTNode.h
+++ b/src/sksl/SkSLASTNode.h
@@ -16,11 +16,6 @@
 
 namespace SkSL {
 
-// std::max isn't constexpr in some compilers
-static constexpr size_t Max(size_t a, size_t b) {
-    return a > b ? a : b;
-}
-
 /**
  * Represents a node in the abstract syntax tree (AST). The AST is based directly on the parse tree;
  * it is a parsed-but-not-yet-analyzed version of the program.
@@ -267,18 +262,18 @@
     };
 
     struct NodeData {
-        char fBytes[Max(sizeof(Token),
-                    Max(sizeof(StringFragment),
-                    Max(sizeof(bool),
-                    Max(sizeof(SKSL_INT),
-                    Max(sizeof(SKSL_FLOAT),
-                    Max(sizeof(Modifiers),
-                    Max(sizeof(TypeData),
-                    Max(sizeof(FunctionData),
-                    Max(sizeof(ParameterData),
-                    Max(sizeof(VarData),
-                    Max(sizeof(InterfaceBlockData),
-                        sizeof(SectionData))))))))))))];
+        char fBytes[std::max({sizeof(Token),
+                              sizeof(StringFragment),
+                              sizeof(bool),
+                              sizeof(SKSL_INT),
+                              sizeof(SKSL_FLOAT),
+                              sizeof(Modifiers),
+                              sizeof(TypeData),
+                              sizeof(FunctionData),
+                              sizeof(ParameterData),
+                              sizeof(VarData),
+                              sizeof(InterfaceBlockData),
+                              sizeof(SectionData)})];
 
         enum class Kind {
             kToken,
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index 6496965..82ac423 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -266,7 +266,7 @@
             return false;
         case Expression::Kind::kBinary: {
             const BinaryExpression& b = e.as<BinaryExpression>();
-            return this->visitExpression(*b.fLeft) || this->visitExpression(*b.fRight); }
+            return this->visitExpression(b.left()) || this->visitExpression(b.right()); }
         case Expression::Kind::kConstructor: {
             const Constructor& c = e.as<Constructor>();
             for (const auto& arg : c.fArguments) {
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index a213ca4..1e9dab0 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -681,28 +681,29 @@
 }
 
 bool ByteCodeGenerator::writeBinaryExpression(const BinaryExpression& b, bool discard) {
-    if (b.fOperator == Token::Kind::TK_EQ) {
-        std::unique_ptr<LValue> lvalue = this->getLValue(*b.fLeft);
-        this->writeExpression(*b.fRight);
+    const Expression& left = b.left();
+    const Expression& right = b.right();
+    Token::Kind op = b.getOperator();
+    if (op == Token::Kind::TK_EQ) {
+        std::unique_ptr<LValue> lvalue = this->getLValue(left);
+        this->writeExpression(right);
         lvalue->store(discard);
         discard = false;
         return discard;
     }
-    const Type& lType = b.fLeft->type();
-    const Type& rType = b.fRight->type();
+    const Type& lType = left.type();
+    const Type& rType = right.type();
     bool lVecOrMtx = (lType.typeKind() == Type::TypeKind::kVector ||
                       lType.typeKind() == Type::TypeKind::kMatrix);
     bool rVecOrMtx = (rType.typeKind() == Type::TypeKind::kVector ||
                       rType.typeKind() == Type::TypeKind::kMatrix);
-    Token::Kind op;
     std::unique_ptr<LValue> lvalue;
-    if (Compiler::IsAssignment(b.fOperator)) {
-        lvalue = this->getLValue(*b.fLeft);
+    if (Compiler::IsAssignment(op)) {
+        lvalue = this->getLValue(left);
         lvalue->load();
-        op = Compiler::RemoveAssignment(b.fOperator);
+        op = Compiler::RemoveAssignment(op);
     } else {
-        this->writeExpression(*b.fLeft);
-        op = b.fOperator;
+        this->writeExpression(left);
         if (!lVecOrMtx && rVecOrMtx) {
             for (int i = SlotCount(rType); i > 1; --i) {
                 this->write(ByteCodeInstruction::kDup, 1);
@@ -718,7 +719,7 @@
             this->write(ByteCodeInstruction::kMaskPush);
             this->write(ByteCodeInstruction::kBranchIfAllFalse);
             DeferredLocation falseLocation(this);
-            this->writeExpression(*b.fRight);
+            this->writeExpression(right);
             this->write(ByteCodeInstruction::kAndB, 1);
             falseLocation.set();
             this->write(ByteCodeInstruction::kMaskPop);
@@ -731,7 +732,7 @@
             this->write(ByteCodeInstruction::kMaskPush);
             this->write(ByteCodeInstruction::kBranchIfAllFalse);
             DeferredLocation falseLocation(this);
-            this->writeExpression(*b.fRight);
+            this->writeExpression(right);
             this->write(ByteCodeInstruction::kOrB, 1);
             falseLocation.set();
             this->write(ByteCodeInstruction::kMaskPop);
@@ -741,13 +742,13 @@
         case Token::Kind::TK_SHR: {
             SkASSERT(count == 1 && (tc == SkSL::TypeCategory::kSigned ||
                                     tc == SkSL::TypeCategory::kUnsigned));
-            if (!b.fRight->isCompileTimeConstant()) {
-                fErrors.error(b.fRight->fOffset, "Shift amounts must be constant");
+            if (!right.isCompileTimeConstant()) {
+                fErrors.error(right.fOffset, "Shift amounts must be constant");
                 return false;
             }
-            int64_t shift = b.fRight->getConstantInt();
+            int64_t shift = right.getConstantInt();
             if (shift < 0 || shift > 31) {
-                fErrors.error(b.fRight->fOffset, "Shift amount out of range");
+                fErrors.error(right.fOffset, "Shift amount out of range");
                 return false;
             }
 
@@ -765,7 +766,7 @@
         default:
             break;
     }
-    this->writeExpression(*b.fRight);
+    this->writeExpression(right);
     if (lVecOrMtx && !rVecOrMtx) {
         for (int i = SlotCount(lType); i > 1; --i) {
             this->write(ByteCodeInstruction::kDup, 1);
diff --git a/src/sksl/SkSLByteCodeGenerator.h b/src/sksl/SkSLByteCodeGenerator.h
index 9e8b080..f69bbb9 100644
--- a/src/sksl/SkSLByteCodeGenerator.h
+++ b/src/sksl/SkSLByteCodeGenerator.h
@@ -154,9 +154,18 @@
     struct Intrinsic {
         Intrinsic(SpecialIntrinsic    s) : is_special(true), special(s) {}
         Intrinsic(ByteCodeInstruction i) : Intrinsic(i, i, i) {}
+        // Workaround: We should be able to leave special uninitialized here, and were for a long
+        // time. Unrelated changes have made valgrind suddenly start complaining about us accessing
+        // uninitialized memory in the code:
+        //     if (intrin.is_special && intrin.special == SpecialIntrinsic::kSample)
+        // despite intrin.is_special being false at the time and therefore, one would think, not
+        // actually accessing intrin.special. I'm not sure whether this is a buggy optimization on
+        // clang's part or a false positive on valgrind's part, but either way initializing the
+        // field works around it.
         Intrinsic(ByteCodeInstruction f,
                   ByteCodeInstruction s,
-                  ByteCodeInstruction u) : is_special(false), inst_f(f), inst_s(s), inst_u(u) {}
+                  ByteCodeInstruction u) : is_special(false), special((SpecialIntrinsic) -1),
+                                           inst_f(f), inst_s(s), inst_u(u) {}
 
         bool                is_special;
         SpecialIntrinsic    special;
diff --git a/src/sksl/SkSLCFGGenerator.cpp b/src/sksl/SkSLCFGGenerator.cpp
index 009784c..3dd75e9 100644
--- a/src/sksl/SkSLCFGGenerator.cpp
+++ b/src/sksl/SkSLCFGGenerator.cpp
@@ -171,14 +171,14 @@
     switch (expr->kind()) {
         case Expression::Kind::kBinary: {
             BinaryExpression& b = expr->as<BinaryExpression>();
-            if (b.fOperator == Token::Kind::TK_EQ) {
-                if (!this->tryRemoveLValueBefore(iter, b.fLeft.get())) {
+            if (b.getOperator() == Token::Kind::TK_EQ) {
+                if (!this->tryRemoveLValueBefore(iter, &b.left())) {
                     return false;
                 }
-            } else if (!this->tryRemoveExpressionBefore(iter, b.fLeft.get())) {
+            } else if (!this->tryRemoveExpressionBefore(iter, &b.left())) {
                 return false;
             }
-            if (!this->tryRemoveExpressionBefore(iter, b.fRight.get())) {
+            if (!this->tryRemoveExpressionBefore(iter, &b.right())) {
                 return false;
             }
             SkASSERT((*iter)->expression()->get() == expr);
@@ -272,11 +272,12 @@
     switch ((*expr)->kind()) {
         case Expression::Kind::kBinary: {
             BinaryExpression& b = expr->get()->as<BinaryExpression>();
-            if (!this->tryInsertExpression(iter, &b.fRight)) {
+            if (!this->tryInsertExpression(iter, &b.rightPointer())) {
                 return false;
             }
+
             ++(*iter);
-            if (!this->tryInsertExpression(iter, &b.fLeft)) {
+            if (!this->tryInsertExpression(iter, &b.leftPointer())) {
                 return false;
             }
             ++(*iter);
@@ -324,16 +325,17 @@
     switch ((*e)->kind()) {
         case Expression::Kind::kBinary: {
             BinaryExpression& b = e->get()->as<BinaryExpression>();
-            switch (b.fOperator) {
+            Token::Kind op = b.getOperator();
+            switch (op) {
                 case Token::Kind::TK_LOGICALAND: // fall through
                 case Token::Kind::TK_LOGICALOR: {
                     // this isn't as precise as it could be -- we don't bother to track that if we
                     // early exit from a logical and/or, we know which branch of an 'if' we're going
                     // to hit -- but it won't make much difference in practice.
-                    this->addExpression(cfg, &b.fLeft, constantPropagate);
+                    this->addExpression(cfg, &b.leftPointer(), constantPropagate);
                     BlockId start = cfg.fCurrent;
                     cfg.newBlock();
-                    this->addExpression(cfg, &b.fRight, constantPropagate);
+                    this->addExpression(cfg, &b.rightPointer(), constantPropagate);
                     cfg.newBlock();
                     cfg.addExit(start, cfg.fCurrent);
                     cfg.currentBlock().fNodes.push_back(
@@ -341,15 +343,16 @@
                     break;
                 }
                 case Token::Kind::TK_EQ: {
-                    this->addExpression(cfg, &b.fRight, constantPropagate);
-                    this->addLValue(cfg, &b.fLeft);
+                    this->addExpression(cfg, &b.rightPointer(), constantPropagate);
+                    this->addLValue(cfg, &b.leftPointer());
                     cfg.currentBlock().fNodes.push_back(
                             BasicBlock::MakeExpression(e, constantPropagate));
                     break;
                 }
                 default:
-                    this->addExpression(cfg, &b.fLeft, !Compiler::IsAssignment(b.fOperator));
-                    this->addExpression(cfg, &b.fRight, constantPropagate);
+                    this->addExpression(cfg, &b.leftPointer(),
+                                        !Compiler::IsAssignment(b.getOperator()));
+                    this->addExpression(cfg, &b.rightPointer(), constantPropagate);
                     cfg.currentBlock().fNodes.push_back(
                             BasicBlock::MakeExpression(e, constantPropagate));
             }
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 3eacb80..a62374e 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -70,42 +70,45 @@
 
 void CPPCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                              Precedence parentPrecedence) {
-    if (b.fOperator == Token::Kind::TK_PERCENT) {
+    const Expression& left = b.left();
+    const Expression& right = b.right();
+    Token::Kind op = b.getOperator();
+    if (op == Token::Kind::TK_PERCENT) {
         // need to use "%%" instead of "%" b/c the code will be inside of a printf
-        Precedence precedence = GetBinaryPrecedence(b.fOperator);
+        Precedence precedence = GetBinaryPrecedence(op);
         if (precedence >= parentPrecedence) {
             this->write("(");
         }
-        this->writeExpression(*b.fLeft, precedence);
+        this->writeExpression(left, precedence);
         this->write(" %% ");
-        this->writeExpression(*b.fRight, precedence);
+        this->writeExpression(right, precedence);
         if (precedence >= parentPrecedence) {
             this->write(")");
         }
-    } else if (b.fLeft->kind() == Expression::Kind::kNullLiteral ||
-               b.fRight->kind() == Expression::Kind::kNullLiteral) {
+    } else if (left.kind() == Expression::Kind::kNullLiteral ||
+               right.kind() == Expression::Kind::kNullLiteral) {
         const Variable* var;
-        if (b.fLeft->kind() != Expression::Kind::kNullLiteral) {
-            var = &b.fLeft->as<VariableReference>().fVariable;
+        if (left.kind() != Expression::Kind::kNullLiteral) {
+            var = &left.as<VariableReference>().fVariable;
         } else {
-            var = &b.fRight->as<VariableReference>().fVariable;
+            var = &right.as<VariableReference>().fVariable;
         }
         SkASSERT(var->type().typeKind() == Type::TypeKind::kNullable &&
                  var->type().componentType() == *fContext.fFragmentProcessor_Type);
         this->write("%s");
-        const char* op = "";
-        switch (b.fOperator) {
+        const char* prefix = "";
+        switch (op) {
             case Token::Kind::TK_EQEQ:
-                op = "!";
+                prefix = "!";
                 break;
             case Token::Kind::TK_NEQ:
-                op = "";
+                prefix = "";
                 break;
             default:
                 SkASSERT(false);
         }
         int childIndex = this->getChildFPIndex(*var);
-        fFormatArgs.push_back(String(op) + "_outer.childProcessor(" + to_string(childIndex) +
+        fFormatArgs.push_back(String(prefix) + "_outer.childProcessor(" + to_string(childIndex) +
                               ") ? \"true\" : \"false\"");
     } else {
         INHERITED::writeBinaryExpression(b, parentPrecedence);
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index ffe6811..8ade97f 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -427,11 +427,11 @@
             switch (expr->kind()) {
                 case Expression::Kind::kBinary: {
                     BinaryExpression* b = &expr->as<BinaryExpression>();
-                    if (b->fOperator == Token::Kind::TK_EQ) {
-                        this->addDefinition(b->fLeft.get(), &b->fRight, definitions);
-                    } else if (Compiler::IsAssignment(b->fOperator)) {
+                    if (b->getOperator() == Token::Kind::TK_EQ) {
+                        this->addDefinition(&b->left(), &b->rightPointer(), definitions);
+                    } else if (Compiler::IsAssignment(b->getOperator())) {
                         this->addDefinition(
-                                      b->fLeft.get(),
+                                      &b->left(),
                                       (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
                                       definitions);
 
@@ -598,10 +598,10 @@
  * to a dead target and lack of side effects on the left hand side.
  */
 static bool dead_assignment(const BinaryExpression& b) {
-    if (!Compiler::IsAssignment(b.fOperator)) {
+    if (!Compiler::IsAssignment(b.getOperator())) {
         return false;
     }
-    return is_dead(*b.fLeft);
+    return is_dead(b.left());
 }
 
 void Compiler::computeDataFlow(CFG* cfg) {
@@ -696,14 +696,16 @@
     *outUpdated = true;
     std::unique_ptr<Expression>* target = (*iter)->expression();
     BinaryExpression& bin = (*target)->as<BinaryExpression>();
-    SkASSERT(!bin.fLeft->hasSideEffects());
+    Expression& left = bin.left();
+    std::unique_ptr<Expression>& rightPointer = bin.rightPointer();
+    SkASSERT(!left.hasSideEffects());
     bool result;
-    if (bin.fOperator == Token::Kind::TK_EQ) {
-        result = b->tryRemoveLValueBefore(iter, bin.fLeft.get());
+    if (bin.getOperator() == Token::Kind::TK_EQ) {
+        result = b->tryRemoveLValueBefore(iter, &left);
     } else {
-        result = b->tryRemoveExpressionBefore(iter, bin.fLeft.get());
+        result = b->tryRemoveExpressionBefore(iter, &left);
     }
-    *target = std::move(bin.fRight);
+    *target = std::move(rightPointer);
     if (!result) {
         *outNeedsRescan = true;
         return;
@@ -714,7 +716,7 @@
     }
     --(*iter);
     if ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
-        (*iter)->expression() != &bin.fRight) {
+        (*iter)->expression() != &rightPointer) {
         *outNeedsRescan = true;
         return;
     }
@@ -733,20 +735,22 @@
     *outUpdated = true;
     std::unique_ptr<Expression>* target = (*iter)->expression();
     BinaryExpression& bin = (*target)->as<BinaryExpression>();
-    SkASSERT(!bin.fRight->hasSideEffects());
-    if (!b->tryRemoveExpressionBefore(iter, bin.fRight.get())) {
-        *target = std::move(bin.fLeft);
+    std::unique_ptr<Expression>& leftPointer = bin.leftPointer();
+    Expression& right = bin.right();
+    SkASSERT(!right.hasSideEffects());
+    if (!b->tryRemoveExpressionBefore(iter, &right)) {
+        *target = std::move(leftPointer);
         *outNeedsRescan = true;
         return;
     }
-    *target = std::move(bin.fLeft);
+    *target = std::move(leftPointer);
     if (*iter == b->fNodes.begin()) {
         *outNeedsRescan = true;
         return;
     }
     --(*iter);
     if (((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
-        (*iter)->expression() != &bin.fLeft)) {
+        (*iter)->expression() != &leftPointer)) {
         *outNeedsRescan = true;
         return;
     }
@@ -799,7 +803,7 @@
                            bool* outUpdated,
                            bool* outNeedsRescan) {
     BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
-    vectorize(b, iter, bin.fRight->type(), &bin.fLeft, outUpdated, outNeedsRescan);
+    vectorize(b, iter, bin.right().type(), &bin.leftPointer(), outUpdated, outNeedsRescan);
 }
 
 /**
@@ -811,7 +815,7 @@
                             bool* outUpdated,
                             bool* outNeedsRescan) {
     BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
-    vectorize(b, iter, bin.fLeft->type(), &bin.fRight, outUpdated, outNeedsRescan);
+    vectorize(b, iter, bin.left().type(), &bin.rightPointer(), outUpdated, outNeedsRescan);
 }
 
 // Mark that an expression which we were writing to is no longer being written to
@@ -893,8 +897,10 @@
                 delete_left(&b, iter, outUpdated, outNeedsRescan);
                 break;
             }
-            const Type& leftType = bin->fLeft->type();
-            const Type& rightType = bin->fRight->type();
+            Expression& left = bin->left();
+            Expression& right = bin->right();
+            const Type& leftType = left.type();
+            const Type& rightType = right.type();
             // collapse useless expressions like x * 1 or x + 0
             if (((leftType.typeKind() != Type::TypeKind::kScalar) &&
                  (leftType.typeKind() != Type::TypeKind::kVector)) ||
@@ -902,9 +908,9 @@
                  (rightType.typeKind() != Type::TypeKind::kVector))) {
                 break;
             }
-            switch (bin->fOperator) {
+            switch (bin->getOperator()) {
                 case Token::Kind::TK_STAR:
-                    if (is_constant(*bin->fLeft, 1)) {
+                    if (is_constant(left, 1)) {
                         if (leftType.typeKind() == Type::TypeKind::kVector &&
                             rightType.typeKind() == Type::TypeKind::kScalar) {
                             // float4(1) * x -> float4(x)
@@ -916,22 +922,22 @@
                             delete_left(&b, iter, outUpdated, outNeedsRescan);
                         }
                     }
-                    else if (is_constant(*bin->fLeft, 0)) {
+                    else if (is_constant(left, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector &&
-                            !bin->fRight->hasSideEffects()) {
+                            !right.hasSideEffects()) {
                             // 0 * float4(x) -> float4(0)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
                             // 0 * x -> 0
                             // float4(0) * x -> float4(0)
                             // float4(0) * float4(x) -> float4(0)
-                            if (!bin->fRight->hasSideEffects()) {
+                            if (!right.hasSideEffects()) {
                                 delete_right(&b, iter, outUpdated, outNeedsRescan);
                             }
                         }
                     }
-                    else if (is_constant(*bin->fRight, 1)) {
+                    else if (is_constant(right, 1)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector) {
                             // x * float4(1) -> float4(x)
@@ -943,24 +949,24 @@
                             delete_right(&b, iter, outUpdated, outNeedsRescan);
                         }
                     }
-                    else if (is_constant(*bin->fRight, 0)) {
+                    else if (is_constant(right, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kVector &&
                             rightType.typeKind() == Type::TypeKind::kScalar &&
-                            !bin->fLeft->hasSideEffects()) {
+                            !left.hasSideEffects()) {
                             // float4(x) * 0 -> float4(0)
                             vectorize_right(&b, iter, outUpdated, outNeedsRescan);
                         } else {
                             // x * 0 -> 0
                             // x * float4(0) -> float4(0)
                             // float4(x) * float4(0) -> float4(0)
-                            if (!bin->fLeft->hasSideEffects()) {
+                            if (!left.hasSideEffects()) {
                                 delete_left(&b, iter, outUpdated, outNeedsRescan);
                             }
                         }
                     }
                     break;
                 case Token::Kind::TK_PLUS:
-                    if (is_constant(*bin->fLeft, 0)) {
+                    if (is_constant(left, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kVector &&
                             rightType.typeKind() == Type::TypeKind::kScalar) {
                             // float4(0) + x -> float4(x)
@@ -971,7 +977,7 @@
                             // float4(0) + float4(x) -> float4(x)
                             delete_left(&b, iter, outUpdated, outNeedsRescan);
                         }
-                    } else if (is_constant(*bin->fRight, 0)) {
+                    } else if (is_constant(right, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector) {
                             // x + float4(0) -> float4(x)
@@ -985,7 +991,7 @@
                     }
                     break;
                 case Token::Kind::TK_MINUS:
-                    if (is_constant(*bin->fRight, 0)) {
+                    if (is_constant(right, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector) {
                             // x - float4(0) -> float4(x)
@@ -999,7 +1005,7 @@
                     }
                     break;
                 case Token::Kind::TK_SLASH:
-                    if (is_constant(*bin->fRight, 1)) {
+                    if (is_constant(right, 1)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector) {
                             // x / float4(1) -> float4(x)
@@ -1010,43 +1016,43 @@
                             // float4(x) / float4(1) -> float4(x)
                             delete_right(&b, iter, outUpdated, outNeedsRescan);
                         }
-                    } else if (is_constant(*bin->fLeft, 0)) {
+                    } else if (is_constant(left, 0)) {
                         if (leftType.typeKind() == Type::TypeKind::kScalar &&
                             rightType.typeKind() == Type::TypeKind::kVector &&
-                            !bin->fRight->hasSideEffects()) {
+                            !right.hasSideEffects()) {
                             // 0 / float4(x) -> float4(0)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
                             // 0 / x -> 0
                             // float4(0) / x -> float4(0)
                             // float4(0) / float4(x) -> float4(0)
-                            if (!bin->fRight->hasSideEffects()) {
+                            if (!right.hasSideEffects()) {
                                 delete_right(&b, iter, outUpdated, outNeedsRescan);
                             }
                         }
                     }
                     break;
                 case Token::Kind::TK_PLUSEQ:
-                    if (is_constant(*bin->fRight, 0)) {
-                        clear_write(*bin->fLeft);
+                    if (is_constant(right, 0)) {
+                        clear_write(left);
                         delete_right(&b, iter, outUpdated, outNeedsRescan);
                     }
                     break;
                 case Token::Kind::TK_MINUSEQ:
-                    if (is_constant(*bin->fRight, 0)) {
-                        clear_write(*bin->fLeft);
+                    if (is_constant(right, 0)) {
+                        clear_write(left);
                         delete_right(&b, iter, outUpdated, outNeedsRescan);
                     }
                     break;
                 case Token::Kind::TK_STAREQ:
-                    if (is_constant(*bin->fRight, 1)) {
-                        clear_write(*bin->fLeft);
+                    if (is_constant(right, 1)) {
+                        clear_write(left);
                         delete_right(&b, iter, outUpdated, outNeedsRescan);
                     }
                     break;
                 case Token::Kind::TK_SLASHEQ:
-                    if (is_constant(*bin->fRight, 1)) {
-                        clear_write(*bin->fLeft);
+                    if (is_constant(right, 1)) {
+                        clear_write(left);
                         delete_right(&b, iter, outUpdated, outNeedsRescan);
                     }
                     break;
diff --git a/src/sksl/SkSLDehydrator.cpp b/src/sksl/SkSLDehydrator.cpp
index 4f93647..b320343 100644
--- a/src/sksl/SkSLDehydrator.cpp
+++ b/src/sksl/SkSLDehydrator.cpp
@@ -258,9 +258,9 @@
             case Expression::Kind::kBinary: {
                 const BinaryExpression& b = e->as<BinaryExpression>();
                 this->writeU8(Rehydrator::kBinary_Command);
-                this->write(b.fLeft.get());
-                this->writeU8((int) b.fOperator);
-                this->write(b.fRight.get());
+                this->write(&b.left());
+                this->writeU8((int) b.getOperator());
+                this->write(&b.right());
                 this->write(b.type());
                 break;
             }
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 433b11b..6ef3b9b 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -913,31 +913,33 @@
 
 void GLSLCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                               Precedence parentPrecedence) {
+    const Expression& left = b.left();
+    const Expression& right = b.right();
+    Token::Kind op = b.getOperator();
     if (fProgram.fSettings.fCaps->unfoldShortCircuitAsTernary() &&
-            (b.fOperator == Token::Kind::TK_LOGICALAND ||
-             b.fOperator == Token::Kind::TK_LOGICALOR)) {
+            (op == Token::Kind::TK_LOGICALAND || op == Token::Kind::TK_LOGICALOR)) {
         this->writeShortCircuitWorkaroundExpression(b, parentPrecedence);
         return;
     }
 
-    Precedence precedence = GetBinaryPrecedence(b.fOperator);
+    Precedence precedence = GetBinaryPrecedence(op);
     if (precedence >= parentPrecedence) {
         this->write("(");
     }
     bool positionWorkaround = fProgramKind == Program::Kind::kVertex_Kind &&
-                              Compiler::IsAssignment(b.fOperator) &&
-                              b.fLeft->kind() == Expression::Kind::kFieldAccess &&
-                              is_sk_position((FieldAccess&) *b.fLeft) &&
-                              !b.fRight->containsRTAdjust() &&
+                              Compiler::IsAssignment(op) &&
+                              left.kind() == Expression::Kind::kFieldAccess &&
+                              is_sk_position((FieldAccess&) left) &&
+                              !right.containsRTAdjust() &&
                               !fProgram.fSettings.fCaps->canUseFragCoord();
     if (positionWorkaround) {
         this->write("sk_FragCoord_Workaround = (");
     }
-    this->writeExpression(*b.fLeft, precedence);
+    this->writeExpression(left, precedence);
     this->write(" ");
-    this->write(Compiler::OperatorName(b.fOperator));
+    this->write(Compiler::OperatorName(op));
     this->write(" ");
-    this->writeExpression(*b.fRight, precedence);
+    this->writeExpression(right, precedence);
     if (positionWorkaround) {
         this->write(")");
     }
@@ -955,20 +957,20 @@
     // Transform:
     // a && b  =>   a ? b : false
     // a || b  =>   a ? true : b
-    this->writeExpression(*b.fLeft, kTernary_Precedence);
+    this->writeExpression(b.left(), kTernary_Precedence);
     this->write(" ? ");
-    if (b.fOperator == Token::Kind::TK_LOGICALAND) {
-        this->writeExpression(*b.fRight, kTernary_Precedence);
+    if (b.getOperator() == Token::Kind::TK_LOGICALAND) {
+        this->writeExpression(b.right(), kTernary_Precedence);
     } else {
         BoolLiteral boolTrue(fContext, -1, true);
         this->writeBoolLiteral(boolTrue);
     }
     this->write(" : ");
-    if (b.fOperator == Token::Kind::TK_LOGICALAND) {
+    if (b.getOperator() == Token::Kind::TK_LOGICALAND) {
         BoolLiteral boolFalse(fContext, -1, false);
         this->writeBoolLiteral(boolFalse);
     } else {
-        this->writeExpression(*b.fRight, kTernary_Precedence);
+        this->writeExpression(b.right(), kTernary_Precedence);
     }
     if (kTernary_Precedence >= parentPrecedence) {
         this->write(")");
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index f17b9fe..f197481 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -314,9 +314,9 @@
         case Expression::Kind::kBinary: {
             const BinaryExpression& b = expression.as<BinaryExpression>();
             return std::make_unique<BinaryExpression>(offset,
-                                                      expr(b.fLeft),
-                                                      b.fOperator,
-                                                      expr(b.fRight),
+                                                      expr(b.leftPointer()),
+                                                      b.getOperator(),
+                                                      expr(b.rightPointer()),
                                                       &b.type());
         }
         case Expression::Kind::kBoolLiteral:
@@ -948,7 +948,7 @@
 
                 case Expression::Kind::kBinary: {
                     BinaryExpression& binaryExpr = (*expr)->as<BinaryExpression>();
-                    this->visitExpression(&binaryExpr.fLeft);
+                    this->visitExpression(&binaryExpr.leftPointer());
 
                     // Logical-and and logical-or binary expressions do not inline the right side,
                     // because that would invalidate short-circuiting. That is, when evaluating
@@ -958,10 +958,11 @@
                     // It is illegal for side-effects from x() or y() to occur. The simplest way to
                     // enforce that rule is to avoid inlining the right side entirely. However, it
                     // is safe for other types of binary expression to inline both sides.
-                    bool shortCircuitable = (binaryExpr.fOperator == Token::Kind::TK_LOGICALAND ||
-                                             binaryExpr.fOperator == Token::Kind::TK_LOGICALOR);
+                    Token::Kind op = binaryExpr.getOperator();
+                    bool shortCircuitable = (op == Token::Kind::TK_LOGICALAND ||
+                                             op == Token::Kind::TK_LOGICALOR);
                     if (!shortCircuitable) {
-                        this->visitExpression(&binaryExpr.fRight);
+                        this->visitExpression(&binaryExpr.rightPointer());
                     }
                     break;
                 }
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index d573c38..1ee05a4 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -808,11 +808,14 @@
 
 void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                                Precedence parentPrecedence) {
-    const Type& leftType = b.fLeft->type();
-    const Type& rightType = b.fRight->type();
-    Precedence precedence = GetBinaryPrecedence(b.fOperator);
+    const Expression& left = b.left();
+    const Expression& right = b.right();
+    const Type& leftType = left.type();
+    const Type& rightType = right.type();
+    Token::Kind op = b.getOperator();
+    Precedence precedence = GetBinaryPrecedence(b.getOperator());
     bool needParens = precedence >= parentPrecedence;
-    switch (b.fOperator) {
+    switch (op) {
         case Token::Kind::TK_EQEQ:
             if (leftType.typeKind() == Type::TypeKind::kVector) {
                 this->write("all");
@@ -831,22 +834,21 @@
     if (needParens) {
         this->write("(");
     }
-    if (Compiler::IsAssignment(b.fOperator) &&
-        Expression::Kind::kVariableReference == b.fLeft->kind() &&
-        Variable::kParameter_Storage == ((VariableReference&) *b.fLeft).fVariable.fStorage &&
-        (((VariableReference&) *b.fLeft).fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) {
+    if (Compiler::IsAssignment(op) &&
+        Expression::Kind::kVariableReference == left.kind() &&
+        Variable::kParameter_Storage == left.as<VariableReference>().fVariable.fStorage &&
+        left.as<VariableReference>().fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
         // writing to an out parameter. Since we have to turn those into pointers, we have to
         // dereference it here.
         this->write("*");
     }
-    if (b.fOperator == Token::Kind::TK_STAREQ &&
-        leftType.typeKind() == Type::TypeKind::kMatrix &&
+    if (op == Token::Kind::TK_STAREQ && leftType.typeKind() == Type::TypeKind::kMatrix &&
         rightType.typeKind() == Type::TypeKind::kMatrix) {
         this->writeMatrixTimesEqualHelper(leftType, rightType, b.type());
     }
-    this->writeExpression(*b.fLeft, precedence);
-    if (b.fOperator != Token::Kind::TK_EQ && Compiler::IsAssignment(b.fOperator) &&
-        b.fLeft->kind() == Expression::Kind::kSwizzle && !b.fLeft->hasSideEffects()) {
+    this->writeExpression(left, precedence);
+    if (op != Token::Kind::TK_EQ && Compiler::IsAssignment(op) &&
+        left.kind() == Expression::Kind::kSwizzle && !left.hasSideEffects()) {
         // This doesn't compile in Metal:
         // float4 x = float4(1);
         // x.xy *= float2x2(...);
@@ -854,16 +856,16 @@
         // but switching it to x.xy = x.xy * float2x2(...) fixes it. We perform this tranformation
         // as long as the LHS has no side effects, and hope for the best otherwise.
         this->write(" = ");
-        this->writeExpression(*b.fLeft, kAssignment_Precedence);
+        this->writeExpression(left, kAssignment_Precedence);
         this->write(" ");
-        String op = Compiler::OperatorName(b.fOperator);
-        SkASSERT(op.endsWith("="));
-        this->write(op.substr(0, op.size() - 1).c_str());
+        String opName = Compiler::OperatorName(op);
+        SkASSERT(opName.endsWith("="));
+        this->write(opName.substr(0, opName.size() - 1).c_str());
         this->write(" ");
     } else {
-        this->write(String(" ") + Compiler::OperatorName(b.fOperator) + " ");
+        this->write(String(" ") + Compiler::OperatorName(op) + " ");
     }
-    this->writeExpression(*b.fRight, precedence);
+    this->writeExpression(right, precedence);
     if (needParens) {
         this->write(")");
     }
@@ -1730,8 +1732,8 @@
             return this->requirements(e->as<Swizzle>().fBase.get());
         case Expression::Kind::kBinary: {
             const BinaryExpression& bin = e->as<BinaryExpression>();
-            return this->requirements(bin.fLeft.get()) |
-                   this->requirements(bin.fRight.get());
+            return this->requirements(&bin.left()) |
+                   this->requirements(&bin.right());
         }
         case Expression::Kind::kIndex: {
             const IndexExpression& idx = e->as<IndexExpression>();
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index ab3d709..6e07dfb 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2302,11 +2302,14 @@
 }
 
 SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) {
+    const Expression& left = b.left();
+    const Expression& right = b.right();
+    Token::Kind op = b.getOperator();
     // handle cases where we don't necessarily evaluate both LHS and RHS
-    switch (b.fOperator) {
+    switch (op) {
         case Token::Kind::TK_EQ: {
-            SpvId rhs = this->writeExpression(*b.fRight, out);
-            this->getLValue(*b.fLeft, out)->store(rhs, out);
+            SpvId rhs = this->writeExpression(right, out);
+            this->getLValue(left, out)->store(rhs, out);
             return rhs;
         }
         case Token::Kind::TK_LOGICALAND:
@@ -2319,17 +2322,16 @@
 
     std::unique_ptr<LValue> lvalue;
     SpvId lhs;
-    if (Compiler::IsAssignment(b.fOperator)) {
-        lvalue = this->getLValue(*b.fLeft, out);
+    if (Compiler::IsAssignment(op)) {
+        lvalue = this->getLValue(left, out);
         lhs = lvalue->load(out);
     } else {
         lvalue = nullptr;
-        lhs = this->writeExpression(*b.fLeft, out);
+        lhs = this->writeExpression(left, out);
     }
-    SpvId rhs = this->writeExpression(*b.fRight, out);
-    SpvId result = this->writeBinaryExpression(b.fLeft->type(), lhs,
-                                               Compiler::RemoveAssignment(b.fOperator),
-                                               b.fRight->type(), rhs, b.type(), out);
+    SpvId rhs = this->writeExpression(right, out);
+    SpvId result = this->writeBinaryExpression(left.type(), lhs, Compiler::RemoveAssignment(op),
+                                               right.type(), rhs, b.type(), out);
     if (lvalue) {
         lvalue->store(result, out);
     }
@@ -2337,17 +2339,17 @@
 }
 
 SpvId SPIRVCodeGenerator::writeLogicalAnd(const BinaryExpression& a, OutputStream& out) {
-    SkASSERT(a.fOperator == Token::Kind::TK_LOGICALAND);
+    SkASSERT(a.getOperator() == Token::Kind::TK_LOGICALAND);
     BoolLiteral falseLiteral(fContext, -1, false);
     SpvId falseConstant = this->writeBoolLiteral(falseLiteral);
-    SpvId lhs = this->writeExpression(*a.fLeft, out);
+    SpvId lhs = this->writeExpression(a.left(), out);
     SpvId rhsLabel = this->nextId();
     SpvId end = this->nextId();
     SpvId lhsBlock = fCurrentBlock;
     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
     this->writeInstruction(SpvOpBranchConditional, lhs, rhsLabel, end, out);
     this->writeLabel(rhsLabel, out);
-    SpvId rhs = this->writeExpression(*a.fRight, out);
+    SpvId rhs = this->writeExpression(a.right(), out);
     SpvId rhsBlock = fCurrentBlock;
     this->writeInstruction(SpvOpBranch, end, out);
     this->writeLabel(end, out);
@@ -2358,17 +2360,17 @@
 }
 
 SpvId SPIRVCodeGenerator::writeLogicalOr(const BinaryExpression& o, OutputStream& out) {
-    SkASSERT(o.fOperator == Token::Kind::TK_LOGICALOR);
+    SkASSERT(o.getOperator() == Token::Kind::TK_LOGICALOR);
     BoolLiteral trueLiteral(fContext, -1, true);
     SpvId trueConstant = this->writeBoolLiteral(trueLiteral);
-    SpvId lhs = this->writeExpression(*o.fLeft, out);
+    SpvId lhs = this->writeExpression(o.left(), out);
     SpvId rhsLabel = this->nextId();
     SpvId end = this->nextId();
     SpvId lhsBlock = fCurrentBlock;
     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
     this->writeInstruction(SpvOpBranchConditional, lhs, end, rhsLabel, out);
     this->writeLabel(rhsLabel, out);
-    SpvId rhs = this->writeExpression(*o.fRight, out);
+    SpvId rhs = this->writeExpression(o.right(), out);
     SpvId rhsBlock = fCurrentBlock;
     this->writeInstruction(SpvOpBranch, end, out);
     this->writeLabel(end, out);
diff --git a/src/sksl/ir/SkSLBinaryExpression.h b/src/sksl/ir/SkSLBinaryExpression.h
index 18ce2b7..47ccd7a 100644
--- a/src/sksl/ir/SkSLBinaryExpression.h
+++ b/src/sksl/ir/SkSLBinaryExpression.h
@@ -19,24 +19,24 @@
 
 namespace SkSL {
 
-static inline bool check_ref(Expression* expr) {
-    switch (expr->kind()) {
+static inline bool check_ref(const Expression& expr) {
+    switch (expr.kind()) {
         case Expression::Kind::kExternalValue:
             return true;
         case Expression::Kind::kFieldAccess:
-            return check_ref(((FieldAccess*) expr)->fBase.get());
+            return check_ref(*expr.as<FieldAccess>().fBase);
         case Expression::Kind::kIndex:
-            return check_ref(((IndexExpression*) expr)->fBase.get());
+            return check_ref(*expr.as<IndexExpression>().fBase);
         case Expression::Kind::kSwizzle:
-            return check_ref(((Swizzle*) expr)->fBase.get());
+            return check_ref(*expr.as<Swizzle>().fBase);
         case Expression::Kind::kTernary: {
-            TernaryExpression* t = (TernaryExpression*) expr;
-            return check_ref(t->fIfTrue.get()) && check_ref(t->fIfFalse.get());
+            const TernaryExpression& t = expr.as<TernaryExpression>();
+            return check_ref(*t.fIfTrue) && check_ref(*t.fIfFalse);
         }
         case Expression::Kind::kVariableReference: {
-            VariableReference* ref = (VariableReference*) expr;
-            return ref->fRefKind == VariableReference::kWrite_RefKind ||
-                   ref->fRefKind == VariableReference::kReadWrite_RefKind;
+            const VariableReference& ref = expr.as<VariableReference>();
+            return ref.fRefKind == VariableReference::kWrite_RefKind ||
+                   ref.fRefKind == VariableReference::kReadWrite_RefKind;
         }
         default:
             return false;
@@ -51,46 +51,75 @@
 
     BinaryExpression(int offset, std::unique_ptr<Expression> left, Token::Kind op,
                      std::unique_ptr<Expression> right, const Type* type)
-    : INHERITED(offset, kExpressionKind, type)
-    , fLeft(std::move(left))
-    , fOperator(op)
-    , fRight(std::move(right)) {
+    : INHERITED(offset, kExpressionKind, { type, op }) {
+        fExpressionChildren.reserve(2);
+        fExpressionChildren.push_back(std::move(left));
+        fExpressionChildren.push_back(std::move(right));
         // If we are assigning to a VariableReference, ensure that it is set to Write or ReadWrite
-        SkASSERT(!Compiler::IsAssignment(op) || check_ref(fLeft.get()));
+        SkASSERT(!Compiler::IsAssignment(op) || check_ref(this->left()));
+    }
+
+    Expression& left() const {
+        return this->expressionChild(0);
+    }
+
+    std::unique_ptr<Expression>& leftPointer() {
+        return this->expressionPointer(0);
+    }
+
+    const std::unique_ptr<Expression>& leftPointer() const {
+        return this->expressionPointer(0);
+    }
+
+    Expression& right() const {
+        return this->expressionChild(1);
+    }
+
+    std::unique_ptr<Expression>& rightPointer() {
+        return this->expressionPointer(1);
+    }
+
+    const std::unique_ptr<Expression>& rightPointer() const {
+        return this->expressionPointer(1);
+    }
+
+    Token::Kind getOperator() const {
+        return this->typeTokenData().fToken;
     }
 
     bool isConstantOrUniform() const override {
-        return fLeft->isConstantOrUniform() && fRight->isConstantOrUniform();
+        return this->left().isConstantOrUniform() && this->right().isConstantOrUniform();
     }
 
     std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
                                                   const DefinitionMap& definitions) override {
-        return irGenerator.constantFold(*fLeft,
-                                        fOperator,
-                                        *fRight);
+        return irGenerator.constantFold(this->left(),
+                                        this->getOperator(),
+                                        this->right());
     }
 
     bool hasProperty(Property property) const override {
-        if (property == Property::kSideEffects && Compiler::IsAssignment(fOperator)) {
+        if (property == Property::kSideEffects && Compiler::IsAssignment(this->getOperator())) {
             return true;
         }
-        return fLeft->hasProperty(property) || fRight->hasProperty(property);
+        return this->left().hasProperty(property) || this->right().hasProperty(property);
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new BinaryExpression(fOffset, fLeft->clone(), fOperator,
-                                                                fRight->clone(), &this->type()));
+        return std::unique_ptr<Expression>(new BinaryExpression(fOffset,
+                                                                this->left().clone(),
+                                                                this->getOperator(),
+                                                                this->right().clone(),
+                                                                &this->type()));
     }
 
     String description() const override {
-        return "(" + fLeft->description() + " " + Compiler::OperatorName(fOperator) + " " +
-               fRight->description() + ")";
+        return "(" + this->left().description() + " " +
+               Compiler::OperatorName(this->getOperator()) + " " + this->right().description() +
+               ")";
     }
 
-    std::unique_ptr<Expression> fLeft;
-    const Token::Kind fOperator;
-    std::unique_ptr<Expression> fRight;
-
+private:
     using INHERITED = Expression;
 };
 
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
index 7beb341..14c9708 100644
--- a/src/sksl/ir/SkSLExpression.h
+++ b/src/sksl/ir/SkSLExpression.h
@@ -57,7 +57,12 @@
     };
 
     Expression(int offset, Kind kind, const Type* type)
-    : INHERITED(offset, (int) kind, type) {
+        : INHERITED(offset, (int) kind, type) {
+        SkASSERT(kind >= Kind::kFirst && kind <= Kind::kLast);
+    }
+
+    Expression(int offset, Kind kind, TypeTokenData data)
+        : INHERITED(offset, (int) kind, data) {
         SkASSERT(kind >= Kind::kFirst && kind <= Kind::kLast);
     }
 
diff --git a/src/sksl/ir/SkSLIRNode.cpp b/src/sksl/ir/SkSLIRNode.cpp
new file mode 100644
index 0000000..19eaf8b
--- /dev/null
+++ b/src/sksl/ir/SkSLIRNode.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/sksl/ir/SkSLIRNode.h"
+
+#include "src/sksl/ir/SkSLExpression.h"
+
+namespace SkSL {
+
+IRNode::IRNode(int offset, int kind, const Type* data)
+: fOffset(offset)
+, fKind(kind)
+, fData(data) {}
+
+IRNode::IRNode(int offset, int kind, TypeTokenData data)
+: fOffset(offset)
+, fKind(kind)
+, fData(data) {}
+
+IRNode::IRNode(const IRNode& other)
+    : fOffset(other.fOffset)
+    , fKind(other.fKind)
+    , fData(other.fData) {
+    // For now, we can't use a default copy constructor because of the std::unique_ptr children.
+    // Since we never copy nodes containing children, it's easiest just to assert we don't have any
+    // than bother with cloning them.
+    SkASSERT(other.fExpressionChildren.empty());
+}
+
+IRNode::~IRNode() {}
+
+} // namespace SkSL
diff --git a/src/sksl/ir/SkSLIRNode.h b/src/sksl/ir/SkSLIRNode.h
index 4a6aa90..d50afbe 100644
--- a/src/sksl/ir/SkSLIRNode.h
+++ b/src/sksl/ir/SkSLIRNode.h
@@ -8,11 +8,15 @@
 #ifndef SKSL_IRNODE
 #define SKSL_IRNODE
 
+#include "src/sksl/SkSLASTNode.h"
 #include "src/sksl/SkSLLexer.h"
 #include "src/sksl/SkSLString.h"
 
+#include <vector>
+
 namespace SkSL {
 
+struct Expression;
 class Type;
 
 /**
@@ -21,12 +25,19 @@
  */
 class IRNode {
 public:
-    IRNode(int offset, int kind, const Type* type = nullptr)
-    : fOffset(offset)
-    , fKind(kind)
-    , fType(type) {}
+    virtual ~IRNode();
 
-    virtual ~IRNode() {}
+    IRNode& operator=(const IRNode& other) {
+        // Need to have a copy assignment operator because Type requires it, but can't use the
+        // default version until we finish migrating away from std::unique_ptr children. For now,
+        // just assert that there are no children (we could theoretically clone them, but we never
+        // actually copy nodes containing children).
+        SkASSERT(other.fExpressionChildren.empty());
+        fKind = other.fKind;
+        fOffset = other.fOffset;
+        fData = other.fData;
+        return *this;
+    }
 
     virtual String description() const = 0;
 
@@ -35,15 +46,81 @@
     int fOffset;
 
     const Type& type() const {
-        SkASSERT(fType);
-        return *fType;
+        switch (fData.fKind) {
+            case NodeData::Kind::kType:
+                return *this->typeData();
+            case NodeData::Kind::kTypeToken:
+                return *this->typeTokenData().fType;
+            default:
+                SkUNREACHABLE;
+        }
     }
 
 protected:
+    struct TypeTokenData {
+        const Type* fType;
+        Token::Kind fToken;
+    };
+
+    struct NodeData {
+        char fBytes[std::max(sizeof(Type*),
+                             sizeof(TypeTokenData))];
+
+        enum class Kind {
+            kType,
+            kTypeToken,
+        } fKind;
+
+        NodeData() = default;
+
+        NodeData(const Type* data)
+            : fKind(Kind::kType) {
+            memcpy(fBytes, &data, sizeof(data));
+        }
+
+        NodeData(TypeTokenData data)
+            : fKind(Kind::kTypeToken) {
+            memcpy(fBytes, &data, sizeof(data));
+        }
+    };
+
+    IRNode(int offset, int kind, const Type* data = nullptr);
+
+    IRNode(int offset, int kind, TypeTokenData data);
+
+    IRNode(const IRNode& other);
+
+    Expression& expressionChild(int index) const {
+        return *fExpressionChildren[index];
+    }
+
+    std::unique_ptr<Expression>& expressionPointer(int index) {
+        return fExpressionChildren[index];
+    }
+
+    const std::unique_ptr<Expression>& expressionPointer(int index) const {
+        return fExpressionChildren[index];
+    }
+
+    Type* typeData() const {
+        SkASSERT(fData.fKind == NodeData::Kind::kType);
+        Type* result;
+        memcpy(&result, fData.fBytes, sizeof(result));
+        return result;
+    }
+
+    TypeTokenData typeTokenData() const {
+        SkASSERT(fData.fKind == NodeData::Kind::kTypeToken);
+        TypeTokenData result;
+        memcpy(&result, fData.fBytes, sizeof(result));
+        return result;
+    }
+
     int fKind;
+    std::vector<std::unique_ptr<Expression>> fExpressionChildren;
 
 private:
-    const Type* fType;
+    NodeData fData;
 };
 
 }  // namespace SkSL