// RUN: %clang_cc1 -O1 -emit-llvm %s -o - -triple=x86_64-linux-gnu | FileCheck -DLIKELY=2000 -DUNLIKELY=1 %s
// RUN: %clang_cc1 -O1 -emit-llvm %s -triple=x86_64-linux-gnu -mllvm -likely-branch-weight=99 -mllvm -unlikely-branch-weight=42 -o - | FileCheck -DLIKELY=99 -DUNLIKELY=42 %s

extern volatile bool b;
extern volatile int i;
extern bool A();
extern bool B();

bool f() {
  // CHECK-LABEL: define{{.*}} zeroext i1 @_Z1fv
  // CHECK: br {{.*}} !prof !7
  if (b)
    [[likely]] {
      return A();
    }
  return B();
}

bool g() {
  // CHECK-LABEL: define{{.*}} zeroext i1 @_Z1gv
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] {
      return A();
    }

  return B();
}

bool h() {
  // CHECK-LABEL: define{{.*}} zeroext i1 @_Z1hv
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] return A();

  return B();
}

void NullStmt() {
  // CHECK-LABEL: define{{.*}}NullStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]];
  else {
    // Make sure the branches aren't optimized away.
    b = true;
  }
}

void IfStmt() {
  // CHECK-LABEL: define{{.*}}IfStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] if (B()) {}

  // CHECK-NOT: br {{.*}} !prof
  // CHECK: br {{.*}} !prof
  if (b) {
    if (B())
      [[unlikely]] { b = false; }
  }
}

void WhileStmt() {
  // CHECK-LABEL: define{{.*}}WhileStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] while (B()) {}

  // CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
  if (b)
    // CHECK: br {{.*}} !prof !7
    while (B())
      [[unlikely]] { b = false; }
}

void DoStmt() {
  // CHECK-LABEL: define{{.*}}DoStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] do {}
    while (B())
      ;

  // CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
  if (b)
    do
      [[unlikely]] {}
    while (B());
}

void ForStmt() {
  // CHECK-LABEL: define{{.*}}ForStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] for (; B();) {}

  // CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
  if (b)
    // CHECK: br {{.*}} !prof !7
    for (; B();)
      [[unlikely]] {}
}

void GotoStmt() {
  // CHECK-LABEL: define{{.*}}GotoStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] goto end;
  else {
    // Make sure the branches aren't optimized away.
    b = true;
  }
end:;
}

void ReturnStmt() {
  // CHECK-LABEL: define{{.*}}ReturnStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] return;
  else {
    // Make sure the branches aren't optimized away.
    b = true;
  }
}

void SwitchStmt() {
  // CHECK-LABEL: define{{.*}}SwitchStmt
  // CHECK: br {{.*}} !prof !8
  if (b)
    [[unlikely]] switch (i) {}
  else {
    // Make sure the branches aren't optimized away.
    b = true;
  }
  // CHECK-NOT: br {{.*}} %if.end{{.*}} !prof
  if (b)
    switch (i)
      [[unlikely]] {}
  else {
    // Make sure the branches aren't optimized away.
    b = true;
  }
}

// CHECK: !7 = !{!"branch_weights", i32 [[UNLIKELY]], i32 [[LIKELY]]}
// CHECK: !8 = !{!"branch_weights", i32 [[LIKELY]], i32 [[UNLIKELY]]}
