For a left outer join, the IR looks like: define void @CreateOutputRow(%"class.impala::BlockingJoinNode"* %this_ptr, %"class.impala::TupleRow"* %out_arg, %"class.impala::TupleRow"* %probe_arg, %"class.impala::TupleRow"* %build_arg) #20 { entry: %out = bitcast %"class.impala::TupleRow"* %out_arg to i8 %probe = bitcast %"class.impala::TupleRow"* %probe_arg to i8 %build = bitcast %"class.impala::Tupl
| 1329 | // ret void |
| 1330 | // } |
| 1331 | Status PartitionedHashJoinPlanNode::CodegenCreateOutputRow( |
| 1332 | LlvmCodeGen* codegen, llvm::Function** fn) { |
| 1333 | llvm::PointerType* tuple_row_ptr_type = codegen->GetStructPtrType<TupleRow>(); |
| 1334 | |
| 1335 | llvm::PointerType* this_ptr_type = codegen->GetStructPtrType<BlockingJoinNode>(); |
| 1336 | |
| 1337 | // TupleRows are really just an array of pointers. Easier to work with them |
| 1338 | // this way. |
| 1339 | llvm::PointerType* tuple_row_working_type = codegen->ptr_ptr_type(); |
| 1340 | |
| 1341 | // Construct function signature to match CreateOutputRow() |
| 1342 | LlvmCodeGen::FnPrototype prototype(codegen, "CreateOutputRow", codegen->void_type()); |
| 1343 | prototype.AddArgument(LlvmCodeGen::NamedVariable("this_ptr", this_ptr_type)); |
| 1344 | prototype.AddArgument(LlvmCodeGen::NamedVariable("out_arg", tuple_row_ptr_type)); |
| 1345 | prototype.AddArgument(LlvmCodeGen::NamedVariable("probe_arg", tuple_row_ptr_type)); |
| 1346 | prototype.AddArgument(LlvmCodeGen::NamedVariable("build_arg", tuple_row_ptr_type)); |
| 1347 | |
| 1348 | llvm::LLVMContext& context = codegen->context(); |
| 1349 | LlvmBuilder builder(context); |
| 1350 | llvm::Value* args[4]; |
| 1351 | *fn = prototype.GeneratePrototype(&builder, args); |
| 1352 | llvm::Value* out_row_arg = |
| 1353 | builder.CreateBitCast(args[1], tuple_row_working_type, "out"); |
| 1354 | llvm::Value* probe_row_arg = |
| 1355 | builder.CreateBitCast(args[2], tuple_row_working_type, "probe"); |
| 1356 | llvm::Value* build_row_arg = |
| 1357 | builder.CreateBitCast(args[3], tuple_row_working_type, "build"); |
| 1358 | |
| 1359 | int num_probe_tuples = probe_row_desc().tuple_descriptors().size(); |
| 1360 | int num_build_tuples = build_row_desc().tuple_descriptors().size(); |
| 1361 | int probe_tuple_row_size = num_probe_tuples * sizeof(Tuple*); |
| 1362 | int build_tuple_row_size = num_build_tuples * sizeof(Tuple*); |
| 1363 | |
| 1364 | // Copy probe row |
| 1365 | codegen->CodegenMemcpy(&builder, out_row_arg, probe_row_arg, probe_tuple_row_size); |
| 1366 | llvm::Value* build_row_idx[] = {codegen->GetI32Constant(num_probe_tuples)}; |
| 1367 | llvm::Value* build_row_dst = |
| 1368 | builder.CreateInBoundsGEP(out_row_arg, build_row_idx, "build_dst_ptr"); |
| 1369 | |
| 1370 | // Copy build row. |
| 1371 | llvm::BasicBlock* build_not_null_block = |
| 1372 | llvm::BasicBlock::Create(context, "build_not_null", *fn); |
| 1373 | llvm::BasicBlock* build_null_block = nullptr; |
| 1374 | |
| 1375 | if (join_op_ == TJoinOp::LEFT_ANTI_JOIN || join_op_ == TJoinOp::LEFT_OUTER_JOIN || |
| 1376 | join_op_ == TJoinOp::FULL_OUTER_JOIN || |
| 1377 | join_op_ == TJoinOp::NULL_AWARE_LEFT_ANTI_JOIN) { |
| 1378 | // build tuple can be null |
| 1379 | build_null_block = llvm::BasicBlock::Create(context, "build_null", *fn); |
| 1380 | llvm::Value* is_build_null = builder.CreateIsNull(build_row_arg, "is_build_null"); |
| 1381 | builder.CreateCondBr(is_build_null, build_null_block, build_not_null_block); |
| 1382 | |
| 1383 | // Set tuple build ptrs to NULL |
| 1384 | // TODO: this should be replaced with memset() but I can't get the llvm intrinsic |
| 1385 | // to work. |
| 1386 | builder.SetInsertPoint(build_null_block); |
| 1387 | for (int i = 0; i < num_build_tuples; ++i) { |
| 1388 | llvm::Value* array_idx[] = { |
nothing calls this directly
no test coverage detected