• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/SkSLConstantFolder.h"
9 #include "src/sksl/SkSLProgramSettings.h"
10 #include "src/sksl/ir/SkSLBinaryExpression.h"
11 #include "src/sksl/ir/SkSLConstructorArray.h"
12 #include "src/sksl/ir/SkSLConstructorCompound.h"
13 #include "src/sksl/ir/SkSLIndexExpression.h"
14 #include "src/sksl/ir/SkSLLiteral.h"
15 #include "src/sksl/ir/SkSLSwizzle.h"
16 #include "src/sksl/ir/SkSLSymbolTable.h"
17 #include "src/sksl/ir/SkSLTypeReference.h"
18 
19 namespace SkSL {
20 
index_out_of_range(const Context & context,SKSL_INT index,const Expression & base)21 static bool index_out_of_range(const Context& context, SKSL_INT index, const Expression& base) {
22     if (index >= 0 && index < base.type().columns()) {
23         return false;
24     }
25 
26     context.fErrors->error(base.fLine, "index " + to_string(index) + " out of range for '" +
27                                        base.type().displayName() + "'");
28     return true;
29 }
30 
IndexType(const Context & context,const Type & type)31 const Type& IndexExpression::IndexType(const Context& context, const Type& type) {
32     if (type.isMatrix()) {
33         if (type.componentType() == *context.fTypes.fFloat) {
34             switch (type.rows()) {
35                 case 2: return *context.fTypes.fFloat2;
36                 case 3: return *context.fTypes.fFloat3;
37                 case 4: return *context.fTypes.fFloat4;
38                 default: SkASSERT(false);
39             }
40         } else if (type.componentType() == *context.fTypes.fHalf) {
41             switch (type.rows()) {
42                 case 2: return *context.fTypes.fHalf2;
43                 case 3: return *context.fTypes.fHalf3;
44                 case 4: return *context.fTypes.fHalf4;
45                 default: SkASSERT(false);
46             }
47         }
48     }
49     return type.componentType();
50 }
51 
Convert(const Context & context,SymbolTable & symbolTable,std::unique_ptr<Expression> base,std::unique_ptr<Expression> index)52 std::unique_ptr<Expression> IndexExpression::Convert(const Context& context,
53                                                      SymbolTable& symbolTable,
54                                                      std::unique_ptr<Expression> base,
55                                                      std::unique_ptr<Expression> index) {
56     // Convert an array type reference: `int[10]`.
57     if (base->is<TypeReference>()) {
58         const Type& baseType = base->as<TypeReference>().value();
59         SKSL_INT arraySize = baseType.convertArraySize(context, std::move(index));
60         if (!arraySize) {
61             return nullptr;
62         }
63         return TypeReference::Convert(context, base->fLine,
64                                       symbolTable.addArrayDimension(&baseType, arraySize));
65     }
66     // Convert an index expression with an expression inside of it: `arr[a * 3]`.
67     const Type& baseType = base->type();
68     if (!baseType.isArray() && !baseType.isMatrix() && !baseType.isVector()) {
69         context.fErrors->error(base->fLine,
70                                "expected array, but found '" + baseType.displayName() + "'");
71         return nullptr;
72     }
73     if (!index->type().isInteger()) {
74         index = context.fTypes.fInt->coerceExpression(std::move(index), context);
75         if (!index) {
76             return nullptr;
77         }
78     }
79     // Perform compile-time bounds checking on constant-expression indices.
80     const Expression* indexExpr = ConstantFolder::GetConstantValueForVariable(*index);
81     if (indexExpr->isIntLiteral()) {
82         SKSL_INT indexValue = indexExpr->as<Literal>().intValue();
83         if (index_out_of_range(context, indexValue, *base)) {
84             return nullptr;
85         }
86     }
87     return IndexExpression::Make(context, std::move(base), std::move(index));
88 }
89 
Make(const Context & context,std::unique_ptr<Expression> base,std::unique_ptr<Expression> index)90 std::unique_ptr<Expression> IndexExpression::Make(const Context& context,
91                                                   std::unique_ptr<Expression> base,
92                                                   std::unique_ptr<Expression> index) {
93     const Type& baseType = base->type();
94     SkASSERT(baseType.isArray() || baseType.isMatrix() || baseType.isVector());
95     SkASSERT(index->type().isInteger());
96 
97     const Expression* indexExpr = ConstantFolder::GetConstantValueForVariable(*index);
98     if (indexExpr->isIntLiteral()) {
99         SKSL_INT indexValue = indexExpr->as<Literal>().intValue();
100         if (!index_out_of_range(context, indexValue, *base)) {
101             if (baseType.isVector()) {
102                 // Constant array indexes on vectors can be converted to swizzles: `v[2]` --> `v.z`.
103                 // Swizzling is harmless and can unlock further simplifications for some base types.
104                 return Swizzle::Make(context, std::move(base), ComponentArray{(int8_t)indexValue});
105             }
106 
107             if (baseType.isArray() && !base->hasSideEffects()) {
108                 // Indexing an constant array constructor with a constant index can just pluck out
109                 // the requested value from the array.
110                 const Expression* baseExpr = ConstantFolder::GetConstantValueForVariable(*base);
111                 if (baseExpr->is<ConstructorArray>()) {
112                     const ConstructorArray& arrayCtor = baseExpr->as<ConstructorArray>();
113                     const ExpressionArray& arguments = arrayCtor.arguments();
114                     SkASSERT(arguments.count() == baseType.columns());
115 
116                     return arguments[indexValue]->clone();
117                 }
118             }
119 
120             if (baseType.isMatrix() && !base->hasSideEffects()) {
121                 // Matrices can be constructed with vectors that don't line up on column boundaries,
122                 // so extracting out the values from the constructor can be tricky. Fortunately, we
123                 // can reconstruct an equivalent vector using `getConstantValue`. If we
124                 // can't extract the data using `getConstantValue`, it wasn't constant and
125                 // we're not obligated to simplify anything.
126                 const Expression* baseExpr = ConstantFolder::GetConstantValueForVariable(*base);
127                 int vecWidth = baseType.rows();
128                 const Type& scalarType = baseType.componentType();
129                 const Type& vecType = scalarType.toCompound(context, vecWidth, /*rows=*/1);
130                 indexValue *= vecWidth;
131 
132                 ExpressionArray ctorArgs;
133                 ctorArgs.reserve_back(vecWidth);
134                 for (int slot = 0; slot < vecWidth; ++slot) {
135                     skstd::optional<double> slotVal = baseExpr->getConstantValue(indexValue + slot);
136                     if (slotVal.has_value()) {
137                         ctorArgs.push_back(Literal::Make(baseExpr->fLine, *slotVal, &scalarType));
138                     } else {
139                         ctorArgs.reset();
140                         break;
141                     }
142                 }
143 
144                 if (!ctorArgs.empty()) {
145                     int line = ctorArgs.front()->fLine;
146                     return ConstructorCompound::Make(context, line, vecType, std::move(ctorArgs));
147                 }
148             }
149         }
150     }
151 
152     return std::make_unique<IndexExpression>(context, std::move(base), std::move(index));
153 }
154 
155 }  // namespace SkSL
156