[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:
1 parent
a1609094a0
commit
0f9f5a2aba
11 files changed
+263
-9
No files matched your search
@@ -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
|
||||
|
||||
@@ -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 *)">;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_"}};
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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));
|
||||
Reference in new issue
Block a user