aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp')
-rw-r--r--clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp436
1 files changed, 436 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp
new file mode 100644
index 0000000..a6f49e9
--- /dev/null
+++ b/clang/lib/CodeGen/TargetBuiltins/BuiltinRISCV.cpp
@@ -0,0 +1,436 @@
+//===-------- BuiltinRISCV.cpp - Emit LLVM Code for builtins --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code to emit Builtin calls as LLVM code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenFunction.h"
+#include "clang/Basic/TargetBuiltins.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
+#include "llvm/TargetParser/RISCVISAInfo.h"
+#include "llvm/TargetParser/RISCVTargetParser.h"
+
+using namespace clang;
+using namespace CodeGen;
+using namespace llvm;
+
+Value *CodeGenFunction::EmitRISCVCpuInit() {
+ llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, {VoidPtrTy}, false);
+ llvm::FunctionCallee Func =
+ CGM.CreateRuntimeFunction(FTy, "__init_riscv_feature_bits");
+ auto *CalleeGV = cast<llvm::GlobalValue>(Func.getCallee());
+ CalleeGV->setDSOLocal(true);
+ CalleeGV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
+ return Builder.CreateCall(Func, {llvm::ConstantPointerNull::get(VoidPtrTy)});
+}
+
+Value *CodeGenFunction::EmitRISCVCpuSupports(const CallExpr *E) {
+
+ const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts();
+ StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString();
+ if (!getContext().getTargetInfo().validateCpuSupports(FeatureStr))
+ return Builder.getFalse();
+
+ return EmitRISCVCpuSupports(ArrayRef<StringRef>(FeatureStr));
+}
+
+static Value *loadRISCVFeatureBits(unsigned Index, CGBuilderTy &Builder,
+ CodeGenModule &CGM) {
+ llvm::Type *Int32Ty = Builder.getInt32Ty();
+ llvm::Type *Int64Ty = Builder.getInt64Ty();
+ llvm::ArrayType *ArrayOfInt64Ty =
+ llvm::ArrayType::get(Int64Ty, llvm::RISCVISAInfo::FeatureBitSize);
+ llvm::Type *StructTy = llvm::StructType::get(Int32Ty, ArrayOfInt64Ty);
+ llvm::Constant *RISCVFeaturesBits =
+ CGM.CreateRuntimeVariable(StructTy, "__riscv_feature_bits");
+ cast<llvm::GlobalValue>(RISCVFeaturesBits)->setDSOLocal(true);
+ Value *IndexVal = llvm::ConstantInt::get(Int32Ty, Index);
+ llvm::Value *GEPIndices[] = {Builder.getInt32(0), Builder.getInt32(1),
+ IndexVal};
+ Value *Ptr =
+ Builder.CreateInBoundsGEP(StructTy, RISCVFeaturesBits, GEPIndices);
+ Value *FeaturesBit =
+ Builder.CreateAlignedLoad(Int64Ty, Ptr, CharUnits::fromQuantity(8));
+ return FeaturesBit;
+}
+
+Value *CodeGenFunction::EmitRISCVCpuSupports(ArrayRef<StringRef> FeaturesStrs) {
+ const unsigned RISCVFeatureLength = llvm::RISCVISAInfo::FeatureBitSize;
+ uint64_t RequireBitMasks[RISCVFeatureLength] = {0};
+
+ for (auto Feat : FeaturesStrs) {
+ auto [GroupID, BitPos] = RISCVISAInfo::getRISCVFeaturesBitsInfo(Feat);
+
+ // If there isn't BitPos for this feature, skip this version.
+ // It also report the warning to user during compilation.
+ if (BitPos == -1)
+ return Builder.getFalse();
+
+ RequireBitMasks[GroupID] |= (1ULL << BitPos);
+ }
+
+ Value *Result = nullptr;
+ for (unsigned Idx = 0; Idx < RISCVFeatureLength; Idx++) {
+ if (RequireBitMasks[Idx] == 0)
+ continue;
+
+ Value *Mask = Builder.getInt64(RequireBitMasks[Idx]);
+ Value *Bitset =
+ Builder.CreateAnd(loadRISCVFeatureBits(Idx, Builder, CGM), Mask);
+ Value *CmpV = Builder.CreateICmpEQ(Bitset, Mask);
+ Result = (!Result) ? CmpV : Builder.CreateAnd(Result, CmpV);
+ }
+
+ assert(Result && "Should have value here.");
+
+ return Result;
+}
+
+Value *CodeGenFunction::EmitRISCVCpuIs(const CallExpr *E) {
+ const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
+ StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
+ return EmitRISCVCpuIs(CPUStr);
+}
+
+Value *CodeGenFunction::EmitRISCVCpuIs(StringRef CPUStr) {
+ llvm::Type *Int32Ty = Builder.getInt32Ty();
+ llvm::Type *Int64Ty = Builder.getInt64Ty();
+ llvm::StructType *StructTy = llvm::StructType::get(Int32Ty, Int64Ty, Int64Ty);
+ llvm::Constant *RISCVCPUModel =
+ CGM.CreateRuntimeVariable(StructTy, "__riscv_cpu_model");
+ cast<llvm::GlobalValue>(RISCVCPUModel)->setDSOLocal(true);
+
+ auto loadRISCVCPUID = [&](unsigned Index) {
+ Value *Ptr = Builder.CreateStructGEP(StructTy, RISCVCPUModel, Index);
+ Value *CPUID = Builder.CreateAlignedLoad(StructTy->getTypeAtIndex(Index),
+ Ptr, llvm::MaybeAlign());
+ return CPUID;
+ };
+
+ const llvm::RISCV::CPUModel Model = llvm::RISCV::getCPUModel(CPUStr);
+
+ // Compare mvendorid.
+ Value *VendorID = loadRISCVCPUID(0);
+ Value *Result =
+ Builder.CreateICmpEQ(VendorID, Builder.getInt32(Model.MVendorID));
+
+ // Compare marchid.
+ Value *ArchID = loadRISCVCPUID(1);
+ Result = Builder.CreateAnd(
+ Result, Builder.CreateICmpEQ(ArchID, Builder.getInt64(Model.MArchID)));
+
+ // Compare mimpid.
+ Value *ImpID = loadRISCVCPUID(2);
+ Result = Builder.CreateAnd(
+ Result, Builder.CreateICmpEQ(ImpID, Builder.getInt64(Model.MImpID)));
+
+ return Result;
+}
+
+Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
+ const CallExpr *E,
+ ReturnValueSlot ReturnValue) {
+
+ if (BuiltinID == Builtin::BI__builtin_cpu_supports)
+ return EmitRISCVCpuSupports(E);
+ if (BuiltinID == Builtin::BI__builtin_cpu_init)
+ return EmitRISCVCpuInit();
+ if (BuiltinID == Builtin::BI__builtin_cpu_is)
+ return EmitRISCVCpuIs(E);
+
+ SmallVector<Value *, 4> Ops;
+ llvm::Type *ResultType = ConvertType(E->getType());
+
+ // Find out if any arguments are required to be integer constant expressions.
+ unsigned ICEArguments = 0;
+ ASTContext::GetBuiltinTypeError Error;
+ getContext().GetBuiltinType(BuiltinID, Error, &ICEArguments);
+ if (Error == ASTContext::GE_Missing_type) {
+ // Vector intrinsics don't have a type string.
+ assert(BuiltinID >= clang::RISCV::FirstRVVBuiltin &&
+ BuiltinID <= clang::RISCV::LastRVVBuiltin);
+ ICEArguments = 0;
+ if (BuiltinID == RISCVVector::BI__builtin_rvv_vget_v ||
+ BuiltinID == RISCVVector::BI__builtin_rvv_vset_v)
+ ICEArguments = 1 << 1;
+ } else {
+ assert(Error == ASTContext::GE_None && "Unexpected error");
+ }
+
+ if (BuiltinID == RISCV::BI__builtin_riscv_ntl_load)
+ ICEArguments |= (1 << 1);
+ if (BuiltinID == RISCV::BI__builtin_riscv_ntl_store)
+ ICEArguments |= (1 << 2);
+
+ for (unsigned i = 0, e = E->getNumArgs(); i != e; i++) {
+ // Handle aggregate argument, namely RVV tuple types in segment load/store
+ if (hasAggregateEvaluationKind(E->getArg(i)->getType())) {
+ LValue L = EmitAggExprToLValue(E->getArg(i));
+ llvm::Value *AggValue = Builder.CreateLoad(L.getAddress());
+ Ops.push_back(AggValue);
+ continue;
+ }
+ Ops.push_back(EmitScalarOrConstFoldImmArg(ICEArguments, i, E));
+ }
+
+ Intrinsic::ID ID = Intrinsic::not_intrinsic;
+ // The 0th bit simulates the `vta` of RVV
+ // The 1st bit simulates the `vma` of RVV
+ constexpr unsigned RVV_VTA = 0x1;
+ constexpr unsigned RVV_VMA = 0x2;
+ int PolicyAttrs = 0;
+ bool IsMasked = false;
+ // This is used by segment load/store to determine it's llvm type.
+ unsigned SegInstSEW = 8;
+
+ // Required for overloaded intrinsics.
+ llvm::SmallVector<llvm::Type *, 2> IntrinsicTypes;
+ switch (BuiltinID) {
+ default: llvm_unreachable("unexpected builtin ID");
+ case RISCV::BI__builtin_riscv_orc_b_32:
+ case RISCV::BI__builtin_riscv_orc_b_64:
+ case RISCV::BI__builtin_riscv_clmul_32:
+ case RISCV::BI__builtin_riscv_clmul_64:
+ case RISCV::BI__builtin_riscv_clmulh_32:
+ case RISCV::BI__builtin_riscv_clmulh_64:
+ case RISCV::BI__builtin_riscv_clmulr_32:
+ case RISCV::BI__builtin_riscv_clmulr_64:
+ case RISCV::BI__builtin_riscv_xperm4_32:
+ case RISCV::BI__builtin_riscv_xperm4_64:
+ case RISCV::BI__builtin_riscv_xperm8_32:
+ case RISCV::BI__builtin_riscv_xperm8_64:
+ case RISCV::BI__builtin_riscv_brev8_32:
+ case RISCV::BI__builtin_riscv_brev8_64:
+ case RISCV::BI__builtin_riscv_zip_32:
+ case RISCV::BI__builtin_riscv_unzip_32: {
+ switch (BuiltinID) {
+ default: llvm_unreachable("unexpected builtin ID");
+ // Zbb
+ case RISCV::BI__builtin_riscv_orc_b_32:
+ case RISCV::BI__builtin_riscv_orc_b_64:
+ ID = Intrinsic::riscv_orc_b;
+ break;
+
+ // Zbc
+ case RISCV::BI__builtin_riscv_clmul_32:
+ case RISCV::BI__builtin_riscv_clmul_64:
+ ID = Intrinsic::riscv_clmul;
+ break;
+ case RISCV::BI__builtin_riscv_clmulh_32:
+ case RISCV::BI__builtin_riscv_clmulh_64:
+ ID = Intrinsic::riscv_clmulh;
+ break;
+ case RISCV::BI__builtin_riscv_clmulr_32:
+ case RISCV::BI__builtin_riscv_clmulr_64:
+ ID = Intrinsic::riscv_clmulr;
+ break;
+
+ // Zbkx
+ case RISCV::BI__builtin_riscv_xperm8_32:
+ case RISCV::BI__builtin_riscv_xperm8_64:
+ ID = Intrinsic::riscv_xperm8;
+ break;
+ case RISCV::BI__builtin_riscv_xperm4_32:
+ case RISCV::BI__builtin_riscv_xperm4_64:
+ ID = Intrinsic::riscv_xperm4;
+ break;
+
+ // Zbkb
+ case RISCV::BI__builtin_riscv_brev8_32:
+ case RISCV::BI__builtin_riscv_brev8_64:
+ ID = Intrinsic::riscv_brev8;
+ break;
+ case RISCV::BI__builtin_riscv_zip_32:
+ ID = Intrinsic::riscv_zip;
+ break;
+ case RISCV::BI__builtin_riscv_unzip_32:
+ ID = Intrinsic::riscv_unzip;
+ break;
+ }
+
+ IntrinsicTypes = {ResultType};
+ break;
+ }
+
+ // Zk builtins
+
+ // Zknh
+ case RISCV::BI__builtin_riscv_sha256sig0:
+ ID = Intrinsic::riscv_sha256sig0;
+ break;
+ case RISCV::BI__builtin_riscv_sha256sig1:
+ ID = Intrinsic::riscv_sha256sig1;
+ break;
+ case RISCV::BI__builtin_riscv_sha256sum0:
+ ID = Intrinsic::riscv_sha256sum0;
+ break;
+ case RISCV::BI__builtin_riscv_sha256sum1:
+ ID = Intrinsic::riscv_sha256sum1;
+ break;
+
+ // Zksed
+ case RISCV::BI__builtin_riscv_sm4ks:
+ ID = Intrinsic::riscv_sm4ks;
+ break;
+ case RISCV::BI__builtin_riscv_sm4ed:
+ ID = Intrinsic::riscv_sm4ed;
+ break;
+
+ // Zksh
+ case RISCV::BI__builtin_riscv_sm3p0:
+ ID = Intrinsic::riscv_sm3p0;
+ break;
+ case RISCV::BI__builtin_riscv_sm3p1:
+ ID = Intrinsic::riscv_sm3p1;
+ break;
+
+ case RISCV::BI__builtin_riscv_clz_32:
+ case RISCV::BI__builtin_riscv_clz_64: {
+ Function *F = CGM.getIntrinsic(Intrinsic::ctlz, Ops[0]->getType());
+ Value *Result = Builder.CreateCall(F, {Ops[0], Builder.getInt1(false)});
+ if (Result->getType() != ResultType)
+ Result =
+ Builder.CreateIntCast(Result, ResultType, /*isSigned*/ false, "cast");
+ return Result;
+ }
+ case RISCV::BI__builtin_riscv_ctz_32:
+ case RISCV::BI__builtin_riscv_ctz_64: {
+ Function *F = CGM.getIntrinsic(Intrinsic::cttz, Ops[0]->getType());
+ Value *Result = Builder.CreateCall(F, {Ops[0], Builder.getInt1(false)});
+ if (Result->getType() != ResultType)
+ Result =
+ Builder.CreateIntCast(Result, ResultType, /*isSigned*/ false, "cast");
+ return Result;
+ }
+
+ // Zihintntl
+ case RISCV::BI__builtin_riscv_ntl_load: {
+ llvm::Type *ResTy = ConvertType(E->getType());
+ unsigned DomainVal = 5; // Default __RISCV_NTLH_ALL
+ if (Ops.size() == 2)
+ DomainVal = cast<ConstantInt>(Ops[1])->getZExtValue();
+
+ llvm::MDNode *RISCVDomainNode = llvm::MDNode::get(
+ getLLVMContext(),
+ llvm::ConstantAsMetadata::get(Builder.getInt32(DomainVal)));
+ llvm::MDNode *NontemporalNode = llvm::MDNode::get(
+ getLLVMContext(), llvm::ConstantAsMetadata::get(Builder.getInt32(1)));
+
+ int Width;
+ if(ResTy->isScalableTy()) {
+ const ScalableVectorType *SVTy = cast<ScalableVectorType>(ResTy);
+ llvm::Type *ScalarTy = ResTy->getScalarType();
+ Width = ScalarTy->getPrimitiveSizeInBits() *
+ SVTy->getElementCount().getKnownMinValue();
+ } else
+ Width = ResTy->getPrimitiveSizeInBits();
+ LoadInst *Load = Builder.CreateLoad(
+ Address(Ops[0], ResTy, CharUnits::fromQuantity(Width / 8)));
+
+ Load->setMetadata(llvm::LLVMContext::MD_nontemporal, NontemporalNode);
+ Load->setMetadata(CGM.getModule().getMDKindID("riscv-nontemporal-domain"),
+ RISCVDomainNode);
+
+ return Load;
+ }
+ case RISCV::BI__builtin_riscv_ntl_store: {
+ unsigned DomainVal = 5; // Default __RISCV_NTLH_ALL
+ if (Ops.size() == 3)
+ DomainVal = cast<ConstantInt>(Ops[2])->getZExtValue();
+
+ llvm::MDNode *RISCVDomainNode = llvm::MDNode::get(
+ getLLVMContext(),
+ llvm::ConstantAsMetadata::get(Builder.getInt32(DomainVal)));
+ llvm::MDNode *NontemporalNode = llvm::MDNode::get(
+ getLLVMContext(), llvm::ConstantAsMetadata::get(Builder.getInt32(1)));
+
+ StoreInst *Store = Builder.CreateDefaultAlignedStore(Ops[1], Ops[0]);
+ Store->setMetadata(llvm::LLVMContext::MD_nontemporal, NontemporalNode);
+ Store->setMetadata(CGM.getModule().getMDKindID("riscv-nontemporal-domain"),
+ RISCVDomainNode);
+
+ return Store;
+ }
+ // Zihintpause
+ case RISCV::BI__builtin_riscv_pause: {
+ llvm::Function *Fn = CGM.getIntrinsic(llvm::Intrinsic::riscv_pause);
+ return Builder.CreateCall(Fn, {});
+ }
+
+ // XCValu
+ case RISCV::BI__builtin_riscv_cv_alu_addN:
+ ID = Intrinsic::riscv_cv_alu_addN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_addRN:
+ ID = Intrinsic::riscv_cv_alu_addRN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_adduN:
+ ID = Intrinsic::riscv_cv_alu_adduN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_adduRN:
+ ID = Intrinsic::riscv_cv_alu_adduRN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_clip:
+ ID = Intrinsic::riscv_cv_alu_clip;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_clipu:
+ ID = Intrinsic::riscv_cv_alu_clipu;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_extbs:
+ return Builder.CreateSExt(Builder.CreateTrunc(Ops[0], Int8Ty), Int32Ty,
+ "extbs");
+ case RISCV::BI__builtin_riscv_cv_alu_extbz:
+ return Builder.CreateZExt(Builder.CreateTrunc(Ops[0], Int8Ty), Int32Ty,
+ "extbz");
+ case RISCV::BI__builtin_riscv_cv_alu_exths:
+ return Builder.CreateSExt(Builder.CreateTrunc(Ops[0], Int16Ty), Int32Ty,
+ "exths");
+ case RISCV::BI__builtin_riscv_cv_alu_exthz:
+ return Builder.CreateZExt(Builder.CreateTrunc(Ops[0], Int16Ty), Int32Ty,
+ "exthz");
+ case RISCV::BI__builtin_riscv_cv_alu_sle:
+ return Builder.CreateZExt(Builder.CreateICmpSLE(Ops[0], Ops[1]), Int32Ty,
+ "sle");
+ case RISCV::BI__builtin_riscv_cv_alu_sleu:
+ return Builder.CreateZExt(Builder.CreateICmpULE(Ops[0], Ops[1]), Int32Ty,
+ "sleu");
+ case RISCV::BI__builtin_riscv_cv_alu_subN:
+ ID = Intrinsic::riscv_cv_alu_subN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_subRN:
+ ID = Intrinsic::riscv_cv_alu_subRN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_subuN:
+ ID = Intrinsic::riscv_cv_alu_subuN;
+ break;
+ case RISCV::BI__builtin_riscv_cv_alu_subuRN:
+ ID = Intrinsic::riscv_cv_alu_subuRN;
+ break;
+
+ // XAndesBFHCvt
+ case RISCV::BI__builtin_riscv_nds_fcvt_s_bf16:
+ return Builder.CreateFPExt(Ops[0], FloatTy);
+ case RISCV::BI__builtin_riscv_nds_fcvt_bf16_s:
+ return Builder.CreateFPTrunc(Ops[0], BFloatTy);
+
+ // Vector builtins are handled from here.
+#include "clang/Basic/riscv_vector_builtin_cg.inc"
+
+ // SiFive Vector builtins are handled from here.
+#include "clang/Basic/riscv_sifive_vector_builtin_cg.inc"
+
+ // Andes Vector builtins are handled from here.
+#include "clang/Basic/riscv_andes_vector_builtin_cg.inc"
+ }
+
+ assert(ID != Intrinsic::not_intrinsic);
+
+ llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
+ return Builder.CreateCall(F, Ops, "");
+}