Skip to content

Commit

Permalink
[LLVM] Add a C API for creating instructions with custom syncscopes. (#…
Browse files Browse the repository at this point in the history
…104775)

Another upstreaming of C API extensions we have in Julia/LLVM.jl.
Although [we went](maleadt/LLVM.jl#431) with a
string-based API there, here I'm proposing something that's similar to
existing metadata/attribute APIs:
- explicit functions to map syncscope names to IDs, and back
- `LLVM*SyncScope` versions of builder APIs that already take a
`SingleThread` argument: atomic rmw, atomic xchg, fence
- `LLVMGetAtomicSyncScopeID` and `LLVMSetAtomicSyncScopeID` for other
atomic instructions
- testing through `llvm-c-test`'s `--echo` functionality
  • Loading branch information
maleadt authored Aug 20, 2024
1 parent 7cfc9a3 commit eb7d535
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 56 deletions.
13 changes: 13 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ Changes to the C API
* It is now also possible to run the new pass manager on a single function, by calling
``LLVMRunPassesOnFunction`` instead of ``LLVMRunPasses``.

* Support for creating instructions with custom synchronization scopes has been added:

* ``LLVMGetSyncScopeID`` to map a synchronization scope name to an ID.
* ``LLVMBuildFenceSyncScope``, ``LLVMBuildAtomicRMWSyncScope`` and
``LLVMBuildAtomicCmpXchgSyncScope`` versions of the existing builder functions
with an additional synchronization scope ID parameter.
* ``LLVMGetAtomicSyncScopeID`` and ``LLVMSetAtomicSyncScopeID`` to get and set the
synchronization scope of any atomic instruction.
* ``LLVMIsAtomic`` to check if an instruction is atomic, for use with the above functions.
Because of backwards compatibility, ``LLVMIsAtomicSingleThread`` and
``LLVMSetAtomicSingleThread`` continue to work with any instruction type.


Changes to the CodeGen infrastructure
-------------------------------------

Expand Down
34 changes: 34 additions & 0 deletions llvm/include/llvm-c/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@ unsigned LLVMGetMDKindIDInContext(LLVMContextRef C, const char *Name,
unsigned SLen);
unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);

/**
* Maps a synchronization scope name to a ID unique within this context.
*/
unsigned LLVMGetSyncScopeID(LLVMContextRef C, const char *Name, size_t SLen);

/**
* Return an unique id given the name of a enum attribute,
* or 0 if no attribute by that name exists.
Expand Down Expand Up @@ -4592,15 +4597,28 @@ LLVMValueRef LLVMBuildPtrDiff2(LLVMBuilderRef, LLVMTypeRef ElemTy,
const char *Name);
LLVMValueRef LLVMBuildFence(LLVMBuilderRef B, LLVMAtomicOrdering ordering,
LLVMBool singleThread, const char *Name);
LLVMValueRef LLVMBuildFenceSyncScope(LLVMBuilderRef B,
LLVMAtomicOrdering ordering, unsigned SSID,
const char *Name);
LLVMValueRef LLVMBuildAtomicRMW(LLVMBuilderRef B, LLVMAtomicRMWBinOp op,
LLVMValueRef PTR, LLVMValueRef Val,
LLVMAtomicOrdering ordering,
LLVMBool singleThread);
LLVMValueRef LLVMBuildAtomicRMWSyncScope(LLVMBuilderRef B,
LLVMAtomicRMWBinOp op,
LLVMValueRef PTR, LLVMValueRef Val,
LLVMAtomicOrdering ordering,
unsigned SSID);
LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr,
LLVMValueRef Cmp, LLVMValueRef New,
LLVMAtomicOrdering SuccessOrdering,
LLVMAtomicOrdering FailureOrdering,
LLVMBool SingleThread);
LLVMValueRef LLVMBuildAtomicCmpXchgSyncScope(LLVMBuilderRef B, LLVMValueRef Ptr,
LLVMValueRef Cmp, LLVMValueRef New,
LLVMAtomicOrdering SuccessOrdering,
LLVMAtomicOrdering FailureOrdering,
unsigned SSID);

/**
* Get the number of elements in the mask of a ShuffleVector instruction.
Expand All @@ -4625,6 +4643,22 @@ int LLVMGetMaskValue(LLVMValueRef ShuffleVectorInst, unsigned Elt);
LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst);
void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool SingleThread);

/**
* Returns whether an instruction is an atomic instruction, e.g., atomicrmw,
* cmpxchg, fence, or loads and stores with atomic ordering.
*/
LLVMBool LLVMIsAtomic(LLVMValueRef Inst);

/**
* Returns the synchronization scope ID of an atomic instruction.
*/
unsigned LLVMGetAtomicSyncScopeID(LLVMValueRef AtomicInst);

/**
* Sets the synchronization scope ID of an atomic instruction.
*/
void LLVMSetAtomicSyncScopeID(LLVMValueRef AtomicInst, unsigned SSID);

LLVMAtomicOrdering LLVMGetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst);
void LLVMSetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst,
LLVMAtomicOrdering Ordering);
Expand Down
17 changes: 17 additions & 0 deletions llvm/include/llvm/IR/Instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -4942,6 +4942,23 @@ inline std::optional<SyncScope::ID> getAtomicSyncScopeID(const Instruction *I) {
llvm_unreachable("unhandled atomic operation");
}

/// A helper function that sets an atomic operation's sync scope.
inline void setAtomicSyncScopeID(Instruction *I, SyncScope::ID SSID) {
assert(I->isAtomic());
if (auto *AI = dyn_cast<LoadInst>(I))
AI->setSyncScopeID(SSID);
else if (auto *AI = dyn_cast<StoreInst>(I))
AI->setSyncScopeID(SSID);
else if (auto *AI = dyn_cast<FenceInst>(I))
AI->setSyncScopeID(SSID);
else if (auto *AI = dyn_cast<AtomicCmpXchgInst>(I))
AI->setSyncScopeID(SSID);
else if (auto *AI = dyn_cast<AtomicRMWInst>(I))
AI->setSyncScopeID(SSID);
else
llvm_unreachable("unhandled atomic operation");
}

//===----------------------------------------------------------------------===//
// FreezeInst Class
//===----------------------------------------------------------------------===//
Expand Down
83 changes: 60 additions & 23 deletions llvm/lib/IR/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
Expand Down Expand Up @@ -146,6 +147,10 @@ unsigned LLVMGetMDKindID(const char *Name, unsigned SLen) {
return LLVMGetMDKindIDInContext(LLVMGetGlobalContext(), Name, SLen);
}

unsigned LLVMGetSyncScopeID(LLVMContextRef C, const char *Name, size_t SLen) {
return unwrap(C)->getOrInsertSyncScopeID(StringRef(Name, SLen));
}

unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen) {
return Attribute::getAttrKindFromName(StringRef(Name, SLen));
}
Expand Down Expand Up @@ -3957,8 +3962,6 @@ static LLVMAtomicRMWBinOp mapToLLVMRMWBinOp(AtomicRMWInst::BinOp BinOp) {
llvm_unreachable("Invalid AtomicRMWBinOp value!");
}

// TODO: Should this and other atomic instructions support building with
// "syncscope"?
LLVMValueRef LLVMBuildFence(LLVMBuilderRef B, LLVMAtomicOrdering Ordering,
LLVMBool isSingleThread, const char *Name) {
return wrap(
Expand All @@ -3968,6 +3971,13 @@ LLVMValueRef LLVMBuildFence(LLVMBuilderRef B, LLVMAtomicOrdering Ordering,
Name));
}

LLVMValueRef LLVMBuildFenceSyncScope(LLVMBuilderRef B,
LLVMAtomicOrdering Ordering, unsigned SSID,
const char *Name) {
return wrap(
unwrap(B)->CreateFence(mapFromLLVMOrdering(Ordering), SSID, Name));
}

LLVMValueRef LLVMBuildGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
LLVMValueRef Pointer, LLVMValueRef *Indices,
unsigned NumIndices, const char *Name) {
Expand Down Expand Up @@ -4317,6 +4327,17 @@ LLVMValueRef LLVMBuildAtomicRMW(LLVMBuilderRef B,LLVMAtomicRMWBinOp op,
singleThread ? SyncScope::SingleThread : SyncScope::System));
}

LLVMValueRef LLVMBuildAtomicRMWSyncScope(LLVMBuilderRef B,
LLVMAtomicRMWBinOp op,
LLVMValueRef PTR, LLVMValueRef Val,
LLVMAtomicOrdering ordering,
unsigned SSID) {
AtomicRMWInst::BinOp intop = mapFromLLVMRMWBinOp(op);
return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR), unwrap(Val),
MaybeAlign(),
mapFromLLVMOrdering(ordering), SSID));
}

LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr,
LLVMValueRef Cmp, LLVMValueRef New,
LLVMAtomicOrdering SuccessOrdering,
Expand All @@ -4330,6 +4351,17 @@ LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr,
singleThread ? SyncScope::SingleThread : SyncScope::System));
}

LLVMValueRef LLVMBuildAtomicCmpXchgSyncScope(LLVMBuilderRef B, LLVMValueRef Ptr,
LLVMValueRef Cmp, LLVMValueRef New,
LLVMAtomicOrdering SuccessOrdering,
LLVMAtomicOrdering FailureOrdering,
unsigned SSID) {
return wrap(unwrap(B)->CreateAtomicCmpXchg(
unwrap(Ptr), unwrap(Cmp), unwrap(New), MaybeAlign(),
mapFromLLVMOrdering(SuccessOrdering),
mapFromLLVMOrdering(FailureOrdering), SSID));
}

unsigned LLVMGetNumMaskElements(LLVMValueRef SVInst) {
Value *P = unwrap(SVInst);
ShuffleVectorInst *I = cast<ShuffleVectorInst>(P);
Expand All @@ -4344,34 +4376,39 @@ int LLVMGetMaskValue(LLVMValueRef SVInst, unsigned Elt) {

int LLVMGetUndefMaskElem(void) { return PoisonMaskElem; }

LLVMBool LLVMIsAtomic(LLVMValueRef Inst) {
return unwrap<Instruction>(Inst)->isAtomic();
}

LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst) {
Value *P = unwrap(AtomicInst);
// Backwards compatibility: return false for non-atomic instructions
Instruction *I = unwrap<Instruction>(AtomicInst);
if (!I->isAtomic())
return 0;

if (AtomicRMWInst *I = dyn_cast<AtomicRMWInst>(P))
return I->getSyncScopeID() == SyncScope::SingleThread;
else if (FenceInst *FI = dyn_cast<FenceInst>(P))
return FI->getSyncScopeID() == SyncScope::SingleThread;
else if (StoreInst *SI = dyn_cast<StoreInst>(P))
return SI->getSyncScopeID() == SyncScope::SingleThread;
else if (LoadInst *LI = dyn_cast<LoadInst>(P))
return LI->getSyncScopeID() == SyncScope::SingleThread;
return cast<AtomicCmpXchgInst>(P)->getSyncScopeID() ==
SyncScope::SingleThread;
return *getAtomicSyncScopeID(I) == SyncScope::SingleThread;
}

void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool NewValue) {
Value *P = unwrap(AtomicInst);
// Backwards compatibility: ignore non-atomic instructions
Instruction *I = unwrap<Instruction>(AtomicInst);
if (!I->isAtomic())
return;

SyncScope::ID SSID = NewValue ? SyncScope::SingleThread : SyncScope::System;
setAtomicSyncScopeID(I, SSID);
}

if (AtomicRMWInst *I = dyn_cast<AtomicRMWInst>(P))
return I->setSyncScopeID(SSID);
else if (FenceInst *FI = dyn_cast<FenceInst>(P))
return FI->setSyncScopeID(SSID);
else if (StoreInst *SI = dyn_cast<StoreInst>(P))
return SI->setSyncScopeID(SSID);
else if (LoadInst *LI = dyn_cast<LoadInst>(P))
return LI->setSyncScopeID(SSID);
return cast<AtomicCmpXchgInst>(P)->setSyncScopeID(SSID);
unsigned LLVMGetAtomicSyncScopeID(LLVMValueRef AtomicInst) {
Instruction *I = unwrap<Instruction>(AtomicInst);
assert(I->isAtomic() && "Expected an atomic instruction");
return *getAtomicSyncScopeID(I);
}

void LLVMSetAtomicSyncScopeID(LLVMValueRef AtomicInst, unsigned SSID) {
Instruction *I = unwrap<Instruction>(AtomicInst);
assert(I->isAtomic() && "Expected an atomic instruction");
setAtomicSyncScopeID(I, SSID);
}

LLVMAtomicOrdering LLVMGetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst) {
Expand Down
18 changes: 13 additions & 5 deletions llvm/test/Bindings/llvm-c/echo.ll
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,23 @@ define void @memops(ptr %ptr) {
%b = load volatile i8, ptr %ptr
%c = load i8, ptr %ptr, align 8
%d = load atomic i8, ptr %ptr acquire, align 32
%e = load atomic i8, ptr %ptr syncscope("singlethread") acquire, align 32
store i8 0, ptr %ptr
store volatile i8 0, ptr %ptr
store i8 0, ptr %ptr, align 8
store atomic i8 0, ptr %ptr release, align 32
%e = atomicrmw add ptr %ptr, i8 0 monotonic, align 1
%f = atomicrmw volatile xchg ptr %ptr, i8 0 acq_rel, align 8
%g = cmpxchg ptr %ptr, i8 1, i8 2 seq_cst acquire, align 1
%h = cmpxchg weak ptr %ptr, i8 1, i8 2 seq_cst acquire, align 8
%i = cmpxchg volatile ptr %ptr, i8 1, i8 2 monotonic monotonic, align 16
store atomic i8 0, ptr %ptr syncscope("singlethread") release, align 32
%f = atomicrmw add ptr %ptr, i8 0 monotonic, align 1
%g = atomicrmw volatile xchg ptr %ptr, i8 0 acq_rel, align 8
%h = atomicrmw volatile xchg ptr %ptr, i8 0 syncscope("singlethread") acq_rel, align 8
%i = atomicrmw volatile xchg ptr %ptr, i8 0 syncscope("agent") acq_rel, align 8
%j = cmpxchg ptr %ptr, i8 1, i8 2 seq_cst acquire, align 1
%k = cmpxchg weak ptr %ptr, i8 1, i8 2 seq_cst acquire, align 8
%l = cmpxchg volatile ptr %ptr, i8 1, i8 2 monotonic monotonic, align 16
%m = cmpxchg volatile ptr %ptr, i8 1, i8 2 syncscope("singlethread") monotonic monotonic, align 16
%n = cmpxchg volatile ptr %ptr, i8 1, i8 2 syncscope("agent") monotonic monotonic, align 16
fence syncscope("singlethread") acquire
fence syncscope("agent") acquire
ret void
}

Expand Down
4 changes: 2 additions & 2 deletions llvm/tools/llvm-c-test/attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
int llvm_test_function_attributes(void) {
LLVMEnablePrettyStackTrace();

LLVMModuleRef M = llvm_load_module(false, true);
LLVMModuleRef M = llvm_load_module(LLVMGetGlobalContext(), false, true);

LLVMValueRef F = LLVMGetFirstFunction(M);
while (F) {
Expand Down Expand Up @@ -49,7 +49,7 @@ int llvm_test_function_attributes(void) {
int llvm_test_callsite_attributes(void) {
LLVMEnablePrettyStackTrace();

LLVMModuleRef M = llvm_load_module(false, true);
LLVMModuleRef M = llvm_load_module(LLVMGetGlobalContext(), false, true);

LLVMValueRef F = LLVMGetFirstFunction(M);
while (F) {
Expand Down
26 changes: 13 additions & 13 deletions llvm/tools/llvm-c-test/echo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ struct FunCloner {
check_value_kind(Src, LLVMInstructionValueKind);
if (!LLVMIsAInstruction(Src))
report_fatal_error("Expected an instruction");
LLVMContextRef Ctx = LLVMGetTypeContext(LLVMTypeOf(Src));

size_t NameLen;
const char *Name = LLVMGetValueName2(Src, &NameLen);
Expand Down Expand Up @@ -754,7 +755,8 @@ struct FunCloner {
LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
LLVMSetOrdering(Dst, LLVMGetOrdering(Src));
LLVMSetVolatile(Dst, LLVMGetVolatile(Src));
LLVMSetAtomicSingleThread(Dst, LLVMIsAtomicSingleThread(Src));
if (LLVMIsAtomic(Src))
LLVMSetAtomicSyncScopeID(Dst, LLVMGetAtomicSyncScopeID(Src));
break;
}
case LLVMStore: {
Expand All @@ -764,7 +766,8 @@ struct FunCloner {
LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
LLVMSetOrdering(Dst, LLVMGetOrdering(Src));
LLVMSetVolatile(Dst, LLVMGetVolatile(Src));
LLVMSetAtomicSingleThread(Dst, LLVMIsAtomicSingleThread(Src));
if (LLVMIsAtomic(Src))
LLVMSetAtomicSyncScopeID(Dst, LLVMGetAtomicSyncScopeID(Src));
break;
}
case LLVMGetElementPtr: {
Expand All @@ -785,8 +788,8 @@ struct FunCloner {
LLVMValueRef Val = CloneValue(LLVMGetOperand(Src, 1));
LLVMAtomicRMWBinOp BinOp = LLVMGetAtomicRMWBinOp(Src);
LLVMAtomicOrdering Ord = LLVMGetOrdering(Src);
LLVMBool SingleThread = LLVMIsAtomicSingleThread(Src);
Dst = LLVMBuildAtomicRMW(Builder, BinOp, Ptr, Val, Ord, SingleThread);
Dst = LLVMBuildAtomicRMWSyncScope(Builder, BinOp, Ptr, Val, Ord,
LLVMGetAtomicSyncScopeID(Src));
LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
LLVMSetVolatile(Dst, LLVMGetVolatile(Src));
LLVMSetValueName2(Dst, Name, NameLen);
Expand All @@ -798,10 +801,8 @@ struct FunCloner {
LLVMValueRef New = CloneValue(LLVMGetOperand(Src, 2));
LLVMAtomicOrdering Succ = LLVMGetCmpXchgSuccessOrdering(Src);
LLVMAtomicOrdering Fail = LLVMGetCmpXchgFailureOrdering(Src);
LLVMBool SingleThread = LLVMIsAtomicSingleThread(Src);

Dst = LLVMBuildAtomicCmpXchg(Builder, Ptr, Cmp, New, Succ, Fail,
SingleThread);
Dst = LLVMBuildAtomicCmpXchgSyncScope(
Builder, Ptr, Cmp, New, Succ, Fail, LLVMGetAtomicSyncScopeID(Src));
LLVMSetAlignment(Dst, LLVMGetAlignment(Src));
LLVMSetVolatile(Dst, LLVMGetVolatile(Src));
LLVMSetWeak(Dst, LLVMGetWeak(Src));
Expand Down Expand Up @@ -992,8 +993,8 @@ struct FunCloner {
}
case LLVMFence: {
LLVMAtomicOrdering Ordering = LLVMGetOrdering(Src);
LLVMBool IsSingleThreaded = LLVMIsAtomicSingleThread(Src);
Dst = LLVMBuildFence(Builder, Ordering, IsSingleThreaded, Name);
Dst = LLVMBuildFenceSyncScope(Builder, Ordering,
LLVMGetAtomicSyncScopeID(Src), Name);
break;
}
case LLVMZExt: {
Expand Down Expand Up @@ -1059,7 +1060,6 @@ struct FunCloner {
if (LLVMCanValueUseFastMathFlags(Src))
LLVMSetFastMathFlags(Dst, LLVMGetFastMathFlags(Src));

auto Ctx = LLVMGetModuleContext(M);
size_t NumMetadataEntries;
auto *AllMetadata =
LLVMInstructionGetAllMetadataOtherThanDebugLoc(Src,
Expand Down Expand Up @@ -1609,12 +1609,12 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
int llvm_echo(void) {
LLVMEnablePrettyStackTrace();

LLVMModuleRef Src = llvm_load_module(false, true);
LLVMContextRef Ctx = LLVMContextCreate();
LLVMModuleRef Src = llvm_load_module(Ctx, false, true);
size_t SourceFileLen;
const char *SourceFileName = LLVMGetSourceFileName(Src, &SourceFileLen);
size_t ModuleIdentLen;
const char *ModuleName = LLVMGetModuleIdentifier(Src, &ModuleIdentLen);
LLVMContextRef Ctx = LLVMContextCreate();
LLVMModuleRef M = LLVMModuleCreateWithNameInContext(ModuleName, Ctx);

LLVMSetSourceFileName(M, SourceFileName, SourceFileLen);
Expand Down
2 changes: 1 addition & 1 deletion llvm/tools/llvm-c-test/llvm-c-test.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extern "C" {
void llvm_tokenize_stdin(void (*cb)(char **tokens, int ntokens));

// module.c
LLVMModuleRef llvm_load_module(bool Lazy, bool New);
LLVMModuleRef llvm_load_module(LLVMContextRef C, bool Lazy, bool New);
int llvm_module_dump(bool Lazy, bool New);
int llvm_module_list_functions(void);
int llvm_module_list_globals(void);
Expand Down
Loading

0 comments on commit eb7d535

Please sign in to comment.