// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefixes=LLVM,BOTH
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefixes=OGCG,BOTH


bool hasSideEffect();

constexpr int globalVar = 5;

void assume1(), assume2(), assume3();

void usage(int i, int j) {
  // CIR: cir.func{{.*}}@_Z5usageii
  // BOTH: define{{.*}}@_Z5usageii

  // CIR: %[[I:.*]] = cir.alloca "i" {{.*}} init : !cir.ptr<!s32i>
  // CIR: %[[J:.*]] = cir.alloca "j" {{.*}} init : !cir.ptr<!s32i>
  // CIR: %[[LOCAL:.*]] = cir.alloca "local" {{.*}} init const : !cir.ptr<!s32i>
  //
  // BOTH: %[[I:.*]] = alloca i32
  // BOTH: %[[J:.*]] = alloca i32
  // BOTH: %[[LOCAL:.*]] = alloca i32

  [[assume(globalVar == i)]]
    ;

  // CIR: %[[GET_GLOB_VAR:.*]] = cir.get_global @_ZL9globalVar
  // CIR: %[[LOAD_GLOB_VAR:.*]] = cir.load {{.*}}%[[GET_GLOB_VAR]]
  // CIR: %[[LOAD_I:.*]] = cir.load {{.*}}%[[I]]
  // CIR: %[[CMP:.*]] = cir.cmp eq %[[LOAD_GLOB_VAR]], %[[LOAD_I]] 
  // CIR: cir.assume %[[CMP]] : !cir.bool
  // LLVM: %[[LOAD_GLOB_VAR:.*]] = load i32, ptr @_ZL9globalVar
  // BOTH: %[[LOAD_I:.*]] = load i32, ptr %[[I]]
  // LLVM: %[[CMP:.*]] = icmp eq i32 %[[LOAD_GLOB_VAR]], %[[LOAD_I]]
  // OGCG: %[[CMP:.*]] = icmp eq i32 5, %[[LOAD_I]]
  // BOTH: call void @llvm.assume(i1 %[[CMP]])
  assume1();
  // CIR: cir.call @_Z7assume1v()
  // BOTH: call void @_Z7assume1v()

  constexpr int local = 12;
  [[assume(j == i && j > local)]]
    ;
  // CIR: %[[LOAD_J:.*]] = cir.load {{.*}}%[[J]]
  // CIR: %[[LOAD_I:.*]] = cir.load {{.*}}%[[I]]
  // CIR: %[[J_EQ_I:.*]] = cir.cmp eq %[[LOAD_J]], %[[LOAD_I]]
  // CIR: %[[TERN:.*]] = cir.ternary(%[[J_EQ_I]], true {
  // CIR-NEXT: %[[LOAD_J:.*]] = cir.load {{.*}}%[[J]]
  // CIR-NEXT: %[[LOAD_LOCAL:.*]] = cir.load {{.*}}%[[LOCAL]]
  // CIR-NEXT: %[[J_GT_LOCAL:.*]] = cir.cmp gt %[[LOAD_J]], %[[LOAD_LOCAL]]
  // CIR-NEXT: cir.yield %[[J_GT_LOCAL]]
  // CIR-NEXT: }, false {
  // CIR-NEXT: %[[FALSE:.*]] = cir.const #false
  // CIR-NEXT: cir.yield %[[FALSE]]
  // CIR-NEXT: }) : (!cir.bool) -> !cir.bool
  // CIR: cir.assume %[[TERN:.*]]
  // 
  // BOTH: %[[LOAD_J:.*]] = load i32, ptr %[[J]]
  // BOTH: %[[LOAD_I:.*]] = load i32, ptr %[[I]]
  // BOTH: %[[J_EQ_I:.*]] = icmp eq i32 %[[LOAD_J]], %[[LOAD_I]]
  // BOTH: br i1 %[[J_EQ_I]], label %[[TRUE:.*]], label %[[FALSE:.*]]
  // BOTH: [[TRUE]]:
  // BOTH: %[[LOAD_J:.*]] = load i32, ptr %[[J]]
  // LLVM: %[[LOAD_LOCAL:.*]] = load i32, ptr %[[LOCAL]]
  // LLVM: %[[J_GT_LOCAL:.*]] = icmp sgt i32 %[[LOAD_J]], %[[LOAD_LOCAL]]
  // OGCG: %[[J_GT_LOCAL:.*]] = icmp sgt i32 %[[LOAD_J]], 12
  // LLVM: br label %[[DONE:.*]]
  // OGCG: br label %[[FALSE]]
  // LLVM: [[FALSE]]:
  // LLVM: br label %[[DONE]]
  // LLVM: [[DONE]]:
  // OGCG: [[FALSE]]:
  // LLVM: %[[TERN:.*]] = phi i1 [ false, %[[FALSE]] ], [ %[[J_GT_LOCAL]], %[[TRUE]] ]
  // OGCG: %[[TERN:.*]] = phi i1 [ false, %entry ], [ %[[J_GT_LOCAL]], %[[TRUE]] ]
  // BOTH: call void @llvm.assume(i1 %[[TERN]])
  assume2();
  // CIR: cir.call @_Z7assume2v()
  // BOTH: call void @_Z7assume2v()

  [[assume(hasSideEffect())]]
    ;
  // CIR-NOT: cir.assume
  // BOTH-NOT: call void @llvm.assume
  assume3();
  // CIR: cir.call @_Z7assume3v()
  // BOTH: call void @_Z7assume3v()
}

