[z/OS][Clang] Add compare and swap builtin functions (#202362)

Implements the following compare and swap builtins:
```
int __cs(unsigned int *, unsigned int *, unsigned int), 4 byte compare & swap
int __cs1(void* OP1, void* OP2, void* OP3), 4 byte compare & swap
int __csg(void* OP1, void* OP2, void* OP3), 8 byte compare & swap
int __cds1(void* OP1, void* OP2, void* OP3), 8 byte compare & swap
int __cdsg(void* OP1, void* OP2, void* OP3), 16 byte compare & swap
```
The implementation utilizes the llvm cmpxchg intrinsic to generate the
CS instructions, then it generates a store instruction to store OP2 into
OP1, and finally invert the success flag of cmpxchg by an XOR
instruction with 1 and return the flag.
This commit is contained in:
Maryam Moghadas authored and GitHub committed 2026-07-09 16:53:18 -04:00
1 parent a1609094a0
commit 0f9f5a2aba
11 files changed
+263 -9

No files matched your search

+34
View File
@@ -5538,6 +5538,40 @@ int a = 1;
__builtin_dcbf (&a);
```
### z/OS Language Extensions
#### z/OS builtins
z/OS supports builtins for compare-and-swap operations that generate the
corresponding z/OS assembly instructions (CS, CSG, CDSG). These builtins compare
the value pointed to by oldptr to the value pointed to by curptr. If they are
equal, the value pointed to by newword (or newword itself for `__cs`) is
copied into the location pointed to by curptr. If they are unequal, the value
pointed to by curptr is copied into the location pointed to by oldptr.
The builtins return 0 if the values are equal, or 1 if they are unequal.
- `int __cs(unsigned int *oldptr, unsigned int *curptr, unsigned int newword)`
Generates a 4-byte compare-and-swap using the CS instruction.
Use `__cs` instead of `__cs1` when the newword arg is an r-value instead
of an l-value.
- `int __cs1(void *oldptr, void *curptr, void *newword)`
Generates a 4-byte compare-and-swap using the CS instruction.
- `int __csg(void *oldptr, void *curptr, void *newword)`
Generates an 8-byte compare-and-swap using the CSG instruction.
- `int __cds1(void *oldptr, void *curptr, void *newword)`
Generates an 8-byte compare-and-swap using the CSG instruction.
- `int __cdsg(void *oldptr, void *curptr, void *newword)`
Generates a 16-byte compare-and-swap using the CDSG instruction.
## Extensions for Static Analysis
Clang supports additional attributes that are useful for documenting program
+27
View File
@@ -0,0 +1,27 @@
//===-- BuiltinsZOS.td - z/OS Builtin function database ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the z/OS-specific builtin function database.
//
//===----------------------------------------------------------------------===//
include "clang/Basic/BuiltinsBase.td"
class SystemZNoPrefixBuiltin<string prototype> : Builtin {
let Spellings = [NAME];
let Prototype = prototype;
}
// compare and swap functions
let Attributes = [NoThrow] in {
def __cs : SystemZNoPrefixBuiltin<"int(unsigned int *, unsigned int *, unsigned int)">;
def __cs1 : SystemZNoPrefixBuiltin<"int(void *, void *, void *)">;
def __csg : SystemZNoPrefixBuiltin<"int(void *, void *, void *)">;
def __cds1 : SystemZNoPrefixBuiltin<"int(void *, void *, void *)">;
def __cdsg : SystemZNoPrefixBuiltin<"int(void *, void *, void *)">;
}
+4
View File
@@ -146,6 +146,10 @@ clang_tablegen(BuiltinsX86_64.inc -gen-clang-builtins
SOURCE BuiltinsX86_64.td
TARGET ClangBuiltinsX86_64)
clang_tablegen(BuiltinsZOS.inc -gen-clang-builtins
SOURCE BuiltinsZOS.td
TARGET ClangBuiltinsZOS)
clang_tablegen(BuiltinTemplates.inc -gen-clang-builtin-templates
SOURCE BuiltinTemplates.td
TARGET ClangBuiltinTemplates)
+9 -4
View File
@@ -457,13 +457,18 @@ namespace clang {
/// SystemZ builtins
namespace SystemZ {
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1,
enum {
LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1,
#define GET_BUILTIN_ENUMERATORS
#include "clang/Basic/BuiltinsSystemZ.inc"
#undef GET_BUILTIN_ENUMERATORS
LastTSBuiltin
};
FirstZOSBuiltin,
LastSystemZBuiltin = FirstZOSBuiltin - 1,
#define GET_BUILTIN_ENUMERATORS
#include "clang/Basic/BuiltinsZOS.inc"
#undef GET_BUILTIN_ENUMERATORS
LastTSBuiltin
};
}
/// WebAssembly builtins
+21 -1
View File
@@ -22,7 +22,9 @@ using namespace clang;
using namespace clang::targets;
static constexpr int NumBuiltins =
clang::SystemZ::LastTSBuiltin - Builtin::FirstTSBuiltin;
clang::SystemZ::LastSystemZBuiltin - Builtin::FirstTSBuiltin + 1;
static constexpr int NumBuiltinsZOS =
clang::SystemZ::LastTSBuiltin - clang::SystemZ::LastSystemZBuiltin - 1;
#define GET_BUILTIN_STR_TABLE
#include "clang/Basic/BuiltinsSystemZ.inc"
@@ -41,6 +43,20 @@ static constexpr Builtin::Info PrefixedBuiltinInfos[] = {
};
static_assert((std::size(BuiltinInfos) + std::size(PrefixedBuiltinInfos)) ==
NumBuiltins);
namespace clang {
namespace ZOS {
#define GET_BUILTIN_STR_TABLE
#include "clang/Basic/BuiltinsZOS.inc"
#undef GET_BUILTIN_STR_TABLE
static constexpr Builtin::Info BuiltinInfos[] = {
#define GET_BUILTIN_INFOS
#include "clang/Basic/BuiltinsZOS.inc"
#undef GET_BUILTIN_INFOS
};
static_assert((std::size(BuiltinInfos)) == NumBuiltinsZOS);
} // namespace ZOS
} // namespace clang
const char *const SystemZTargetInfo::GCCRegNames[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
@@ -216,6 +232,10 @@ void SystemZTargetInfo::getTargetDefines(const LangOptions &Opts,
llvm::SmallVector<Builtin::InfosShard>
SystemZTargetInfo::getTargetBuiltins() const {
if (getTriple().isOSzOS())
return {{&BuiltinStrings, BuiltinInfos},
{&BuiltinStrings, PrefixedBuiltinInfos, "__builtin_s390_"},
{&ZOS::BuiltinStrings, ZOS::BuiltinInfos}};
return {{&BuiltinStrings, BuiltinInfos},
{&BuiltinStrings, PrefixedBuiltinInfos, "__builtin_s390_"}};
}
+65 -4
View File
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
#include "CGBuiltin.h"
#include "CodeGenFunction.h"
#include "clang/Basic/TargetBuiltins.h"
#include "llvm/IR/IntrinsicsS390.h"
@@ -36,6 +37,52 @@ static Value *EmitSystemZIntrinsicWithCC(CodeGenFunction &CGF,
return CGF.Builder.CreateExtractValue(Call, 0);
}
/// For z/OS, the builtin __cs1 has the following signature:
/// int __cs1(void * Comparand,
/// void * Destination,
/// void * Exchange);
/// Whereas the llvm 'cmpxchg' instruction has the following syntax:
/// compxchg *Destination, Comparand, Exchange.
/// So we need to swap Comparand & Destination and dereference Comparand &
/// Exchange when invoking CreateAtomicCmpXchg. For this reason we can not use
/// the utility function MakeAtomicCmpXchgValue.
static Value *EmitAtomicCmpXchgForZOSIntrin(CodeGenFunction &CGF,
const CallExpr *E,
llvm::Type *IntType, bool HasAddr) {
CharUnits Alignment =
CGF.getContext().toCharUnitsFromBits(IntType->getScalarSizeInBits());
llvm::Value *ComparandPtr = CGF.EmitScalarExpr(E->getArg(0));
llvm::Value *DestinationPtr = CGF.EmitScalarExpr(E->getArg(1));
Address DestinationAddr = Address(DestinationPtr, IntType, Alignment);
Address ComparandAddr = Address(ComparandPtr, IntType, Alignment);
llvm::Value *Comparand = CGF.Builder.CreateLoad(ComparandAddr);
llvm::Value *Exchange;
if (HasAddr) {
llvm::Value *ExchangePtr = CGF.EmitScalarExpr(E->getArg(2));
Address ExchangeAddr = Address(ExchangePtr, IntType, Alignment);
Exchange = CGF.Builder.CreateLoad(ExchangeAddr);
} else {
Exchange = CGF.EmitScalarExpr(E->getArg(2));
}
Value *Result = CGF.Builder.CreateAtomicCmpXchg(
DestinationAddr, Comparand, Exchange,
llvm::AtomicOrdering::SequentiallyConsistent,
llvm::AtomicOrdering::SequentiallyConsistent);
// Store Destination in Comparand.
CGF.Builder.CreateStore(CGF.Builder.CreateExtractValue(Result, 0),
ComparandAddr);
// Extract boolean success flag, invert it and zext it to int.
llvm::Value *RetVal =
CGF.Builder.CreateNot(CGF.Builder.CreateExtractValue(Result, 1));
return CGF.Builder.CreateZExt(RetVal, CGF.ConvertType(E->getType()));
}
Value *CodeGenFunction::EmitSystemZBuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
switch (BuiltinID) {
@@ -68,11 +115,25 @@ Value *CodeGenFunction::EmitSystemZBuiltinExpr(unsigned BuiltinID,
Function *F = CGM.getIntrinsic(Intrinsic::s390_ntstg);
return Builder.CreateCall(F, {Data, Address});
}
case SystemZ::BI__cs: {
return EmitAtomicCmpXchgForZOSIntrin(*this, E, Int32Ty, false);
}
case SystemZ::BI__cs1: {
return EmitAtomicCmpXchgForZOSIntrin(*this, E, Int32Ty, true);
}
case SystemZ::BI__csg:
case SystemZ::BI__cds1: {
return EmitAtomicCmpXchgForZOSIntrin(*this, E, Int64Ty, true);
}
case SystemZ::BI__cdsg: {
llvm::Type *Int128Ty = llvm::IntegerType::get(getLLVMContext(), 128);
return EmitAtomicCmpXchgForZOSIntrin(*this, E, Int128Ty, true);
}
// Vector builtins. Note that most vector builtins are mapped automatically
// to target-specific LLVM intrinsics. The ones handled specially here can
// be represented via standard LLVM IR, which is preferable to enable common
// LLVM optimizations.
// Vector builtins. Note that most vector builtins are mapped automatically
// to target-specific LLVM intrinsics. The ones handled specially here can
// be represented via standard LLVM IR, which is preferable to enable common
// LLVM optimizations.
case SystemZ::BI__builtin_s390_vclzb:
case SystemZ::BI__builtin_s390_vclzh:
+1
View File
@@ -415,6 +415,7 @@ set(zos_wrapper_files
zos_wrappers/locale.h
zos_wrappers/math.h
zos_wrappers/poll.h
zos_wrappers/stdlib.h
zos_wrappers/string.h
zos_wrappers/time.h
zos_wrappers/variant.h
+20
View File
@@ -0,0 +1,20 @@
/*===----------------------------- stdlib.h --------------------------------===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===-----------------------------------------------------------------------===
*/
#ifndef __ZOS_WRAPPERS_STDLIB_H
#define __ZOS_WRAPPERS_STDLIB_H
#if __has_include_next(<stdlib.h>)
#include_next <stdlib.h>
#ifdef _EXT
#ifndef __CS1
#undef __cs
#endif
#endif /* _EXT */
#endif /* __has_include_next(<stdlib.h>) */
#endif /* __ZOS_WRAPPERS_STDLIB_H */
@@ -0,0 +1,66 @@
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 -triple s390x-none-zos %s -emit-llvm -o - | FileCheck -check-prefix=CHECK %s
// RUN: %clang_cc1 -triple s390x-linux-gnu %s -verify
int test_cs(unsigned int *a, unsigned int *b, unsigned int c) {
return __cs(a, b, c);
}
// expected-error@-2 {{call to undeclared function '__cs'; ISO C99 and later do not support implicit function declarations}}
// CHECK-LABEL: define{{.*}} @test_cs
// CHECK: cmpxchg ptr {{.*}}, i32 {{.*}}, i32 {{.*}} seq_cst seq_cst, align 4
// CHECK: store i32 {{.*}}, ptr {{.*}}, align 4
// CHECK: xor i1 {{.*}}, true
int test_cs1(int *a, int *b, int *c) {
return __cs1(a, b, c);
}
// expected-error@-2 {{call to undeclared function '__cs1'; ISO C99 and later do not support implicit function declarations}}
// CHECK-LABEL: define{{.*}} @test_cs1
// CHECK: cmpxchg ptr {{.*}}, i32 {{.*}}, i32 {{.*}} seq_cst seq_cst, align 4
// CHECK: store i32 {{.*}}, ptr {{.*}}, align 4
// CHECK: xor i1 {{.*}}, true
typedef struct __attribute__((__aligned__(8))) {
int a;
int b;
} double_word;
int test_cds1(double_word *a, double_word *b, double_word *c) {
return __cds1(a, b, c);
}
// expected-error@-2 {{call to undeclared function '__cds1'; ISO C99 and later do not support implicit function declarations}}
// CHECK-LABEL: define{{.*}} @test_cds1
// CHECK: cmpxchg ptr {{.*}}, i64 {{.*}}, i64 {{.*}} seq_cst seq_cst, align 8
// CHECK: store i64 {{.*}}, ptr {{.*}}, align 8
// CHECK: xor i1 {{.*}}, true
typedef struct __attribute__((__aligned__(16))) {
int a;
int b;
int c;
int d;
} quadruple_word;
int test_cdsg(quadruple_word *a, quadruple_word *b, quadruple_word *c) {
return __cdsg(a, b, c);
}
// expected-error@-2 {{call to undeclared function '__cdsg'; ISO C99 and later do not support implicit function declarations}}
// CHECK-LABEL: define{{.*}} @test_cdsg
// CHECK: cmpxchg ptr {{.*}}, i128 {{.*}}, i128 {{.*}} seq_cst seq_cst, align 16
// CHECK: store i128 {{.*}}, ptr {{.*}}, align 16
// CHECK: xor i1 {{.*}}, true
int test_csg(double_word *a, double_word *b, double_word *c) {
return __csg(a, b, c);
}
// expected-error@-2 {{call to undeclared function '__csg'; ISO C99 and later do not support implicit function declarations}}
// CHECK-LABEL: define{{.*}} @test_csg
// CHECK: cmpxchg ptr {{.*}}, i64 {{.*}}, i64 {{.*}} seq_cst seq_cst, align 8
// CHECK: store i64 {{.*}}, ptr {{.*}}, align 8
// CHECK: xor i1 {{.*}}, true
@@ -0,0 +1,3 @@
#ifdef _EXT
#define __cs(x, y, z) __cs1(x, y, &(z))
#endif
+13
View File
@@ -0,0 +1,13 @@
// REQUIRES: systemz-registered-target
// RUN: %clang -E --target=s390x-ibm-zos %s -mzos-sys-include=%S/Inputs/zos/usr/include | FileCheck %s --check-prefix=NOCS1
// RUN: %clang -E --target=s390x-ibm-zos -D__CS1 %s -mzos-sys-include=%S/Inputs/zos/usr/include | FileCheck %s --check-prefix=CS1
#define _EXT
#include <stdlib.h>
int func(unsigned int *a, unsigned int *b, unsigned int c) {
return __cs(a, b, c);
}
// NOCS1: return __cs(a, b, c);
// CS1: return __cs1(a, b, &(c));