| /* |
| * 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. |
| */ |
| |
| /*! |
| * \file codegen_x86_64.cc |
| * \brief X86-64 specific code generator |
| */ |
| #ifdef TVM_LLVM_VERSION |
| |
| #include <llvm/IR/DerivedTypes.h> |
| #include <llvm/IR/Function.h> |
| #include <llvm/IR/Intrinsics.h> |
| #include <llvm/IR/IntrinsicsX86.h> |
| #include <llvm/Support/Casting.h> |
| #include <tvm/ffi/function.h> |
| #include <tvm/ffi/reflection/registry.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "codegen_cpu.h" |
| #include "llvm_instance.h" |
| |
| namespace tvm { |
| namespace codegen { |
| |
| class CodeGenX86_64 final : public CodeGenCPU { |
| public: |
| llvm::Value* VisitExpr_(const CastNode* op) override; |
| |
| private: |
| llvm::Value* CallVectorIntrin(llvm::Intrinsic::ID id, size_t intrin_lanes, llvm::Type* result_ty, |
| const std::vector<llvm::Value*>& args); |
| }; |
| |
| llvm::Value* CodeGenX86_64::VisitExpr_(const CastNode* op) { |
| // LLVM does not automatically generate the correct instruction sequences for |
| // half -> float conversion (i.e. using AVX2/AVX-512 vectorized variants of |
| // vcvtph2ps), so we explicitly generate them ourselves. |
| const auto from = PrimType(op->value.ty()->dtype); |
| const auto to = PrimType(op->ty.as_or_throw<PrimType>()->dtype); |
| if (from.MatchesCode(DLDataTypeCode::kDLFloat) && to.MatchesCode(DLDataTypeCode::kDLFloat) && |
| from.bits() == 16 && to.bits() == 32) { |
| TVM_FFI_ICHECK_EQ(from.lanes(), to.lanes()); |
| |
| const auto has_avx512 = llvm_target_->TargetHasCPUFeature("avx512f"); |
| |
| if (from.lanes() >= 16 && has_avx512) { |
| return CallVectorIntrin( |
| llvm::Intrinsic::x86_avx512_mask_vcvtph2ps_512, 16, |
| DTypeToLLVMType(PrimType::Float(32, from.lanes())), |
| { |
| MakeValue( |
| Call(PrimType::Int(16, from.lanes()), tirx::builtin::reinterpret(), {op->value}) |
| .as_or_throw<PrimExpr>()), |
| MakeValue(tirx::Broadcast(FloatImm(PrimType::Float(32), 0), from.lanes())), |
| /*mask=*/MakeValue(IntImm(PrimType::Int(16), -1)), |
| /*rounding-mode=*/MakeValue(IntImm::Int32(4)), |
| }); |
| } |
| } |
| |
| return CodeGenCPU::VisitExpr_(op); |
| } |
| |
| llvm::Value* CodeGenX86_64::CallVectorIntrin(llvm::Intrinsic::ID id, size_t intrin_lanes, |
| llvm::Type* result_ty, |
| const std::vector<llvm::Value*>& args) { |
| #if TVM_LLVM_VERSION >= 200 |
| llvm::Function* f = |
| llvm::cast<llvm::Function>(llvm::Intrinsic::getOrInsertDeclaration(module_.get(), id, {})); |
| #else |
| llvm::Function* f = llvm::Intrinsic::getDeclaration(module_.get(), id); |
| #endif |
| size_t num_elems = llvm::cast<llvm::FixedVectorType>(result_ty)->getNumElements(); |
| if (intrin_lanes == num_elems) { |
| return builder_->CreateCall(f, args); |
| } |
| |
| // Otherwise, we split the vector into intrin_lanes sized elements (widening where necessary), |
| // compute each result, and then concatenate the vectors (slicing the result if necessary). |
| TVM_FFI_ICHECK_LT(intrin_lanes, num_elems); |
| std::vector<llvm::Value*> split_results; |
| for (size_t i = 0; i < num_elems; i += intrin_lanes) { |
| std::vector<llvm::Value*> split_args; |
| for (const auto& v : args) { |
| if (v->getType()->isVectorTy()) { |
| TVM_FFI_ICHECK_EQ(GetVectorNumElements(v), num_elems); |
| split_args.push_back(CreateVecSlice(v, i, intrin_lanes)); |
| } else { |
| split_args.push_back(v); |
| } |
| } |
| llvm::Type* type = llvm::FixedVectorType::get(result_ty->getScalarType(), intrin_lanes); |
| split_results.push_back(CallVectorIntrin(id, intrin_lanes, type, split_args)); |
| } |
| return CreateVecSlice(CreateVecConcat(split_results), 0, num_elems); |
| } |
| |
| TVM_FFI_STATIC_INIT_BLOCK() { |
| namespace refl = tvm::ffi::reflection; |
| refl::GlobalDef().def_packed("tvm.codegen.llvm.target_x86-64", |
| [](const ffi::PackedArgs& targs, ffi::Any* rv) { |
| *rv = static_cast<void*>(new CodeGenX86_64()); |
| }); |
| } |
| |
| } // namespace codegen |
| } // namespace tvm |
| |
| #endif // TVM_LLVM_VERSION |