//===- 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