diff options
Diffstat (limited to 'clang/lib/CodeGen/TargetBuiltins/BuiltinHexagon.cpp')
-rw-r--r-- | clang/lib/CodeGen/TargetBuiltins/BuiltinHexagon.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/TargetBuiltins/BuiltinHexagon.cpp b/clang/lib/CodeGen/TargetBuiltins/BuiltinHexagon.cpp new file mode 100644 index 0000000..6204658 --- /dev/null +++ b/clang/lib/CodeGen/TargetBuiltins/BuiltinHexagon.cpp @@ -0,0 +1,263 @@ +//===------ BuiltinHexagon.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/IntrinsicsHexagon.h" + +using namespace clang; +using namespace CodeGen; +using namespace llvm; + +static std::pair<Intrinsic::ID, unsigned> +getIntrinsicForHexagonNonClangBuiltin(unsigned BuiltinID) { + struct Info { + unsigned BuiltinID; + Intrinsic::ID IntrinsicID; + unsigned VecLen; + }; + static Info Infos[] = { +#define CUSTOM_BUILTIN_MAPPING(x,s) \ + { Hexagon::BI__builtin_HEXAGON_##x, Intrinsic::hexagon_##x, s }, + CUSTOM_BUILTIN_MAPPING(L2_loadrub_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrb_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadruh_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrh_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadri_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrd_pci, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrub_pcr, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrb_pcr, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadruh_pcr, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrh_pcr, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadri_pcr, 0) + CUSTOM_BUILTIN_MAPPING(L2_loadrd_pcr, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerb_pci, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerh_pci, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerf_pci, 0) + CUSTOM_BUILTIN_MAPPING(S2_storeri_pci, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerd_pci, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerb_pcr, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerh_pcr, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerf_pcr, 0) + CUSTOM_BUILTIN_MAPPING(S2_storeri_pcr, 0) + CUSTOM_BUILTIN_MAPPING(S2_storerd_pcr, 0) + // Legacy builtins that take a vector in place of a vector predicate. + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstoreq, 64) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorenq, 64) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorentq, 64) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorentnq, 64) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstoreq_128B, 128) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorenq_128B, 128) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorentq_128B, 128) + CUSTOM_BUILTIN_MAPPING(V6_vmaskedstorentnq_128B, 128) +#include "clang/Basic/BuiltinsHexagonMapCustomDep.def" +#undef CUSTOM_BUILTIN_MAPPING + }; + + auto CmpInfo = [] (Info A, Info B) { return A.BuiltinID < B.BuiltinID; }; + static const bool SortOnce = (llvm::sort(Infos, CmpInfo), true); + (void)SortOnce; + + const Info *F = llvm::lower_bound(Infos, Info{BuiltinID, 0, 0}, CmpInfo); + if (F == std::end(Infos) || F->BuiltinID != BuiltinID) + return {Intrinsic::not_intrinsic, 0}; + + return {F->IntrinsicID, F->VecLen}; +} + +Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID, + const CallExpr *E) { + Intrinsic::ID ID; + unsigned VecLen; + std::tie(ID, VecLen) = getIntrinsicForHexagonNonClangBuiltin(BuiltinID); + + auto MakeCircOp = [this, E](unsigned IntID, bool IsLoad) { + // The base pointer is passed by address, so it needs to be loaded. + Address A = EmitPointerWithAlignment(E->getArg(0)); + Address BP = Address(A.emitRawPointer(*this), Int8PtrTy, A.getAlignment()); + llvm::Value *Base = Builder.CreateLoad(BP); + // The treatment of both loads and stores is the same: the arguments for + // the builtin are the same as the arguments for the intrinsic. + // Load: + // builtin(Base, Inc, Mod, Start) -> intr(Base, Inc, Mod, Start) + // builtin(Base, Mod, Start) -> intr(Base, Mod, Start) + // Store: + // builtin(Base, Inc, Mod, Val, Start) -> intr(Base, Inc, Mod, Val, Start) + // builtin(Base, Mod, Val, Start) -> intr(Base, Mod, Val, Start) + SmallVector<llvm::Value*,5> Ops = { Base }; + for (unsigned i = 1, e = E->getNumArgs(); i != e; ++i) + Ops.push_back(EmitScalarExpr(E->getArg(i))); + + llvm::Value *Result = Builder.CreateCall(CGM.getIntrinsic(IntID), Ops); + // The load intrinsics generate two results (Value, NewBase), stores + // generate one (NewBase). The new base address needs to be stored. + llvm::Value *NewBase = IsLoad ? Builder.CreateExtractValue(Result, 1) + : Result; + llvm::Value *LV = EmitScalarExpr(E->getArg(0)); + Address Dest = EmitPointerWithAlignment(E->getArg(0)); + llvm::Value *RetVal = + Builder.CreateAlignedStore(NewBase, LV, Dest.getAlignment()); + if (IsLoad) + RetVal = Builder.CreateExtractValue(Result, 0); + return RetVal; + }; + + // Handle the conversion of bit-reverse load intrinsics to bit code. + // The intrinsic call after this function only reads from memory and the + // write to memory is dealt by the store instruction. + auto MakeBrevLd = [this, E](unsigned IntID, llvm::Type *DestTy) { + // The intrinsic generates one result, which is the new value for the base + // pointer. It needs to be returned. The result of the load instruction is + // passed to intrinsic by address, so the value needs to be stored. + llvm::Value *BaseAddress = EmitScalarExpr(E->getArg(0)); + + // Expressions like &(*pt++) will be incremented per evaluation. + // EmitPointerWithAlignment and EmitScalarExpr evaluates the expression + // per call. + Address DestAddr = EmitPointerWithAlignment(E->getArg(1)); + DestAddr = DestAddr.withElementType(Int8Ty); + llvm::Value *DestAddress = DestAddr.emitRawPointer(*this); + + // Operands are Base, Dest, Modifier. + // The intrinsic format in LLVM IR is defined as + // { ValueType, i8* } (i8*, i32). + llvm::Value *Result = Builder.CreateCall( + CGM.getIntrinsic(IntID), {BaseAddress, EmitScalarExpr(E->getArg(2))}); + + // The value needs to be stored as the variable is passed by reference. + llvm::Value *DestVal = Builder.CreateExtractValue(Result, 0); + + // The store needs to be truncated to fit the destination type. + // While i32 and i64 are natively supported on Hexagon, i8 and i16 needs + // to be handled with stores of respective destination type. + DestVal = Builder.CreateTrunc(DestVal, DestTy); + + Builder.CreateAlignedStore(DestVal, DestAddress, DestAddr.getAlignment()); + // The updated value of the base pointer is returned. + return Builder.CreateExtractValue(Result, 1); + }; + + auto V2Q = [this, VecLen] (llvm::Value *Vec) { + Intrinsic::ID ID = VecLen == 128 ? Intrinsic::hexagon_V6_vandvrt_128B + : Intrinsic::hexagon_V6_vandvrt; + return Builder.CreateCall(CGM.getIntrinsic(ID), + {Vec, Builder.getInt32(-1)}); + }; + auto Q2V = [this, VecLen] (llvm::Value *Pred) { + Intrinsic::ID ID = VecLen == 128 ? Intrinsic::hexagon_V6_vandqrt_128B + : Intrinsic::hexagon_V6_vandqrt; + return Builder.CreateCall(CGM.getIntrinsic(ID), + {Pred, Builder.getInt32(-1)}); + }; + + switch (BuiltinID) { + // These intrinsics return a tuple {Vector, VectorPred} in LLVM IR, + // and the corresponding C/C++ builtins use loads/stores to update + // the predicate. + case Hexagon::BI__builtin_HEXAGON_V6_vaddcarry: + case Hexagon::BI__builtin_HEXAGON_V6_vaddcarry_128B: + case Hexagon::BI__builtin_HEXAGON_V6_vsubcarry: + case Hexagon::BI__builtin_HEXAGON_V6_vsubcarry_128B: { + // Get the type from the 0-th argument. + llvm::Type *VecType = ConvertType(E->getArg(0)->getType()); + Address PredAddr = + EmitPointerWithAlignment(E->getArg(2)).withElementType(VecType); + llvm::Value *PredIn = V2Q(Builder.CreateLoad(PredAddr)); + llvm::Value *Result = Builder.CreateCall(CGM.getIntrinsic(ID), + {EmitScalarExpr(E->getArg(0)), EmitScalarExpr(E->getArg(1)), PredIn}); + + llvm::Value *PredOut = Builder.CreateExtractValue(Result, 1); + Builder.CreateAlignedStore(Q2V(PredOut), PredAddr.emitRawPointer(*this), + PredAddr.getAlignment()); + return Builder.CreateExtractValue(Result, 0); + } + // These are identical to the builtins above, except they don't consume + // input carry, only generate carry-out. Since they still produce two + // outputs, generate the store of the predicate, but no load. + case Hexagon::BI__builtin_HEXAGON_V6_vaddcarryo: + case Hexagon::BI__builtin_HEXAGON_V6_vaddcarryo_128B: + case Hexagon::BI__builtin_HEXAGON_V6_vsubcarryo: + case Hexagon::BI__builtin_HEXAGON_V6_vsubcarryo_128B: { + // Get the type from the 0-th argument. + llvm::Type *VecType = ConvertType(E->getArg(0)->getType()); + Address PredAddr = + EmitPointerWithAlignment(E->getArg(2)).withElementType(VecType); + llvm::Value *Result = Builder.CreateCall(CGM.getIntrinsic(ID), + {EmitScalarExpr(E->getArg(0)), EmitScalarExpr(E->getArg(1))}); + + llvm::Value *PredOut = Builder.CreateExtractValue(Result, 1); + Builder.CreateAlignedStore(Q2V(PredOut), PredAddr.emitRawPointer(*this), + PredAddr.getAlignment()); + return Builder.CreateExtractValue(Result, 0); + } + + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstoreq: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorenq: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorentq: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorentnq: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstoreq_128B: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorenq_128B: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorentq_128B: + case Hexagon::BI__builtin_HEXAGON_V6_vmaskedstorentnq_128B: { + SmallVector<llvm::Value*,4> Ops; + const Expr *PredOp = E->getArg(0); + // There will be an implicit cast to a boolean vector. Strip it. + if (auto *Cast = dyn_cast<ImplicitCastExpr>(PredOp)) { + if (Cast->getCastKind() == CK_BitCast) + PredOp = Cast->getSubExpr(); + Ops.push_back(V2Q(EmitScalarExpr(PredOp))); + } + for (int i = 1, e = E->getNumArgs(); i != e; ++i) + Ops.push_back(EmitScalarExpr(E->getArg(i))); + return Builder.CreateCall(CGM.getIntrinsic(ID), Ops); + } + + case Hexagon::BI__builtin_HEXAGON_L2_loadrub_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadrb_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadruh_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadrh_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadri_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadrd_pci: + case Hexagon::BI__builtin_HEXAGON_L2_loadrub_pcr: + case Hexagon::BI__builtin_HEXAGON_L2_loadrb_pcr: + case Hexagon::BI__builtin_HEXAGON_L2_loadruh_pcr: + case Hexagon::BI__builtin_HEXAGON_L2_loadrh_pcr: + case Hexagon::BI__builtin_HEXAGON_L2_loadri_pcr: + case Hexagon::BI__builtin_HEXAGON_L2_loadrd_pcr: + return MakeCircOp(ID, /*IsLoad=*/true); + case Hexagon::BI__builtin_HEXAGON_S2_storerb_pci: + case Hexagon::BI__builtin_HEXAGON_S2_storerh_pci: + case Hexagon::BI__builtin_HEXAGON_S2_storerf_pci: + case Hexagon::BI__builtin_HEXAGON_S2_storeri_pci: + case Hexagon::BI__builtin_HEXAGON_S2_storerd_pci: + case Hexagon::BI__builtin_HEXAGON_S2_storerb_pcr: + case Hexagon::BI__builtin_HEXAGON_S2_storerh_pcr: + case Hexagon::BI__builtin_HEXAGON_S2_storerf_pcr: + case Hexagon::BI__builtin_HEXAGON_S2_storeri_pcr: + case Hexagon::BI__builtin_HEXAGON_S2_storerd_pcr: + return MakeCircOp(ID, /*IsLoad=*/false); + case Hexagon::BI__builtin_brev_ldub: + return MakeBrevLd(Intrinsic::hexagon_L2_loadrub_pbr, Int8Ty); + case Hexagon::BI__builtin_brev_ldb: + return MakeBrevLd(Intrinsic::hexagon_L2_loadrb_pbr, Int8Ty); + case Hexagon::BI__builtin_brev_lduh: + return MakeBrevLd(Intrinsic::hexagon_L2_loadruh_pbr, Int16Ty); + case Hexagon::BI__builtin_brev_ldh: + return MakeBrevLd(Intrinsic::hexagon_L2_loadrh_pbr, Int16Ty); + case Hexagon::BI__builtin_brev_ldw: + return MakeBrevLd(Intrinsic::hexagon_L2_loadri_pbr, Int32Ty); + case Hexagon::BI__builtin_brev_ldd: + return MakeBrevLd(Intrinsic::hexagon_L2_loadrd_pbr, Int64Ty); + } // switch + + return nullptr; +} |