// Modifications copyright (C) 2017, Baidu.com, Inc. // Copyright 2017 The Apache Software Foundation // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #include "exprs/binary_predicate.h" #include #include "codegen/llvm_codegen.h" #include "codegen/codegen_anyval.h" #include "util/debug_util.h" #include "gen_cpp/Exprs_types.h" #include "runtime/runtime_state.h" #include "runtime/string_value.h" #include "runtime/datetime_value.h" #include "runtime/decimal_value.h" using llvm::BasicBlock; using llvm::CmpInst; using llvm::Constant; using llvm::ConstantInt; using llvm::Function; using llvm::LLVMContext; using llvm::PHINode; using llvm::Value; namespace palo { Expr* BinaryPredicate::from_thrift(const TExprNode& node) { switch (node.opcode) { case TExprOpcode::EQ: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new EqBooleanValPred(node); case TPrimitiveType::TINYINT: return new EqTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new EqSmallIntValPred(node); case TPrimitiveType::INT: return new EqIntValPred(node); case TPrimitiveType::BIGINT: return new EqBigIntValPred(node); case TPrimitiveType::LARGEINT: return new EqLargeIntValPred(node); case TPrimitiveType::FLOAT: return new EqFloatValPred(node); case TPrimitiveType::DOUBLE: return new EqDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new EqStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new EqDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new EqDecimalValPred(node); default: return NULL; } } case TExprOpcode::NE: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new NeBooleanValPred(node); case TPrimitiveType::TINYINT: return new NeTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new NeSmallIntValPred(node); case TPrimitiveType::INT: return new NeIntValPred(node); case TPrimitiveType::BIGINT: return new NeBigIntValPred(node); case TPrimitiveType::LARGEINT: return new NeLargeIntValPred(node); case TPrimitiveType::FLOAT: return new NeFloatValPred(node); case TPrimitiveType::DOUBLE: return new NeDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new NeStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new NeDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new NeDecimalValPred(node); default: return NULL; } } case TExprOpcode::LT: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new LtBooleanValPred(node); case TPrimitiveType::TINYINT: return new LtTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new LtSmallIntValPred(node); case TPrimitiveType::INT: return new LtIntValPred(node); case TPrimitiveType::BIGINT: return new LtBigIntValPred(node); case TPrimitiveType::LARGEINT: return new LtLargeIntValPred(node); case TPrimitiveType::FLOAT: return new LtFloatValPred(node); case TPrimitiveType::DOUBLE: return new LtDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new LtStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new LtDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new LtDecimalValPred(node); default: return NULL; } } case TExprOpcode::LE: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new LeBooleanValPred(node); case TPrimitiveType::TINYINT: return new LeTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new LeSmallIntValPred(node); case TPrimitiveType::INT: return new LeIntValPred(node); case TPrimitiveType::BIGINT: return new LeBigIntValPred(node); case TPrimitiveType::LARGEINT: return new LeLargeIntValPred(node); case TPrimitiveType::FLOAT: return new LeFloatValPred(node); case TPrimitiveType::DOUBLE: return new LeDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new LeStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new LeDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new LeDecimalValPred(node); default: return NULL; } } case TExprOpcode::GT: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new GtBooleanValPred(node); case TPrimitiveType::TINYINT: return new GtTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new GtSmallIntValPred(node); case TPrimitiveType::INT: return new GtIntValPred(node); case TPrimitiveType::BIGINT: return new GtBigIntValPred(node); case TPrimitiveType::LARGEINT: return new GtLargeIntValPred(node); case TPrimitiveType::FLOAT: return new GtFloatValPred(node); case TPrimitiveType::DOUBLE: return new GtDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new GtStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new GtDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new GtDecimalValPred(node); default: return NULL; } } case TExprOpcode::GE: { switch (node.child_type) { case TPrimitiveType::BOOLEAN: return new GeBooleanValPred(node); case TPrimitiveType::TINYINT: return new GeTinyIntValPred(node); case TPrimitiveType::SMALLINT: return new GeSmallIntValPred(node); case TPrimitiveType::INT: return new GeIntValPred(node); case TPrimitiveType::BIGINT: return new GeBigIntValPred(node); case TPrimitiveType::LARGEINT: return new GeLargeIntValPred(node); case TPrimitiveType::FLOAT: return new GeFloatValPred(node); case TPrimitiveType::DOUBLE: return new GeDoubleValPred(node); case TPrimitiveType::CHAR: case TPrimitiveType::VARCHAR: return new GeStringValPred(node); case TPrimitiveType::DATE: case TPrimitiveType::DATETIME: return new GeDateTimeValPred(node); case TPrimitiveType::DECIMAL: return new GeDecimalValPred(node); default: return NULL; } } default: return NULL; } return NULL; } std::string BinaryPredicate::debug_string() const { std::stringstream out; out << "BinaryPredicate(" << Expr::debug_string() << ")"; return out.str(); } // IR codegen for compound add predicates. Compound predicate has non trivial // null handling as well as many branches so this is pretty complicated. The IR // for x && y is: // // define i16 @Add(%"class.palo::ExprContext"* %context, // %"class.palo::TupleRow"* %row) #20 { // entry: // %lhs_val = call { i8, i64 } @get_slot_ref(%"class.palo::ExprContext"* %context, // %"class.palo::TupleRow"* %row) // %0 = extractvalue { i8, i64 } %lhs_val, 0 // %lhs_is_null = trunc i8 %0 to i1 // br i1 %lhs_is_null, label %null, label %lhs_not_null // // lhs_not_null: ; preds = %entry // %rhs_val = call { i8, i64 } @get_slot_ref(%"class.palo::ExprContext"* %context, // %"class.palo::TupleRow"* %row) // %1 = extractvalue { i8, i64 } %lhs_val, 0 // %rhs_is_null = trunc i8 %1 to i1 // br i1 %rhs_is_null, label %null, label %rhs_not_null // // rhs_not_null: ; preds = %entry // %2 = extractvalue { i8, i64 } %lhs_val, 1 // %3 = extractvalue { i8, i64 } %rhs_val, 1 // %val = add i64 %2, %3 // br label %ret // // ret: ; preds = %not_null_block, %null_block // %ret3 = phi i1 [ false, %null_block ], [ %4, %not_null_block ] // %5 = zext i1 %ret3 to i16 // %6 = shl i16 %5, 8 // %7 = or i16 0, %6 // ret i16 %7 // } Status BinaryPredicate::codegen_compare_fn( RuntimeState* state, llvm::Function** fn, CmpInst::Predicate pred) { LlvmCodeGen* codegen = NULL; RETURN_IF_ERROR(state->get_codegen(&codegen)); Function* lhs_fn = NULL; RETURN_IF_ERROR(_children[0]->get_codegend_compute_fn(state, &lhs_fn)); Function* rhs_fn = NULL; RETURN_IF_ERROR(_children[1]->get_codegend_compute_fn(state, &rhs_fn)); // Function protocol Value* args[2]; *fn = create_ir_function_prototype(codegen, "compare", &args); LLVMContext& cg_ctx = codegen->context(); LlvmCodeGen::LlvmBuilder builder(cg_ctx); // Constant Value* zero = ConstantInt::get(codegen->get_type(TYPE_TINYINT), 0); Value* one = ConstantInt::get(codegen->get_type(TYPE_TINYINT), 1); // Block BasicBlock* entry_block = BasicBlock::Create(cg_ctx, "entry", *fn); BasicBlock* lhs_not_null_block = BasicBlock::Create(cg_ctx, "lhs_not_null", *fn); BasicBlock* compute_block = BasicBlock::Create(cg_ctx, "compute", *fn); BasicBlock* null_block = BasicBlock::Create(cg_ctx, "null", *fn); BasicBlock* ret_block = BasicBlock::Create(cg_ctx, "ret", *fn); // entry block builder.SetInsertPoint(entry_block); CodegenAnyVal lhs_val = CodegenAnyVal::create_call_wrapped( codegen, &builder, _children[0]->type(), lhs_fn, args, "lhs_val"); // if (v1.is_null) return null; Value* lhs_is_null = lhs_val.get_is_null(); builder.CreateCondBr(lhs_is_null, null_block, lhs_not_null_block); // lhs_not_null_block builder.SetInsertPoint(lhs_not_null_block); CodegenAnyVal rhs_val = CodegenAnyVal::create_call_wrapped( codegen, &builder, _children[1]->type(), rhs_fn, args, "rhs_val"); Value* rhs_is_null = rhs_val.get_is_null(); builder.CreateCondBr(rhs_is_null, null_block, compute_block); // compute_block builder.SetInsertPoint(compute_block); Value* val = NULL; if (_children[0]->type().type == TYPE_DOUBLE || _children[0]->type().type == TYPE_FLOAT) { val = builder.CreateFCmp(pred, lhs_val.get_val(), rhs_val.get_val(), "val"); } else { val = builder.CreateICmp(pred, lhs_val.get_val(), rhs_val.get_val(), "val"); } builder.CreateBr(ret_block); // null block builder.SetInsertPoint(null_block); builder.CreateBr(ret_block); // ret block builder.SetInsertPoint(ret_block); PHINode* is_null_phi = builder.CreatePHI(codegen->tinyint_type(), 2, "is_null_phi"); is_null_phi->addIncoming(one, null_block); is_null_phi->addIncoming(zero, compute_block); PHINode* val_phi = builder.CreatePHI(val->getType(), 2, "val_phi"); Value* null = Constant::getNullValue(val->getType()); val_phi->addIncoming(null, null_block); val_phi->addIncoming(val, compute_block); CodegenAnyVal result = CodegenAnyVal::get_non_null_val( codegen, &builder, type(), "result"); result.set_is_null(is_null_phi); result.set_val(val_phi); builder.CreateRet(result.value()); *fn = codegen->finalize_function(*fn); return Status::OK; } #define BINARY_PRED_FN(CLASS, TYPE, FN, OP, LLVM_PRED) \ BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ TYPE v1 = _children[0]->FN(ctx, row); \ if (v1.is_null) { \ return BooleanVal::null(); \ } \ TYPE v2 = _children[1]->FN(ctx, row); \ if (v2.is_null) { \ return BooleanVal::null(); \ } \ return BooleanVal(v1.val OP v2.val); \ } \ Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ if (_ir_compute_fn != NULL) { \ *fn = _ir_compute_fn; \ return Status::OK; \ } \ RETURN_IF_ERROR(codegen_compare_fn(state, fn, LLVM_PRED)); \ _ir_compute_fn = *fn; \ return Status::OK; \ } \ // add '/**/' to pass codestyle check of cooder #define BINARY_PRED_INT_FNS(TYPE, FN) \ BINARY_PRED_FN(Eq##TYPE##Pred, TYPE, FN, /**/ == /**/, CmpInst::ICMP_EQ) \ BINARY_PRED_FN(Ne##TYPE##Pred, TYPE, FN, /**/ != /**/, CmpInst::ICMP_NE) \ BINARY_PRED_FN(Lt##TYPE##Pred, TYPE, FN, /**/ < /**/, CmpInst::ICMP_SLT) \ BINARY_PRED_FN(Le##TYPE##Pred, TYPE, FN, /**/ <= /**/, CmpInst::ICMP_SLE) \ BINARY_PRED_FN(Gt##TYPE##Pred, TYPE, FN, /**/ > /**/, CmpInst::ICMP_SGT) \ BINARY_PRED_FN(Ge##TYPE##Pred, TYPE, FN, /**/ >= /**/, CmpInst::ICMP_SGE) BINARY_PRED_INT_FNS(BooleanVal, get_boolean_val); BINARY_PRED_INT_FNS(TinyIntVal, get_tiny_int_val); BINARY_PRED_INT_FNS(SmallIntVal, get_small_int_val); BINARY_PRED_INT_FNS(IntVal, get_int_val); BINARY_PRED_INT_FNS(BigIntVal, get_big_int_val); BINARY_PRED_INT_FNS(LargeIntVal, get_large_int_val); #define BINARY_PRED_FLOAT_FNS(TYPE, FN) \ BINARY_PRED_FN(Eq##TYPE##Pred, TYPE, FN, ==, CmpInst::FCMP_OEQ) \ BINARY_PRED_FN(Ne##TYPE##Pred, TYPE, FN, !=, CmpInst::FCMP_UNE) \ BINARY_PRED_FN(Lt##TYPE##Pred, TYPE, FN, <, CmpInst::FCMP_OLT) \ BINARY_PRED_FN(Le##TYPE##Pred, TYPE, FN, <=, CmpInst::FCMP_OLE) \ BINARY_PRED_FN(Gt##TYPE##Pred, TYPE, FN, >, CmpInst::FCMP_OGT) \ BINARY_PRED_FN(Ge##TYPE##Pred, TYPE, FN, >=, CmpInst::FCMP_OGE) BINARY_PRED_FLOAT_FNS(FloatVal, get_float_val); BINARY_PRED_FLOAT_FNS(DoubleVal, get_double_val); #define COMPLICATE_BINARY_PRED_FN(CLASS, TYPE, FN, PALO_TYPE, FROM_FUNC, OP) \ BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ TYPE v1 = _children[0]->FN(ctx, row); \ if (v1.is_null) { \ return BooleanVal::null(); \ } \ TYPE v2 = _children[1]->FN(ctx, row); \ if (v2.is_null) { \ return BooleanVal::null(); \ } \ PALO_TYPE pv1 = PALO_TYPE::FROM_FUNC(v1); \ PALO_TYPE pv2 = PALO_TYPE::FROM_FUNC(v2); \ return BooleanVal(pv1 OP pv2); \ } \ Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ return get_codegend_compute_fn_wrapper(state, fn); \ } \ #define COMPLICATE_BINARY_PRED_FNS(TYPE, FN, PALO_TYPE, FROM_FUNC) \ COMPLICATE_BINARY_PRED_FN(Eq##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, ==) \ COMPLICATE_BINARY_PRED_FN(Ne##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, !=) \ COMPLICATE_BINARY_PRED_FN(Lt##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, <) \ COMPLICATE_BINARY_PRED_FN(Le##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, <=) \ COMPLICATE_BINARY_PRED_FN(Gt##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, >) \ COMPLICATE_BINARY_PRED_FN(Ge##TYPE##Pred, TYPE, FN, PALO_TYPE, FROM_FUNC, >=) COMPLICATE_BINARY_PRED_FNS(DecimalVal, get_decimal_val, DecimalValue, from_decimal_val) #define DATETIME_BINARY_PRED_FN(CLASS, OP, LLVM_PRED) \ BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ DateTimeVal v1 = _children[0]->get_datetime_val(ctx, row); \ if (v1.is_null) { \ return BooleanVal::null(); \ } \ DateTimeVal v2 = _children[1]->get_datetime_val(ctx, row); \ if (v2.is_null) { \ return BooleanVal::null(); \ } \ return BooleanVal(v1.packed_time OP v2.packed_time); \ } \ Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ if (_ir_compute_fn != NULL) { \ *fn = _ir_compute_fn; \ return Status::OK; \ } \ RETURN_IF_ERROR(codegen_compare_fn(state, fn , LLVM_PRED)); \ _ir_compute_fn = *fn; \ return Status::OK; \ } \ #define DATETIME_BINARY_PRED_FNS() \ DATETIME_BINARY_PRED_FN(Eq##DateTimeVal##Pred, ==, CmpInst::ICMP_EQ) \ DATETIME_BINARY_PRED_FN(Ne##DateTimeVal##Pred, !=, CmpInst::ICMP_NE) \ DATETIME_BINARY_PRED_FN(Lt##DateTimeVal##Pred, <, CmpInst::ICMP_SLT) \ DATETIME_BINARY_PRED_FN(Le##DateTimeVal##Pred, <=, CmpInst::ICMP_SLE) \ DATETIME_BINARY_PRED_FN(Gt##DateTimeVal##Pred, >, CmpInst::ICMP_SGT) \ DATETIME_BINARY_PRED_FN(Ge##DateTimeVal##Pred, >=, CmpInst::ICMP_SGE) DATETIME_BINARY_PRED_FNS() #define STRING_BINARY_PRED_FN(CLASS, OP) \ BooleanVal CLASS::get_boolean_val(ExprContext* ctx, TupleRow* row) { \ StringVal v1 = _children[0]->get_string_val(ctx, row); \ if (v1.is_null) { \ return BooleanVal::null(); \ } \ StringVal v2 = _children[1]->get_string_val(ctx, row); \ if (v2.is_null) { \ return BooleanVal::null(); \ } \ StringValue pv1 = StringValue::from_string_val(v1); \ StringValue pv2 = StringValue::from_string_val(v2); \ return BooleanVal(pv1 OP pv2); \ } \ Status CLASS::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { \ return get_codegend_compute_fn_wrapper(state, fn); \ } #define STRING_BINARY_PRED_FNS() \ STRING_BINARY_PRED_FN(Ne##StringVal##Pred, !=) \ STRING_BINARY_PRED_FN(Lt##StringVal##Pred, <) \ STRING_BINARY_PRED_FN(Le##StringVal##Pred, <=) \ STRING_BINARY_PRED_FN(Gt##StringVal##Pred, >) \ STRING_BINARY_PRED_FN(Ge##StringVal##Pred, >=) STRING_BINARY_PRED_FNS() BooleanVal EqStringValPred::get_boolean_val(ExprContext* ctx, TupleRow* row) { StringVal v1 = _children[0]->get_string_val(ctx, row); if (v1.is_null) { return BooleanVal::null(); } StringVal v2 = _children[1]->get_string_val(ctx, row); if (v2.is_null) { return BooleanVal::null(); } if (v1.len != v2.len) { return BooleanVal(false); } return BooleanVal(string_compare((char*)v1.ptr, v1.len, (char*)v2.ptr, v2.len, v1.len) == 0); } Status EqStringValPred::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { return get_codegend_compute_fn_wrapper(state, fn); } #if 0 Status EqStringValPred::get_codegend_compute_fn(RuntimeState* state, llvm::Function** fn) { LlvmCodeGen* codegen = NULL; RETURN_IF_ERROR(state->get_codegen(&codegen)); Function* lhs_fn = NULL; RETURN_IF_ERROR(_children[0]->get_codegend_compute_fn(state, &lhs_fn)); Function* rhs_fn = NULL; RETURN_IF_ERROR(_children[1]->get_codegend_compute_fn(state, &rhs_fn)); // Function protocol Value* args[2]; *fn = create_ir_function_prototype(codegen, "compare", &args); LLVMContext& cg_ctx = codegen->context(); LlvmCodeGen::LlvmBuilder builder(cg_ctx); // Constant Value* zero = ConstantInt::get(codegen->get_type(TYPE_TINYINT), 0); Value* one = ConstantInt::get(codegen->get_type(TYPE_TINYINT), 1); Value* false_val = ConstantInt::get(llvm::Type::getInt1Ty(cg_ctx), 0); // Value* true_val = ConstantInt::get(llvm::Type::getInt1Ty(cg_ctx), 1); // Block BasicBlock* entry_block = BasicBlock::Create(cg_ctx, "entry", *fn); BasicBlock* lhs_not_null_block = BasicBlock::Create(cg_ctx, "lhs_not_null", *fn); BasicBlock* rhs_not_null_block = BasicBlock::Create(cg_ctx, "rhs_not_null", *fn); BasicBlock* length_equal_block = BasicBlock::Create(cg_ctx, "length_equal", *fn); BasicBlock* null_block = BasicBlock::Create(cg_ctx, "null", *fn); BasicBlock* false_block = BasicBlock::Create(cg_ctx, "false", *fn); BasicBlock* ret_block = BasicBlock::Create(cg_ctx, "ret", *fn); // entry block builder.SetInsertPoint(entry_block); CodegenAnyVal lhs_val = CodegenAnyVal::create_call_wrapped( codegen, &builder, _children[0]->type(), lhs_fn, args, "lhs_val"); // if (v1.is_null) return null; Value* lhs_is_null = lhs_val.get_is_null(); builder.CreateCondBr(lhs_is_null, null_block, lhs_not_null_block); // lhs_not_null_block builder.SetInsertPoint(lhs_not_null_block); CodegenAnyVal rhs_val = CodegenAnyVal::create_call_wrapped( codegen, &builder, _children[1]->type(), rhs_fn, args, "rhs_val"); Value* rhs_is_null = rhs_val.get_is_null(); builder.CreateCondBr(rhs_is_null, null_block, rhs_not_null_block); // rhs_not_null_block builder.SetInsertPoint(rhs_not_null_block); Value* lhs_len = lhs_val.get_len(); Value* rhs_len = rhs_val.get_len(); // if (v1.len != v2.len) Value* len_eq = builder.CreateICmpEQ(lhs_len, rhs_len, "len_eq"); builder.CreateCondBr(len_eq, length_equal_block, false_block); // length_equal_block builder.SetInsertPoint(length_equal_block); Function* compare_fn = codegen->get_function(IRFunction::IR_STRING_COMPARE); Value* compare_args[4] = {lhs_val.get_ptr(), lhs_len, lhs_val.get_ptr(), lhs_len}; Value* compare_res = builder.CreateCall(compare_fn, compare_args, "compare_res"); Value* int_zero = ConstantInt::get(codegen->get_type(TYPE_INT), 0); Value* val = builder.CreateICmpEQ(compare_res, int_zero, "val"); builder.CreateBr(ret_block); // null block builder.SetInsertPoint(null_block); builder.CreateBr(ret_block); // false block builder.SetInsertPoint(false_block); builder.CreateBr(ret_block); // ret block builder.SetInsertPoint(ret_block); PHINode* is_null_phi = builder.CreatePHI(codegen->tinyint_type(), 3, "is_null_phi"); is_null_phi->addIncoming(one, null_block); is_null_phi->addIncoming(zero, false_block); is_null_phi->addIncoming(zero, length_equal_block); PHINode* val_phi = builder.CreatePHI(val->getType(), 3, "val_phi"); Value* null = Constant::getNullValue(val->getType()); val_phi->addIncoming(null, null_block); val_phi->addIncoming(false_val, false_block); val_phi->addIncoming(val, length_equal_block); CodegenAnyVal result = CodegenAnyVal::get_non_null_val( codegen, &builder, type(), "result"); result.set_is_null(is_null_phi); result.set_val(val_phi); builder.CreateRet(result.value()); *fn = codegen->finalize_function(*fn); return Status::OK; } #endif }