Commit e8e05de0 authored by Alexey Bataev's avatar Alexey Bataev
Browse files

[OPENMP50]Add codegen for acq_rel clause in atomic|flush directives.

Added codegen support for atomic|flush directives with acq_rel clause.
parent 5752bcf9
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1826,7 +1826,7 @@ void AtomicInfo::EmitAtomicUpdateOp(
  auto Failure = llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);

  // Do the atomic load.
  auto *OldVal = EmitAtomicLoadOp(AO, IsVolatile);
  auto *OldVal = EmitAtomicLoadOp(Failure, IsVolatile);
  // For non-simple lvalues perform compare-and-swap procedure.
  auto *ContBB = CGF.createBasicBlock("atomic_cont");
  auto *ExitBB = CGF.createBasicBlock("atomic_exit");
@@ -1908,7 +1908,7 @@ void AtomicInfo::EmitAtomicUpdateOp(llvm::AtomicOrdering AO, RValue UpdateRVal,
  auto Failure = llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO);

  // Do the atomic load.
  auto *OldVal = EmitAtomicLoadOp(AO, IsVolatile);
  auto *OldVal = EmitAtomicLoadOp(Failure, IsVolatile);
  // For non-simple lvalues perform compare-and-swap procedure.
  auto *ContBB = CGF.createBasicBlock("atomic_cont");
  auto *ExitBB = CGF.createBasicBlock("atomic_exit");
@@ -2018,6 +2018,10 @@ void CodeGenFunction::EmitAtomicStore(RValue rvalue, LValue dest,
        intValue, addr.getElementType(), /*isSigned=*/false);
    llvm::StoreInst *store = Builder.CreateStore(intValue, addr);

    if (AO == llvm::AtomicOrdering::Acquire)
      AO = llvm::AtomicOrdering::Monotonic;
    else if (AO == llvm::AtomicOrdering::AcquireRelease)
      AO = llvm::AtomicOrdering::Release;
    // Initializations don't need to be atomic.
    if (!isInit)
      store->setAtomic(AO);
+3 −2
Original line number Diff line number Diff line
@@ -3875,7 +3875,7 @@ void CGOpenMPRuntime::emitProcBindClause(CodeGenFunction &CGF,
}

void CGOpenMPRuntime::emitFlush(CodeGenFunction &CGF, ArrayRef<const Expr *>,
                                SourceLocation Loc) {
                                SourceLocation Loc, llvm::AtomicOrdering AO) {
  llvm::OpenMPIRBuilder *OMPBuilder = CGF.CGM.getOpenMPIRBuilder();
  if (OMPBuilder) {
    OMPBuilder->CreateFlush(CGF.Builder);
@@ -11943,7 +11943,8 @@ Address CGOpenMPSIMDRuntime::getAddrOfArtificialThreadPrivate(

void CGOpenMPSIMDRuntime::emitFlush(CodeGenFunction &CGF,
                                    ArrayRef<const Expr *> Vars,
                                    SourceLocation Loc) {
                                    SourceLocation Loc,
                                    llvm::AtomicOrdering AO) {
  llvm_unreachable("Not supported in SIMD-only mode");
}

+2 −2
Original line number Diff line number Diff line
@@ -1246,7 +1246,7 @@ public:
  /// Emit flush of the variables specified in 'omp flush' directive.
  /// \param Vars List of variables to flush.
  virtual void emitFlush(CodeGenFunction &CGF, ArrayRef<const Expr *> Vars,
                         SourceLocation Loc);
                         SourceLocation Loc, llvm::AtomicOrdering AO);

  /// Emit task region for the task directive. The task region is
  /// emitted in several steps:
@@ -2034,7 +2034,7 @@ public:
  /// Emit flush of the variables specified in 'omp flush' directive.
  /// \param Vars List of variables to flush.
  void emitFlush(CodeGenFunction &CGF, ArrayRef<const Expr *> Vars,
                 SourceLocation Loc) override;
                 SourceLocation Loc, llvm::AtomicOrdering AO) override;

  /// Emit task region for the task directive. The task region is
  /// emitted in several steps:
+126 −66
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/Instructions.h"
using namespace clang;
using namespace CodeGen;
using namespace llvm::omp;
@@ -3743,6 +3744,9 @@ void CodeGenFunction::EmitOMPTaskgroupDirective(
}

void CodeGenFunction::EmitOMPFlushDirective(const OMPFlushDirective &S) {
  llvm::AtomicOrdering AO = S.getSingleClause<OMPFlushClause>()
                                ? llvm::AtomicOrdering::NotAtomic
                                : llvm::AtomicOrdering::AcquireRelease;
  CGM.getOpenMPRuntime().emitFlush(
      *this,
      [&S]() -> ArrayRef<const Expr *> {
@@ -3751,7 +3755,7 @@ void CodeGenFunction::EmitOMPFlushDirective(const OMPFlushDirective &S) {
                                    FlushClause->varlist_end());
        return llvm::None;
      }(),
      S.getBeginLoc());
      S.getBeginLoc(), AO);
}

void CodeGenFunction::EmitOMPDistributeLoop(const OMPLoopDirective &S,
@@ -4070,16 +4074,22 @@ convertToComplexValue(CodeGenFunction &CGF, RValue Val, QualType SrcType,
  return ComplexVal;
}

static void emitSimpleAtomicStore(CodeGenFunction &CGF, bool IsSeqCst,
static void emitSimpleAtomicStore(CodeGenFunction &CGF, llvm::AtomicOrdering AO,
                                  LValue LVal, RValue RVal) {
  if (LVal.isGlobalReg()) {
  if (LVal.isGlobalReg())
    CGF.EmitStoreThroughGlobalRegLValue(RVal, LVal);
  } else {
    CGF.EmitAtomicStore(RVal, LVal,
                        IsSeqCst ? llvm::AtomicOrdering::SequentiallyConsistent
                                 : llvm::AtomicOrdering::Monotonic,
                        LVal.isVolatile(), /*isInit=*/false);
  else
    CGF.EmitAtomicStore(RVal, LVal, AO, LVal.isVolatile(), /*isInit=*/false);
}

static RValue emitSimpleAtomicLoad(CodeGenFunction &CGF,
                                   llvm::AtomicOrdering AO, LValue LVal,
                                   SourceLocation Loc) {
  if (LVal.isGlobalReg())
    return CGF.EmitLoadOfLValue(LVal, Loc);
  return CGF.EmitAtomicLoad(
      LVal, Loc, llvm::AtomicCmpXchgInst::getStrongestFailureOrdering(AO),
      LVal.isVolatile());
}

void CodeGenFunction::emitOMPSimpleStore(LValue LVal, RValue RVal,
@@ -4100,7 +4110,7 @@ void CodeGenFunction::emitOMPSimpleStore(LValue LVal, RValue RVal,
  }
}

static void emitOMPAtomicReadExpr(CodeGenFunction &CGF, bool IsSeqCst,
static void emitOMPAtomicReadExpr(CodeGenFunction &CGF, llvm::AtomicOrdering AO,
                                  const Expr *X, const Expr *V,
                                  SourceLocation Loc) {
  // v = x;
@@ -4108,36 +4118,54 @@ static void emitOMPAtomicReadExpr(CodeGenFunction &CGF, bool IsSeqCst,
  assert(X->isLValue() && "X of 'omp atomic read' is not lvalue");
  LValue XLValue = CGF.EmitLValue(X);
  LValue VLValue = CGF.EmitLValue(V);
  RValue Res = XLValue.isGlobalReg()
                   ? CGF.EmitLoadOfLValue(XLValue, Loc)
                   : CGF.EmitAtomicLoad(
                         XLValue, Loc,
                         IsSeqCst ? llvm::AtomicOrdering::SequentiallyConsistent
                                  : llvm::AtomicOrdering::Monotonic,
                         XLValue.isVolatile());
  // OpenMP, 2.12.6, atomic Construct
  // Any atomic construct with a seq_cst clause forces the atomically
  // performed operation to include an implicit flush operation without a
  // list.
  if (IsSeqCst)
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc);
  RValue Res = emitSimpleAtomicLoad(CGF, AO, XLValue, Loc);
  // OpenMP, 2.17.7, atomic Construct
  // If the read or capture clause is specified and the acquire, acq_rel, or
  // seq_cst clause is specified then the strong flush on exit from the atomic
  // operation is also an acquire flush.
  switch (AO) {
  case llvm::AtomicOrdering::Acquire:
  case llvm::AtomicOrdering::AcquireRelease:
  case llvm::AtomicOrdering::SequentiallyConsistent:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::Acquire);
    break;
  case llvm::AtomicOrdering::Monotonic:
  case llvm::AtomicOrdering::Release:
    break;
  case llvm::AtomicOrdering::NotAtomic:
  case llvm::AtomicOrdering::Unordered:
    llvm_unreachable("Unexpected ordering.");
  }
  CGF.emitOMPSimpleStore(VLValue, Res, X->getType().getNonReferenceType(), Loc);
  CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, V);
}

static void emitOMPAtomicWriteExpr(CodeGenFunction &CGF, bool IsSeqCst,
                                   const Expr *X, const Expr *E,
                                   SourceLocation Loc) {
static void emitOMPAtomicWriteExpr(CodeGenFunction &CGF,
                                   llvm::AtomicOrdering AO, const Expr *X,
                                   const Expr *E, SourceLocation Loc) {
  // x = expr;
  assert(X->isLValue() && "X of 'omp atomic write' is not lvalue");
  emitSimpleAtomicStore(CGF, IsSeqCst, CGF.EmitLValue(X), CGF.EmitAnyExpr(E));
  emitSimpleAtomicStore(CGF, AO, CGF.EmitLValue(X), CGF.EmitAnyExpr(E));
  CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, X);
  // OpenMP, 2.12.6, atomic Construct
  // Any atomic construct with a seq_cst clause forces the atomically
  // performed operation to include an implicit flush operation without a
  // list.
  if (IsSeqCst)
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc);
  // OpenMP, 2.17.7, atomic Construct
  // If the write, update, or capture clause is specified and the release,
  // acq_rel, or seq_cst clause is specified then the strong flush on entry to
  // the atomic operation is also a release flush.
  switch (AO) {
  case llvm::AtomicOrdering::Release:
  case llvm::AtomicOrdering::AcquireRelease:
  case llvm::AtomicOrdering::SequentiallyConsistent:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::Release);
    break;
  case llvm::AtomicOrdering::Acquire:
  case llvm::AtomicOrdering::Monotonic:
    break;
  case llvm::AtomicOrdering::NotAtomic:
  case llvm::AtomicOrdering::Unordered:
    llvm_unreachable("Unexpected ordering.");
  }
}

static std::pair<bool, RValue> emitOMPAtomicRMW(CodeGenFunction &CGF, LValue X,
@@ -4258,10 +4286,10 @@ std::pair<bool, RValue> CodeGenFunction::EmitOMPAtomicSimpleUpdateExpr(
  return Res;
}

static void emitOMPAtomicUpdateExpr(CodeGenFunction &CGF, bool IsSeqCst,
                                    const Expr *X, const Expr *E,
                                    const Expr *UE, bool IsXLHSInRHSPart,
                                    SourceLocation Loc) {
static void emitOMPAtomicUpdateExpr(CodeGenFunction &CGF,
                                    llvm::AtomicOrdering AO, const Expr *X,
                                    const Expr *E, const Expr *UE,
                                    bool IsXLHSInRHSPart, SourceLocation Loc) {
  assert(isa<BinaryOperator>(UE->IgnoreImpCasts()) &&
         "Update expr in 'atomic update' must be a binary operator.");
  const auto *BOUE = cast<BinaryOperator>(UE->IgnoreImpCasts());
@@ -4274,9 +4302,6 @@ static void emitOMPAtomicUpdateExpr(CodeGenFunction &CGF, bool IsSeqCst,
  assert(X->isLValue() && "X of 'omp atomic update' is not lvalue");
  LValue XLValue = CGF.EmitLValue(X);
  RValue ExprRValue = CGF.EmitAnyExpr(E);
  llvm::AtomicOrdering AO = IsSeqCst
                                ? llvm::AtomicOrdering::SequentiallyConsistent
                                : llvm::AtomicOrdering::Monotonic;
  const auto *LHS = cast<OpaqueValueExpr>(BOUE->getLHS()->IgnoreImpCasts());
  const auto *RHS = cast<OpaqueValueExpr>(BOUE->getRHS()->IgnoreImpCasts());
  const OpaqueValueExpr *XRValExpr = IsXLHSInRHSPart ? LHS : RHS;
@@ -4289,12 +4314,24 @@ static void emitOMPAtomicUpdateExpr(CodeGenFunction &CGF, bool IsSeqCst,
  (void)CGF.EmitOMPAtomicSimpleUpdateExpr(
      XLValue, ExprRValue, BOUE->getOpcode(), IsXLHSInRHSPart, AO, Loc, Gen);
  CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, X);
  // OpenMP, 2.12.6, atomic Construct
  // Any atomic construct with a seq_cst clause forces the atomically
  // performed operation to include an implicit flush operation without a
  // list.
  if (IsSeqCst)
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc);
  // OpenMP, 2.17.7, atomic Construct
  // If the write, update, or capture clause is specified and the release,
  // acq_rel, or seq_cst clause is specified then the strong flush on entry to
  // the atomic operation is also a release flush.
  switch (AO) {
  case llvm::AtomicOrdering::Release:
  case llvm::AtomicOrdering::AcquireRelease:
  case llvm::AtomicOrdering::SequentiallyConsistent:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::Release);
    break;
  case llvm::AtomicOrdering::Acquire:
  case llvm::AtomicOrdering::Monotonic:
    break;
  case llvm::AtomicOrdering::NotAtomic:
  case llvm::AtomicOrdering::Unordered:
    llvm_unreachable("Unexpected ordering.");
  }
}

static RValue convertToType(CodeGenFunction &CGF, RValue Value,
@@ -4314,7 +4351,8 @@ static RValue convertToType(CodeGenFunction &CGF, RValue Value,
  llvm_unreachable("Must be a scalar or complex.");
}

static void emitOMPAtomicCaptureExpr(CodeGenFunction &CGF, bool IsSeqCst,
static void emitOMPAtomicCaptureExpr(CodeGenFunction &CGF,
                                     llvm::AtomicOrdering AO,
                                     bool IsPostfixUpdate, const Expr *V,
                                     const Expr *X, const Expr *E,
                                     const Expr *UE, bool IsXLHSInRHSPart,
@@ -4325,9 +4363,6 @@ static void emitOMPAtomicCaptureExpr(CodeGenFunction &CGF, bool IsSeqCst,
  LValue VLValue = CGF.EmitLValue(V);
  LValue XLValue = CGF.EmitLValue(X);
  RValue ExprRValue = CGF.EmitAnyExpr(E);
  llvm::AtomicOrdering AO = IsSeqCst
                                ? llvm::AtomicOrdering::SequentiallyConsistent
                                : llvm::AtomicOrdering::Monotonic;
  QualType NewVValType;
  if (UE) {
    // 'x' is updated with some additional value.
@@ -4391,32 +4426,53 @@ static void emitOMPAtomicCaptureExpr(CodeGenFunction &CGF, bool IsSeqCst,
  // Emit post-update store to 'v' of old/new 'x' value.
  CGF.emitOMPSimpleStore(VLValue, NewVVal, NewVValType, Loc);
  CGF.CGM.getOpenMPRuntime().checkAndEmitLastprivateConditional(CGF, V);
  // OpenMP, 2.12.6, atomic Construct
  // Any atomic construct with a seq_cst clause forces the atomically
  // performed operation to include an implicit flush operation without a
  // list.
  if (IsSeqCst)
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc);
  // OpenMP, 2.17.7, atomic Construct
  // If the write, update, or capture clause is specified and the release,
  // acq_rel, or seq_cst clause is specified then the strong flush on entry to
  // the atomic operation is also a release flush.
  // If the read or capture clause is specified and the acquire, acq_rel, or
  // seq_cst clause is specified then the strong flush on exit from the atomic
  // operation is also an acquire flush.
  switch (AO) {
  case llvm::AtomicOrdering::Release:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::Release);
    break;
  case llvm::AtomicOrdering::Acquire:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::Acquire);
    break;
  case llvm::AtomicOrdering::AcquireRelease:
  case llvm::AtomicOrdering::SequentiallyConsistent:
    CGF.CGM.getOpenMPRuntime().emitFlush(CGF, llvm::None, Loc,
                                         llvm::AtomicOrdering::AcquireRelease);
    break;
  case llvm::AtomicOrdering::Monotonic:
    break;
  case llvm::AtomicOrdering::NotAtomic:
  case llvm::AtomicOrdering::Unordered:
    llvm_unreachable("Unexpected ordering.");
  }
}

static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind,
                              bool IsSeqCst, bool IsPostfixUpdate,
                              llvm::AtomicOrdering AO, bool IsPostfixUpdate,
                              const Expr *X, const Expr *V, const Expr *E,
                              const Expr *UE, bool IsXLHSInRHSPart,
                              SourceLocation Loc) {
  switch (Kind) {
  case OMPC_read:
    emitOMPAtomicReadExpr(CGF, IsSeqCst, X, V, Loc);
    emitOMPAtomicReadExpr(CGF, AO, X, V, Loc);
    break;
  case OMPC_write:
    emitOMPAtomicWriteExpr(CGF, IsSeqCst, X, E, Loc);
    emitOMPAtomicWriteExpr(CGF, AO, X, E, Loc);
    break;
  case OMPC_unknown:
  case OMPC_update:
    emitOMPAtomicUpdateExpr(CGF, IsSeqCst, X, E, UE, IsXLHSInRHSPart, Loc);
    emitOMPAtomicUpdateExpr(CGF, AO, X, E, UE, IsXLHSInRHSPart, Loc);
    break;
  case OMPC_capture:
    emitOMPAtomicCaptureExpr(CGF, IsSeqCst, IsPostfixUpdate, V, X, E, UE,
    emitOMPAtomicCaptureExpr(CGF, AO, IsPostfixUpdate, V, X, E, UE,
                             IsXLHSInRHSPart, Loc);
    break;
  case OMPC_if:
@@ -4482,7 +4538,11 @@ static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind,
}

void CodeGenFunction::EmitOMPAtomicDirective(const OMPAtomicDirective &S) {
  bool IsSeqCst = S.getSingleClause<OMPSeqCstClause>();
  llvm::AtomicOrdering AO = llvm::AtomicOrdering::Monotonic;
  if (S.getSingleClause<OMPSeqCstClause>())
    AO = llvm::AtomicOrdering::SequentiallyConsistent;
  else if (S.getSingleClause<OMPAcqRelClause>())
    AO = llvm::AtomicOrdering::AcquireRelease;
  OpenMPClauseKind Kind = OMPC_unknown;
  for (const OMPClause *C : S.clauses()) {
    // Find first clause (skip seq_cst|acq_rel clause, if it is first).
@@ -4504,12 +4564,12 @@ void CodeGenFunction::EmitOMPAtomicDirective(const OMPAtomicDirective &S) {
    }
  }

  auto &&CodeGen = [&S, Kind, IsSeqCst, CS](CodeGenFunction &CGF,
  auto &&CodeGen = [&S, Kind, AO, CS](CodeGenFunction &CGF,
                                            PrePostActionTy &) {
    CGF.EmitStopPoint(CS);
    emitOMPAtomicExpr(CGF, Kind, IsSeqCst, S.isPostfixUpdate(), S.getX(),
                      S.getV(), S.getExpr(), S.getUpdateExpr(),
                      S.isXLHSInRHSPart(), S.getBeginLoc());
    emitOMPAtomicExpr(CGF, Kind, AO, S.isPostfixUpdate(), S.getX(), S.getV(),
                      S.getExpr(), S.getUpdateExpr(), S.isXLHSInRHSPart(),
                      S.getBeginLoc());
  };
  OMPLexicalScope Scope(*this, S, OMPD_unknown);
  CGM.getOpenMPRuntime().emitInlinedDirective(*this, OMPD_atomic, CodeGen);
+10 −9
Original line number Diff line number Diff line
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -fopenmp-version=50 -x c -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s

// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp-simd -x c -emit-llvm %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
// RUN: %clang_cc1 -fopenmp-simd -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp-simd -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp-simd -fopenmp-version=50 -x c -emit-llvm %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=50 -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=50 -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s
// SIMD-ONLY0-NOT: {{__kmpc|__tgt}}
// expected-no-diagnostics
#ifndef HEADER
@@ -939,7 +939,7 @@ int main() {
  iv = bfx4_packed.b += ldv;
// CHECK: load i64, i64*
// CHECK: [[EXPR:%.+]] = uitofp i64 %{{.+}} to float
// CHECK: [[I64VAL:%.+]] = load atomic i64, i64* bitcast (<2 x float>* [[DEST:@.+]] to i64*) monotonic
// CHECK: [[I64VAL:%.+]] = load atomic i64, i64* bitcast (<2 x float>* [[DEST:@.+]] to i64*) acquire
// CHECK: br label %[[CONT:.+]]
// CHECK: [[CONT]]
// CHECK: [[OLD_I64:%.+]] = phi i64 [ [[I64VAL]], %{{.+}} ], [ [[FAILED_I64_OLD_VAL:%.+]], %[[CONT]] ]
@@ -954,13 +954,14 @@ int main() {
// CHECK: [[NEW_VEC_VAL:%.+]] = insertelement <2 x float> [[VEC_VAL]], float [[VEC_ITEM_VAL]], i64 0
// CHECK: store <2 x float> [[NEW_VEC_VAL]], <2 x float>* [[LDTEMP1]]
// CHECK: [[NEW_I64:%.+]] = load i64, i64* [[BITCAST]]
// CHECK: [[RES:%.+]] = cmpxchg i64* bitcast (<2 x float>* [[DEST]] to i64*), i64 [[OLD_I64]], i64 [[NEW_I64]] monotonic monotonic
// CHECK: [[RES:%.+]] = cmpxchg i64* bitcast (<2 x float>* [[DEST]] to i64*), i64 [[OLD_I64]], i64 [[NEW_I64]] acq_rel acquire
// CHECK: [[FAILED_I64_OLD_VAL:%.+]] = extractvalue { i64, i1 } [[RES]], 0
// CHECK: [[FAIL_SUCCESS:%.+]] = extractvalue { i64, i1 } [[RES]], 1
// CHECK: br i1 [[FAIL_SUCCESS]], label %[[EXIT:.+]], label %[[CONT]]
// CHECK: [[EXIT]]
// CHECK: store float [[X]], float* @{{.+}},
#pragma omp atomic capture
// CHECK: call{{.*}} @__kmpc_flush(
#pragma omp atomic capture acq_rel
  {fv = float2x.x; float2x.x = ulv - float2x.x;}
// CHECK: [[EXPR:%.+]] = load double, double* @{{.+}},
// CHECK: [[OLD_VAL:%.+]] = call i32 @llvm.read_register.i32([[REG:metadata ![0-9]+]])
Loading