| // 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 "codegen/codegen-anyval.h" | |
| #include "codegen/codegen-util.h" | |
| #include "llvm/IR/BasicBlock.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "runtime/multi-precision.h" | |
| #include "runtime/raw-value.h" | |
| #include "common/names.h" | |
| using namespace impala; | |
| using namespace impala_udf; | |
| const char* CodegenAnyVal::LLVM_ANYVAL_NAME = "struct.impala_udf::AnyVal"; | |
| const char* CodegenAnyVal::LLVM_BOOLEANVAL_NAME = "struct.impala_udf::BooleanVal"; | |
| const char* CodegenAnyVal::LLVM_TINYINTVAL_NAME = "struct.impala_udf::TinyIntVal"; | |
| const char* CodegenAnyVal::LLVM_SMALLINTVAL_NAME = "struct.impala_udf::SmallIntVal"; | |
| const char* CodegenAnyVal::LLVM_INTVAL_NAME = "struct.impala_udf::IntVal"; | |
| const char* CodegenAnyVal::LLVM_BIGINTVAL_NAME = "struct.impala_udf::BigIntVal"; | |
| const char* CodegenAnyVal::LLVM_FLOATVAL_NAME = "struct.impala_udf::FloatVal"; | |
| const char* CodegenAnyVal::LLVM_DOUBLEVAL_NAME = "struct.impala_udf::DoubleVal"; | |
| const char* CodegenAnyVal::LLVM_STRINGVAL_NAME = "struct.impala_udf::StringVal"; | |
| const char* CodegenAnyVal::LLVM_TIMESTAMPVAL_NAME = "struct.impala_udf::TimestampVal"; | |
| const char* CodegenAnyVal::LLVM_DECIMALVAL_NAME = "struct.impala_udf::DecimalVal"; | |
| const char* CodegenAnyVal::LLVM_DATEVAL_NAME = "struct.impala_udf::DateVal"; | |
| const char* CodegenAnyVal::LLVM_COLLECTIONVAL_NAME = "struct.impala_udf::CollectionVal"; | |
| bool CodegenAnyVal::IsSimpleType(const ColumnType& type) { | |
| switch (type.type) { | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_BIGINT: | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE: | |
| case TYPE_DECIMAL: | |
| case TYPE_DATE: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| llvm::Type* CodegenAnyVal::GetLoweredType(LlvmCodeGen* cg, const ColumnType& type) { | |
| switch (type.type) { | |
| case TYPE_BOOLEAN: // i16 | |
| return cg->i16_type(); | |
| case TYPE_TINYINT: // i16 | |
| return cg->i16_type(); | |
| case TYPE_SMALLINT: // i32 | |
| return cg->i32_type(); | |
| case TYPE_INT: // i64 | |
| return cg->i64_type(); | |
| case TYPE_BIGINT: // { i8, i64 } | |
| #ifndef __aarch64__ | |
| return llvm::StructType::get(cg->i8_type(), cg->i64_type()); | |
| #else | |
| return llvm::ArrayType::get(cg->i64_type(), 2); | |
| #endif | |
| case TYPE_FLOAT: // i64 | |
| return cg->i64_type(); | |
| case TYPE_DOUBLE: // { i8, double } | |
| #ifndef __aarch64__ | |
| return llvm::StructType::get(cg->i8_type(), cg->double_type()); | |
| #else | |
| return llvm::ArrayType::get(cg->i64_type(), 2); | |
| #endif | |
| case TYPE_STRING: // { i64, i8* } | |
| case TYPE_VARCHAR: // { i64, i8* } | |
| case TYPE_CHAR: // Uses StringVal, so same as STRING/VARCHAR. | |
| case TYPE_UUID: // Inline 16-byte slot, StringVal layout. | |
| case TYPE_FIXED_UDA_INTERMEDIATE: // { i64, i8* } | |
| case TYPE_ARRAY: // CollectionVal has same memory layout as StringVal. | |
| case TYPE_MAP: // CollectionVal has same memory layout as StringVal. | |
| case TYPE_STRUCT: // StructVal has same memory layout as StringVal. | |
| #ifndef __aarch64__ | |
| return llvm::StructType::get(cg->i64_type(), cg->ptr_type()); | |
| #else | |
| return llvm::ArrayType::get(cg->i64_type(), 2); | |
| #endif | |
| case TYPE_TIMESTAMP: // { i64, i64 } | |
| #ifndef __aarch64__ | |
| return llvm::StructType::get(cg->i64_type(), cg->i64_type()); | |
| #else | |
| return llvm::ArrayType::get(cg->i64_type(), 2); | |
| #endif | |
| case TYPE_DECIMAL: // %"struct.impala_udf::DecimalVal" (isn't lowered) | |
| // = { {i8}, [15 x i8], {i128} } | |
| return cg->GetNamedType(LLVM_DECIMALVAL_NAME); | |
| case TYPE_DATE: // i64 | |
| return cg->i64_type(); | |
| default: | |
| DCHECK(false) << "Unsupported type: " << type; | |
| return NULL; | |
| } | |
| } | |
| llvm::Type* CodegenAnyVal::GetUnloweredType(LlvmCodeGen* cg, const ColumnType& type) { | |
| llvm::Type* result; | |
| switch(type.type) { | |
| case TYPE_BOOLEAN: | |
| result = cg->GetNamedType(LLVM_BOOLEANVAL_NAME); | |
| break; | |
| case TYPE_TINYINT: | |
| result = cg->GetNamedType(LLVM_TINYINTVAL_NAME); | |
| break; | |
| case TYPE_SMALLINT: | |
| result = cg->GetNamedType(LLVM_SMALLINTVAL_NAME); | |
| break; | |
| case TYPE_INT: | |
| result = cg->GetNamedType(LLVM_INTVAL_NAME); | |
| break; | |
| case TYPE_BIGINT: | |
| result = cg->GetNamedType(LLVM_BIGINTVAL_NAME); | |
| break; | |
| case TYPE_FLOAT: | |
| result = cg->GetNamedType(LLVM_FLOATVAL_NAME); | |
| break; | |
| case TYPE_DOUBLE: | |
| result = cg->GetNamedType(LLVM_DOUBLEVAL_NAME); | |
| break; | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: | |
| case TYPE_CHAR: | |
| case TYPE_UUID: | |
| case TYPE_FIXED_UDA_INTERMEDIATE: | |
| result = cg->GetNamedType(LLVM_STRINGVAL_NAME); | |
| break; | |
| case TYPE_TIMESTAMP: | |
| result = cg->GetNamedType(LLVM_TIMESTAMPVAL_NAME); | |
| break; | |
| case TYPE_DECIMAL: | |
| result = cg->GetNamedType(LLVM_DECIMALVAL_NAME); | |
| break; | |
| case TYPE_DATE: | |
| result = cg->GetNamedType(LLVM_DATEVAL_NAME); | |
| break; | |
| case TYPE_ARRAY: | |
| case TYPE_MAP: | |
| result = cg->GetNamedType(LLVM_COLLECTIONVAL_NAME); | |
| break; | |
| default: | |
| DCHECK(false) << "Unsupported type: " << type; | |
| return NULL; | |
| } | |
| DCHECK(result != NULL) << type.DebugString(); | |
| return result; | |
| } | |
| llvm::Value* CodegenAnyVal::CreateCall(LlvmCodeGen* cg, LlvmBuilder* builder, | |
| llvm::Function* fn, llvm::ArrayRef<llvm::Value*> args, const char* name, | |
| llvm::Value* result_ptr) { | |
| if (fn->getReturnType()->isVoidTy()) { | |
| // Void return type indicates that this function returns a DecimalVal via the first | |
| // argument (which should be a DecimalVal*). | |
| llvm::Function::arg_iterator ret_arg = fn->arg_begin(); | |
| DCHECK(ret_arg->getType()->isPointerTy()); | |
| llvm::Type* ret_type = cg->GetNamedType(LLVM_DECIMALVAL_NAME); | |
| // We need to pass a DecimalVal pointer to 'fn' that will be populated with the result | |
| // value. Use 'result_ptr' if specified, otherwise alloca one. | |
| llvm::Value* ret_ptr = (result_ptr == NULL) ? | |
| cg->CreateEntryBlockAlloca(*builder, ret_type, name) : | |
| result_ptr; | |
| vector<llvm::Value*> new_args = args.vec(); | |
| new_args.insert(new_args.begin(), ret_ptr); | |
| // Bitcasting the args is often necessary when calling an IR UDF because the types | |
| // in the IR module may have been renamed while linking. Bitcasting them avoids a | |
| // type assertion. | |
| CodeGenUtil::CreateCallWithBitCasts(builder, fn, new_args); | |
| // If 'result_ptr' was specified, we're done. Otherwise load and return the result. | |
| if (result_ptr != NULL) return NULL; | |
| return builder->CreateLoad(ret_type, ret_ptr, name); | |
| } else { | |
| // Function returns *Val normally (note that it could still be returning a | |
| // DecimalVal, since we generate non-compliant functions). | |
| // Bitcasting the args is often necessary when calling an IR UDF because the types | |
| // in the IR module may have been renamed while linking. Bitcasting them avoids a | |
| // type assertion. | |
| llvm::Value* ret = CodeGenUtil::CreateCallWithBitCasts(builder, fn, args, name); | |
| if (result_ptr == NULL) return ret; | |
| builder->CreateStore(ret, result_ptr); | |
| return NULL; | |
| } | |
| } | |
| CodegenAnyVal CodegenAnyVal::CreateCallWrapped(LlvmCodeGen* cg, LlvmBuilder* builder, | |
| const ColumnType& type, llvm::Function* fn, llvm::ArrayRef<llvm::Value*> args, | |
| const char* name) { | |
| llvm::Value* v = CreateCall(cg, builder, fn, args, name); | |
| return CodegenAnyVal(cg, builder, type, v, name); | |
| } | |
| CodegenAnyVal::CodegenAnyVal(LlvmCodeGen* codegen, LlvmBuilder* builder, | |
| const ColumnType& type, llvm::Value* value, const char* name) | |
| : type_(type), value_(value), name_(name), codegen_(codegen), builder_(builder) { | |
| llvm::Type* value_type = GetLoweredType(codegen, type); | |
| if (value_ == NULL) { | |
| // No Value* was specified, so allocate one on the stack and load it. | |
| llvm::Value* ptr = codegen_->CreateEntryBlockAlloca(*builder, value_type); | |
| value_ = builder_->CreateLoad(value_type, ptr, name_); | |
| } | |
| DCHECK_EQ(value_->getType(), value_type); | |
| } | |
| llvm::Value* CodegenAnyVal::GetIsNull(const char* name) const { | |
| switch (type_.type) { | |
| case TYPE_BIGINT: | |
| case TYPE_DOUBLE: { | |
| // Lowered type is of form { i8, * }. Get the i8 value. | |
| // On aarch64, Lowered type is of form { i64, * } | |
| llvm::Value* is_null = builder_->CreateExtractValue(value_, 0); | |
| #ifndef __aarch64__ | |
| DCHECK(is_null->getType() == codegen_->i8_type()); | |
| #else | |
| DCHECK(is_null->getType() == codegen_->i64_type()); | |
| #endif | |
| return builder_->CreateTrunc(is_null, codegen_->bool_type(), name); | |
| } | |
| case TYPE_DECIMAL: { | |
| // Lowered type is of the form { {i8}, ... } | |
| uint32_t idxs[] = {0, 0}; | |
| llvm::Value* is_null_i8 = builder_->CreateExtractValue(value_, idxs); | |
| DCHECK(is_null_i8->getType() == codegen_->i8_type()); | |
| return builder_->CreateTrunc(is_null_i8, codegen_->bool_type(), name); | |
| } | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: | |
| case TYPE_CHAR: | |
| case TYPE_UUID: | |
| case TYPE_FIXED_UDA_INTERMEDIATE: | |
| case TYPE_TIMESTAMP: | |
| case TYPE_ARRAY: | |
| case TYPE_MAP: | |
| case TYPE_STRUCT: { | |
| // Lowered type is of form { i64, *}. Get the first byte of the i64 value. | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| DCHECK(v->getType() == codegen_->i64_type()); | |
| return builder_->CreateTrunc(v, codegen_->bool_type(), name); | |
| } | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_DATE: | |
| case TYPE_FLOAT: | |
| // Lowered type is an integer. Get the first byte. | |
| return builder_->CreateTrunc(value_, codegen_->bool_type(), name); | |
| default: | |
| DCHECK(false); | |
| return NULL; | |
| } | |
| } | |
| void CodegenAnyVal::SetIsNull(llvm::Value* is_null) { | |
| switch(type_.type) { | |
| case TYPE_BIGINT: | |
| case TYPE_DOUBLE: { | |
| // Lowered type is of form { i8, * }. Set the i8 value to 'is_null'. | |
| // On aarch64, lowered type is of form { i64, * } | |
| #ifndef __aarch64__ | |
| llvm::Value* is_null_ext = | |
| builder_->CreateZExt(is_null, codegen_->i8_type(), "is_null_ext"); | |
| #else | |
| llvm::Value* is_null_ext = | |
| builder_->CreateZExt(is_null, codegen_->i64_type(), "is_null_ext"); | |
| #endif | |
| value_ = builder_->CreateInsertValue(value_, is_null_ext, 0, name_); | |
| break; | |
| } | |
| case TYPE_DECIMAL: { | |
| // Lowered type is of form { {i8}, [15 x i8], {i128} }. Set the i8 value to | |
| // 'is_null'. | |
| llvm::Value* is_null_ext = | |
| builder_->CreateZExt(is_null, codegen_->i8_type(), "is_null_ext"); | |
| // Index into the {i8} struct as well as the outer struct. | |
| uint32_t idxs[] = {0, 0}; | |
| value_ = builder_->CreateInsertValue(value_, is_null_ext, idxs, name_); | |
| break; | |
| } | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: | |
| case TYPE_CHAR: | |
| case TYPE_UUID: | |
| case TYPE_FIXED_UDA_INTERMEDIATE: | |
| case TYPE_TIMESTAMP: | |
| case TYPE_ARRAY: | |
| case TYPE_MAP: | |
| case TYPE_STRUCT: { | |
| // Lowered type is of the form { i64, * }. Set the first byte of the i64 value to | |
| // 'is_null' | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| v = builder_->CreateAnd(v, -0x100LL, "masked"); | |
| llvm::Value* is_null_ext = | |
| builder_->CreateZExt(is_null, v->getType(), "is_null_ext"); | |
| v = builder_->CreateOr(v, is_null_ext); | |
| value_ = builder_->CreateInsertValue(value_, v, 0, name_); | |
| break; | |
| } | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_DATE: | |
| case TYPE_FLOAT: { | |
| // Lowered type is an integer. Set the first byte to 'is_null'. | |
| value_ = builder_->CreateAnd(value_, -0x100LL, "masked"); | |
| llvm::Value* is_null_ext = | |
| builder_->CreateZExt(is_null, value_->getType(), "is_null_ext"); | |
| value_ = builder_->CreateOr(value_, is_null_ext, name_); | |
| break; | |
| } | |
| default: | |
| DCHECK(false) << "NYI: " << type_.DebugString(); | |
| } | |
| } | |
| llvm::Value* CodegenAnyVal::GetVal(const char* name) { | |
| DCHECK(type_.type != TYPE_STRING) | |
| << "Use GetPtr and GetLen for StringVal"; | |
| DCHECK(type_.type != TYPE_VARCHAR) | |
| << "Use GetPtr and GetLen for Varchar"; | |
| DCHECK(type_.type != TYPE_CHAR) | |
| << "Use GetPtr and GetLen for Char"; | |
| DCHECK(type_.type != TYPE_UUID) | |
| << "Use GetPtr and GetLen for Uuid"; | |
| DCHECK(type_.type != TYPE_FIXED_UDA_INTERMEDIATE) | |
| << "Use GetPtr and GetLen for FixedUdaIntermediate"; | |
| DCHECK(type_.type != TYPE_TIMESTAMP) | |
| << "Use GetDate and GetTimeOfDay for TimestampVals"; | |
| DCHECK(!type_.IsCollectionType()) | |
| << "Use GetPtr and GetLen for CollectionVal"; | |
| DCHECK(!type_.IsStructType()) | |
| << "Use GetPtr and GetLen for StructVal"; | |
| switch(type_.type) { | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_DATE: { | |
| // Lowered type is an integer. Get the high bytes. | |
| int num_bits = type_.GetByteSize() * 8; | |
| llvm::Value* val = GetHighBits(num_bits, value_, name); | |
| if (type_.type == TYPE_BOOLEAN) { | |
| // Return booleans as i1 (vs. i8) | |
| val = builder_->CreateTrunc(val, builder_->getInt1Ty(), name); | |
| } | |
| return val; | |
| } | |
| case TYPE_FLOAT: { | |
| // Same as above, but we must cast the value to a float. | |
| llvm::Value* val = GetHighBits(32, value_); | |
| return builder_->CreateBitCast(val, codegen_->float_type()); | |
| } | |
| case TYPE_BIGINT: | |
| return builder_->CreateExtractValue(value_, 1, name); | |
| case TYPE_DOUBLE: { | |
| // Lowered type is of form { i8, * }. Get the second value. | |
| llvm::Value* val = builder_->CreateExtractValue(value_, 1, name); | |
| #ifdef __aarch64__ | |
| val = builder_->CreateBitCast(val, codegen_->double_type()); | |
| #endif | |
| return val; | |
| } | |
| case TYPE_DECIMAL: { | |
| // The Lowered type is of form { {i8}, [15 x i8], {i128} }. | |
| uint32_t idxs[] = {2, 0}; | |
| // Get the i128 value and truncate it to the correct size. | |
| // (The {i128} corresponds to the union of the different width int types.) | |
| llvm::Value* val = builder_->CreateExtractValue(value_, idxs, name); | |
| return builder_->CreateTrunc(val, | |
| codegen_->GetSlotType(type_), name); | |
| } | |
| default: | |
| DCHECK(false) << "Unsupported type: " << type_; | |
| return NULL; | |
| } | |
| } | |
| void CodegenAnyVal::SetVal(llvm::Value* val) { | |
| DCHECK(type_.type != TYPE_STRING) << "Use SetPtr and SetLen for StringVals"; | |
| DCHECK(type_.type != TYPE_VARCHAR) << "Use SetPtr and SetLen for StringVals"; | |
| DCHECK(type_.type != TYPE_CHAR) << "Use SetPtr and SetLen for StringVals"; | |
| DCHECK(type_.type != TYPE_UUID) << "Use SetPtr and SetLen for StringVals"; | |
| DCHECK(type_.type != TYPE_FIXED_UDA_INTERMEDIATE) | |
| << "Use SetPtr and SetLen for FixedUdaIntermediate"; | |
| DCHECK(type_.type != TYPE_TIMESTAMP) | |
| << "Use SetDate and SetTimeOfDay for TimestampVals"; | |
| DCHECK(!type_.IsCollectionType()) << "Use SetPtr and SetLen for CollectionVal"; | |
| DCHECK(!type_.IsStructType()) << "Use SetPtr and SetLen for StructVal"; | |
| switch(type_.type) { | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_DATE: { | |
| // Lowered type is an integer. Set the high bytes to 'val'. | |
| int num_bits = type_.GetByteSize() * 8; | |
| value_ = SetHighBits(num_bits, val, value_, name_); | |
| break; | |
| } | |
| case TYPE_FLOAT: | |
| // Same as above, but we must cast 'val' to an integer type. | |
| val = builder_->CreateBitCast(val, codegen_->i32_type()); | |
| value_ = SetHighBits(32, val, value_, name_); | |
| break; | |
| case TYPE_BIGINT: | |
| value_ = builder_->CreateInsertValue(value_, val, 1, name_); | |
| break; | |
| case TYPE_DOUBLE: | |
| #ifdef __aarch64__ | |
| val = builder_->CreateBitCast(val, codegen_->i64_type()); | |
| #endif | |
| // Lowered type is of form { i8, * }. Set the second value to 'val'. | |
| value_ = builder_->CreateInsertValue(value_, val, 1, name_); | |
| break; | |
| case TYPE_DECIMAL: { | |
| // Set the i128 value to 'val'. | |
| // (The {i128} corresponds to the union of the different width int types.) | |
| DCHECK_EQ(val->getType()->getIntegerBitWidth(), type_.GetByteSize() * 8); | |
| val = builder_->CreateSExt(val, llvm::Type::getIntNTy(codegen_->context(), 128)); | |
| // The Lowered type is of form { {i8}, [15 x i8], {i128} }. | |
| uint32_t idxs[] = {2, 0}; | |
| value_ = builder_->CreateInsertValue(value_, val, idxs, name_); | |
| break; | |
| } | |
| default: | |
| DCHECK(false) << "Unsupported type: " << type_; | |
| } | |
| } | |
| void CodegenAnyVal::SetVal(bool val) { | |
| DCHECK_EQ(type_.type, TYPE_BOOLEAN); | |
| SetVal(builder_->getInt1(val)); | |
| } | |
| void CodegenAnyVal::SetVal(int8_t val) { | |
| DCHECK_EQ(type_.type, TYPE_TINYINT); | |
| SetVal(builder_->getInt8(val)); | |
| } | |
| void CodegenAnyVal::SetVal(int16_t val) { | |
| DCHECK_EQ(type_.type, TYPE_SMALLINT); | |
| SetVal(builder_->getInt16(val)); | |
| } | |
| void CodegenAnyVal::SetVal(int32_t val) { | |
| DCHECK(type_.type == TYPE_INT || type_.type == TYPE_DECIMAL || type_.type == TYPE_DATE); | |
| SetVal(builder_->getInt32(val)); | |
| } | |
| void CodegenAnyVal::SetVal(int64_t val) { | |
| DCHECK(type_.type == TYPE_BIGINT || type_.type == TYPE_DECIMAL); | |
| SetVal(builder_->getInt64(val)); | |
| } | |
| void CodegenAnyVal::SetVal(__int128_t val) { | |
| DCHECK_EQ(type_.type, TYPE_DECIMAL); | |
| vector<uint64_t> vals({LowBits(val), HighBits(val)}); | |
| llvm::Value* ir_val = | |
| llvm::ConstantInt::get(codegen_->context(), llvm::APInt(128, vals)); | |
| SetVal(ir_val); | |
| } | |
| void CodegenAnyVal::SetVal(float val) { | |
| DCHECK_EQ(type_.type, TYPE_FLOAT); | |
| SetVal(llvm::ConstantFP::get(builder_->getFloatTy(), val)); | |
| } | |
| void CodegenAnyVal::SetVal(double val) { | |
| DCHECK_EQ(type_.type, TYPE_DOUBLE); | |
| SetVal(llvm::ConstantFP::get(builder_->getDoubleTy(), val)); | |
| } | |
| llvm::Value* CodegenAnyVal::GetPtr() { | |
| // Set the second pointer value to 'ptr'. | |
| DCHECK(type_.IsStringType() || type_.IsCollectionType() | |
| || type_.IsStructType() || type_.IsUuidType()); | |
| llvm::Value* val = builder_->CreateExtractValue(value_, 1, name_); | |
| #ifdef __aarch64__ | |
| val = builder_->CreateIntToPtr(val, codegen_->ptr_type()); | |
| #endif | |
| return val; | |
| } | |
| llvm::Value* CodegenAnyVal::GetLen() { | |
| // Get the high bytes of the first value. | |
| DCHECK(type_.IsStringType() || type_.IsCollectionType() | |
| || type_.IsStructType() || type_.IsUuidType()); | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| return GetHighBits(32, v); | |
| } | |
| void CodegenAnyVal::SetPtr(llvm::Value* ptr) { | |
| // Set the second pointer value to 'ptr'. | |
| DCHECK(type_.IsStringType() || type_.type == TYPE_FIXED_UDA_INTERMEDIATE | |
| || type_.IsCollectionType() || type_.IsStructType() || type_.IsUuidType()); | |
| #ifdef __aarch64__ | |
| ptr = builder_->CreatePtrToInt(ptr, codegen_->i64_type()); | |
| #endif | |
| value_ = builder_->CreateInsertValue(value_, ptr, 1, name_); | |
| } | |
| void CodegenAnyVal::SetLen(llvm::Value* len) { | |
| // Set the high bytes of the first value to 'len'. | |
| DCHECK(type_.IsStringType() || type_.type == TYPE_FIXED_UDA_INTERMEDIATE | |
| || type_.IsCollectionType() || type_.IsStructType() || type_.IsUuidType()); | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| v = SetHighBits(32, len, v); | |
| value_ = builder_->CreateInsertValue(value_, v, 0, name_); | |
| } | |
| llvm::Value* CodegenAnyVal::GetTimeOfDay() { | |
| // Get the second i64 value. | |
| DCHECK_EQ(type_.type, TYPE_TIMESTAMP); | |
| return builder_->CreateExtractValue(value_, 1); | |
| } | |
| llvm::Value* CodegenAnyVal::GetDate() { | |
| // Get the high bytes of the first value. | |
| DCHECK_EQ(type_.type, TYPE_TIMESTAMP); | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| return GetHighBits(32, v); | |
| } | |
| void CodegenAnyVal::SetTimeOfDay(llvm::Value* time_of_day) { | |
| // Set the second i64 value to 'time_of_day'. | |
| DCHECK_EQ(type_.type, TYPE_TIMESTAMP); | |
| value_ = builder_->CreateInsertValue(value_, time_of_day, 1, name_); | |
| } | |
| void CodegenAnyVal::SetDate(llvm::Value* date) { | |
| // Set the high bytes of the first value to 'date'. | |
| DCHECK_EQ(type_.type, TYPE_TIMESTAMP); | |
| llvm::Value* v = builder_->CreateExtractValue(value_, 0); | |
| v = SetHighBits(32, date, v); | |
| value_ = builder_->CreateInsertValue(value_, v, 0, name_); | |
| } | |
| llvm::Value* CodegenAnyVal::ConvertToCanonicalForm(LlvmCodeGen* codegen, | |
| LlvmBuilder* builder, const ColumnType& type, llvm::Value* val) { | |
| // Convert the value to a bit pattern that is unambiguous. | |
| // Specifically, for floating point type values, NaN values are converted to | |
| // the same bit pattern, and -0 is converted to +0. | |
| switch(type.type) { | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE: { | |
| llvm::Value* canonical_val; | |
| if (type.type == TYPE_FLOAT) { | |
| canonical_val = llvm::ConstantFP::getNaN(codegen->float_type()); | |
| } else { | |
| canonical_val = llvm::ConstantFP::getNaN(codegen->double_type()); | |
| } | |
| DCHECK(val != nullptr); | |
| llvm::Value* is_nan = builder->CreateFCmpUNO(val, val, "cmp_nan"); | |
| return builder->CreateSelect(is_nan, canonical_val, | |
| ConvertToPositiveZero(builder, val)); | |
| } | |
| default: | |
| return val; | |
| } | |
| } | |
| void CodegenAnyVal::ConvertToCanonicalForm() { | |
| switch(type_.type) { | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE: { | |
| llvm::Value* new_val = ConvertToCanonicalForm(codegen_, | |
| builder_, type_, GetVal()); | |
| SetVal(new_val); | |
| break; | |
| } | |
| default: | |
| ; | |
| } | |
| } | |
| llvm::Value* CodegenAnyVal::ConvertToPositiveZero(LlvmBuilder* builder, | |
| llvm::Value* val) { | |
| // Replaces negative zero with positive, leaves everything else unchanged. | |
| llvm::Value* is_negative_zero = builder->CreateFCmpOEQ( | |
| val, llvm::ConstantFP::getNegativeZero(val->getType()), "cmp_zero"); | |
| return builder->CreateSelect(is_negative_zero, | |
| llvm::ConstantFP::get(val->getType(), 0.0), val); | |
| } | |
| llvm::Value* CodegenAnyVal::GetPtrTo(const string& name) const { | |
| return codegen_->GetPtrTo(builder_, value_, name.c_str()); | |
| } | |
| llvm::Value* CodegenAnyVal::Eq(CodegenAnyVal* other) { | |
| DCHECK_EQ(type_, other->type_); | |
| switch (type_.type) { | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_BIGINT: | |
| case TYPE_DECIMAL: | |
| case TYPE_DATE: | |
| return builder_->CreateICmpEQ(GetVal(), other->GetVal(), "eq"); | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE: | |
| // Use the ordering version "OEQ" to ensure that 'nan' != 'nan'. | |
| return builder_->CreateFCmpOEQ(GetVal(), other->GetVal(), "eq"); | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: | |
| case TYPE_FIXED_UDA_INTERMEDIATE: | |
| case TYPE_UUID: { | |
| llvm::Function* eq_fn = | |
| codegen_->GetFunction(IRFunction::CODEGEN_ANYVAL_STRING_VAL_EQ, false); | |
| return builder_->CreateCall(eq_fn, | |
| llvm::ArrayRef<llvm::Value*>({GetPtrTo(), other->GetPtrTo()}), | |
| "eq"); | |
| } | |
| case TYPE_TIMESTAMP: { | |
| llvm::Function* eq_fn = | |
| codegen_->GetFunction(IRFunction::CODEGEN_ANYVAL_TIMESTAMP_VAL_EQ, false); | |
| return builder_->CreateCall(eq_fn, | |
| llvm::ArrayRef<llvm::Value*>({GetPtrTo(), other->GetPtrTo()}), | |
| "eq"); | |
| } | |
| default: | |
| DCHECK(false) << "NYI: " << type_.DebugString(); | |
| return NULL; | |
| } | |
| } | |
| llvm::Value* CodegenAnyVal::EqToNativePtr(llvm::Value* native_ptr, | |
| bool inclusive_equality) { | |
| llvm::Value* val = NULL; | |
| if (!type_.IsStringType()) { | |
| val = builder_->CreateAnyValLoad(codegen_->GetSlotType(type_), native_ptr, type_); | |
| } | |
| switch (type_.type) { | |
| case TYPE_NULL: | |
| return codegen_->false_value(); | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_BIGINT: | |
| case TYPE_DECIMAL: | |
| case TYPE_DATE: | |
| return builder_->CreateICmpEQ(GetVal(), val, "cmp_raw"); | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE:{ | |
| // Use the ordering version "OEQ" to ensure that 'nan' != 'nan'. | |
| llvm::Value* local_val = GetVal(); | |
| llvm::Value* cmp_raw = builder_->CreateFCmpOEQ(local_val, val, "cmp_raw"); | |
| if (!inclusive_equality) return cmp_raw; | |
| // Mirror logic in HashTableCtx::Equals - IMPALA-6661 | |
| llvm::Value* local_is_nan = builder_->CreateFCmpUNO(local_val, | |
| local_val, "local_val_is_nan"); | |
| llvm::Value* val_is_nan = builder_->CreateFCmpUNO(val, val, "val_is_nan"); | |
| llvm::Value* both_nan = builder_->CreateAnd(local_is_nan, val_is_nan); | |
| return builder_->CreateOr(cmp_raw, both_nan, "cmp_raw_with_nan"); | |
| } | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: | |
| case TYPE_CHAR: | |
| case TYPE_FIXED_UDA_INTERMEDIATE: { | |
| llvm::Function* eq_fn = | |
| codegen_->GetFunction(IRFunction::CODEGEN_ANYVAL_STRING_VALUE_EQ, false); | |
| return builder_->CreateCall(eq_fn, | |
| llvm::ArrayRef<llvm::Value*>({GetPtrTo(), native_ptr}), "cmp_raw"); | |
| } | |
| case TYPE_UUID: { | |
| llvm::Function* eq_fn = | |
| codegen_->GetFunction(IRFunction::CODEGEN_ANYVAL_UUID_INLINE_BYTES_EQ, false); | |
| return builder_->CreateCall(eq_fn, | |
| llvm::ArrayRef<llvm::Value*>({GetPtrTo(), native_ptr}), "cmp_raw"); | |
| } | |
| case TYPE_TIMESTAMP: { | |
| llvm::Function* eq_fn = | |
| codegen_->GetFunction(IRFunction::CODEGEN_ANYVAL_TIMESTAMP_VALUE_EQ, false); | |
| return builder_->CreateCall(eq_fn, | |
| llvm::ArrayRef<llvm::Value*>({GetPtrTo(), native_ptr}), "cmp_raw"); | |
| } | |
| default: | |
| DCHECK(false) << "NYI: " << type_.DebugString(); | |
| return NULL; | |
| } | |
| } | |
| llvm::Value* CodegenAnyVal::Compare(CodegenAnyVal* other, const char* name) { | |
| DCHECK_EQ(type_, other->type_); | |
| llvm::Value* v1 = SlotDescriptor::CodegenStoreNonNullAnyValToNewAlloca(*this); | |
| llvm::Value* v2 = SlotDescriptor::CodegenStoreNonNullAnyValToNewAlloca(*other); | |
| // Create a global constant of the values' ColumnType. It needs to be a constant | |
| // for constant propagation and dead code elimination in 'compare_fn'. | |
| llvm::Type* col_type = codegen_->GetStructType<ColumnType>(); | |
| llvm::Constant* type_ptr = | |
| codegen_->ConstantToGVPtr(col_type, type_.ToIR(codegen_), "type"); | |
| llvm::Function* compare_fn = | |
| codegen_->GetFunction(IRFunction::RAW_VALUE_COMPARE, false); | |
| llvm::Value* args[] = {v1, v2, type_ptr}; | |
| return builder_->CreateCall(compare_fn, args, name); | |
| } | |
| void CodegenAnyVal::CodegenBranchIfNull( | |
| LlvmBuilder* builder, llvm::BasicBlock* null_block) { | |
| llvm::Value* is_null = GetIsNull(); | |
| llvm::BasicBlock* not_null_block = llvm::BasicBlock::Create( | |
| codegen_->context(), "not_null", builder->GetInsertBlock()->getParent()); | |
| builder->CreateCondBr(is_null, null_block, not_null_block); | |
| builder->SetInsertPoint(not_null_block); | |
| } | |
| llvm::Value* CodegenAnyVal::GetHighBits(int num_bits, llvm::Value* v, const char* name) { | |
| DCHECK_EQ(v->getType()->getIntegerBitWidth(), num_bits * 2); | |
| llvm::Value* shifted = builder_->CreateAShr(v, num_bits); | |
| return builder_->CreateTrunc( | |
| shifted, llvm::IntegerType::get(codegen_->context(), num_bits)); | |
| } | |
| // Example output: (num_bits = 8) | |
| // %1 = zext i1 %src to i16 | |
| // %2 = shl i16 %1, 8 | |
| // %3 = and i16 %dst1 255 ; clear the top half of dst | |
| // %dst2 = or i16 %3, %2 ; set the top of half of dst to src | |
| llvm::Value* CodegenAnyVal::SetHighBits( | |
| int num_bits, llvm::Value* src, llvm::Value* dst, const char* name) { | |
| DCHECK_LE(src->getType()->getIntegerBitWidth(), num_bits); | |
| DCHECK_EQ(dst->getType()->getIntegerBitWidth(), num_bits * 2); | |
| llvm::Value* extended_src = builder_->CreateZExt( | |
| src, llvm::IntegerType::get(codegen_->context(), num_bits * 2)); | |
| llvm::Value* shifted_src = builder_->CreateShl(extended_src, num_bits); | |
| llvm::Value* masked_dst = builder_->CreateAnd(dst, (1LL << num_bits) - 1); | |
| return builder_->CreateOr(masked_dst, shifted_src, name); | |
| } | |
| llvm::Value* CodegenAnyVal::GetNullVal(LlvmCodeGen* codegen, const ColumnType& type) { | |
| llvm::Type* val_type = GetLoweredType(codegen, type); | |
| return GetNullVal(codegen, val_type); | |
| } | |
| llvm::Value* CodegenAnyVal::GetNullVal(LlvmCodeGen* codegen, llvm::Type* val_type) { | |
| if (val_type->isStructTy()) { | |
| llvm::StructType* struct_type = llvm::cast<llvm::StructType>(val_type); | |
| if (struct_type->getNumElements() == 3) { | |
| DCHECK_EQ(val_type, codegen->GetNamedType(LLVM_DECIMALVAL_NAME)); | |
| // Return the struct { {1}, 0, 0 } (the 'is_null' byte, i.e. the first value's first | |
| // byte, is set to 1, the other bytes don't matter) | |
| llvm::StructType* anyval_struct_type = | |
| llvm::cast<llvm::StructType>(struct_type->getElementType(0)); | |
| llvm::Type* is_null_type = anyval_struct_type->getElementType(0); | |
| llvm::Constant* null_anyval = llvm::ConstantStruct::get( | |
| anyval_struct_type, llvm::ConstantInt::get(is_null_type, 1)); | |
| llvm::Type* type2 = struct_type->getElementType(1); | |
| llvm::Type* type3 = struct_type->getElementType(2); | |
| return llvm::ConstantStruct::get(struct_type, null_anyval, | |
| llvm::Constant::getNullValue(type2), llvm::Constant::getNullValue(type3)); | |
| } | |
| // Two-element struct: either { {i8}, T } (DecimalVal in LLVM 19+, or aarch64) | |
| // or { integer, T } (BigIntVal, DoubleVal, StringVal, etc.). | |
| if (struct_type->getElementType(0)->isStructTy()) { | |
| DCHECK_EQ(val_type, codegen->GetNamedType(LLVM_DECIMALVAL_NAME)); | |
| llvm::StructType* anyval_struct_type = | |
| llvm::cast<llvm::StructType>(struct_type->getElementType(0)); | |
| llvm::Type* is_null_type = anyval_struct_type->getElementType(0); | |
| llvm::Constant* null_anyval = llvm::ConstantStruct::get( | |
| anyval_struct_type, llvm::ConstantInt::get(is_null_type, 1)); | |
| llvm::Type* type1 = struct_type->getElementType(1); | |
| return llvm::ConstantStruct::get(struct_type, null_anyval, | |
| llvm::Constant::getNullValue(type1)); | |
| } | |
| // Return the struct { 1, 0 } (the 'is_null' byte, i.e. the first value's first byte, | |
| // is set to 1, the other bytes don't matter) | |
| DCHECK_EQ(struct_type->getNumElements(), 2); | |
| llvm::Type* type1 = struct_type->getElementType(0); | |
| DCHECK(type1->isIntegerTy()) << LlvmCodeGen::Print(type1); | |
| llvm::Type* type2 = struct_type->getElementType(1); | |
| return llvm::ConstantStruct::get(struct_type, llvm::ConstantInt::get(type1, 1), | |
| llvm::Constant::getNullValue(type2)); | |
| } | |
| #ifdef __aarch64__ | |
| if (val_type->isArrayTy()) { | |
| llvm::ArrayType* array_type = llvm::cast<llvm::ArrayType>(val_type); | |
| DCHECK_EQ(array_type->getNumElements(), 2); | |
| llvm::Type* type1 = array_type->getElementType(); | |
| DCHECK(type1->isIntegerTy()) << LlvmCodeGen::Print(type1); | |
| std::vector<llvm::Constant *> arrayElts; | |
| arrayElts.push_back(llvm::ConstantInt::get(type1, 1)); | |
| arrayElts.push_back(llvm::Constant::getNullValue(type1)); | |
| return llvm::ConstantArray::get(array_type, arrayElts); | |
| } | |
| #endif | |
| // Return the int 1 ('is_null' byte is 1, other bytes don't matter) | |
| DCHECK(val_type->isIntegerTy()); | |
| return llvm::ConstantInt::get(val_type, 1); | |
| } | |
| CodegenAnyVal CodegenAnyVal::GetNonNullVal(LlvmCodeGen* codegen, LlvmBuilder* builder, | |
| const ColumnType& type, const char* name) { | |
| llvm::Type* val_type = GetLoweredType(codegen, type); | |
| // All zeros => 'is_null' = false | |
| llvm::Value* value = llvm::Constant::getNullValue(val_type); | |
| return CodegenAnyVal(codegen, builder, type, value, name); | |
| } | |
| CodegenAnyVal CodegenAnyVal::GetNullableVal(LlvmCodeGen* codegen, LlvmBuilder* builder, | |
| const ColumnType& type, llvm::Value* val, llvm::Value* is_null, const char* name) { | |
| CodegenAnyVal result = GetNonNullVal(codegen, builder, type, name); | |
| result.SetVal(val); | |
| result.SetIsNull(is_null); | |
| return result; | |
| } | |
| // Returns the last block generated so we can set it as a predecessor in PHI nodes. | |
| llvm::BasicBlock* CodegenAnyVal::CreateStructValFromReadWriteInfo( | |
| const CodegenAnyValReadWriteInfo& read_write_info, | |
| llvm::Value** ptr, llvm::Value** len, llvm::BasicBlock* struct_produce_value_block) { | |
| LlvmCodeGen* codegen = read_write_info.codegen(); | |
| LlvmBuilder* builder = read_write_info.builder(); | |
| DCHECK(read_write_info.type().IsStructType() && read_write_info.children().size() > 0); | |
| DCHECK(read_write_info.GetEval() != nullptr); | |
| DCHECK_GT(read_write_info.GetFnCtxIdx(), -1); | |
| // Cross-compiled functions this hand-crafted function will call. | |
| llvm::Function* const allocate_for_results_fn = | |
| codegen->GetFunction(IRFunction::FN_CTX_ALLOCATE_FOR_RESULTS, false); | |
| llvm::Function* const store_result_in_eval_fn = | |
| codegen->GetFunction(IRFunction::STORE_RESULT_IN_EVALUATOR, false); | |
| builder->SetInsertPoint(read_write_info.non_null_block()); | |
| std::size_t num_children = read_write_info.children().size(); | |
| DCHECK_GT(num_children, 0); | |
| llvm::Value* fn_ctx = read_write_info.CodegenGetFnCtx(); | |
| // Allocate a buffer for the child pointers. If allocation fails, the struct will be | |
| // null. | |
| llvm::Value* children_ptrs_buffer = builder->CreateCall(allocate_for_results_fn, | |
| {fn_ctx, codegen->GetI64Constant(num_children * sizeof(uint8_t*))}, | |
| "children_ptrs_buffer"); | |
| llvm::Value* buffer_is_null = builder->CreateIsNull( | |
| children_ptrs_buffer, "buffer_is_null"); | |
| // Branch based on 'buffer_is_null'. | |
| read_write_info.children()[0].entry_block().BranchToIfNot(builder, buffer_is_null, | |
| NonWritableBasicBlock(read_write_info.null_block())); | |
| for (std::size_t i = 0; i < num_children; ++i) { | |
| const CodegenAnyValReadWriteInfo& child_codegen_value_read_write_info = | |
| read_write_info.children()[i]; | |
| CodegenAnyVal child_any_val = | |
| CreateFromReadWriteInfo(child_codegen_value_read_write_info); | |
| llvm::ConstantStruct* child_type_ir = | |
| child_codegen_value_read_write_info.GetIrType(); | |
| llvm::Value* child_type_ir_ptr = codegen->CreateEntryBlockAlloca( | |
| *builder, child_type_ir->getType(), "child_type_ptr"); | |
| builder->CreateStore(child_type_ir, child_type_ir_ptr); | |
| llvm::Value* child_ptr = child_any_val.GetPtrTo("child_ptr"); | |
| // Convert and store the child in the corresponding ScalarExprEvaluator - this takes | |
| // care of the lifetime of the object. | |
| llvm::Value* stored_child_ptr = builder->CreateCall(store_result_in_eval_fn, | |
| {child_codegen_value_read_write_info.GetEval(), child_ptr, child_type_ir_ptr}, | |
| "stored_value"); | |
| // The address where the child pointer should be written. This is in the pointer list | |
| // of the StructVal. | |
| llvm::Value* dst_child_ptr_addr = builder->CreateInBoundsGEP(codegen->ptr_type(), | |
| children_ptrs_buffer, codegen->GetI32Constant(i), "child_ptr_addr"); | |
| builder->CreateStore(stored_child_ptr, dst_child_ptr_addr); | |
| if (i < num_children - 1) { | |
| // Do not add a new block after the last child. | |
| read_write_info.children()[i+1].entry_block().BranchTo(builder); | |
| } | |
| } | |
| llvm::BasicBlock* last_block = builder->GetInsertBlock(); | |
| builder->CreateBr(struct_produce_value_block); | |
| builder->SetInsertPoint(struct_produce_value_block); | |
| *ptr = children_ptrs_buffer; | |
| *len = codegen->GetI32Constant(num_children); | |
| return last_block; | |
| } | |
| CodegenAnyVal CodegenAnyVal::CreateFromReadWriteInfo( | |
| const CodegenAnyValReadWriteInfo& read_write_info) { | |
| LlvmCodeGen* codegen = read_write_info.codegen(); | |
| LlvmBuilder* builder = read_write_info.builder(); | |
| const ColumnType& type = read_write_info.type(); | |
| llvm::LLVMContext& context = codegen->context(); | |
| llvm::Function* fn = read_write_info.non_null_block()->getParent(); | |
| llvm::BasicBlock* produce_value_block = | |
| llvm::BasicBlock::Create(context, "produce_value", fn); | |
| builder->SetInsertPoint(read_write_info.null_block()); | |
| builder->CreateBr(produce_value_block); | |
| if (!type.IsStructType()) { | |
| builder->SetInsertPoint(read_write_info.non_null_block()); | |
| builder->CreateBr(produce_value_block); | |
| } | |
| builder->SetInsertPoint(produce_value_block); | |
| CodegenAnyVal result = CodegenAnyVal::GetNonNullVal(codegen, builder, type, "result"); | |
| // For structs the code that reads the value consists of multiple basic blocks, so the | |
| // block that should branch to 'produce_value_block' is not | |
| // 'read_write_info.non_null_block'. This variable will be set to the appropriate block. | |
| llvm::BasicBlock* non_null_incoming_block = read_write_info.non_null_block(); | |
| if (type.IsStringType() || type.type == TYPE_FIXED_UDA_INTERMEDIATE | |
| || type.IsCollectionType() || type.IsStructType() || type.IsUuidType()) { | |
| llvm::Value* ptr = nullptr; | |
| llvm::Value* len = nullptr; | |
| if (type.IsStructType()) { | |
| non_null_incoming_block = CreateStructValFromReadWriteInfo( | |
| read_write_info, &ptr, &len, produce_value_block); | |
| } else { | |
| ptr = read_write_info.GetPtrAndLen().ptr; | |
| len = read_write_info.GetPtrAndLen().len; | |
| } | |
| DCHECK(ptr != nullptr); | |
| DCHECK(len != nullptr); | |
| llvm::Value* ptr_null = llvm::Constant::getNullValue(ptr->getType()); | |
| llvm::PHINode* ptr_phi = LlvmCodeGen::CreateBinaryPhiNode(builder, ptr, ptr_null, | |
| non_null_incoming_block, read_write_info.null_block(), "ptr_phi"); | |
| llvm::Value* len_null = llvm::ConstantInt::get(len->getType(), 0); | |
| llvm::PHINode* len_phi = LlvmCodeGen::CreateBinaryPhiNode(builder, len, len_null, | |
| non_null_incoming_block, read_write_info.null_block(), "len_phi"); | |
| result.SetPtr(ptr_phi); | |
| result.SetLen(len_phi); | |
| } else if (type.type == TYPE_TIMESTAMP) { | |
| llvm::Value* time_of_day_null = llvm::ConstantInt::get( | |
| read_write_info.GetTimeAndDate().time_of_day->getType(), 0); | |
| llvm::PHINode* time_of_day_phi = LlvmCodeGen::CreateBinaryPhiNode(builder, | |
| read_write_info.GetTimeAndDate().time_of_day, time_of_day_null, | |
| non_null_incoming_block, read_write_info.null_block(), "time_of_day_phi"); | |
| llvm::Value* date_null = llvm::ConstantInt::get( | |
| read_write_info.GetTimeAndDate().date->getType(), 0); | |
| llvm::PHINode* date_phi = | |
| LlvmCodeGen::CreateBinaryPhiNode(builder, read_write_info.GetTimeAndDate().date, | |
| date_null, non_null_incoming_block, read_write_info.null_block(), "date_phi"); | |
| result.SetTimeOfDay(time_of_day_phi); | |
| result.SetDate(date_phi); | |
| } else { | |
| llvm::Value* null = llvm::Constant::getNullValue( | |
| read_write_info.GetSimpleVal()->getType()); | |
| llvm::PHINode* val_phi = | |
| LlvmCodeGen::CreateBinaryPhiNode(builder, read_write_info.GetSimpleVal(), null, | |
| non_null_incoming_block, read_write_info.null_block(), "val_phi"); | |
| result.SetVal(val_phi); | |
| } | |
| llvm::Value* zero = codegen->GetI8Constant(0); | |
| llvm::Value* one = codegen->GetI8Constant(1); | |
| // PHI nodes must be inserted at the beginning of basic blocks. | |
| llvm::IRBuilderBase::InsertPoint ip = builder->saveIP(); | |
| builder->SetInsertPoint(produce_value_block, produce_value_block->begin()); | |
| llvm::PHINode* is_null_phi = LlvmCodeGen::CreateBinaryPhiNode(builder, zero, one, | |
| non_null_incoming_block, read_write_info.null_block(), "is_null_phi"); | |
| builder->restoreIP(ip); | |
| result.SetIsNull(is_null_phi); | |
| return result; | |
| } | |
| CodegenAnyValReadWriteInfo CodegenAnyVal::ToReadWriteInfo() { | |
| llvm::IRBuilderBase::InsertPoint ip = builder_->saveIP(); | |
| llvm::LLVMContext& context = codegen_->context(); | |
| llvm::Function* fn = builder_->GetInsertBlock()->getParent(); | |
| CodegenAnyValReadWriteInfo res(codegen_, builder_, type_); | |
| llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(context, "entry", fn); | |
| builder_->SetInsertPoint(entry_block); | |
| // Create new basic blocks and branch instruction | |
| llvm::BasicBlock* non_null_block = llvm::BasicBlock::Create(context, "non_null", fn); | |
| llvm::BasicBlock* null_block = llvm::BasicBlock::Create(context, "null", fn); | |
| llvm::Value* is_null = GetIsNull(); | |
| builder_->CreateCondBr(is_null, null_block, non_null_block); | |
| builder_->SetInsertPoint(non_null_block); | |
| if (type_.IsStructType()) { | |
| StructToReadWriteInfo(&res, GetPtr()); | |
| } else if (type_.IsStringType() || type_.IsCollectionType() || type_.IsUuidType()) { | |
| res.SetPtrAndLen(GetPtr(), GetLen()); | |
| } else if (type_.type == TYPE_TIMESTAMP) { | |
| res.SetTimeAndDate(GetTimeOfDay(), GetDate()); | |
| } else { | |
| res.SetSimpleVal(GetVal()); | |
| } | |
| res.SetBlocks(entry_block, null_block, non_null_block); | |
| builder_->restoreIP(ip); | |
| return res; | |
| } | |
| void CodegenAnyVal::StructChildToReadWriteInfo( | |
| CodegenAnyValReadWriteInfo* read_write_info, | |
| const ColumnType& type, llvm::Value* child_ptr) { | |
| LlvmCodeGen* codegen = read_write_info->codegen(); | |
| LlvmBuilder* builder = read_write_info->builder(); | |
| llvm::Type* slot_type = codegen->GetSlotType(type); | |
| // Children of struct-typed 'AnyVal's are stored in one of the members of an 'ExprValue' | |
| // belonging to the appropriate 'ScalarExprEvaluator'. These have the same type and | |
| // layout as the values stored in the slots, except for BOOLEANs, which are stored as i1 | |
| // in the slots but are stored as 'bool' variables in 'ExprValue' (as children of struct | |
| // 'AnyVal's). As this code deals with values in 'AnyVal's, for BOOLEANS we use i8, | |
| // which is the LLVM type corresponding to 'bool', and for other types we use the slot | |
| // type. | |
| switch (type.type) { | |
| case TYPE_CHAR: | |
| case TYPE_UUID: { | |
| llvm::Value* len = codegen->GetI32Constant(type.len); | |
| read_write_info->SetPtrAndLen(child_ptr, len); | |
| break; | |
| } | |
| case TYPE_STRING: | |
| case TYPE_VARCHAR: { | |
| llvm::Function* str_ptr_fn = codegen->GetFunction( | |
| IRFunction::STRING_VALUE_PTR, false); | |
| llvm::Value* ptr = builder->CreateCall(str_ptr_fn, | |
| llvm::ArrayRef<llvm::Value*>({child_ptr}), "ptr"); | |
| llvm::Function* str_len_fn = codegen->GetFunction( | |
| IRFunction::STRING_VALUE_LEN, false); | |
| llvm::Value* len = builder->CreateCall(str_len_fn, | |
| llvm::ArrayRef<llvm::Value*>({child_ptr}), "len"); | |
| read_write_info->SetPtrAndLen(ptr, len); | |
| break; | |
| } | |
| case TYPE_ARRAY: | |
| case TYPE_MAP: { // Arrays and maps have the same memory layout. | |
| llvm::Value* ptr_addr = builder->CreateStructGEP( | |
| slot_type, child_ptr, 0, "ptr_addr"); | |
| llvm::Value* ptr = builder->CreateLoad(codegen->ptr_type(), ptr_addr, "ptr"); | |
| llvm::Value* len; | |
| llvm::Value* len_addr = builder->CreateStructGEP( | |
| slot_type, child_ptr, 1, "len_addr"); | |
| len = builder->CreateLoad(codegen->i32_type(), len_addr, "len"); | |
| read_write_info->SetPtrAndLen(ptr, len); | |
| break; | |
| } | |
| case TYPE_FIXED_UDA_INTERMEDIATE: | |
| DCHECK(false) << "FIXED_UDA_INTERMEDIATE does not need to be copied: the " | |
| << "StringVal must be set up to point to the output slot"; | |
| break; | |
| case TYPE_TIMESTAMP: { | |
| llvm::Value* time_of_day_addr = builder->CreateStructGEP( | |
| slot_type, child_ptr, 0, "time_of_day_addr"); | |
| llvm::Value* time_of_day = builder->CreateLoad( | |
| codegen->i64_type(), time_of_day_addr, "time_of_day"); | |
| llvm::Value* date_addr = builder->CreateStructGEP( | |
| slot_type, child_ptr, 1, "date_addr"); | |
| llvm::Value* date = builder->CreateLoad(codegen->i32_type(), date_addr, "date"); | |
| read_write_info->SetTimeAndDate(time_of_day, date); | |
| break; | |
| } | |
| case TYPE_BOOLEAN: | |
| case TYPE_TINYINT: | |
| case TYPE_SMALLINT: | |
| case TYPE_INT: | |
| case TYPE_BIGINT: | |
| case TYPE_FLOAT: | |
| case TYPE_DOUBLE: | |
| case TYPE_DECIMAL: | |
| case TYPE_DATE: { | |
| // The representations of the types match - just take the value. | |
| llvm::Type* child_type = type.type == TYPE_BOOLEAN | |
| || type.type == TYPE_CHAR || type.type == TYPE_UUID ? | |
| codegen->i8_type() : slot_type; | |
| llvm::Value* child = builder->CreateLoad(child_type, child_ptr, "child"); | |
| read_write_info->SetSimpleVal(child); | |
| break; | |
| } | |
| default: | |
| DCHECK(false) << type.DebugString(); | |
| break; | |
| } | |
| } | |
| void CodegenAnyVal::StructToReadWriteInfo( | |
| CodegenAnyValReadWriteInfo* read_write_info, | |
| llvm::Value* children_ptr) { | |
| const ColumnType& type = read_write_info->type(); | |
| DCHECK(type.IsStructType()); | |
| LlvmCodeGen* codegen = read_write_info->codegen(); | |
| llvm::LLVMContext& context = codegen->context(); | |
| LlvmBuilder* builder = read_write_info->builder(); | |
| llvm::Function* fn = builder->GetInsertBlock()->getParent(); | |
| for (int i = 0; i < type.children.size(); ++i) { | |
| const ColumnType& child_type = type.children[i]; | |
| CodegenAnyValReadWriteInfo child_read_write_info(codegen, builder, child_type); | |
| llvm::BasicBlock* child_entry_block = llvm::BasicBlock::Create(context, "entry", fn); | |
| builder->SetInsertPoint(child_entry_block); | |
| llvm::Value* child_ptr_addr = builder->CreateInBoundsGEP(codegen->ptr_type(), | |
| children_ptr, codegen->GetI32Constant(i), "child_ptr_addr"); | |
| llvm::Value* child_ptr = builder->CreateLoad(codegen->ptr_type(), child_ptr_addr, | |
| "child_ptr"); | |
| // Check whether child_ptr is NULL. | |
| llvm::Value* child_is_null = builder->CreateIsNull(child_ptr, "child_is_null"); | |
| llvm::BasicBlock* non_null_block = | |
| llvm::BasicBlock::Create(context, "non_null", fn); | |
| llvm::BasicBlock* null_block = | |
| llvm::BasicBlock::Create(context, "null", fn); | |
| builder->CreateCondBr(child_is_null, null_block, non_null_block); | |
| builder->SetInsertPoint(non_null_block); | |
| if (child_type.IsStructType()) { | |
| llvm::Value* child_struct = builder->CreateLoad( | |
| GetLoweredType(codegen, child_type), child_ptr, "child_struct"); | |
| CodegenAnyVal child_anyval = CodegenAnyVal( | |
| codegen, builder, child_type, child_struct); | |
| llvm::Value* child_children_ptr = child_anyval.GetPtr(); | |
| StructToReadWriteInfo(&child_read_write_info, child_children_ptr); | |
| } else { | |
| StructChildToReadWriteInfo(&child_read_write_info, child_type, child_ptr); | |
| } | |
| child_read_write_info.SetBlocks(child_entry_block, null_block, non_null_block); | |
| read_write_info->children().emplace_back(std::move(child_read_write_info)); | |
| } | |
| } |