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