blob: 3a0a3658997bf2e63e5736dc93d30361ad952be3 [file]
/*
* 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 src/target/llvm/codegen_aarch64.cc
* \brief AArch64 specific LLVM code generator.
*/
#ifdef TVM_LLVM_VERSION
#include <llvm/IR/Intrinsics.h>
#include <llvm/Target/TargetMachine.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/runtime/logging.h>
#include "codegen_cpu.h"
#include "llvm_instance.h"
namespace tvm {
namespace codegen {
class CodeGenAArch64 final : public CodeGenCPU {
public:
CodeGenAArch64() = default;
virtual ~CodeGenAArch64() = default;
void VisitStmt_(const AttrStmtNode* op);
void AddFunction(const GlobalVar& gvar, const PrimFunc& f);
void SetTargetAttributes(llvm::Function* func);
bool func_has_pstate_sm = false;
bool func_has_pstate_za = false;
};
void CodeGenAArch64::AddFunction(const GlobalVar& gvar, const PrimFunc& f) {
func_has_pstate_sm = false;
func_has_pstate_za = false;
CodeGenCPU::AddFunction(gvar, f);
}
void CodeGenAArch64::SetTargetAttributes(llvm::Function* func) {
// Add vscale_range() function attribute when appropriate.
if (llvm_target_->TargetHasCPUFeature("sve") || llvm_target_->TargetHasCPUFeature("sme")) {
// Compute max_val = largest power-of-two <= vector_width/8.
// Guard against calling llvm_get_vector_width_fn when no target is active —
// Target::Current() returns an undefined Target outside a compilation context.
static auto llvm_get_vector_width_fn =
tvm::ffi::Function::GetGlobalRequired("target.llvm_get_vector_width");
unsigned int max_val = 0;
if (auto target = Target::Current(); target.defined()) {
unsigned int vector_width =
static_cast<unsigned int>(llvm_get_vector_width_fn(target).cast<int>());
for (unsigned int i = 0;; ++i) {
unsigned int power = 1u << i;
if (power > (vector_width / 8)) break;
max_val = power;
}
}
if (max_val > 0) {
func->addFnAttr(
llvm::Attribute::getWithVScaleRangeArgs(*llvm_target_->GetContext(), 1, max_val));
}
}
CodeGenCPU::SetTargetAttributes(func);
}
/*!
* \brief Visit and handle AArch64 specific pragmas. To be AArch64 specific,
* the expectation is that they are prepended with "pragma_aarch64".
*/
void CodeGenAArch64::VisitStmt_(const AttrStmtNode* op) {
std::string attr_key = op->attr_key;
if (!tirx::attr::IsPragmaKey(attr_key)) {
CodeGenCPU::VisitStmt_(op);
return;
}
bool is_aarch64_specific_pragma = attr_key.substr(7, 7) == "aarch64";
if (!is_aarch64_specific_pragma) {
CodeGenCPU::VisitStmt_(op);
return;
}
const auto* attr_value = op->value.as<StringImmNode>();
TVM_FFI_ICHECK(attr_value) << "Expect " << attr_key << " to have a ffi::String value but was "
<< op->value->GetTypeKey();
std::string aarch64_attr_key = attr_key.substr(7);
if (aarch64_attr_key == "aarch64_pstate_sm") {
TVM_FFI_ICHECK(!func_has_pstate_sm)
<< "Multiple definitions of " << op->attr_key << " attribute found in the function "
<< function_->getName().data();
function_->addFnAttr({aarch64_attr_key + "_" + attr_value->value});
func_has_pstate_sm = true;
} else if (aarch64_attr_key == "aarch64_pstate_za") {
TVM_FFI_ICHECK(!func_has_pstate_za)
<< "Multiple definitions of " << op->attr_key << " attribute found in the function "
<< function_->getName().data();
function_->addFnAttr({aarch64_attr_key + "_" + attr_value->value});
func_has_pstate_za = true;
} else {
LOG(WARNING) << "Unknown pragma " << op->attr_key;
}
this->VisitStmt(op->body);
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef().def_packed("tvm.codegen.llvm.target_aarch64",
[](const ffi::PackedArgs& targs, ffi::Any* rv) {
*rv = static_cast<void*>(new CodeGenAArch64());
});
}
} // namespace codegen
} // namespace tvm
#endif // TVM_LLVM_VERSION