| 592 | } |
| 593 | |
| 594 | static void compileSortDescription(llvm::Module & module, |
| 595 | SortDescription & description, |
| 596 | const DataTypes & sort_description_types, |
| 597 | const std::string & sort_description_dump) |
| 598 | { |
| 599 | llvm::IRBuilder<> b(module.getContext()); |
| 600 | |
| 601 | auto * size_type = b.getIntNTy(sizeof(size_t) * 8); |
| 602 | |
| 603 | auto * column_data_type = buildColumnDataStruct(b); |
| 604 | |
| 605 | std::vector<llvm::Type *> function_argument_types = {size_type, size_type, column_data_type->getPointerTo(), column_data_type->getPointerTo()}; |
| 606 | auto * comparator_func_declaration = llvm::FunctionType::get(b.getInt8Ty(), function_argument_types, false); |
| 607 | auto * comparator_func = llvm::Function::Create(comparator_func_declaration, llvm::Function::ExternalLinkage, sort_description_dump, module); |
| 608 | /// The result is read back through an `int8_t (*)(...)` function pointer, so the i8 return |
| 609 | /// must obey the C `int8_t` calling convention. This matters only on Darwin/Apple arm64: |
| 610 | /// Apple's arm64 ABI makes the *callee* sign-extend sub-word return values and the caller |
| 611 | /// relies on it (it emits no `sxtb` after the call). Without `signext` the JIT callee leaves |
| 612 | /// the upper bits unset, so e.g. a -1 result is read as 255 and NULL ordering breaks. |
| 613 | /// Linux arm64 (AAPCS64) is unaffected because there the *caller* sign-extends the return |
| 614 | /// itself (`sxtb w0, w0`); x86-64 is likewise a no-op. |
| 615 | comparator_func->addRetAttr(llvm::Attribute::SExt); |
| 616 | |
| 617 | auto * arguments = comparator_func->args().begin(); |
| 618 | llvm::Value * lhs_index_arg = arguments++; |
| 619 | llvm::Value * rhs_index_arg = arguments++; |
| 620 | llvm::Value * columns_lhs_arg = arguments++; |
| 621 | llvm::Value * columns_rhs_arg = arguments++; |
| 622 | |
| 623 | size_t columns_size = description.size(); |
| 624 | |
| 625 | std::vector<std::pair<llvm::BasicBlock *, llvm::Value *>> comparator_steps_and_results; |
| 626 | for (size_t i = 0; i < columns_size; ++i) |
| 627 | { |
| 628 | auto * step = llvm::BasicBlock::Create(b.getContext(), "step_" + std::to_string(i), comparator_func); |
| 629 | comparator_steps_and_results.emplace_back(step, nullptr); |
| 630 | } |
| 631 | |
| 632 | auto * lhs_equals_rhs_result = llvm::ConstantInt::getSigned(b.getInt8Ty(), 0); |
| 633 | |
| 634 | auto * comparator_join = llvm::BasicBlock::Create(b.getContext(), "comparator_join", comparator_func); |
| 635 | |
| 636 | for (size_t i = 0; i < columns_size; ++i) |
| 637 | { |
| 638 | b.SetInsertPoint(comparator_steps_and_results[i].first); |
| 639 | |
| 640 | const auto & sort_description = description[i]; |
| 641 | const auto & column_type = sort_description_types[i]; |
| 642 | auto nested_column_type = removeNullable(column_type); |
| 643 | |
| 644 | auto dummy_column = column_type->createColumn(); |
| 645 | |
| 646 | auto try_load_nullable_value = [&](llvm::Value * value, llvm::Value * nullable_uninitialized, llvm::Value * column_null_data, llvm::Value * index_arg) -> llvm::Value * |
| 647 | { |
| 648 | if (column_null_data) |
| 649 | { |
| 650 | auto * is_null_value_pointer = b.CreateInBoundsGEP(b.getInt8Ty(), column_null_data, index_arg); |
| 651 | auto * is_null = b.CreateICmpNE(b.CreateLoad(b.getInt8Ty(), is_null_value_pointer), b.getInt8(0)); |
no test coverage detected