bridge: support atomic intrinsics
Add support of some __atomic functions, expand them to LLVM
atomic instructions.
Change-Id: I6276b5ad033f5c13597e34ca4eaadbe20986d763
Reviewed-on: https://go-review.googlesource.com/c/gollvm/+/176979
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/bridge/go-llvm-builtins.cpp b/bridge/go-llvm-builtins.cpp
index 1fb64f3..3563941 100644
--- a/bridge/go-llvm-builtins.cpp
+++ b/bridge/go-llvm-builtins.cpp
@@ -319,8 +319,9 @@
registerIntrinsicBuiltin(name, libname, iid, overloadTypes);
}
-static llvm::Value *builtinExtractReturnAddrMaker(llvm::SmallVector<llvm::Value*, 16> args,
- BinstructionsLIRBuilder *builder)
+static llvm::Value *builtinExtractReturnAddrMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
{
// __builtin_extract_return_addr(uintptr) uintptr
// extracts the actual encoded address from the address as returned
@@ -333,22 +334,189 @@
return args[0];
}
-static llvm::Value *builtinUnreachableMaker(llvm::SmallVector<llvm::Value*, 16> args,
- BinstructionsLIRBuilder *builder)
+static llvm::Value *builtinUnreachableMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
{
llvm::UnreachableInst *unr = builder->CreateUnreachable();
return unr;
}
+static llvm::AtomicOrdering llvmOrder(int o)
+{
+ switch (o) {
+ case __ATOMIC_RELAXED:
+ return llvm::AtomicOrdering::Monotonic;
+ case __ATOMIC_CONSUME:
+ return llvm::AtomicOrdering::Acquire; // LLVM does not define consume
+ case __ATOMIC_ACQUIRE:
+ return llvm::AtomicOrdering::Acquire;
+ case __ATOMIC_RELEASE:
+ return llvm::AtomicOrdering::Release;
+ case __ATOMIC_ACQ_REL:
+ return llvm::AtomicOrdering::AcquireRelease;
+ case __ATOMIC_SEQ_CST:
+ return llvm::AtomicOrdering::SequentiallyConsistent;
+ }
+ llvm_unreachable("unknown atomic order");
+}
+
+static llvm::Value *atomicLoadMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm, int sz)
+{
+ assert(args.size() == 2);
+ llvm::Type *t = sz == 8 ? tm->llvmInt64Type() : tm->llvmInt32Type();
+ llvm::LoadInst *load = builder->CreateLoad(t, args[0]);
+ // FIXME: we assume the FE always emits constant memory order.
+ // in case it doesn't, conservatively use SequentiallyConsistent.
+ llvm::AtomicOrdering o =
+ llvm::isa<llvm::ConstantInt>(args[1]) ?
+ llvmOrder(llvm::cast<llvm::ConstantInt>(args[1])->getZExtValue()) :
+ llvm::AtomicOrdering::SequentiallyConsistent;
+ load->setAtomic(o);
+ load->setAlignment(sz);
+ return load;
+}
+
+static llvm::Value *atomicLoad4Maker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ return atomicLoadMaker(args, builder, tm, 4);
+}
+
+static llvm::Value *atomicLoad8Maker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ return atomicLoadMaker(args, builder, tm, 8);
+}
+
+static llvm::Value *atomicStoreMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm, int sz)
+{
+ assert(args.size() == 3);
+ llvm::StoreInst *store = builder->CreateStore(args[1], args[0]);
+ // FIXME: see atomicLoadMaker.
+ llvm::AtomicOrdering o =
+ llvm::isa<llvm::ConstantInt>(args[2]) ?
+ llvmOrder(llvm::cast<llvm::ConstantInt>(args[2])->getZExtValue()) :
+ llvm::AtomicOrdering::SequentiallyConsistent;
+ store->setAtomic(o);
+ store->setAlignment(sz);
+ return store;
+}
+
+static llvm::Value *atomicStore4Maker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ return atomicStoreMaker(args, builder, tm, 4);
+}
+
+static llvm::Value *atomicStore8Maker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ return atomicStoreMaker(args, builder, tm, 8);
+}
+
+static llvm::Value *atomicRMWMaker(llvm::AtomicRMWInst::BinOp op,
+ llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ assert(args.size() == 3);
+ // FIXME: see atomicLoadMaker.
+ llvm::AtomicOrdering o =
+ llvm::isa<llvm::ConstantInt>(args[2]) ?
+ llvmOrder(llvm::cast<llvm::ConstantInt>(args[2])->getZExtValue()) :
+ llvm::AtomicOrdering::SequentiallyConsistent;
+ return builder->CreateAtomicRMW(op, args[0], args[1], o);
+}
+
+static llvm::Value *atomicXchgMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ return atomicRMWMaker(llvm::AtomicRMWInst::Xchg, args, builder, tm);
+}
+
+static llvm::Value *atomicAddMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ // atomicrmw returns the old content. We need to do the add.
+ llvm::Value* old = atomicRMWMaker(llvm::AtomicRMWInst::Add, args, builder, tm);
+ return builder->CreateAdd(old, args[1]);
+}
+
+static llvm::Value *atomicAndMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ // atomicrmw returns the old content. We need to do the and.
+ llvm::Value* old = atomicRMWMaker(llvm::AtomicRMWInst::And, args, builder, tm);
+ return builder->CreateAnd(old, args[1]);
+}
+
+static llvm::Value *atomicOrMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ // atomicrmw returns the old content. We need to do the or.
+ llvm::Value* old = atomicRMWMaker(llvm::AtomicRMWInst::Or, args, builder, tm);
+ return builder->CreateOr(old, args[1]);
+}
+
+static llvm::Value *atomicCasMaker(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm)
+{
+ assert(args.size() == 6);
+ // GCC __atomic_compare_exchange_n takes a pointer to the old value.
+ // We need to load it.
+ llvm::Value *old = builder->CreateLoad(args[1]);
+ // FIXME: see atomicLoadMaker, but default to SequentiallyConsistent
+ // for success order, Monotonic (i.e. relaxed) for failed order,
+ // and false for weak.
+ llvm::AtomicOrdering o =
+ llvm::isa<llvm::ConstantInt>(args[4]) ?
+ llvmOrder(llvm::cast<llvm::ConstantInt>(args[4])->getZExtValue()) :
+ llvm::AtomicOrdering::SequentiallyConsistent;
+ llvm::AtomicOrdering o2 =
+ llvm::isa<llvm::ConstantInt>(args[5]) ?
+ llvmOrder(llvm::cast<llvm::ConstantInt>(args[5])->getZExtValue()) :
+ llvm::AtomicOrdering::Monotonic;
+ bool weak =
+ llvm::isa<llvm::ConstantInt>(args[3]) &&
+ !llvm::cast<llvm::ConstantInt>(args[3])->isZero();
+ llvm::AtomicCmpXchgInst *cas =
+ builder->CreateAtomicCmpXchg(args[0], old, args[2], o, o2);
+ cas->setWeak(weak);
+ // LLVM cmpxchg instruction returns { valType, i1 }. Extract the second
+ // value, and cast to Go bool type (i8).
+ llvm::Value *ret = builder->CreateExtractValue(cas, {1});
+ return builder->CreateZExt(ret, tm->llvmInt8Type());
+}
+
void BuiltinTable::defineExprBuiltins()
{
+ Btype *boolType = tman_->boolType();
+ Btype *int32Type = tman_->integerType(false, 32);
+ Btype *uint8Type = tman_->integerType(true, 8);
+ Btype *uint8PtrType = tman_->pointerType(uint8Type);
+ Btype *uint32Type = tman_->integerType(true, 32);
+ Btype *uint32PtrType = tman_->pointerType(uint32Type);
+ Btype *uint64Type = tman_->integerType(true, 64);
+ Btype *uint64PtrType = tman_->pointerType(uint64Type);
unsigned bitsInPtr = tman_->datalayout()->getPointerSizeInBits();
Btype *uintPtrType = tman_->integerType(true, bitsInPtr);
{
- BuiltinEntryTypeVec typeVec(2);
- typeVec[0] = uintPtrType;
- typeVec[1] = uintPtrType;
+ BuiltinEntryTypeVec typeVec = {uintPtrType, uintPtrType};
registerExprBuiltin("__builtin_extract_return_addr", nullptr,
typeVec, builtinExtractReturnAddrMaker);
}
@@ -358,4 +526,62 @@
registerExprBuiltin("__builtin_unreachable", nullptr,
typeVec, builtinUnreachableMaker);
}
+
+ {
+ BuiltinEntryTypeVec typeVec = {uint32Type, uint32PtrType, int32Type};
+ registerExprBuiltin("__atomic_load_4", nullptr,
+ typeVec, atomicLoad4Maker);
+ }
+ {
+ BuiltinEntryTypeVec typeVec = {uint64Type, uint64PtrType, int32Type};
+ registerExprBuiltin("__atomic_load_8", nullptr,
+ typeVec, atomicLoad8Maker);
+ }
+
+ {
+ BuiltinEntryTypeVec typeVec = {nullptr, uint32PtrType, uint32Type, int32Type};
+ registerExprBuiltin("__atomic_store_4", nullptr,
+ typeVec, atomicStore4Maker);
+ }
+ {
+ BuiltinEntryTypeVec typeVec = {nullptr, uint64PtrType, uint64Type, int32Type};
+ registerExprBuiltin("__atomic_store_8", nullptr,
+ typeVec, atomicStore8Maker);
+ }
+
+ {
+ BuiltinEntryTypeVec typeVec = {uint32Type, uint32PtrType, uint32Type, int32Type};
+ registerExprBuiltin("__atomic_exchange_4", nullptr,
+ typeVec, atomicXchgMaker);
+ registerExprBuiltin("__atomic_add_fetch_4", nullptr,
+ typeVec, atomicAddMaker);
+ }
+ {
+ BuiltinEntryTypeVec typeVec = {uint64Type, uint64PtrType, uint64Type, int32Type};
+ registerExprBuiltin("__atomic_exchange_8", nullptr,
+ typeVec, atomicXchgMaker);
+ registerExprBuiltin("__atomic_add_fetch_8", nullptr,
+ typeVec, atomicAddMaker);
+ }
+
+ {
+ BuiltinEntryTypeVec typeVec = {uint8Type, uint8PtrType, uint8Type, int32Type};
+ registerExprBuiltin("__atomic_and_fetch_1", nullptr,
+ typeVec, atomicAndMaker);
+ registerExprBuiltin("__atomic_or_fetch_1", nullptr,
+ typeVec, atomicOrMaker);
+ }
+
+ {
+ BuiltinEntryTypeVec typeVec = {boolType, uint32PtrType, uint32PtrType, uint32Type,
+ boolType, int32Type, int32Type};
+ registerExprBuiltin("__atomic_compare_exchange_4", nullptr,
+ typeVec, atomicCasMaker);
+ }
+ {
+ BuiltinEntryTypeVec typeVec = {boolType, uint64PtrType, uint64PtrType, uint64Type,
+ boolType, int32Type, int32Type};
+ registerExprBuiltin("__atomic_compare_exchange_8", nullptr,
+ typeVec, atomicCasMaker);
+ }
}
diff --git a/bridge/go-llvm-builtins.h b/bridge/go-llvm-builtins.h
index bf31d38..1e5399a 100644
--- a/bridge/go-llvm-builtins.h
+++ b/bridge/go-llvm-builtins.h
@@ -25,8 +25,9 @@
typedef std::vector<Btype*> BuiltinEntryTypeVec;
-typedef llvm::Value *(*BuiltinExprMaker)(llvm::SmallVector<llvm::Value*, 16> args,
- BinstructionsLIRBuilder *builder);
+typedef llvm::Value *(*BuiltinExprMaker)(llvm::SmallVectorImpl<llvm::Value*> &args,
+ BinstructionsLIRBuilder *builder,
+ TypeManager *tm);
// An entry in a table of interesting builtin functions. A given entry
// is either an intrinsic or a libcall builtin.
diff --git a/bridge/go-llvm-materialize.cpp b/bridge/go-llvm-materialize.cpp
index 249dccd..9d47274 100644
--- a/bridge/go-llvm-materialize.cpp
+++ b/bridge/go-llvm-materialize.cpp
@@ -1461,7 +1461,7 @@
if (be) {
BuiltinExprMaker makerfn = be->exprMaker();
if (makerfn)
- callValue = makerfn(state.llargs, &state.builder);
+ callValue = makerfn(state.llargs, &state.builder, this);
}
}
if (!callValue) {
diff --git a/bridge/go-llvm.cpp b/bridge/go-llvm.cpp
index 86e56e8..75c3998 100644
--- a/bridge/go-llvm.cpp
+++ b/bridge/go-llvm.cpp
@@ -473,7 +473,7 @@
std::vector<Btyped_identifier> results;
Location bloc(linemap_->get_predeclared_location());
const BuiltinEntryTypeVec &types = be->types();
- if (types.size() > 0) {
+ if (types.size() > 0 && types[0] != nullptr) {
Btyped_identifier result("ret", types[0], bloc);
results.push_back(result);
}