Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
old-hash-table.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 
17 #include "codegen/codegen-anyval.h"
18 #include "codegen/llvm-codegen.h"
19 #include "exprs/expr.h"
20 #include "exprs/expr-context.h"
21 #include "exprs/slot-ref.h"
22 #include "runtime/mem-tracker.h"
23 #include "runtime/raw-value.h"
24 #include "runtime/runtime-state.h"
26 #include "util/debug-util.h"
27 #include "util/error-util.h"
28 #include "util/impalad-metrics.h"
29 
30 #include "common/names.h"
31 
32 using namespace impala;
33 using namespace llvm;
34 
35 const char* OldHashTable::LLVM_CLASS_NAME = "class.impala::OldHashTable";
36 
38 static const int PAGE_SIZE = 8 * 1024 * 1024;
39 
40 // Put a non-zero constant in the result location for NULL.
41 // We don't want(NULL, 1) to hash to the same as (0, 1).
42 // This needs to be as big as the biggest primitive type since the bytes
43 // get copied directly.
44 // TODO find a better approach, since primitives like CHAR(N) can be up to 128 bytes
52  HashUtil::FNV_SEED, HashUtil::FNV_SEED };
53 
54 OldHashTable::OldHashTable(RuntimeState* state, const vector<ExprContext*>& build_expr_ctxs,
55  const vector<ExprContext*>& probe_expr_ctxs, int num_build_tuples, bool stores_nulls,
56  bool finds_nulls, int32_t initial_seed, MemTracker* mem_tracker, bool stores_tuples,
57  int64_t num_buckets)
58  : state_(state),
59  build_expr_ctxs_(build_expr_ctxs),
60  probe_expr_ctxs_(probe_expr_ctxs),
61  num_build_tuples_(num_build_tuples),
62  stores_nulls_(stores_nulls),
63  finds_nulls_(finds_nulls),
64  stores_tuples_(stores_tuples),
65  initial_seed_(initial_seed),
66  num_filled_buckets_(0),
67  num_nodes_(0),
68  mem_pool_(new MemPool(mem_tracker)),
69  num_data_pages_(0),
70  next_node_(NULL),
71  node_remaining_current_page_(0),
72  mem_tracker_(mem_tracker),
73  mem_limit_exceeded_(false) {
74  DCHECK(mem_tracker != NULL);
75  DCHECK_EQ(build_expr_ctxs_.size(), probe_expr_ctxs_.size());
76 
77  DCHECK_EQ((num_buckets & (num_buckets-1)), 0) << "num_buckets must be a power of 2";
78  buckets_.resize(num_buckets);
81  mem_tracker_->Consume(buckets_.capacity() * sizeof(Bucket));
82 
83  // Compute the layout and buffer size to store the evaluated expr results
87  memset(expr_values_buffer_, 0, sizeof(uint8_t) * results_buffer_size_);
88  expr_value_null_bits_ = new uint8_t[build_expr_ctxs_.size()];
89 
90  GrowNodeArray();
91 }
92 
94  // TODO: use tr1::array?
95  delete[] expr_values_buffer_;
96  delete[] expr_value_null_bits_;
97  expr_values_buffer_ = NULL;
98  expr_value_null_bits_ = NULL;
99  mem_pool_->FreeAll();
102  }
103  mem_tracker_->Release(buckets_.capacity() * sizeof(Bucket));
104  buckets_.clear();
105 }
106 
108  TupleRow* row, const vector<ExprContext*>& ctxs) {
109  bool has_null = false;
110  for (int i = 0; i < ctxs.size(); ++i) {
112  void* val = ctxs[i]->GetValue(row);
113  if (val == NULL) {
114  // If the table doesn't store nulls, no reason to keep evaluating
115  if (!stores_nulls_) return true;
116 
117  expr_value_null_bits_[i] = true;
118  val = &NULL_VALUE;
119  has_null = true;
120  } else {
121  expr_value_null_bits_[i] = false;
122  }
123  RawValue::Write(val, loc, build_expr_ctxs_[i]->root()->type(), NULL);
124  }
125  return has_null;
126 }
127 
129  DCHECK_EQ(build_expr_ctxs_.size(), probe_expr_ctxs_.size());
130  vector<pair<SlotId, Bitmap*> > bitmaps;
131  bitmaps.resize(probe_expr_ctxs_.size());
132  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
133  if (probe_expr_ctxs_[i]->root()->is_slotref()) {
134  bitmaps[i].first =
135  reinterpret_cast<SlotRef*>(probe_expr_ctxs_[i]->root())->slot_id();
136  bitmaps[i].second = new Bitmap(state_->slot_filter_bitmap_size());
137  } else {
138  bitmaps[i].second = NULL;
139  }
140  }
141 
142  // Walk the build table and generate a bitmap for each probe side slot.
143  OldHashTable::Iterator iter = Begin();
144  while (iter != End()) {
145  TupleRow* row = iter.GetRow();
146  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
147  if (bitmaps[i].second == NULL) continue;
148  void* e = build_expr_ctxs_[i]->GetValue(row);
149  uint32_t h =
150  RawValue::GetHashValue(e, build_expr_ctxs_[i]->root()->type(), initial_seed_);
151  bitmaps[i].second->Set<true>(h, true);
152  }
153  iter.Next<false>();
154  }
155 
156  // Add all the bitmaps to the runtime state.
157  bool acquired_ownership = false;
158  for (int i = 0; i < bitmaps.size(); ++i) {
159  if (bitmaps[i].second == NULL) continue;
160  state_->AddBitmapFilter(bitmaps[i].first, bitmaps[i].second, &acquired_ownership);
161  VLOG(2) << "Bitmap filter added on slot: " << bitmaps[i].first;
162  if (!acquired_ownership) delete bitmaps[i].second;
163  }
164 }
165 
166 // Helper function to store a value into the results buffer if the expr
167 // evaluated to NULL. We don't want (NULL, 1) to hash to the same as (0,1) so
168 // we'll pick a more random value.
169 static void CodegenAssignNullValue(LlvmCodeGen* codegen,
170  LlvmCodeGen::LlvmBuilder* builder, Value* dst, const ColumnType& type) {
171  int64_t fvn_seed = HashUtil::FNV_SEED;
172 
173  if (type.type == TYPE_STRING || type.type == TYPE_VARCHAR) {
174  Value* dst_ptr = builder->CreateStructGEP(dst, 0, "string_ptr");
175  Value* dst_len = builder->CreateStructGEP(dst, 1, "string_len");
176  Value* null_len = codegen->GetIntConstant(TYPE_INT, fvn_seed);
177  Value* null_ptr = builder->CreateIntToPtr(null_len, codegen->ptr_type());
178  builder->CreateStore(null_ptr, dst_ptr);
179  builder->CreateStore(null_len, dst_len);
180  return;
181  } else {
182  Value* null_value = NULL;
183  // Get a type specific representation of fvn_seed
184  switch (type.type) {
185  case TYPE_BOOLEAN:
186  // In results, booleans are stored as 1 byte
187  dst = builder->CreateBitCast(dst, codegen->ptr_type());
188  null_value = codegen->GetIntConstant(TYPE_TINYINT, fvn_seed);
189  break;
190  case TYPE_TINYINT:
191  case TYPE_SMALLINT:
192  case TYPE_INT:
193  case TYPE_BIGINT:
194  null_value = codegen->GetIntConstant(type.type, fvn_seed);
195  break;
196  case TYPE_FLOAT: {
197  // Don't care about the value, just the bit pattern
198  float fvn_seed_float = *reinterpret_cast<float*>(&fvn_seed);
199  null_value = ConstantFP::get(codegen->context(), APFloat(fvn_seed_float));
200  break;
201  }
202  case TYPE_DOUBLE: {
203  // Don't care about the value, just the bit pattern
204  double fvn_seed_double = *reinterpret_cast<double*>(&fvn_seed);
205  null_value = ConstantFP::get(codegen->context(), APFloat(fvn_seed_double));
206  break;
207  }
208  default:
209  DCHECK(false);
210  }
211  builder->CreateStore(null_value, dst);
212  }
213 }
214 
215 // Codegen for evaluating a tuple row over either build_expr_ctxs_ or probe_expr_ctxs_.
216 // For the case where we are joining on a single int, the IR looks like
217 // define i1 @EvaBuildRow(%"class.impala::OldHashTable"* %this_ptr,
218 // %"class.impala::TupleRow"* %row) {
219 // entry:
220 // %null_ptr = alloca i1
221 // %0 = bitcast %"class.impala::TupleRow"* %row to i8**
222 // %eval = call i32 @SlotRef(i8** %0, i8* null, i1* %null_ptr)
223 // %1 = load i1* %null_ptr
224 // br i1 %1, label %null, label %not_null
225 //
226 // null: ; preds = %entry
227 // ret i1 true
228 //
229 // not_null: ; preds = %entry
230 // store i32 %eval, i32* inttoptr (i64 46146336 to i32*)
231 // br label %continue
232 //
233 // continue: ; preds = %not_null
234 // %2 = zext i1 %1 to i8
235 // store i8 %2, i8* inttoptr (i64 46146248 to i8*)
236 // ret i1 false
237 // }
238 // For each expr, we create 3 code blocks. The null, not null and continue blocks.
239 // Both the null and not null branch into the continue block. The continue block
240 // becomes the start of the next block for codegen (either the next expr or just the
241 // end of the function).
242 Function* OldHashTable::CodegenEvalTupleRow(RuntimeState* state, bool build) {
243  // TODO: CodegenAssignNullValue() can't handle TYPE_TIMESTAMP or TYPE_DECIMAL yet
244  const vector<ExprContext*>& ctxs = build ? build_expr_ctxs_ : probe_expr_ctxs_;
245  for (int i = 0; i < ctxs.size(); ++i) {
246  PrimitiveType type = ctxs[i]->root()->type().type;
247  if (type == TYPE_TIMESTAMP || type == TYPE_DECIMAL || type == TYPE_CHAR) return NULL;
248  }
249 
250  LlvmCodeGen* codegen;
251  if (!state->GetCodegen(&codegen).ok()) return NULL;
252 
253  // Get types to generate function prototype
254  Type* tuple_row_type = codegen->GetType(TupleRow::LLVM_CLASS_NAME);
255  DCHECK(tuple_row_type != NULL);
256  PointerType* tuple_row_ptr_type = PointerType::get(tuple_row_type, 0);
257 
258  Type* this_type = codegen->GetType(OldHashTable::LLVM_CLASS_NAME);
259  DCHECK(this_type != NULL);
260  PointerType* this_ptr_type = PointerType::get(this_type, 0);
261 
262  LlvmCodeGen::FnPrototype prototype(codegen, build ? "EvalBuildRow" : "EvalProbeRow",
263  codegen->GetType(TYPE_BOOLEAN));
264  prototype.AddArgument(LlvmCodeGen::NamedVariable("this_ptr", this_ptr_type));
265  prototype.AddArgument(LlvmCodeGen::NamedVariable("row", tuple_row_ptr_type));
266 
267  LLVMContext& context = codegen->context();
268  LlvmCodeGen::LlvmBuilder builder(context);
269  Value* args[2];
270  Function* fn = prototype.GeneratePrototype(&builder, args);
271 
272  Value* row = args[1];
273  Value* has_null = codegen->false_value();
274 
275  // Aggregation with no grouping exprs also use the hash table interface for
276  // code simplicity. In that case, there are no build exprs.
277  if (!build_expr_ctxs_.empty()) {
278  const vector<ExprContext*>& ctxs = build ? build_expr_ctxs_ : probe_expr_ctxs_;
279  for (int i = 0; i < ctxs.size(); ++i) {
280  // TODO: refactor this to somewhere else? This is not hash table specific
281  // except for the null handling bit and would be used for anyone that needs
282  // to materialize a vector of exprs
283  // Convert result buffer to llvm ptr type
285  Value* llvm_loc = codegen->CastPtrToLlvmPtr(
286  codegen->GetPtrType(ctxs[i]->root()->type()), loc);
287 
288  BasicBlock* null_block = BasicBlock::Create(context, "null", fn);
289  BasicBlock* not_null_block = BasicBlock::Create(context, "not_null", fn);
290  BasicBlock* continue_block = BasicBlock::Create(context, "continue", fn);
291 
292  // Call expr
293  Function* expr_fn;
294  Status status = ctxs[i]->root()->GetCodegendComputeFn(state, &expr_fn);
295  if (!status.ok()) {
296  stringstream ss;
297  ss << "Problem with codegen: " << status.GetDetail();
298  state->LogError(ErrorMsg(TErrorCode::GENERAL, ss.str()));
299  fn->eraseFromParent(); // deletes function
300  return NULL;
301  }
302 
303  Value* ctx_arg = codegen->CastPtrToLlvmPtr(
304  codegen->GetPtrType(ExprContext::LLVM_CLASS_NAME), ctxs[i]);
305  Value* expr_fn_args[] = { ctx_arg, row };
307  codegen, &builder, ctxs[i]->root()->type(), expr_fn, expr_fn_args, "result");
308  Value* is_null = result.GetIsNull();
309 
310  // Set null-byte result
311  Value* null_byte = builder.CreateZExt(is_null, codegen->GetType(TYPE_TINYINT));
312  uint8_t* null_byte_loc = &expr_value_null_bits_[i];
313  Value* llvm_null_byte_loc =
314  codegen->CastPtrToLlvmPtr(codegen->ptr_type(), null_byte_loc);
315  builder.CreateStore(null_byte, llvm_null_byte_loc);
316 
317  builder.CreateCondBr(is_null, null_block, not_null_block);
318 
319  // Null block
320  builder.SetInsertPoint(null_block);
321  if (!stores_nulls_) {
322  // hash table doesn't store nulls, no reason to keep evaluating exprs
323  builder.CreateRet(codegen->true_value());
324  } else {
325  CodegenAssignNullValue(codegen, &builder, llvm_loc, ctxs[i]->root()->type());
326  has_null = codegen->true_value();
327  builder.CreateBr(continue_block);
328  }
329 
330  // Not null block
331  builder.SetInsertPoint(not_null_block);
332  result.ToNativePtr(llvm_loc);
333  builder.CreateBr(continue_block);
334 
335  builder.SetInsertPoint(continue_block);
336  }
337  }
338  builder.CreateRet(has_null);
339 
340  return codegen->FinalizeFunction(fn);
341 }
342 
343 
345  uint32_t hash = initial_seed_;
346  // Hash the non-var length portions (if there are any)
347  if (var_result_begin_ != 0) {
349  }
350 
351  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
352  // non-string and null slots are already part of expr_values_buffer
353  if (build_expr_ctxs_[i]->root()->type().type != TYPE_STRING
354  && build_expr_ctxs_[i]->root()->type().type != TYPE_VARCHAR) continue;
355 
357  if (expr_value_null_bits_[i]) {
358  // Hash the null random seed values at 'loc'
359  hash = HashUtil::Hash(loc, sizeof(StringValue), hash);
360  } else {
361  // Hash the string
362  StringValue* str = reinterpret_cast<StringValue*>(loc);
363  hash = HashUtil::Hash(str->ptr, str->len, hash);
364  }
365  }
366  return hash;
367 }
368 
369 // Codegen for hashing the current row. In the case with both string and non-string data
370 // (group by int_col, string_col), the IR looks like:
371 // define i32 @HashCurrentRow(%"class.impala::OldHashTable"* %this_ptr) {
372 // entry:
373 // %0 = call i32 @IrCrcHash(i8* inttoptr (i64 51107808 to i8*), i32 16, i32 0)
374 // %1 = load i8* inttoptr (i64 29500112 to i8*)
375 // %2 = icmp ne i8 %1, 0
376 // br i1 %2, label %null, label %not_null
377 //
378 // null: ; preds = %entry
379 // %3 = call i32 @IrCrcHash(i8* inttoptr (i64 51107824 to i8*), i32 16, i32 %0)
380 // br label %continue
381 //
382 // not_null: ; preds = %entry
383 // %4 = load i8** getelementptr inbounds (
384 // %"struct.impala::StringValue"* inttoptr
385 // (i64 51107824 to %"struct.impala::StringValue"*), i32 0, i32 0)
386 // %5 = load i32* getelementptr inbounds (
387 // %"struct.impala::StringValue"* inttoptr
388 // (i64 51107824 to %"struct.impala::StringValue"*), i32 0, i32 1)
389 // %6 = call i32 @IrCrcHash(i8* %4, i32 %5, i32 %0)
390 // br label %continue
391 //
392 // continue: ; preds = %not_null, %null
393 // %7 = phi i32 [ %6, %not_null ], [ %3, %null ]
394 // ret i32 %7
395 // }
396 // TODO: can this be cross-compiled?
398  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
399  // Disable codegen for CHAR
400  if (build_expr_ctxs_[i]->root()->type().type == TYPE_CHAR) return NULL;
401  }
402 
403  LlvmCodeGen* codegen;
404  if (!state->GetCodegen(&codegen).ok()) return NULL;
405 
406  // Get types to generate function prototype
407  Type* this_type = codegen->GetType(OldHashTable::LLVM_CLASS_NAME);
408  DCHECK(this_type != NULL);
409  PointerType* this_ptr_type = PointerType::get(this_type, 0);
410 
411  LlvmCodeGen::FnPrototype prototype(codegen, "HashCurrentRow",
412  codegen->GetType(TYPE_INT));
413  prototype.AddArgument(LlvmCodeGen::NamedVariable("this_ptr", this_ptr_type));
414 
415  LLVMContext& context = codegen->context();
416  LlvmCodeGen::LlvmBuilder builder(context);
417  Value* this_arg;
418  Function* fn = prototype.GeneratePrototype(&builder, &this_arg);
419 
420  Value* hash_result = codegen->GetIntConstant(TYPE_INT, initial_seed_);
421  Value* data = codegen->CastPtrToLlvmPtr(codegen->ptr_type(), expr_values_buffer_);
422  if (var_result_begin_ == -1) {
423  // No variable length slots, just hash what is in 'expr_values_buffer_'
424  if (results_buffer_size_ > 0) {
425  Function* hash_fn = codegen->GetHashFunction(results_buffer_size_);
426  Value* len = codegen->GetIntConstant(TYPE_INT, results_buffer_size_);
427  hash_result = builder.CreateCall3(hash_fn, data, len, hash_result);
428  }
429  } else {
430  if (var_result_begin_ > 0) {
431  Function* hash_fn = codegen->GetHashFunction(var_result_begin_);
432  Value* len = codegen->GetIntConstant(TYPE_INT, var_result_begin_);
433  hash_result = builder.CreateCall3(hash_fn, data, len, hash_result);
434  }
435 
436  // Hash string slots
437  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
438  if (build_expr_ctxs_[i]->root()->type().type != TYPE_STRING
439  && build_expr_ctxs_[i]->root()->type().type != TYPE_VARCHAR) continue;
440 
441  BasicBlock* null_block = NULL;
442  BasicBlock* not_null_block = NULL;
443  BasicBlock* continue_block = NULL;
444  Value* str_null_result = NULL;
445 
447 
448  // If the hash table stores nulls, we need to check if the stringval
449  // evaluated to NULL
450  if (stores_nulls_) {
451  null_block = BasicBlock::Create(context, "null", fn);
452  not_null_block = BasicBlock::Create(context, "not_null", fn);
453  continue_block = BasicBlock::Create(context, "continue", fn);
454 
455  uint8_t* null_byte_loc = &expr_value_null_bits_[i];
456  Value* llvm_null_byte_loc =
457  codegen->CastPtrToLlvmPtr(codegen->ptr_type(), null_byte_loc);
458  Value* null_byte = builder.CreateLoad(llvm_null_byte_loc);
459  Value* is_null = builder.CreateICmpNE(null_byte,
460  codegen->GetIntConstant(TYPE_TINYINT, 0));
461  builder.CreateCondBr(is_null, null_block, not_null_block);
462 
463  // For null, we just want to call the hash function on the portion of
464  // the data
465  builder.SetInsertPoint(null_block);
466  Function* null_hash_fn = codegen->GetHashFunction(sizeof(StringValue));
467  Value* llvm_loc = codegen->CastPtrToLlvmPtr(codegen->ptr_type(), loc);
468  Value* len = codegen->GetIntConstant(TYPE_INT, sizeof(StringValue));
469  str_null_result = builder.CreateCall3(null_hash_fn, llvm_loc, len, hash_result);
470  builder.CreateBr(continue_block);
471 
472  builder.SetInsertPoint(not_null_block);
473  }
474 
475  // Convert expr_values_buffer_ loc to llvm value
476  Value* str_val = codegen->CastPtrToLlvmPtr(codegen->GetPtrType(TYPE_STRING), loc);
477 
478  Value* ptr = builder.CreateStructGEP(str_val, 0, "ptr");
479  Value* len = builder.CreateStructGEP(str_val, 1, "len");
480  ptr = builder.CreateLoad(ptr);
481  len = builder.CreateLoad(len);
482 
483  // Call hash(ptr, len, hash_result);
484  Function* general_hash_fn = codegen->GetHashFunction();
485  Value* string_hash_result =
486  builder.CreateCall3(general_hash_fn, ptr, len, hash_result);
487 
488  if (stores_nulls_) {
489  builder.CreateBr(continue_block);
490  builder.SetInsertPoint(continue_block);
491  // Use phi node to reconcile that we could have come from the string-null
492  // path and string not null paths.
493  PHINode* phi_node = builder.CreatePHI(codegen->GetType(TYPE_INT), 2);
494  phi_node->addIncoming(string_hash_result, not_null_block);
495  phi_node->addIncoming(str_null_result, null_block);
496  hash_result = phi_node;
497  } else {
498  hash_result = string_hash_result;
499  }
500  }
501  }
502 
503  builder.CreateRet(hash_result);
504  return codegen->FinalizeFunction(fn);
505 }
506 
507 bool OldHashTable::Equals(TupleRow* build_row) {
508  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
509  void* val = build_expr_ctxs_[i]->GetValue(build_row);
510  if (val == NULL) {
511  if (!stores_nulls_) return false;
512  if (!expr_value_null_bits_[i]) return false;
513  continue;
514  } else {
515  if (expr_value_null_bits_[i]) return false;
516  }
517 
519  if (!RawValue::Eq(loc, val, build_expr_ctxs_[i]->root()->type())) {
520  return false;
521  }
522  }
523  return true;
524 }
525 
526 // Codegen for OldHashTable::Equals. For a hash table with two exprs (string,int), the
527 // IR looks like:
528 //
529 // define i1 @Equals(%"class.impala::OldHashTable"* %this_ptr,
530 // %"class.impala::TupleRow"* %row) {
531 // entry:
532 // %result = call i64 @GetSlotRef(%"class.impala::ExprContext"* inttoptr
533 // (i64 146381856 to %"class.impala::ExprContext"*),
534 // %"class.impala::TupleRow"* %row)
535 // %0 = trunc i64 %result to i1
536 // br i1 %0, label %null, label %not_null
537 //
538 // false_block: ; preds = %not_null2, %null1, %not_null, %null
539 // ret i1 false
540 //
541 // null: ; preds = %entry
542 // br i1 false, label %continue, label %false_block
543 //
544 // not_null: ; preds = %entry
545 // %1 = load i32* inttoptr (i64 104774368 to i32*)
546 // %2 = ashr i64 %result, 32
547 // %3 = trunc i64 %2 to i32
548 // %cmp_raw = icmp eq i32 %3, %1
549 // br i1 %cmp_raw, label %continue, label %false_block
550 //
551 // continue: ; preds = %not_null, %null
552 // %result4 = call { i64, i8* } @GetSlotRef1(
553 // %"class.impala::ExprContext"* inttoptr
554 // (i64 146381696 to %"class.impala::ExprContext"*),
555 // %"class.impala::TupleRow"* %row)
556 // %4 = extractvalue { i64, i8* } %result4, 0
557 // %5 = trunc i64 %4 to i1
558 // br i1 %5, label %null1, label %not_null2
559 //
560 // null1: ; preds = %continue
561 // br i1 false, label %continue3, label %false_block
562 //
563 // not_null2: ; preds = %continue
564 // %6 = extractvalue { i64, i8* } %result4, 0
565 // %7 = ashr i64 %6, 32
566 // %8 = trunc i64 %7 to i32
567 // %result5 = extractvalue { i64, i8* } %result4, 1
568 // %cmp_raw6 = call i1 @_Z11StringValEQPciPKN6impala11StringValueE(
569 // i8* %result5, i32 %8, %"struct.impala::StringValue"* inttoptr
570 // (i64 104774384 to %"struct.impala::StringValue"*))
571 // br i1 %cmp_raw6, label %continue3, label %false_block
572 //
573 // continue3: ; preds = %not_null2, %null1
574 // ret i1 true
575 // }
577  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
578  // Disable codegen for CHAR
579  if (build_expr_ctxs_[i]->root()->type().type == TYPE_CHAR) return NULL;
580  }
581 
582  LlvmCodeGen* codegen;
583  if (!state->GetCodegen(&codegen).ok()) return NULL;
584  // Get types to generate function prototype
585  Type* tuple_row_type = codegen->GetType(TupleRow::LLVM_CLASS_NAME);
586  DCHECK(tuple_row_type != NULL);
587  PointerType* tuple_row_ptr_type = PointerType::get(tuple_row_type, 0);
588 
589  Type* this_type = codegen->GetType(OldHashTable::LLVM_CLASS_NAME);
590  DCHECK(this_type != NULL);
591  PointerType* this_ptr_type = PointerType::get(this_type, 0);
592 
593  LlvmCodeGen::FnPrototype prototype(codegen, "Equals", codegen->GetType(TYPE_BOOLEAN));
594  prototype.AddArgument(LlvmCodeGen::NamedVariable("this_ptr", this_ptr_type));
595  prototype.AddArgument(LlvmCodeGen::NamedVariable("row", tuple_row_ptr_type));
596 
597  LLVMContext& context = codegen->context();
598  LlvmCodeGen::LlvmBuilder builder(context);
599  Value* args[2];
600  Function* fn = prototype.GeneratePrototype(&builder, args);
601  Value* row = args[1];
602 
603  if (!build_expr_ctxs_.empty()) {
604  BasicBlock* false_block = BasicBlock::Create(context, "false_block", fn);
605 
606  for (int i = 0; i < build_expr_ctxs_.size(); ++i) {
607  BasicBlock* null_block = BasicBlock::Create(context, "null", fn);
608  BasicBlock* not_null_block = BasicBlock::Create(context, "not_null", fn);
609  BasicBlock* continue_block = BasicBlock::Create(context, "continue", fn);
610 
611  // call GetValue on build_exprs[i]
612  Function* expr_fn;
613  Status status = build_expr_ctxs_[i]->root()->GetCodegendComputeFn(state, &expr_fn);
614  if (!status.ok()) {
615  stringstream ss;
616  ss << "Problem with codegen: " << status.GetDetail();
617  state->LogError(ErrorMsg(TErrorCode::GENERAL, ss.str()));
618  fn->eraseFromParent(); // deletes function
619  return NULL;
620  }
621 
622  Value* ctx_arg = codegen->CastPtrToLlvmPtr(
624  Value* expr_fn_args[] = { ctx_arg, row };
625  CodegenAnyVal result = CodegenAnyVal::CreateCallWrapped(codegen, &builder,
626  build_expr_ctxs_[i]->root()->type(), expr_fn, expr_fn_args, "result");
627  Value* is_null = result.GetIsNull();
628 
629  // Determine if probe is null (i.e. expr_value_null_bits_[i] == true). In
630  // the case where the hash table does not store nulls, this is always false.
631  Value* probe_is_null = codegen->false_value();
632  uint8_t* null_byte_loc = &expr_value_null_bits_[i];
633  if (stores_nulls_) {
634  Value* llvm_null_byte_loc =
635  codegen->CastPtrToLlvmPtr(codegen->ptr_type(), null_byte_loc);
636  Value* null_byte = builder.CreateLoad(llvm_null_byte_loc);
637  probe_is_null = builder.CreateICmpNE(null_byte,
638  codegen->GetIntConstant(TYPE_TINYINT, 0));
639  }
640 
641  // Get llvm value for probe_val from 'expr_values_buffer_'
643  Value* probe_val = codegen->CastPtrToLlvmPtr(
644  codegen->GetPtrType(build_expr_ctxs_[i]->root()->type()), loc);
645 
646  // Branch for GetValue() returning NULL
647  builder.CreateCondBr(is_null, null_block, not_null_block);
648 
649  // Null block
650  builder.SetInsertPoint(null_block);
651  builder.CreateCondBr(probe_is_null, continue_block, false_block);
652 
653  // Not-null block
654  builder.SetInsertPoint(not_null_block);
655  if (stores_nulls_) {
656  BasicBlock* cmp_block = BasicBlock::Create(context, "cmp", fn);
657  // First need to compare that probe expr[i] is not null
658  builder.CreateCondBr(probe_is_null, false_block, cmp_block);
659  builder.SetInsertPoint(cmp_block);
660  }
661  // Check result == probe_val
662  Value* is_equal = result.EqToNativePtr(probe_val);
663  builder.CreateCondBr(is_equal, continue_block, false_block);
664 
665  builder.SetInsertPoint(continue_block);
666  }
667  builder.CreateRet(codegen->true_value());
668 
669  builder.SetInsertPoint(false_block);
670  builder.CreateRet(codegen->false_value());
671  } else {
672  builder.CreateRet(codegen->true_value());
673  }
674 
675  return codegen->FinalizeFunction(fn);
676 }
677 
678 void OldHashTable::ResizeBuckets(int64_t num_buckets) {
679  DCHECK_EQ((num_buckets & (num_buckets-1)), 0)
680  << "num_buckets=" << num_buckets << " must be a power of 2";
681 
682  int64_t old_num_buckets = num_buckets_;
683  // This can be a rather large allocation so check the limit before (to prevent
684  // us from going over the limits too much).
685  int64_t delta_size = (num_buckets - old_num_buckets) * sizeof(Bucket);
686  if (!mem_tracker_->TryConsume(delta_size)) {
687  MemLimitExceeded(delta_size);
688  return;
689  }
690  buckets_.resize(num_buckets);
691 
692  // If we're doubling the number of buckets, all nodes in a particular bucket
693  // either remain there, or move down to an analogous bucket in the other half.
694  // In order to efficiently check which of the two buckets a node belongs in, the number
695  // of buckets must be a power of 2.
696  bool doubled_buckets = (num_buckets == old_num_buckets * 2);
697  for (int i = 0; i < num_buckets_; ++i) {
698  Bucket* bucket = &buckets_[i];
699  Bucket* sister_bucket = &buckets_[i + old_num_buckets];
700  Node* last_node = NULL;
701  Node* node = bucket->node;
702 
703  while (node != NULL) {
704  Node* next = node->next;
705  uint32_t hash = node->hash;
706 
707  bool node_must_move;
708  Bucket* move_to;
709  if (doubled_buckets) {
710  node_must_move = ((hash & old_num_buckets) != 0);
711  move_to = sister_bucket;
712  } else {
713  int64_t bucket_idx = hash & (num_buckets - 1);
714  node_must_move = (bucket_idx != i);
715  move_to = &buckets_[bucket_idx];
716  }
717 
718  if (node_must_move) {
719  MoveNode(bucket, move_to, node, last_node);
720  } else {
721  last_node = node;
722  }
723 
724  node = next;
725  }
726  }
727 
728  num_buckets_ = num_buckets;
730 }
731 
734  next_node_ = reinterpret_cast<Node*>(mem_pool_->Allocate(PAGE_SIZE));
735  ++num_data_pages_;
738  }
740 }
741 
742 void OldHashTable::MemLimitExceeded(int64_t allocation_size) {
743  mem_limit_exceeded_ = true;
744  if (state_ != NULL) state_->SetMemLimitExceeded(mem_tracker_, allocation_size);
745 }
746 
747 string OldHashTable::DebugString(bool skip_empty, bool show_match,
748  const RowDescriptor* desc) {
749  stringstream ss;
750  ss << endl;
751  for (int i = 0; i < buckets_.size(); ++i) {
752  Node* node = buckets_[i].node;
753  bool first = true;
754  if (skip_empty && node == NULL) continue;
755  ss << i << ": ";
756  while (node != NULL) {
757  if (!first) ss << ",";
758  ss << node << "(" << node->data << ")";
759  if (desc != NULL) ss << " " << PrintRow(GetRow(node), *desc);
760  if (show_match) {
761  if (node->matched) {
762  ss << " [M]";
763  } else {
764  ss << " [U]";
765  }
766  }
767  node = node->next;
768  first = false;
769  }
770  ss << endl;
771  }
772  return ss.str();
773 }
stl-like iterator interface.
uint32_t slot_filter_bitmap_size() const
std::vector< Bucket > buckets_
const std::string GetDetail() const
Definition: status.cc:184
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.
OldHashTable(RuntimeState *state, const std::vector< ExprContext * > &build_expr_ctxs, const std::vector< ExprContext * > &probe_expr_ctxs, int num_build_tuples, bool stores_nulls, bool finds_nulls, int32_t initial_seed, MemTracker *mem_tracker, bool stores_tuples=false, int64_t num_buckets=1024)
llvm::PointerType * GetPtrType(llvm::Type *type)
Return a pointer type to 'type'.
static bool Eq(const void *v1, const void *v2, const ColumnType &type)
Definition: raw-value.h:106
bool TryConsume(int64_t bytes)
Definition: mem-tracker.h:163
uint32_t HashVariableLenRow()
Utility struct that wraps a variable name and llvm type.
Definition: llvm-codegen.h:149
llvm::Function * CodegenHashCurrentRow(RuntimeState *state)
static const int PAGE_SIZE
const StringSearch UrlParser::hash_search & hash
Definition: url-parser.cc:41
static int64_t NULL_VALUE[]
std::vector< int > expr_values_buffer_offsets_
TupleRow * GetRow(Node *node) const
uint8_t * expr_values_buffer_
void AddBitmapFilter(SlotId slot, Bitmap *bitmap, bool *acquired_ownership)
const int32_t initial_seed_
LLVM code generator. This is the top level object to generate jitted code.
Definition: llvm-codegen.h:107
static const char * LLVM_CLASS_NAME
Definition: tuple-row.h:76
llvm::Function * CodegenEvalTupleRow(RuntimeState *state, bool build_row)
static const float MAX_BUCKET_OCCUPANCY_FRACTION
static const char * LLVM_CLASS_NAME
Definition: expr-context.h:126
PrimitiveType type
Definition: types.h:60
int node_remaining_current_page_
Number of nodes left in the current page.
llvm::Value * CastPtrToLlvmPtr(llvm::Type *type, const void *ptr)
void AddArgument(const NamedVariable &var)
Add argument.
Definition: llvm-codegen.h:171
MemTracker * mem_tracker_
Node * next_node_
Next node to insert.
bool LogError(const ErrorMsg &msg)
llvm::Function * GetHashFunction(int num_bytes=-1)
const std::vector< ExprContext * > & probe_expr_ctxs_
std::string DebugString(bool skip_empty, bool show_match, const RowDescriptor *build_desc)
static uint32_t Hash(const void *data, int32_t bytes, uint32_t seed)
Definition: hash-util.h:135
PrimitiveType
Definition: types.h:27
int var_result_begin_
byte offset into expr_values_buffer_ that begins the variable length results
void GrowNodeArray()
Grow the node array.
void MemLimitExceeded(int64_t allocation_size)
static void Write(const void *value, Tuple *tuple, const SlotDescriptor *slot_desc, MemPool *pool)
Definition: raw-value.cc:303
boost::scoped_ptr< MemPool > mem_pool_
MemPool used to allocate data pages.
static int ComputeResultsLayout(const std::vector< Expr * > &exprs, std::vector< int > *offsets, int *var_result_begin)
This class is thread-safe.
Definition: mem-tracker.h:61
static const char * LLVM_CLASS_NAME
void Release(int64_t bytes)
Decreases consumption of this tracker and its ancestors by 'bytes'.
Definition: mem-tracker.h:209
llvm::Value * true_value()
Returns true/false constants (bool type)
Definition: llvm-codegen.h:380
int64_t num_buckets_
equal to buckets_.size() but more efficient than the size function
int num_data_pages_
Number of data pages for nodes.
Iterator End()
Returns end marker.
static const uint32_t FNV_SEED
Definition: hash-util.h:99
void IR_ALWAYS_INLINE Next()
void Close()
Call to cleanup any resources. Must be called once.
uint8_t * expr_value_null_bits_
llvm::Value * EqToNativePtr(llvm::Value *native_ptr)
Status SetMemLimitExceeded(MemTracker *tracker=NULL, int64_t failed_allocation_size=0)
const std::vector< ExprContext * > & build_expr_ctxs_
void ResizeBuckets(int64_t num_buckets)
Resize the hash table to 'num_buckets'.
RuntimeState * state_
llvm::Value * false_value()
Definition: llvm-codegen.h:381
Reference to a single slot of a tuple.
Definition: slot-ref.h:23
bool Equals(TupleRow *build_row)
void Consume(int64_t bytes)
Increases consumption of this tracker and its ancestors by 'bytes'.
Definition: mem-tracker.h:118
llvm::Type * GetType(const ColumnType &type)
Returns llvm type for the column type.
void MoveNode(Bucket *from_bucket, Bucket *to_bucket, Node *node, Node *previous_node)
Status GetCodegen(LlvmCodeGen **codegen, bool initialize=true)
bool EvalRow(TupleRow *row, const std::vector< ExprContext * > &ctxs)
llvm::Value * GetIsNull(const char *name="is_null")
Gets the 'is_null' field of the *Val.
static void CodegenAssignNullValue(LlvmCodeGen *codegen, LlvmCodeGen::LlvmBuilder *builder, Value *dst, const ColumnType &type)
llvm::Value * GetIntConstant(PrimitiveType type, int64_t val)
Returns the constant 'val' of 'type'.
llvm::Function * FinalizeFunction(llvm::Function *function)
llvm::Function * CodegenEquals(RuntimeState *state)
static IntGauge * HASH_TABLE_TOTAL_BYTES
int64_t num_buckets() const
Returns the number of buckets.
string PrintRow(TupleRow *row, const RowDescriptor &d)
Definition: debug-util.cc:192
bool ok() const
Definition: status.h:172
llvm::LLVMContext & context()
Definition: llvm-codegen.h:214
int64_t num_buckets_till_resize_
The number of filled buckets to trigger a resize. This is cached for efficiency.
void ToNativePtr(llvm::Value *native_ptr)
int results_buffer_size_
byte size of 'expr_values_buffer_'
static uint32_t GetHashValue(const void *v, const ColumnType &type, uint32_t seed=0)
Definition: raw-value.h:168
llvm::PointerType * ptr_type()
Definition: llvm-codegen.h:393