Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
compound-predicates.cc
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 #include "codegen/codegen-anyval.h"
17 #include "codegen/llvm-codegen.h"
18 #include "runtime/runtime-state.h"
19 
20 using namespace impala;
21 using namespace llvm;
22 
23 // (<> && false) is false, (true && NULL) is NULL
25  DCHECK_EQ(children_.size(), 2);
26  BooleanVal val1 = children_[0]->GetBooleanVal(context, row);
27  if (!val1.is_null && !val1.val) return BooleanVal(false); // short-circuit
28 
29  BooleanVal val2 = children_[1]->GetBooleanVal(context, row);
30  if (!val2.is_null && !val2.val) return BooleanVal(false);
31 
32  if (val1.is_null || val2.is_null) return BooleanVal::null();
33  return BooleanVal(true);
34 }
35 
36 // (<> || true) is true, (false || NULL) is NULL
38  DCHECK_EQ(children_.size(), 2);
39  BooleanVal val1 = children_[0]->GetBooleanVal(context, row);
40  if (!val1.is_null && val1.val) return BooleanVal(true); // short-circuit
41 
42  BooleanVal val2 = children_[1]->GetBooleanVal(context, row);
43  if (!val2.is_null && val2.val) return BooleanVal(true);
44 
45  if (val1.is_null || val2.is_null) return BooleanVal::null();
46  return BooleanVal(false);
47 }
48 
49 // IR codegen for compound and/or predicates. Compound predicate has non trivial
50 // null handling as well as many branches so this is pretty complicated. The IR
51 // for x && y is:
52 //
53 // define i16 @CompoundPredicate(%"class.impala::ExprContext"* %context,
54 // %"class.impala::TupleRow"* %row) #20 {
55 // entry:
56 // %lhs_call = call i16 @GetSlotRef1(%"class.impala::ExprContext"* %context,
57 // %"class.impala::TupleRow"* %row)
58 // %rhs_call = call i16 @Eq_IntVal_IntValWrapper(%"class.impala::ExprContext"* %context,
59 // %"class.impala::TupleRow"* %row)
60 // %is_null = trunc i16 %lhs_call to i1
61 // %is_null1 = trunc i16 %rhs_call to i1
62 // %0 = ashr i16 %lhs_call, 8
63 // %1 = trunc i16 %0 to i8
64 // %val = trunc i8 %1 to i1
65 // %2 = ashr i16 %rhs_call, 8
66 // %3 = trunc i16 %2 to i8
67 // %val2 = trunc i8 %3 to i1
68 // %tmp_and = and i1 %val, %val2
69 // br i1 %is_null, label %lhs_null, label %lhs_not_null
70 //
71 // lhs_null: ; preds = %entry
72 // br i1 %is_null1, label %null_block, label %lhs_null_rhs_not_null
73 //
74 // lhs_not_null: ; preds = %entry
75 // br i1 %is_null1, label %lhs_not_null_rhs_null, label %not_null_block
76 //
77 // lhs_null_rhs_not_null: ; preds = %lhs_null
78 // br i1 %val2, label %null_block, label %not_null_block
79 //
80 // lhs_not_null_rhs_null: ; preds = %lhs_not_null
81 // br i1 %val, label %null_block, label %not_null_block
82 //
83 // null_block: ; preds = %lhs_null_rhs_not_null,
84 // %lhs_not_null_rhs_null, %lhs_null
85 // br label %ret
86 //
87 // not_null_block: ; preds = %lhs_null_rhs_not_null,
88 // %lhs_not_null_rhs_null, %lhs_not_null
89 // %4 = phi i1 [ false, %lhs_null_rhs_not_null ],
90 // [ false, %lhs_not_null_rhs_null ],
91 // [ %tmp_and, %lhs_not_null ]
92 // br label %ret
93 //
94 // ret: ; preds = %not_null_block, %null_block
95 // %ret3 = phi i1 [ false, %null_block ], [ %4, %not_null_block ]
96 // %5 = zext i1 %ret3 to i16
97 // %6 = shl i16 %5, 8
98 // %7 = or i16 0, %6
99 // ret i16 %7
100 // }
102  bool and_fn, RuntimeState* state, Function** fn) {
103  if (ir_compute_fn_ != NULL) {
104  *fn = ir_compute_fn_;
105  return Status::OK;
106  }
107 
108  DCHECK_EQ(GetNumChildren(), 2);
109 
110  Function* lhs_function;
111  RETURN_IF_ERROR(children()[0]->GetCodegendComputeFn(state, &lhs_function));
112  Function* rhs_function;
113  RETURN_IF_ERROR(children()[1]->GetCodegendComputeFn(state, &rhs_function));
114 
115  LlvmCodeGen* codegen;
116  RETURN_IF_ERROR(state->GetCodegen(&codegen));
117  LLVMContext& context = codegen->context();
118  LlvmCodeGen::LlvmBuilder builder(context);
119  Value* args[2];
120  Function* function = CreateIrFunctionPrototype(codegen, "CompoundPredicate", &args);
121 
122  BasicBlock* entry_block = BasicBlock::Create(context, "entry", function);
123  builder.SetInsertPoint(entry_block);
124 
125  // Control blocks for aggregating results
126  BasicBlock* lhs_null_block = BasicBlock::Create(context, "lhs_null", function);
127  BasicBlock* lhs_not_null_block =
128  BasicBlock::Create(context, "lhs_not_null", function);
129  BasicBlock* lhs_null_rhs_not_null_block =
130  BasicBlock::Create(context, "lhs_null_rhs_not_null", function);
131  BasicBlock* lhs_not_null_rhs_null_block =
132  BasicBlock::Create(context, "lhs_not_null_rhs_null", function);
133  BasicBlock* null_block = BasicBlock::Create(context, "null_block", function);
134  BasicBlock* not_null_block = BasicBlock::Create(context, "not_null_block", function);
135  BasicBlock* ret_block = BasicBlock::Create(context, "ret", function);
136 
137  // Call lhs
139  codegen, &builder, TYPE_BOOLEAN, lhs_function, args, "lhs_call");
140  // Call rhs
142  codegen, &builder, TYPE_BOOLEAN, rhs_function, args, "rhs_call");
143 
144  Value* lhs_is_null = lhs_result.GetIsNull();
145  Value* rhs_is_null = rhs_result.GetIsNull();
146  Value* lhs_value = lhs_result.GetVal();
147  Value* rhs_value = rhs_result.GetVal();
148 
149  // Apply predicate
150  Value* compare = NULL;
151  if (and_fn) {
152  compare = builder.CreateAnd(lhs_value, rhs_value, "tmp_and");
153  } else {
154  compare = builder.CreateOr(lhs_value, rhs_value, "tmp_or");
155  }
156 
157  // Branch if lhs is null
158  builder.CreateCondBr(lhs_is_null, lhs_null_block, lhs_not_null_block);
159 
160  // lhs_is_null block
161  builder.SetInsertPoint(lhs_null_block);
162  builder.CreateCondBr(rhs_is_null, null_block, lhs_null_rhs_not_null_block);
163 
164  // lhs_is_not_null block
165  builder.SetInsertPoint(lhs_not_null_block);
166  builder.CreateCondBr(rhs_is_null, lhs_not_null_rhs_null_block, not_null_block);
167 
168  // lhs_not_null rhs_null block
169  builder.SetInsertPoint(lhs_not_null_rhs_null_block);
170  if (and_fn) {
171  // false && null -> false; true && null -> null
172  builder.CreateCondBr(lhs_value, null_block, not_null_block);
173  } else {
174  // true || null -> true; false || null -> null
175  builder.CreateCondBr(lhs_value, not_null_block, null_block);
176  }
177 
178  // lhs_null rhs_not_null block
179  builder.SetInsertPoint(lhs_null_rhs_not_null_block);
180  if (and_fn) {
181  // null && false -> false; null && true -> null
182  builder.CreateCondBr(rhs_value, null_block, not_null_block);
183  } else {
184  // null || true -> true; null || false -> null
185  builder.CreateCondBr(rhs_value, not_null_block, null_block);
186  }
187 
188  // NULL block
189  builder.SetInsertPoint(null_block);
190  builder.CreateBr(ret_block);
191 
192  // not-NULL block
193  builder.SetInsertPoint(not_null_block);
194  PHINode* not_null_phi = builder.CreatePHI(codegen->GetType(TYPE_BOOLEAN), 3);
195  if (and_fn) {
196  not_null_phi->addIncoming(codegen->false_value(), lhs_null_rhs_not_null_block);
197  not_null_phi->addIncoming(codegen->false_value(), lhs_not_null_rhs_null_block);
198  not_null_phi->addIncoming(compare, lhs_not_null_block);
199  } else {
200  not_null_phi->addIncoming(codegen->true_value(), lhs_null_rhs_not_null_block);
201  not_null_phi->addIncoming(codegen->true_value(), lhs_not_null_rhs_null_block);
202  not_null_phi->addIncoming(compare, lhs_not_null_block);
203  }
204  builder.CreateBr(ret_block);
205 
206  // Ret/merge block
207  builder.SetInsertPoint(ret_block);
208  PHINode* is_null_phi = builder.CreatePHI(codegen->boolean_type(), 2, "is_null");
209  is_null_phi->addIncoming(codegen->true_value(), null_block);
210  is_null_phi->addIncoming(codegen->false_value(), not_null_block);
211 
212  PHINode* val_phi = builder.CreatePHI(codegen->boolean_type(), 2, "val");
213  val_phi->addIncoming(codegen->false_value(), null_block);
214  val_phi->addIncoming(not_null_phi, not_null_block);
215 
216  CodegenAnyVal ret(codegen, &builder, TYPE_BOOLEAN, NULL, "ret");
217  ret.SetIsNull(is_null_phi);
218  ret.SetVal(val_phi);
219  builder.CreateRet(ret.value());
220 
221  *fn = codegen->FinalizeFunction(function);
222  DCHECK(*fn != NULL);
223  ir_compute_fn_ = *fn;
224  return Status::OK;
225 }
static CodegenAnyVal CreateCallWrapped(LlvmCodeGen *cg, LlvmCodeGen::LlvmBuilder *builder, const ColumnType &type, llvm::Function *fn, llvm::ArrayRef< llvm::Value * > args, const char *name="", llvm::Value *result_ptr=NULL)
Same as above but wraps the result in a CodegenAnyVal.
Status CodegenComputeFn(bool and_fn, RuntimeState *state, llvm::Function **fn)
void SetIsNull(llvm::Value *is_null)
Sets the 'is_null' field of the *Val.
#define RETURN_IF_ERROR(stmt)
some generally useful macros
Definition: status.h:242
void SetVal(llvm::Value *val)
llvm::Type * boolean_type()
Simple wrappers to reduce code verbosity.
Definition: llvm-codegen.h:385
bool is_null
Definition: udf.h:359
LLVM code generator. This is the top level object to generate jitted code.
Definition: llvm-codegen.h:107
llvm::Value * true_value()
Returns true/false constants (bool type)
Definition: llvm-codegen.h:380
virtual impala_udf::BooleanVal GetBooleanVal(ExprContext *context, TupleRow *)
virtual impala_udf::BooleanVal GetBooleanVal(ExprContext *context, TupleRow *)
llvm::Value * value()
Returns the current type-lowered value.
llvm::Value * false_value()
Definition: llvm-codegen.h:381
llvm::Value * GetVal(const char *name="val")
static const Status OK
Definition: status.h:87
llvm::Type * GetType(const ColumnType &type)
Returns llvm type for the column type.
Status GetCodegen(LlvmCodeGen **codegen, bool initialize=true)
llvm::Value * GetIsNull(const char *name="is_null")
Gets the 'is_null' field of the *Val.
llvm::Function * FinalizeFunction(llvm::Function *function)
llvm::LLVMContext & context()
Definition: llvm-codegen.h:214