//===- subzero/src/IceIntrinsics.cpp - Functions related to intrinsics ----===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Implements the Intrinsics utilities for matching and then dispatching /// by name. /// //===----------------------------------------------------------------------===// #include "IceIntrinsics.h" #include "IceCfg.h" #include "IceCfgNode.h" #include "IceInst.h" #include "IceLiveness.h" #include "IceOperand.h" #include "IceStringPool.h" #include <utility> namespace Ice { static_assert(sizeof(Intrinsics::IntrinsicInfo) == 4, "Unexpected sizeof(IntrinsicInfo)"); namespace { #define INTRIN(ID, SE, RT, MW) \ { Intrinsics::ID, Intrinsics::SE, Intrinsics::RT, Intrinsics::MW } // Build list of intrinsics with their attributes and expected prototypes. List // is sorted alphabetically. const struct IceIntrinsicsEntry_ { Intrinsics::FullIntrinsicInfo Info; const char *IntrinsicName; } IceIntrinsicsTable[] = { #define AtomicCmpxchgInit(Overload, NameSuffix) \ { \ { \ INTRIN(AtomicCmpxchg, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), \ {Overload, IceType_i32, Overload, Overload, IceType_i32, IceType_i32}, \ 6 \ } \ , "llvm.nacl.atomic.cmpxchg." NameSuffix \ } AtomicCmpxchgInit(IceType_i8, "i8"), AtomicCmpxchgInit(IceType_i16, "i16"), AtomicCmpxchgInit(IceType_i32, "i32"), AtomicCmpxchgInit(IceType_i64, "i64"), #undef AtomicCmpxchgInit {{INTRIN(AtomicFence, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), {IceType_void, IceType_i32}, 2}, "llvm.nacl.atomic.fence"}, {{INTRIN(AtomicFenceAll, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), {IceType_void}, 1}, "llvm.nacl.atomic.fence.all"}, {{INTRIN(AtomicIsLockFree, SideEffects_F, ReturnsTwice_F, MemoryWrite_F), {IceType_i1, IceType_i32, IceType_i32}, 3}, "llvm.nacl.atomic.is.lock.free"}, #define AtomicLoadInit(Overload, NameSuffix) \ { \ { \ INTRIN(AtomicLoad, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), \ {Overload, IceType_i32, IceType_i32}, 3 \ } \ , "llvm.nacl.atomic.load." NameSuffix \ } AtomicLoadInit(IceType_i8, "i8"), AtomicLoadInit(IceType_i16, "i16"), AtomicLoadInit(IceType_i32, "i32"), AtomicLoadInit(IceType_i64, "i64"), #undef AtomicLoadInit #define AtomicRMWInit(Overload, NameSuffix) \ { \ { \ INTRIN(AtomicRMW, SideEffects_T, ReturnsTwice_F, MemoryWrite_T) \ , {Overload, IceType_i32, IceType_i32, Overload, IceType_i32}, 5 \ } \ , "llvm.nacl.atomic.rmw." NameSuffix \ } AtomicRMWInit(IceType_i8, "i8"), AtomicRMWInit(IceType_i16, "i16"), AtomicRMWInit(IceType_i32, "i32"), AtomicRMWInit(IceType_i64, "i64"), #undef AtomicRMWInit #define AtomicStoreInit(Overload, NameSuffix) \ { \ { \ INTRIN(AtomicStore, SideEffects_T, ReturnsTwice_F, MemoryWrite_T) \ , {IceType_void, Overload, IceType_i32, IceType_i32}, 4 \ } \ , "llvm.nacl.atomic.store." NameSuffix \ } AtomicStoreInit(IceType_i8, "i8"), AtomicStoreInit(IceType_i16, "i16"), AtomicStoreInit(IceType_i32, "i32"), AtomicStoreInit(IceType_i64, "i64"), #undef AtomicStoreInit #define BswapInit(Overload, NameSuffix) \ { \ { \ INTRIN(Bswap, SideEffects_F, ReturnsTwice_F, MemoryWrite_F) \ , {Overload, Overload}, 2 \ } \ , "llvm.bswap." NameSuffix \ } BswapInit(IceType_i16, "i16"), BswapInit(IceType_i32, "i32"), BswapInit(IceType_i64, "i64"), #undef BswapInit #define CtlzInit(Overload, NameSuffix) \ { \ { \ INTRIN(Ctlz, SideEffects_F, ReturnsTwice_F, MemoryWrite_F) \ , {Overload, Overload, IceType_i1}, 3 \ } \ , "llvm.ctlz." NameSuffix \ } CtlzInit(IceType_i32, "i32"), CtlzInit(IceType_i64, "i64"), #undef CtlzInit #define CtpopInit(Overload, NameSuffix) \ { \ { \ INTRIN(Ctpop, SideEffects_F, ReturnsTwice_F, MemoryWrite_F) \ , {Overload, Overload}, 2 \ } \ , "llvm.ctpop." NameSuffix \ } CtpopInit(IceType_i32, "i32"), CtpopInit(IceType_i64, "i64"), #undef CtpopInit #define CttzInit(Overload, NameSuffix) \ { \ { \ INTRIN(Cttz, SideEffects_F, ReturnsTwice_F, MemoryWrite_F) \ , {Overload, Overload, IceType_i1}, 3 \ } \ , "llvm.cttz." NameSuffix \ } CttzInit(IceType_i32, "i32"), CttzInit(IceType_i64, "i64"), #undef CttzInit #define FabsInit(Overload, NameSuffix) \ { \ { \ INTRIN(Fabs, SideEffects_F, ReturnsTwice_F, MemoryWrite_F), \ {Overload, Overload}, 2 \ } \ , "llvm.fabs." NameSuffix \ } FabsInit(IceType_f32, "f32"), FabsInit(IceType_f64, "f64"), FabsInit(IceType_v4f32, "v4f32"), #undef FabsInit {{INTRIN(Longjmp, SideEffects_T, ReturnsTwice_F, MemoryWrite_F), {IceType_void, IceType_i32, IceType_i32}, 3}, "llvm.nacl.longjmp"}, {{INTRIN(Memcpy, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), {IceType_void, IceType_i32, IceType_i32, IceType_i32, IceType_i32, IceType_i1}, 6}, "llvm.memcpy.p0i8.p0i8.i32"}, {{INTRIN(Memmove, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), {IceType_void, IceType_i32, IceType_i32, IceType_i32, IceType_i32, IceType_i1}, 6}, "llvm.memmove.p0i8.p0i8.i32"}, {{INTRIN(Memset, SideEffects_T, ReturnsTwice_F, MemoryWrite_T), {IceType_void, IceType_i32, IceType_i8, IceType_i32, IceType_i32, IceType_i1}, 6}, "llvm.memset.p0i8.i32"}, {{INTRIN(NaClReadTP, SideEffects_F, ReturnsTwice_F, MemoryWrite_F), {IceType_i32}, 1}, "llvm.nacl.read.tp"}, {{INTRIN(Setjmp, SideEffects_T, ReturnsTwice_T, MemoryWrite_T), {IceType_i32, IceType_i32}, 2}, "llvm.nacl.setjmp"}, #define SqrtInit(Overload, NameSuffix) \ { \ { \ INTRIN(Sqrt, SideEffects_F, ReturnsTwice_F, MemoryWrite_F), \ {Overload, Overload}, 2 \ } \ , "llvm.sqrt." NameSuffix \ } SqrtInit(IceType_f32, "f32"), SqrtInit(IceType_f64, "f64"), #undef SqrtInit {{INTRIN(Stacksave, SideEffects_T, ReturnsTwice_F, MemoryWrite_F), {IceType_i32}, 1}, "llvm.stacksave"}, {{INTRIN(Stackrestore, SideEffects_T, ReturnsTwice_F, MemoryWrite_F), {IceType_void, IceType_i32}, 2}, "llvm.stackrestore"}, {{INTRIN(Trap, SideEffects_T, ReturnsTwice_F, MemoryWrite_F), {IceType_void}, 1}, "llvm.trap"}}; const size_t IceIntrinsicsTableSize = llvm::array_lengthof(IceIntrinsicsTable); #undef INTRIN } // end of anonymous namespace Intrinsics::Intrinsics(GlobalContext *Ctx) { for (size_t I = 0; I < IceIntrinsicsTableSize; ++I) { const struct IceIntrinsicsEntry_ &Entry = IceIntrinsicsTable[I]; assert(Entry.Info.NumTypes <= kMaxIntrinsicParameters); Map.insert( std::make_pair(Ctx->getGlobalString(Entry.IntrinsicName), Entry.Info)); } } const Intrinsics::FullIntrinsicInfo *Intrinsics::find(GlobalString Name, bool &Error) const { static constexpr char LLVMPrefix[] = "llvm."; constexpr size_t LLVMPrefixLen = llvm::array_lengthof(LLVMPrefix) - 1; Error = false; if (Name.toString().substr(0, LLVMPrefixLen) != LLVMPrefix) return nullptr; auto Iter = Map.find(Name); if (Iter == Map.end()) { Error = true; return nullptr; } return &Iter->second; } namespace { // Returns whether PNaCl allows the given memory ordering in general. bool isMemoryOrderValidPNaCl(uint64_t Order) { switch (Order) { case Intrinsics::MemoryOrderAcquire: case Intrinsics::MemoryOrderRelease: case Intrinsics::MemoryOrderAcquireRelease: case Intrinsics::MemoryOrderSequentiallyConsistent: return true; default: return false; } } } // end of anonymous namespace bool Intrinsics::isMemoryOrderValid(IntrinsicID ID, uint64_t Order, uint64_t OrderOther) { // Reject orderings not allowed in PNaCl. if (!isMemoryOrderValidPNaCl(Order)) return false; if (ID == AtomicCmpxchg && !isMemoryOrderValidPNaCl(OrderOther)) return false; // Reject orderings not allowed by C++11. switch (ID) { default: llvm_unreachable("isMemoryOrderValid: Unknown IntrinsicID"); return false; case AtomicFence: case AtomicFenceAll: case AtomicRMW: return true; case AtomicCmpxchg: // Reject orderings that are disallowed by C++11 as invalid combinations // for cmpxchg. switch (OrderOther) { case MemoryOrderRelaxed: case MemoryOrderConsume: case MemoryOrderAcquire: case MemoryOrderSequentiallyConsistent: if (OrderOther > Order) return false; if (Order == MemoryOrderRelease && OrderOther != MemoryOrderRelaxed) return false; return true; default: return false; } case AtomicLoad: switch (Order) { case MemoryOrderRelease: case MemoryOrderAcquireRelease: return false; default: return true; } case AtomicStore: switch (Order) { case MemoryOrderConsume: case MemoryOrderAcquire: case MemoryOrderAcquireRelease: return false; default: return true; } } } Intrinsics::ValidateCallValue Intrinsics::FullIntrinsicInfo::validateCall(const InstCall *Call, SizeT &ArgIndex) const { assert(NumTypes >= 1); Variable *Result = Call->getDest(); if (Result == nullptr) { if (getReturnType() != IceType_void) return Intrinsics::BadReturnType; } else if (getReturnType() != Result->getType()) { return Intrinsics::BadReturnType; } if (Call->getNumArgs() != getNumArgs()) { return Intrinsics::WrongNumOfArgs; } for (size_t i = 1; i < NumTypes; ++i) { if (Call->getArg(i - 1)->getType() != Signature[i]) { ArgIndex = i - 1; return Intrinsics::WrongCallArgType; } } return Intrinsics::IsValidCall; } Type Intrinsics::FullIntrinsicInfo::getArgType(SizeT Index) const { assert(NumTypes > 1); assert(Index + 1 < NumTypes); return Signature[Index + 1]; } } // end of namespace Ice