]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file | |
3 | // distributed with this work for additional information | |
4 | // regarding copyright ownership. The ASF licenses this file | |
5 | // to you under the Apache License, Version 2.0 (the | |
6 | // "License"); you may not use this file except in compliance | |
7 | // with the License. You may obtain a copy of the License at | |
8 | // | |
9 | // http://www.apache.org/licenses/LICENSE-2.0 | |
10 | // | |
11 | // Unless required by applicable law or agreed to in writing, | |
12 | // software distributed under the License is distributed on an | |
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | // KIND, either express or implied. See the License for the | |
15 | // specific language governing permissions and limitations | |
16 | // under the License. | |
17 | ||
18 | #include "gandiva/expr_decomposer.h" | |
19 | ||
20 | #include <gtest/gtest.h> | |
21 | ||
22 | #include "gandiva/annotator.h" | |
23 | #include "gandiva/dex.h" | |
24 | #include "gandiva/function_registry.h" | |
25 | #include "gandiva/gandiva_aliases.h" | |
26 | #include "gandiva/node.h" | |
27 | #include "gandiva/tree_expr_builder.h" | |
28 | ||
29 | namespace gandiva { | |
30 | ||
31 | using arrow::int32; | |
32 | ||
33 | class TestExprDecomposer : public ::testing::Test { | |
34 | protected: | |
35 | FunctionRegistry registry_; | |
36 | }; | |
37 | ||
38 | TEST_F(TestExprDecomposer, TestStackSimple) { | |
39 | Annotator annotator; | |
40 | ExprDecomposer decomposer(registry_, annotator); | |
41 | ||
42 | // if (a) _ | |
43 | // else _ | |
44 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
45 | ||
46 | decomposer.PushConditionEntry(node_a); | |
47 | decomposer.PopConditionEntry(node_a); | |
48 | ||
49 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
50 | EXPECT_EQ(idx_a, 0); | |
51 | decomposer.PopThenEntry(node_a); | |
52 | ||
53 | decomposer.PushElseEntry(node_a, idx_a); | |
54 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
55 | EXPECT_EQ(is_terminal_a, true); | |
56 | EXPECT_EQ(decomposer.if_entries_stack_.empty(), true); | |
57 | } | |
58 | ||
59 | TEST_F(TestExprDecomposer, TestNested) { | |
60 | Annotator annotator; | |
61 | ExprDecomposer decomposer(registry_, annotator); | |
62 | ||
63 | // if (a) _ | |
64 | // else _ | |
65 | // if (b) _ | |
66 | // else _ | |
67 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
68 | IfNode node_b(nullptr, nullptr, nullptr, int32()); | |
69 | ||
70 | decomposer.PushConditionEntry(node_a); | |
71 | decomposer.PopConditionEntry(node_a); | |
72 | ||
73 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
74 | EXPECT_EQ(idx_a, 0); | |
75 | decomposer.PopThenEntry(node_a); | |
76 | ||
77 | decomposer.PushElseEntry(node_a, idx_a); | |
78 | ||
79 | { // start b | |
80 | decomposer.PushConditionEntry(node_b); | |
81 | decomposer.PopConditionEntry(node_b); | |
82 | ||
83 | int idx_b = decomposer.PushThenEntry(node_b, true); | |
84 | EXPECT_EQ(idx_b, 0); // must reuse bitmap. | |
85 | decomposer.PopThenEntry(node_b); | |
86 | ||
87 | decomposer.PushElseEntry(node_b, idx_b); | |
88 | bool is_terminal_b = decomposer.PopElseEntry(node_b); | |
89 | EXPECT_EQ(is_terminal_b, true); | |
90 | } // end b | |
91 | ||
92 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
93 | EXPECT_EQ(is_terminal_a, false); // there was a nested if. | |
94 | ||
95 | EXPECT_EQ(decomposer.if_entries_stack_.empty(), true); | |
96 | } | |
97 | ||
98 | TEST_F(TestExprDecomposer, TestInternalIf) { | |
99 | Annotator annotator; | |
100 | ExprDecomposer decomposer(registry_, annotator); | |
101 | ||
102 | // if (a) _ | |
103 | // if (b) _ | |
104 | // else _ | |
105 | // else _ | |
106 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
107 | IfNode node_b(nullptr, nullptr, nullptr, int32()); | |
108 | ||
109 | decomposer.PushConditionEntry(node_a); | |
110 | decomposer.PopConditionEntry(node_a); | |
111 | ||
112 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
113 | EXPECT_EQ(idx_a, 0); | |
114 | ||
115 | { // start b | |
116 | decomposer.PushConditionEntry(node_b); | |
117 | decomposer.PopConditionEntry(node_b); | |
118 | ||
119 | int idx_b = decomposer.PushThenEntry(node_b, false); | |
120 | EXPECT_EQ(idx_b, 1); // must not reuse bitmap. | |
121 | decomposer.PopThenEntry(node_b); | |
122 | ||
123 | decomposer.PushElseEntry(node_b, idx_b); | |
124 | bool is_terminal_b = decomposer.PopElseEntry(node_b); | |
125 | EXPECT_EQ(is_terminal_b, true); | |
126 | } // end b | |
127 | ||
128 | decomposer.PopThenEntry(node_a); | |
129 | decomposer.PushElseEntry(node_a, idx_a); | |
130 | ||
131 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
132 | EXPECT_EQ(is_terminal_a, true); // there was no nested if. | |
133 | ||
134 | EXPECT_EQ(decomposer.if_entries_stack_.empty(), true); | |
135 | } | |
136 | ||
137 | TEST_F(TestExprDecomposer, TestParallelIf) { | |
138 | Annotator annotator; | |
139 | ExprDecomposer decomposer(registry_, annotator); | |
140 | ||
141 | // if (a) _ | |
142 | // else _ | |
143 | // if (b) _ | |
144 | // else _ | |
145 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
146 | IfNode node_b(nullptr, nullptr, nullptr, int32()); | |
147 | ||
148 | decomposer.PushConditionEntry(node_a); | |
149 | decomposer.PopConditionEntry(node_a); | |
150 | ||
151 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
152 | EXPECT_EQ(idx_a, 0); | |
153 | ||
154 | decomposer.PopThenEntry(node_a); | |
155 | decomposer.PushElseEntry(node_a, idx_a); | |
156 | ||
157 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
158 | EXPECT_EQ(is_terminal_a, true); // there was no nested if. | |
159 | ||
160 | // start b | |
161 | decomposer.PushConditionEntry(node_b); | |
162 | decomposer.PopConditionEntry(node_b); | |
163 | ||
164 | int idx_b = decomposer.PushThenEntry(node_b, false); | |
165 | EXPECT_EQ(idx_b, 1); // must not reuse bitmap. | |
166 | decomposer.PopThenEntry(node_b); | |
167 | ||
168 | decomposer.PushElseEntry(node_b, idx_b); | |
169 | bool is_terminal_b = decomposer.PopElseEntry(node_b); | |
170 | EXPECT_EQ(is_terminal_b, true); | |
171 | ||
172 | EXPECT_EQ(decomposer.if_entries_stack_.empty(), true); | |
173 | } | |
174 | ||
175 | TEST_F(TestExprDecomposer, TestIfInCondition) { | |
176 | Annotator annotator; | |
177 | ExprDecomposer decomposer(registry_, annotator); | |
178 | ||
179 | // if (if _ else _) : a | |
180 | // - | |
181 | // else | |
182 | // if (if _ else _) : b | |
183 | // - | |
184 | // else | |
185 | // - | |
186 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
187 | IfNode node_b(nullptr, nullptr, nullptr, int32()); | |
188 | IfNode cond_node_a(nullptr, nullptr, nullptr, int32()); | |
189 | IfNode cond_node_b(nullptr, nullptr, nullptr, int32()); | |
190 | ||
191 | // start a | |
192 | decomposer.PushConditionEntry(node_a); | |
193 | { | |
194 | // start cond_node_a | |
195 | decomposer.PushConditionEntry(cond_node_a); | |
196 | decomposer.PopConditionEntry(cond_node_a); | |
197 | ||
198 | int idx_cond_a = decomposer.PushThenEntry(cond_node_a, false); | |
199 | EXPECT_EQ(idx_cond_a, 0); | |
200 | decomposer.PopThenEntry(cond_node_a); | |
201 | ||
202 | decomposer.PushElseEntry(cond_node_a, idx_cond_a); | |
203 | bool is_terminal = decomposer.PopElseEntry(cond_node_a); | |
204 | EXPECT_EQ(is_terminal, true); // there was no nested if. | |
205 | } | |
206 | decomposer.PopConditionEntry(node_a); | |
207 | ||
208 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
209 | EXPECT_EQ(idx_a, 1); // no re-use | |
210 | decomposer.PopThenEntry(node_a); | |
211 | ||
212 | decomposer.PushElseEntry(node_a, idx_a); | |
213 | ||
214 | { // start b | |
215 | decomposer.PushConditionEntry(node_b); | |
216 | { | |
217 | // start cond_node_b | |
218 | decomposer.PushConditionEntry(cond_node_b); | |
219 | decomposer.PopConditionEntry(cond_node_b); | |
220 | ||
221 | int idx_cond_b = decomposer.PushThenEntry(cond_node_b, false); | |
222 | EXPECT_EQ(idx_cond_b, 2); // no re-use | |
223 | decomposer.PopThenEntry(cond_node_b); | |
224 | ||
225 | decomposer.PushElseEntry(cond_node_b, idx_cond_b); | |
226 | bool is_terminal = decomposer.PopElseEntry(cond_node_b); | |
227 | EXPECT_EQ(is_terminal, true); // there was no nested if. | |
228 | } | |
229 | decomposer.PopConditionEntry(node_b); | |
230 | ||
231 | int idx_b = decomposer.PushThenEntry(node_b, true); | |
232 | EXPECT_EQ(idx_b, 1); // must reuse bitmap. | |
233 | decomposer.PopThenEntry(node_b); | |
234 | ||
235 | decomposer.PushElseEntry(node_b, idx_b); | |
236 | bool is_terminal = decomposer.PopElseEntry(node_b); | |
237 | EXPECT_EQ(is_terminal, true); | |
238 | } // end b | |
239 | ||
240 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
241 | EXPECT_EQ(is_terminal_a, false); // there was a nested if. | |
242 | ||
243 | EXPECT_EQ(decomposer.if_entries_stack_.empty(), true); | |
244 | } | |
245 | ||
246 | TEST_F(TestExprDecomposer, TestFunctionBetweenNestedIf) { | |
247 | Annotator annotator; | |
248 | ExprDecomposer decomposer(registry_, annotator); | |
249 | ||
250 | // if (a) _ | |
251 | // else | |
252 | // function( | |
253 | // if (b) _ | |
254 | // else _ | |
255 | // ) | |
256 | ||
257 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
258 | IfNode node_b(nullptr, nullptr, nullptr, int32()); | |
259 | ||
260 | // start outer if | |
261 | decomposer.PushConditionEntry(node_a); | |
262 | decomposer.PopConditionEntry(node_a); | |
263 | ||
264 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
265 | EXPECT_EQ(idx_a, 0); | |
266 | decomposer.PopThenEntry(node_a); | |
267 | ||
268 | decomposer.PushElseEntry(node_a, idx_a); | |
269 | { // start b | |
270 | decomposer.PushConditionEntry(node_b); | |
271 | decomposer.PopConditionEntry(node_b); | |
272 | ||
273 | int idx_b = decomposer.PushThenEntry(node_b, false); // not else node of parent if | |
274 | EXPECT_EQ(idx_b, 1); // can't reuse bitmap. | |
275 | decomposer.PopThenEntry(node_b); | |
276 | ||
277 | decomposer.PushElseEntry(node_b, idx_b); | |
278 | bool is_terminal_b = decomposer.PopElseEntry(node_b); | |
279 | EXPECT_EQ(is_terminal_b, true); | |
280 | } | |
281 | bool is_terminal_a = decomposer.PopElseEntry(node_a); | |
282 | EXPECT_EQ(is_terminal_a, true); // a else is also terminal | |
283 | ||
284 | EXPECT_TRUE(decomposer.if_entries_stack_.empty()); | |
285 | } | |
286 | ||
287 | TEST_F(TestExprDecomposer, TestComplexIfCondition) { | |
288 | Annotator annotator; | |
289 | ExprDecomposer decomposer(registry_, annotator); | |
290 | ||
291 | // if (if _ | |
292 | // else | |
293 | // if _ | |
294 | // else _ | |
295 | // ) | |
296 | // then | |
297 | // if _ | |
298 | // else | |
299 | // if _ | |
300 | // else _ | |
301 | // | |
302 | // else | |
303 | // if _ | |
304 | // else | |
305 | // if _ | |
306 | // else _ | |
307 | ||
308 | IfNode node_a(nullptr, nullptr, nullptr, int32()); | |
309 | ||
310 | IfNode cond_node_a(nullptr, nullptr, nullptr, int32()); | |
311 | IfNode cond_node_a_inner_if(nullptr, nullptr, nullptr, int32()); | |
312 | ||
313 | IfNode then_node_a(nullptr, nullptr, nullptr, int32()); | |
314 | IfNode then_node_a_inner_if(nullptr, nullptr, nullptr, int32()); | |
315 | ||
316 | IfNode else_node_a(nullptr, nullptr, nullptr, int32()); | |
317 | IfNode else_node_a_inner_if(nullptr, nullptr, nullptr, int32()); | |
318 | ||
319 | // start outer if | |
320 | decomposer.PushConditionEntry(node_a); | |
321 | { | |
322 | // start the nested if inside the condition of a | |
323 | decomposer.PushConditionEntry(cond_node_a); | |
324 | decomposer.PopConditionEntry(cond_node_a); | |
325 | ||
326 | int idx_cond_a = decomposer.PushThenEntry(cond_node_a, false); | |
327 | EXPECT_EQ(idx_cond_a, 0); | |
328 | decomposer.PopThenEntry(cond_node_a); | |
329 | ||
330 | decomposer.PushElseEntry(cond_node_a, idx_cond_a); | |
331 | { | |
332 | decomposer.PushConditionEntry(cond_node_a_inner_if); | |
333 | decomposer.PopConditionEntry(cond_node_a_inner_if); | |
334 | ||
335 | int idx_cond_a_inner_if = decomposer.PushThenEntry(cond_node_a_inner_if, true); | |
336 | EXPECT_EQ(idx_cond_a_inner_if, | |
337 | 0); // expect bitmap to be resused since nested if else | |
338 | decomposer.PopThenEntry(cond_node_a_inner_if); | |
339 | ||
340 | decomposer.PushElseEntry(cond_node_a_inner_if, idx_cond_a_inner_if); | |
341 | bool is_terminal = decomposer.PopElseEntry(cond_node_a_inner_if); | |
342 | EXPECT_TRUE(is_terminal); | |
343 | } | |
344 | EXPECT_FALSE(decomposer.PopElseEntry(cond_node_a)); | |
345 | } | |
346 | decomposer.PopConditionEntry(node_a); | |
347 | ||
348 | int idx_a = decomposer.PushThenEntry(node_a, false); | |
349 | EXPECT_EQ(idx_a, 1); | |
350 | ||
351 | { | |
352 | // start the nested if inside the then node of a | |
353 | decomposer.PushConditionEntry(then_node_a); | |
354 | decomposer.PopConditionEntry(then_node_a); | |
355 | ||
356 | int idx_then_a = decomposer.PushThenEntry(then_node_a, false); | |
357 | EXPECT_EQ(idx_then_a, 2); | |
358 | decomposer.PopThenEntry(then_node_a); | |
359 | ||
360 | decomposer.PushElseEntry(then_node_a, idx_then_a); | |
361 | { | |
362 | decomposer.PushConditionEntry(then_node_a_inner_if); | |
363 | decomposer.PopConditionEntry(then_node_a_inner_if); | |
364 | ||
365 | int idx_then_a_inner_if = decomposer.PushThenEntry(then_node_a_inner_if, true); | |
366 | EXPECT_EQ(idx_then_a_inner_if, | |
367 | 2); // expect bitmap to be resused since nested if else | |
368 | decomposer.PopThenEntry(then_node_a_inner_if); | |
369 | ||
370 | decomposer.PushElseEntry(then_node_a_inner_if, idx_then_a_inner_if); | |
371 | bool is_terminal = decomposer.PopElseEntry(then_node_a_inner_if); | |
372 | EXPECT_TRUE(is_terminal); | |
373 | } | |
374 | EXPECT_FALSE(decomposer.PopElseEntry(then_node_a)); | |
375 | } | |
376 | decomposer.PopThenEntry(node_a); | |
377 | ||
378 | decomposer.PushElseEntry(node_a, idx_a); | |
379 | { | |
380 | // start the nested if inside the else node of a | |
381 | decomposer.PushConditionEntry(else_node_a); | |
382 | decomposer.PopConditionEntry(else_node_a); | |
383 | ||
384 | int idx_else_a = | |
385 | decomposer.PushThenEntry(else_node_a, true); // else node is another if-node | |
386 | EXPECT_EQ(idx_else_a, 1); // reuse the outer if node bitmap since nested if-else | |
387 | decomposer.PopThenEntry(else_node_a); | |
388 | ||
389 | decomposer.PushElseEntry(else_node_a, idx_else_a); | |
390 | { | |
391 | decomposer.PushConditionEntry(else_node_a_inner_if); | |
392 | decomposer.PopConditionEntry(else_node_a_inner_if); | |
393 | ||
394 | int idx_else_a_inner_if = decomposer.PushThenEntry(else_node_a_inner_if, true); | |
395 | EXPECT_EQ(idx_else_a_inner_if, | |
396 | 1); // expect bitmap to be resused since nested if else | |
397 | decomposer.PopThenEntry(else_node_a_inner_if); | |
398 | ||
399 | decomposer.PushElseEntry(else_node_a_inner_if, idx_else_a_inner_if); | |
400 | bool is_terminal = decomposer.PopElseEntry(else_node_a_inner_if); | |
401 | EXPECT_TRUE(is_terminal); | |
402 | } | |
403 | EXPECT_FALSE(decomposer.PopElseEntry(else_node_a)); | |
404 | } | |
405 | EXPECT_FALSE(decomposer.PopElseEntry(node_a)); | |
406 | EXPECT_TRUE(decomposer.if_entries_stack_.empty()); | |
407 | } | |
408 | ||
409 | } // namespace gandiva |