Skip to content

Commit ab1df14

Browse files
authored
[CIR][CIRGen][Builtin] Support __sync_add_and_fetch (#1077)
Notable change is to introduce helper function `buildBinaryAtomicPost` which models on [OG's `EmitBinaryAtomicPost`](https://github.com/llvm/clangir/blob/dbf320e5c3db0410566ae561067c595308870bad/clang/lib/CodeGen/CGBuiltin.cpp#L340C15-L340C35). Comparing to `EmitBinaryAtomicPost`, `buildBinaryAtomicPost` is more concise as OG's ``EmitBinaryAtomicPost`` duplicates quite a bit of code from [MakeBinaryAtomicValue](https://github.com/llvm/clangir/blob/dbf320e5c3db0410566ae561067c595308870bad/clang/lib/CodeGen/CGBuiltin.cpp#L340) Also, didn't implement invert as __sync_add_and_fetch does not need it, but will add it (which is a trivial work) when we implement a builtin that needs it. Test cases are from [OG](https://github.com/llvm/clangir/blob/dbf320e5c3db0410566ae561067c595308870bad/clang/test/CodeGen/Atomics.c#L134)
1 parent d5b2a8e commit ab1df14

File tree

3 files changed

+124
-2
lines changed

3 files changed

+124
-2
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ struct MissingFeatures {
229229
static bool emitConstrainedFPCall() { return false; }
230230
static bool emitEmptyRecordCheck() { return false; }
231231
static bool isPPC_FP128Ty() { return false; }
232+
static bool emitBinaryAtomicPostHasInvert() { return false; }
232233

233234
// Inline assembly
234235
static bool asmGoto() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ static Address checkAtomicAlignment(CIRGenFunction &CGF, const CallExpr *E) {
244244
/// and the expression node.
245245
static mlir::Value makeBinaryAtomicValue(
246246
CIRGenFunction &cgf, cir::AtomicFetchKind kind, const CallExpr *expr,
247+
mlir::Value *neededValP = nullptr, mlir::Type *neededValT = nullptr,
247248
cir::MemOrder ordering = cir::MemOrder::SequentiallyConsistent) {
248249

249250
QualType typ = expr->getType();
@@ -263,7 +264,15 @@ static mlir::Value makeBinaryAtomicValue(
263264
mlir::Value val = cgf.emitScalarExpr(expr->getArg(1));
264265
mlir::Type valueType = val.getType();
265266
val = emitToInt(cgf, val, typ, intType);
266-
267+
// These output arguments are needed for post atomic fetch operations
268+
// that calculate the result of the operation as return value of
269+
// <binop>_and_fetch builtins. The `AtomicFetch` operation only updates the
270+
// memory location and returns the old value.
271+
if (neededValP) {
272+
assert(neededValT);
273+
*neededValP = val;
274+
*neededValT = valueType;
275+
}
267276
auto rmwi = builder.create<cir::AtomicFetch>(
268277
cgf.getLoc(expr->getSourceRange()), destAddr.emitRawPointer(), val, kind,
269278
ordering, false, /* is volatile */
@@ -276,6 +285,26 @@ static RValue emitBinaryAtomic(CIRGenFunction &CGF, cir::AtomicFetchKind kind,
276285
return RValue::get(makeBinaryAtomicValue(CGF, kind, E));
277286
}
278287

288+
static RValue emitBinaryAtomicPost(CIRGenFunction &cgf,
289+
cir::AtomicFetchKind atomicOpkind,
290+
const CallExpr *e,
291+
cir::BinOpKind binopKind) {
292+
mlir::Value val;
293+
mlir::Type valueType;
294+
clang::QualType typ = e->getType();
295+
mlir::Value result =
296+
makeBinaryAtomicValue(cgf, atomicOpkind, e, &val, &valueType);
297+
clang::CIRGen::CIRGenBuilderTy &builder = cgf.getBuilder();
298+
result = builder.create<cir::BinOp>(result.getLoc(), binopKind, result, val);
299+
result = emitFromInt(cgf, result, typ, valueType);
300+
// FIXME: Some callers of this function expect the result to be inverted,
301+
// which would need invert flag passed in and do the inversion here like
302+
// traditional clang code gen does. When we implment those caller builtins
303+
// we should implement the inversion here.
304+
assert(!MissingFeatures::emitBinaryAtomicPostHasInvert());
305+
return RValue::get(result);
306+
}
307+
279308
static mlir::Value MakeAtomicCmpXchgValue(CIRGenFunction &cgf,
280309
const CallExpr *expr,
281310
bool returnBool) {
@@ -1626,7 +1655,8 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
16261655
case Builtin::BI__sync_add_and_fetch_4:
16271656
case Builtin::BI__sync_add_and_fetch_8:
16281657
case Builtin::BI__sync_add_and_fetch_16:
1629-
llvm_unreachable("BI__sync_add_and_fetch like NYI");
1658+
return emitBinaryAtomicPost(*this, cir::AtomicFetchKind::Add, E,
1659+
cir::BinOpKind::Add);
16301660

16311661
case Builtin::BI__sync_sub_and_fetch_1:
16321662
case Builtin::BI__sync_sub_and_fetch_2:

clang/test/CIR/CodeGen/atomic.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ typedef struct _a {
1212

1313
void m() { at y; }
1414

15+
signed char sc;
16+
unsigned char uc;
17+
signed short ss;
18+
unsigned short us;
19+
signed int si;
20+
unsigned int ui;
21+
signed long long sll;
22+
unsigned long long ull;
23+
1524
// CHECK: ![[A:.*]] = !cir.struct<struct "_a" {!s32i}>
1625

1726
int basic_binop_fetch(int *i) {
@@ -649,3 +658,85 @@ void cmp_val_ushort(unsigned short* p, short x, short u) {
649658
void cmp_val_ulong(unsigned long* p, long x, long u) {
650659
long r = __sync_val_compare_and_swap(p, x, u);
651660
}
661+
662+
// CHECK-LABEL: @test_op_and_fetch
663+
// LLVM-LABEL: @test_op_and_fetch
664+
extern "C" void test_op_and_fetch (void)
665+
{
666+
// CHECK: [[VAL0:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !s8i
667+
// CHECK: [[RES0:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!s8i>, [[VAL0]] : !s8i, seq_cst) fetch_first : !s8i
668+
// CHECK: [[RET0:%.*]] = cir.binop(add, [[RES0]], [[VAL0]]) : !s8i
669+
// LLVM: [[VAL0:%.*]] = load i8, ptr @uc, align 1
670+
// LLVM: [[RES0:%.*]] = atomicrmw add ptr @sc, i8 [[VAL0]] seq_cst, align 1
671+
// LLVM: [[RET0:%.*]] = add i8 [[RES0]], [[VAL0]]
672+
// LLVM: store i8 [[RET0]], ptr @sc, align 1
673+
sc = __sync_add_and_fetch (&sc, uc);
674+
675+
// CHECK: [[RES1:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!u8i>, [[VAL1:%.*]] : !u8i, seq_cst) fetch_first : !u8i
676+
// CHECK: [[RET1:%.*]] = cir.binop(add, [[RES1]], [[VAL1]]) : !u8i
677+
// LLVM: [[VAL1:%.*]] = load i8, ptr @uc, align 1
678+
// LLVM: [[RES1:%.*]] = atomicrmw add ptr @uc, i8 [[VAL1]] seq_cst, align 1
679+
// LLVM: [[RET1:%.*]] = add i8 [[RES1]], [[VAL1]]
680+
// LLVM: store i8 [[RET1]], ptr @uc, align 1
681+
uc = __sync_add_and_fetch (&uc, uc);
682+
683+
// CHECK: [[VAL2:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !s16i
684+
// CHECK: [[RES2:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!s16i>, [[VAL2]] : !s16i, seq_cst) fetch_first : !s16i
685+
// CHECK: [[RET2:%.*]] = cir.binop(add, [[RES2]], [[VAL2]]) : !s16i
686+
// LLVM: [[VAL2:%.*]] = load i8, ptr @uc, align 1
687+
// LLVM: [[CONV2:%.*]] = zext i8 [[VAL2]] to i16
688+
// LLVM: [[RES2:%.*]] = atomicrmw add ptr @ss, i16 [[CONV2]] seq_cst, align 2
689+
// LLVM: [[RET2:%.*]] = add i16 [[RES2]], [[CONV2]]
690+
// LLVM: store i16 [[RET2]], ptr @ss, align 2
691+
ss = __sync_add_and_fetch (&ss, uc);
692+
693+
// CHECK: [[VAL3:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !u16i
694+
// CHECK: [[RES3:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!u16i>, [[VAL3]] : !u16i, seq_cst) fetch_first : !u16i
695+
// CHECK: [[RET3:%.*]] = cir.binop(add, [[RES3]], [[VAL3]]) : !u16i
696+
// LLVM: [[VAL3:%.*]] = load i8, ptr @uc, align 1
697+
// LLVM: [[CONV3:%.*]] = zext i8 [[VAL3]] to i16
698+
// LLVM: [[RES3:%.*]] = atomicrmw add ptr @us, i16 [[CONV3]] seq_cst, align 2
699+
// LLVM: [[RET3:%.*]] = add i16 [[RES3]], [[CONV3]]
700+
// LLVM: store i16 [[RET3]], ptr @us
701+
us = __sync_add_and_fetch (&us, uc);
702+
703+
// CHECK: [[VAL4:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !s32i
704+
// CHECK: [[RES4:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!s32i>, [[VAL4]] : !s32i, seq_cst) fetch_first : !s32i
705+
// CHECK: [[RET4:%.*]] = cir.binop(add, [[RES4]], [[VAL4]]) : !s32i
706+
// LLVM: [[VAL4:%.*]] = load i8, ptr @uc, align 1
707+
// LLVM: [[CONV4:%.*]] = zext i8 [[VAL4]] to i32
708+
// LLVM: [[RES4:%.*]] = atomicrmw add ptr @si, i32 [[CONV4]] seq_cst, align 4
709+
// LLVM: [[RET4:%.*]] = add i32 [[RES4]], [[CONV4]]
710+
// LLVM: store i32 [[RET4]], ptr @si, align 4
711+
si = __sync_add_and_fetch (&si, uc);
712+
713+
// CHECK: [[VAL5:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !u32i
714+
// CHECK: [[RES5:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!u32i>, [[VAL5]] : !u32i, seq_cst) fetch_first : !u32i
715+
// CHECK: [[RET5:%.*]] = cir.binop(add, [[RES5]], [[VAL5]]) : !u32i
716+
// LLVM: [[VAL5:%.*]] = load i8, ptr @uc, align 1
717+
// LLVM: [[CONV5:%.*]] = zext i8 [[VAL5]] to i32
718+
// LLVM: [[RES5:%.*]] = atomicrmw add ptr @ui, i32 [[CONV5]] seq_cst, align 4
719+
// LLVM: [[RET5:%.*]] = add i32 [[RES5]], [[CONV5]]
720+
// LLVM: store i32 [[RET5]], ptr @ui, align 4
721+
ui = __sync_add_and_fetch (&ui, uc);
722+
723+
// CHECK: [[VAL6:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !s64i
724+
// CHECK: [[RES6:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!s64i>, [[VAL6]] : !s64i, seq_cst) fetch_first : !s64i
725+
// CHECK: [[RET6:%.*]] = cir.binop(add, [[RES6]], [[VAL6]]) : !s64i
726+
// LLVM: [[VAL6:%.*]] = load i8, ptr @uc, align 1
727+
// LLVM: [[CONV6:%.*]] = zext i8 [[VAL6]] to i64
728+
// LLVM: [[RES6:%.*]] = atomicrmw add ptr @sll, i64 [[CONV6]] seq_cst, align 8
729+
// LLVM: [[RET6:%.*]] = add i64 [[RES6]], [[CONV6]]
730+
// LLVM: store i64 [[RET6]], ptr @sll, align 8
731+
sll = __sync_add_and_fetch (&sll, uc);
732+
733+
// CHECK: [[VAL7:%.*]] = cir.cast(integral, {{%.*}} : !u8i), !u64i
734+
// CHECK: [[RES7:%.*]] = cir.atomic.fetch(add, {{%.*}} : !cir.ptr<!u64i>, [[VAL7]] : !u64i, seq_cst) fetch_first : !u64i
735+
// CHECK: [[RET7:%.*]] = cir.binop(add, [[RES7]], [[VAL7]]) : !u64i
736+
// LLVM: [[VAL7:%.*]] = load i8, ptr @uc, align 1
737+
// LLVM: [[CONV7:%.*]] = zext i8 [[VAL7]] to i64
738+
// LLVM: [[RES7:%.*]] = atomicrmw add ptr @ull, i64 [[CONV7]] seq_cst, align 8
739+
// LLVM: [[RET7:%.*]] = add i64 [[RES7]], [[CONV7]]
740+
// LLVM: store i64 [[RET7]], ptr @ull, align 8
741+
ull = __sync_add_and_fetch (&ull, uc);
742+
}

0 commit comments

Comments
 (0)