Andy Kaylor 1e45ea12db
[CIR] Add support for function linkage and visibility (#145600)
This change adds support for function linkage and visibility and related
attributes. Most of the test changes are generalizations to allow
'dso_local' to be accepted where we aren't specifically testing for it.
Some tests based on CIR inputs have been updated to add 'private' to
function declarations where required by newly supported interfaces.

The dso-local.c test has been updated to add specific tests for
dso_local being set correctly, and a new test, func-linkage.cpp tests
other linkage settings.

This change sets `comdat` correctly in CIR, but it is not yet applied to
functions when lowering to LLVM IR. That will be handled in a later
change.
2025-06-25 10:59:30 -07:00

775 lines
33 KiB
C++

// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
void b0(int a, int b) {
int x = a * b;
x = x / b;
x = x % b;
x = x + b;
x = x - b;
x = x & b;
x = x ^ b;
x = x | b;
}
// CIR-LABEL: cir.func{{.*}} @_Z2b0ii(
// CIR: %{{.+}} = cir.binop(mul, %{{.+}}, %{{.+}}) nsw : !s32i
// CIR: %{{.+}} = cir.binop(div, %{{.+}}, %{{.+}}) : !s32i
// CIR: %{{.+}} = cir.binop(rem, %{{.+}}, %{{.+}}) : !s32i
// CIR: %{{.+}} = cir.binop(add, %{{.+}}, %{{.+}}) nsw : !s32i
// CIR: %{{.+}} = cir.binop(sub, %{{.+}}, %{{.+}}) nsw : !s32i
// CIR: %{{.+}} = cir.binop(and, %{{.+}}, %{{.+}}) : !s32i
// CIR: %{{.+}} = cir.binop(xor, %{{.+}}, %{{.+}}) : !s32i
// CIR: %{{.+}} = cir.binop(or, %{{.+}}, %{{.+}}) : !s32i
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z2b0ii(
// LLVM-SAME: i32 %[[A:.*]], i32 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i32
// LLVM: %[[B_ADDR:.*]] = alloca i32
// LLVM: %[[X:.*]] = alloca i32
// LLVM: store i32 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i32 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[MUL:.*]] = mul nsw i32 %[[A]], %[[B]]
// LLVM: store i32 %[[MUL]], ptr %[[X]]
// LLVM: %[[X1:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[DIV:.*]] = sdiv i32 %[[X1]], %[[B1]]
// LLVM: store i32 %[[DIV]], ptr %[[X]]
// LLVM: %[[X2:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[REM:.*]] = srem i32 %[[X2]], %[[B2]]
// LLVM: store i32 %[[REM]], ptr %[[X]]
// LLVM: %[[X3:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B3:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[ADD:.*]] = add nsw i32 %[[X3]], %[[B3]]
// LLVM: store i32 %[[ADD]], ptr %[[X]]
// LLVM: %[[X4:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B4:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[SUB:.*]] = sub nsw i32 %[[X4]], %[[B4]]
// LLVM: store i32 %[[SUB]], ptr %[[X]]
// LLVM: %[[X5:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B5:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[AND:.*]] = and i32 %[[X5]], %[[B5]]
// LLVM: store i32 %[[AND]], ptr %[[X]]
// LLVM: %[[X6:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B6:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[XOR:.*]] = xor i32 %[[X6]], %[[B6]]
// LLVM: store i32 %[[XOR]], ptr %[[X]]
// LLVM: %[[X7:.*]] = load i32, ptr %[[X]]
// LLVM: %[[B7:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[OR:.*]] = or i32 %[[X7]], %[[B7]]
// LLVM: store i32 %[[OR]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z2b0ii(i32 {{.*}} %a, i32 {{.*}} %b) {{.*}} {
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i32
// OGCG: %[[X:.*]] = alloca i32
// OGCG: store i32 %a, ptr %[[A_ADDR]]
// OGCG: store i32 %b, ptr %[[B_ADDR]]
// OGCG: %[[A:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[MUL:.*]] = mul nsw i32 %[[A]], %[[B]]
// OGCG: store i32 %[[MUL]], ptr %[[X]]
// OGCG: %[[X1:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[DIV:.*]] = sdiv i32 %[[X1]], %[[B1]]
// OGCG: store i32 %[[DIV]], ptr %[[X]]
// OGCG: %[[X2:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[REM:.*]] = srem i32 %[[X2]], %[[B2]]
// OGCG: store i32 %[[REM]], ptr %[[X]]
// OGCG: %[[X3:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B3:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[ADD:.*]] = add nsw i32 %[[X3]], %[[B3]]
// OGCG: store i32 %[[ADD]], ptr %[[X]]
// OGCG: %[[X4:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B4:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[SUB:.*]] = sub nsw i32 %[[X4]], %[[B4]]
// OGCG: store i32 %[[SUB]], ptr %[[X]]
// OGCG: %[[X5:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B5:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[AND:.*]] = and i32 %[[X5]], %[[B5]]
// OGCG: store i32 %[[AND]], ptr %[[X]]
// OGCG: %[[X6:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B6:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[XOR:.*]] = xor i32 %[[X6]], %[[B6]]
// OGCG: store i32 %[[XOR]], ptr %[[X]]
// OGCG: %[[X7:.*]] = load i32, ptr %[[X]]
// OGCG: %[[B7:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[OR:.*]] = or i32 %[[X7]], %[[B7]]
// OGCG: store i32 %[[OR]], ptr %[[X]]
// OGCG: ret void
void testFloatingPointBinOps(float a, float b) {
a * b;
a / b;
a + b;
a - b;
}
// CIR-LABEL: cir.func{{.*}} @_Z23testFloatingPointBinOpsff(
// CIR: cir.binop(mul, %{{.+}}, %{{.+}}) : !cir.float
// CIR: cir.binop(div, %{{.+}}, %{{.+}}) : !cir.float
// CIR: cir.binop(add, %{{.+}}, %{{.+}}) : !cir.float
// CIR: cir.binop(sub, %{{.+}}, %{{.+}}) : !cir.float
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z23testFloatingPointBinOpsff(
// LLVM-SAME: float %[[A:.*]], float %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca float, i64 1
// LLVM: %[[B_ADDR:.*]] = alloca float, i64 1
// LLVM: store float %[[A]], ptr %[[A_ADDR]]
// LLVM: store float %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load float, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load float, ptr %[[B_ADDR]]
// LLVM: fmul float %[[A1]], %[[B1]]
// LLVM: %[[A2:.*]] = load float, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load float, ptr %[[B_ADDR]]
// LLVM: fdiv float %[[A2]], %[[B2]]
// LLVM: %[[A3:.*]] = load float, ptr %[[A_ADDR]]
// LLVM: %[[B3:.*]] = load float, ptr %[[B_ADDR]]
// LLVM: fadd float %[[A3]], %[[B3]]
// LLVM: %[[A4:.*]] = load float, ptr %[[A_ADDR]]
// LLVM: %[[B4:.*]] = load float, ptr %[[B_ADDR]]
// LLVM: fsub float %[[A4]], %[[B4]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z23testFloatingPointBinOpsff(float {{.*}} %a, float {{.*}} %b)
// OGCG: %a.addr = alloca float
// OGCG: %b.addr = alloca float
// OGCG: store float %a, ptr %a.addr
// OGCG: store float %b, ptr %b.addr
// OGCG: %[[A1:.*]] = load float, ptr %a.addr
// OGCG: %[[B1:.*]] = load float, ptr %b.addr
// OGCG: fmul float %[[A1]], %[[B1]]
// OGCG: %[[A2:.*]] = load float, ptr %a.addr
// OGCG: %[[B2:.*]] = load float, ptr %b.addr
// OGCG: fdiv float %[[A2]], %[[B2]]
// OGCG: %[[A3:.*]] = load float, ptr %a.addr
// OGCG: %[[B3:.*]] = load float, ptr %b.addr
// OGCG: fadd float %[[A3]], %[[B3]]
// OGCG: %[[A4:.*]] = load float, ptr %a.addr
// OGCG: %[[B4:.*]] = load float, ptr %b.addr
// OGCG: fsub float %[[A4]], %[[B4]]
// OGCG: ret void
void signed_shift(int a, int b) {
int x = a >> b;
x = a << b;
}
// CIR-LABEL: cir.func{{.*}} @_Z12signed_shiftii(
// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !s32i{{.*}})
// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
// CIR: %[[B_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
// CIR: %[[X_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
// CIR: cir.store{{.*}} %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store{{.*}} %[[ARG1]], %[[B_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[A1:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B1:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[A2:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B2:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z12signed_shiftii
// LLVM-SAME: (i32 %[[A:.*]], i32 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i32
// LLVM: %[[B_ADDR:.*]] = alloca i32
// LLVM: %[[X:.*]] = alloca i32
// LLVM: store i32 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i32 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1]]
// LLVM: store i32 %[[ASHR]], ptr %[[X]]
// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]]
// LLVM: store i32 %[[SHL]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z12signed_shiftii
// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i32 {{.*}} %[[B:.*]])
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i32
// OGCG: %[[X:.*]] = alloca i32
// OGCG: store i32 %[[A]], ptr %[[A_ADDR]]
// OGCG: store i32 %[[B]], ptr %[[B_ADDR]]
// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1]]
// OGCG: store i32 %[[ASHR]], ptr %[[X]]
// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]]
// OGCG: store i32 %[[SHL]], ptr %[[X]]
// OGCG: ret void
void unsigned_shift(unsigned a, unsigned b) {
unsigned x = a >> b;
x = a << b;
}
// CIR-LABEL: cir.func{{.*}} @_Z14unsigned_shiftjj(
// CIR-SAME: %[[ARG0:.*]]: !u32i{{.*}}, %[[ARG1:.*]]: !u32i{{.*}})
// CIR: %[[A_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["a", init]
// CIR: %[[B_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["b", init]
// CIR: %[[X_PTR:.*]] = cir.alloca !u32i, !cir.ptr<!u32i>, ["x", init]
// CIR: cir.store{{.*}} %[[ARG0]], %[[A_PTR]] : !u32i, !cir.ptr<!u32i>
// CIR: cir.store{{.*}} %[[ARG1]], %[[B_PTR]] : !u32i, !cir.ptr<!u32i>
// CIR: %[[A1:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!u32i>, !u32i
// CIR: %[[B1:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!u32i>, !u32i
// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !u32i, %[[B1]] : !u32i) -> !u32i
// CIR: cir.store{{.*}} %[[ASHR]], %[[X_PTR]] : !u32i, !cir.ptr<!u32i>
// CIR: %[[A2:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!u32i>, !u32i
// CIR: %[[B2:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!u32i>, !u32i
// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !u32i, %[[B2]] : !u32i) -> !u32i
// CIR: cir.store{{.*}} %[[SHL]], %[[X_PTR]] : !u32i, !cir.ptr<!u32i>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z14unsigned_shiftjj
// LLVM-SAME: (i32 %[[A:.*]], i32 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i32
// LLVM: %[[B_ADDR:.*]] = alloca i32
// LLVM: %[[X:.*]] = alloca i32
// LLVM: store i32 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i32 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[ASHR:.*]] = lshr i32 %[[A1]], %[[B1]]
// LLVM: store i32 %[[ASHR]], ptr %[[X]]
// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]]
// LLVM: store i32 %[[SHL]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z14unsigned_shiftjj
// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i32 {{.*}} %[[B:.*]])
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i32
// OGCG: %[[X:.*]] = alloca i32
// OGCG: store i32 %[[A]], ptr %[[A_ADDR]]
// OGCG: store i32 %[[B]], ptr %[[B_ADDR]]
// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B1:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[ASHR:.*]] = lshr i32 %[[A1]], %[[B1]]
// OGCG: store i32 %[[ASHR]], ptr %[[X]]
// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B2:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2]]
// OGCG: store i32 %[[SHL]], ptr %[[X]]
// OGCG: ret void
void zext_shift_example(int a, unsigned char b) {
int x = a >> b;
x = a << b;
}
// CIR-LABEL: cir.func{{.*}} @_Z18zext_shift_exampleih(
// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !u8i{{.*}})
// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
// CIR: %[[B_PTR:.*]] = cir.alloca !u8i, !cir.ptr<!u8i>, ["b", init]
// CIR: %[[X_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
// CIR: cir.store{{.*}} %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store{{.*}} %[[ARG1]], %[[B_PTR]] : !u8i, !cir.ptr<!u8i>
// CIR: %[[A1:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B1:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!u8i>, !u8i
// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !u8i), !s32i
// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1_EXT]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[A2:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B2:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!u8i>, !u8i
// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !u8i), !s32i
// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2_EXT]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z18zext_shift_exampleih
// LLVM-SAME: (i32 %[[A:.*]], i8 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i32
// LLVM: %[[B_ADDR:.*]] = alloca i8
// LLVM: %[[X:.*]] = alloca i32
// LLVM: store i32 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i8 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B1_EXT:.*]] = zext i8 %[[B1]] to i32
// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]]
// LLVM: store i32 %[[ASHR]], ptr %[[X]]
// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B2_EXT:.*]] = zext i8 %[[B2]] to i32
// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]]
// LLVM: store i32 %[[SHL]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z18zext_shift_exampleih
// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i8 {{.*}} %[[B:.*]])
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i8
// OGCG: %[[X:.*]] = alloca i32
// OGCG: store i32 %[[A]], ptr %[[A_ADDR]]
// OGCG: store i8 %[[B]], ptr %[[B_ADDR]]
// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B1:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B1_EXT:.*]] = zext i8 %[[B1]] to i32
// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]]
// OGCG: store i32 %[[ASHR]], ptr %[[X]]
// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B2:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B2_EXT:.*]] = zext i8 %[[B2]] to i32
// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]]
// OGCG: store i32 %[[SHL]], ptr %[[X]]
// OGCG: ret void
void sext_shift_example(int a, signed char b) {
int x = a >> b;
x = a << b;
}
// CIR-LABEL: cir.func{{.*}} @_Z18sext_shift_exampleia(
// CIR-SAME: %[[ARG0:.*]]: !s32i{{.*}}, %[[ARG1:.*]]: !s8i{{.*}})
// CIR: %[[A_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
// CIR: %[[B_PTR:.*]] = cir.alloca !s8i, !cir.ptr<!s8i>, ["b", init]
// CIR: %[[X_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
// CIR: cir.store{{.*}} %[[ARG0]], %[[A_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store{{.*}} %[[ARG1]], %[[B_PTR]] : !s8i, !cir.ptr<!s8i>
// CIR: %[[A1:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B1:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s8i>, !s8i
// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !s8i), !s32i
// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s32i, %[[B1_EXT]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[ASHR]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: %[[A2:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s32i>, !s32i
// CIR: %[[B2:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s8i>, !s8i
// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !s8i), !s32i
// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s32i, %[[B2_EXT]] : !s32i) -> !s32i
// CIR: cir.store{{.*}} %[[SHL]], %[[X_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z18sext_shift_exampleia
// LLVM-SAME: (i32 %[[A:.*]], i8 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i32
// LLVM: %[[B_ADDR:.*]] = alloca i8
// LLVM: %[[X:.*]] = alloca i32
// LLVM: store i32 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i8 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B1_EXT:.*]] = sext i8 %[[B1]] to i32
// LLVM: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]]
// LLVM: store i32 %[[ASHR]], ptr %[[X]]
// LLVM: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B2_EXT:.*]] = sext i8 %[[B2]] to i32
// LLVM: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]]
// LLVM: store i32 %[[SHL]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z18sext_shift_exampleia
// OGCG-SAME: (i32 {{.*}} %[[A:.*]], i8 {{.*}} %[[B:.*]])
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i8
// OGCG: %[[X:.*]] = alloca i32
// OGCG: store i32 %[[A]], ptr %[[A_ADDR]]
// OGCG: store i8 %[[B]], ptr %[[B_ADDR]]
// OGCG: %[[A1:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B1:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B1_EXT:.*]] = sext i8 %[[B1]] to i32
// OGCG: %[[ASHR:.*]] = ashr i32 %[[A1]], %[[B1_EXT]]
// OGCG: store i32 %[[ASHR]], ptr %[[X]]
// OGCG: %[[A2:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B2:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B2_EXT:.*]] = sext i8 %[[B2]] to i32
// OGCG: %[[SHL:.*]] = shl i32 %[[A2]], %[[B2_EXT]]
// OGCG: store i32 %[[SHL]], ptr %[[X]]
// OGCG: ret void
void long_shift_example(long long a, short b) {
long long x = a >> b;
x = a << b;
}
// CIR-LABEL: cir.func{{.*}} @_Z18long_shift_examplexs(
// CIR-SAME: %[[ARG0:.*]]: !s64i{{.*}}, %[[ARG1:.*]]: !s16i{{.*}})
// CIR: %[[A_PTR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["a", init]
// CIR: %[[B_PTR:.*]] = cir.alloca !s16i, !cir.ptr<!s16i>, ["b", init]
// CIR: %[[X_PTR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["x", init]
// CIR: cir.store{{.*}} %[[ARG0]], %[[A_PTR]] : !s64i, !cir.ptr<!s64i>
// CIR: cir.store{{.*}} %[[ARG1]], %[[B_PTR]] : !s16i, !cir.ptr<!s16i>
// CIR: %[[A1:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s64i>, !s64i
// CIR: %[[B1:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s16i>, !s16i
// CIR: %[[B1_EXT:.*]] = cir.cast(integral, %[[B1]] : !s16i), !s32i
// CIR: %[[ASHR:.*]] = cir.shift(right, %[[A1]] : !s64i, %[[B1_EXT]] : !s32i) -> !s64i
// CIR: cir.store{{.*}} %[[ASHR]], %[[X_PTR]] : !s64i, !cir.ptr<!s64i>
// CIR: %[[A2:.*]] = cir.load{{.*}} %[[A_PTR]] : !cir.ptr<!s64i>, !s64i
// CIR: %[[B2:.*]] = cir.load{{.*}} %[[B_PTR]] : !cir.ptr<!s16i>, !s16i
// CIR: %[[B2_EXT:.*]] = cir.cast(integral, %[[B2]] : !s16i), !s32i
// CIR: %[[SHL:.*]] = cir.shift(left, %[[A2]] : !s64i, %[[B2_EXT]] : !s32i) -> !s64i
// CIR: cir.store{{.*}} %[[SHL]], %[[X_PTR]] : !s64i, !cir.ptr<!s64i>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z18long_shift_examplexs
// LLVM-SAME: (i64 %[[A:.*]], i16 %[[B:.*]])
// LLVM: %[[A_ADDR:.*]] = alloca i64
// LLVM: %[[B_ADDR:.*]] = alloca i16
// LLVM: %[[X:.*]] = alloca i64
// LLVM: store i64 %[[A]], ptr %[[A_ADDR]]
// LLVM: store i16 %[[B]], ptr %[[B_ADDR]]
// LLVM: %[[A1:.*]] = load i64, ptr %[[A_ADDR]]
// LLVM: %[[B1:.*]] = load i16, ptr %[[B_ADDR]]
// LLVM: %[[B1_SEXT:.*]] = sext i16 %[[B1]] to i32
// LLVM: %[[B1_ZEXT:.*]] = zext i32 %[[B1_SEXT]] to i64
// LLVM: %[[ASHR:.*]] = ashr i64 %[[A1]], %[[B1_ZEXT]]
// LLVM: store i64 %[[ASHR]], ptr %[[X]]
// LLVM: %[[A2:.*]] = load i64, ptr %[[A_ADDR]]
// LLVM: %[[B2:.*]] = load i16, ptr %[[B_ADDR]]
// LLVM: %[[B2_SEXT:.*]] = sext i16 %[[B2]] to i32
// LLVM: %[[B2_ZEXT:.*]] = zext i32 %[[B2_SEXT]] to i64
// LLVM: %[[SHL:.*]] = shl i64 %[[A2]], %[[B2_ZEXT]]
// LLVM: store i64 %[[SHL]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z18long_shift_examplexs
// OGCG-SAME: (i64 {{.*}} %[[A:.*]], i16 {{.*}} %[[B:.*]])
// OGCG: %[[A_ADDR:.*]] = alloca i64
// OGCG: %[[B_ADDR:.*]] = alloca i16
// OGCG: %[[X:.*]] = alloca i64
// OGCG: store i64 %[[A]], ptr %[[A_ADDR]]
// OGCG: store i16 %[[B]], ptr %[[B_ADDR]]
// OGCG: %[[A1:.*]] = load i64, ptr %[[A_ADDR]]
// OGCG: %[[B1:.*]] = load i16, ptr %[[B_ADDR]]
// OGCG: %[[B1_SEXT:.*]] = sext i16 %[[B1]] to i32
// OGCG: %[[B1_ZEXT:.*]] = zext i32 %[[B1_SEXT]] to i64
// OGCG: %[[ASHR:.*]] = ashr i64 %[[A1]], %[[B1_ZEXT]]
// OGCG: store i64 %[[ASHR]], ptr %[[X]]
// OGCG: %[[A2:.*]] = load i64, ptr %[[A_ADDR]]
// OGCG: %[[B2:.*]] = load i16, ptr %[[B_ADDR]]
// OGCG: %[[B2_SEXT:.*]] = sext i16 %[[B2]] to i32
// OGCG: %[[B2_ZEXT:.*]] = zext i32 %[[B2_SEXT]] to i64
// OGCG: %[[SHL:.*]] = shl i64 %[[A2]], %[[B2_ZEXT]]
// OGCG: store i64 %[[SHL]], ptr %[[X]]
// OGCG: ret void
void b1(bool a, bool b) {
bool x = a && b;
x = x || b;
}
// CIR-LABEL: cir.func{{.*}} @_Z2b1bb(
// CIR-SAME: %[[ARG0:.*]]: !cir.bool {{.*}}, %[[ARG1:.*]]: !cir.bool {{.*}})
// CIR: [[A:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a", init]
// CIR: [[B:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init]
// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init]
// CIR: cir.store %[[ARG0]], [[A]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: cir.store %[[ARG1]], [[B]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: [[AVAL:%[0-9]+]] = cir.load align(1) [[A]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: [[RES1:%[0-9]+]] = cir.ternary([[AVAL]], true {
// CIR: [[BVAL:%[0-9]+]] = cir.load align(1) [[B]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: cir.yield [[BVAL]] : !cir.bool
// CIR: }, false {
// CIR: [[FALSE:%[0-9]+]] = cir.const #false
// CIR: cir.yield [[FALSE]] : !cir.bool
// CIR: }) : (!cir.bool) -> !cir.bool
// CIR: cir.store align(1) [[RES1]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: [[XVAL:%[0-9]+]] = cir.load align(1) [[X]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: [[RES2:%[0-9]+]] = cir.ternary([[XVAL]], true {
// CIR: [[TRUE:%[0-9]+]] = cir.const #true
// CIR: cir.yield [[TRUE]] : !cir.bool
// CIR: }, false {
// CIR: [[BVAL2:%[0-9]+]] = cir.load align(1) [[B]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: cir.yield [[BVAL2]] : !cir.bool
// CIR: }) : (!cir.bool) -> !cir.bool
// CIR: cir.store align(1) [[RES2]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z2b1bb(
// LLVM-SAME: i1 %[[ARG0:.+]], i1 %[[ARG1:.+]])
// LLVM: %[[A_ADDR:.*]] = alloca i8
// LLVM: %[[B_ADDR:.*]] = alloca i8
// LLVM: %[[X:.*]] = alloca i8
// LLVM: %[[ZEXT0:.*]] = zext i1 %[[ARG0]] to i8
// LLVM: store i8 %[[ZEXT0]], ptr %[[A_ADDR]]
// LLVM: %[[ZEXT1:.*]] = zext i1 %[[ARG1]] to i8
// LLVM: store i8 %[[ZEXT1]], ptr %[[B_ADDR]]
// LLVM: %[[A_VAL:.*]] = load i8, ptr %[[A_ADDR]]
// LLVM: %[[A_BOOL:.*]] = trunc i8 %[[A_VAL]] to i1
// LLVM: br i1 %[[A_BOOL]], label %[[AND_TRUE:.+]], label %[[AND_FALSE:.+]]
// LLVM: [[AND_TRUE]]:
// LLVM: %[[B_VAL:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B_BOOL:.*]] = trunc i8 %[[B_VAL]] to i1
// LLVM: br label %[[AND_MERGE:.+]]
// LLVM: [[AND_FALSE]]:
// LLVM: br label %[[AND_MERGE]]
// LLVM: [[AND_MERGE]]:
// LLVM: %[[AND_PHI:.*]] = phi i1 [ false, %[[AND_FALSE]] ], [ %[[B_BOOL]], %[[AND_TRUE]] ]
// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8
// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]]
// LLVM: %[[X_VAL:.*]] = load i8, ptr %[[X]]
// LLVM: %[[X_BOOL:.*]] = trunc i8 %[[X_VAL]] to i1
// LLVM: br i1 %[[X_BOOL]], label %[[OR_TRUE:.+]], label %[[OR_FALSE:.+]]
// LLVM: [[OR_TRUE]]:
// LLVM: br label %[[OR_MERGE:.+]]
// LLVM: [[OR_FALSE]]:
// LLVM: %[[B_VAL2:.*]] = load i8, ptr %[[B_ADDR]]
// LLVM: %[[B_BOOL2:.*]] = trunc i8 %[[B_VAL2]] to i1
// LLVM: br label %[[OR_MERGE]]
// LLVM: [[OR_MERGE]]:
// LLVM: %[[OR_PHI:.*]] = phi i1 [ %[[B_BOOL2]], %[[OR_FALSE]] ], [ true, %[[OR_TRUE]] ]
// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8
// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z2b1bb
// OGCG-SAME: (i1 {{.*}} %[[ARG0:.+]], i1 {{.*}} %[[ARG1:.+]])
// OGCG: [[ENTRY:.*]]:
// OGCG: %[[A_ADDR:.*]] = alloca i8
// OGCG: %[[B_ADDR:.*]] = alloca i8
// OGCG: %[[X:.*]] = alloca i8
// OGCG: %[[ZEXT0:.*]] = zext i1 %[[ARG0]] to i8
// OGCG: store i8 %[[ZEXT0]], ptr %[[A_ADDR]]
// OGCG: %[[ZEXT1:.*]] = zext i1 %[[ARG1]] to i8
// OGCG: store i8 %[[ZEXT1]], ptr %[[B_ADDR]]
// OGCG: %[[A_VAL:.*]] = load i8, ptr %[[A_ADDR]]
// OGCG: %[[A_BOOL:.*]] = trunc i8 %[[A_VAL]] to i1
// OGCG: br i1 %[[A_BOOL]], label %[[AND_TRUE:.+]], label %[[AND_MERGE:.+]]
// OGCG: [[AND_TRUE]]:
// OGCG: %[[B_VAL:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B_BOOL:.*]] = trunc i8 %[[B_VAL]] to i1
// OGCG: br label %[[AND_MERGE:.+]]
// OGCG: [[AND_MERGE]]:
// OGCG: %[[AND_PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[B_BOOL]], %[[AND_TRUE]] ]
// OGCG: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8
// OGCG: store i8 %[[ZEXT_AND]], ptr %[[X]]
// OGCG: %[[X_VAL:.*]] = load i8, ptr %[[X]]
// OGCG: %[[X_BOOL:.*]] = trunc i8 %[[X_VAL]] to i1
// OGCG: br i1 %[[X_BOOL]], label %[[OR_MERGE:.+]], label %[[OR_FALSE:.+]]
// OGCG: [[OR_FALSE]]:
// OGCG: %[[B_VAL2:.*]] = load i8, ptr %[[B_ADDR]]
// OGCG: %[[B_BOOL2:.*]] = trunc i8 %[[B_VAL2]] to i1
// OGCG: br label %[[OR_MERGE]]
// OGCG: [[OR_MERGE]]:
// OGCG: %[[OR_PHI:.*]] = phi i1 [ true, %[[AND_MERGE]] ], [ %[[B_BOOL2]], %[[OR_FALSE]] ]
// OGCG: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8
// OGCG: store i8 %[[ZEXT_OR]], ptr %[[X]]
// OGCG: ret void
void b3(int a, int b, int c, int d) {
bool x = (a == b) && (c == d);
x = (a == b) || (c == d);
}
// CIR-LABEL: cir.func{{.*}} @_Z2b3iiii(
// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}, %[[ARG2:.*]]: !s32i {{.*}}, %[[ARG3:.*]]: !s32i {{.*}})
// CIR: [[A:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
// CIR: [[B:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init]
// CIR: [[C:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["c", init]
// CIR: [[D:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["d", init]
// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init]
// CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store %[[ARG2]], [[C]] : !s32i, !cir.ptr<!s32i>
// CIR: cir.store %[[ARG3]], [[D]] : !s32i, !cir.ptr<!s32i>
// CIR: [[AVAL1:%[0-9]+]] = cir.load align(4) [[A]] : !cir.ptr<!s32i>, !s32i
// CIR: [[BVAL1:%[0-9]+]] = cir.load align(4) [[B]] : !cir.ptr<!s32i>, !s32i
// CIR: [[CMP1:%[0-9]+]] = cir.cmp(eq, [[AVAL1]], [[BVAL1]]) : !s32i, !cir.bool
// CIR: [[AND_RESULT:%[0-9]+]] = cir.ternary([[CMP1]], true {
// CIR: [[CVAL1:%[0-9]+]] = cir.load align(4) [[C]] : !cir.ptr<!s32i>, !s32i
// CIR: [[DVAL1:%[0-9]+]] = cir.load align(4) [[D]] : !cir.ptr<!s32i>, !s32i
// CIR: [[CMP2:%[0-9]+]] = cir.cmp(eq, [[CVAL1]], [[DVAL1]]) : !s32i, !cir.bool
// CIR: cir.yield [[CMP2]] : !cir.bool
// CIR: }, false {
// CIR: [[FALSE:%[0-9]+]] = cir.const #false
// CIR: cir.yield [[FALSE]] : !cir.bool
// CIR: }) : (!cir.bool) -> !cir.bool
// CIR: cir.store align(1) [[AND_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: [[AVAL2:%[0-9]+]] = cir.load align(4) [[A]] : !cir.ptr<!s32i>, !s32i
// CIR: [[BVAL2:%[0-9]+]] = cir.load align(4) [[B]] : !cir.ptr<!s32i>, !s32i
// CIR: [[CMP3:%[0-9]+]] = cir.cmp(eq, [[AVAL2]], [[BVAL2]]) : !s32i, !cir.bool
// CIR: [[OR_RESULT:%[0-9]+]] = cir.ternary([[CMP3]], true {
// CIR: [[TRUE:%[0-9]+]] = cir.const #true
// CIR: cir.yield [[TRUE]] : !cir.bool
// CIR: }, false {
// CIR: [[CVAL2:%[0-9]+]] = cir.load align(4) [[C]] : !cir.ptr<!s32i>, !s32i
// CIR: [[DVAL2:%[0-9]+]] = cir.load align(4) [[D]] : !cir.ptr<!s32i>, !s32i
// CIR: [[CMP4:%[0-9]+]] = cir.cmp(eq, [[CVAL2]], [[DVAL2]]) : !s32i, !cir.bool
// CIR: cir.yield [[CMP4]] : !cir.bool
// CIR: }) : (!cir.bool) -> !cir.bool
// CIR: cir.store align(1) [[OR_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool>
// CIR: cir.return
// LLVM-LABEL: define{{.*}} void @_Z2b3iiii(
// LLVM-SAME: i32 %[[ARG0:.+]], i32 %[[ARG1:.+]], i32 %[[ARG2:.+]], i32 %[[ARG3:.+]])
// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1
// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1
// LLVM: %[[C_ADDR:.*]] = alloca i32, i64 1
// LLVM: %[[D_ADDR:.*]] = alloca i32, i64 1
// LLVM: %[[X:.*]] = alloca i8, i64 1
// LLVM: store i32 %[[ARG0]], ptr %[[A_ADDR]]
// LLVM: store i32 %[[ARG1]], ptr %[[B_ADDR]]
// LLVM: store i32 %[[ARG2]], ptr %[[C_ADDR]]
// LLVM: store i32 %[[ARG3]], ptr %[[D_ADDR]]
// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[CMP1:.*]] = icmp eq i32 %[[A_VAL]], %[[B_VAL]]
// LLVM: br i1 %[[CMP1]], label %[[AND_TRUE:.+]], label %[[AND_FALSE:.+]]
// LLVM: [[AND_TRUE]]:
// LLVM: %[[C_VAL:.*]] = load i32, ptr %[[C_ADDR]]
// LLVM: %[[D_VAL:.*]] = load i32, ptr %[[D_ADDR]]
// LLVM: %[[CMP2:.*]] = icmp eq i32 %[[C_VAL]], %[[D_VAL]]
// LLVM: br label %[[AND_MERGE:.+]]
// LLVM: [[AND_FALSE]]:
// LLVM: br label %[[AND_MERGE]]
// LLVM: [[AND_MERGE]]:
// LLVM: %[[AND_PHI:.*]] = phi i1 [ false, %[[AND_FALSE]] ], [ %[[CMP2]], %[[AND_TRUE]] ]
// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8
// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]]
// LLVM: %[[A_VAL2:.*]] = load i32, ptr %[[A_ADDR]]
// LLVM: %[[B_VAL2:.*]] = load i32, ptr %[[B_ADDR]]
// LLVM: %[[CMP3:.*]] = icmp eq i32 %[[A_VAL2]], %[[B_VAL2]]
// LLVM: br i1 %[[CMP3]], label %[[OR_TRUE:.+]], label %[[OR_FALSE:.+]]
// LLVM: [[OR_TRUE]]:
// LLVM: br label %[[OR_MERGE:.+]]
// LLVM: [[OR_FALSE]]:
// LLVM: %[[C_VAL2:.*]] = load i32, ptr %[[C_ADDR]]
// LLVM: %[[D_VAL2:.*]] = load i32, ptr %[[D_ADDR]]
// LLVM: %[[CMP4:.*]] = icmp eq i32 %[[C_VAL2]], %[[D_VAL2]]
// LLVM: br label %[[OR_MERGE]]
// LLVM: [[OR_MERGE]]:
// LLVM: %[[OR_PHI:.*]] = phi i1 [ %[[CMP4]], %[[OR_FALSE]] ], [ true, %[[OR_TRUE]] ]
// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8
// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]]
// LLVM: ret void
// OGCG-LABEL: define{{.*}} void @_Z2b3iiii(
// OGCG-SAME: i32 {{.*}} %[[ARG0:.+]], i32 {{.*}} %[[ARG1:.+]], i32 {{.*}} %[[ARG2:.+]], i32 {{.*}} %[[ARG3:.+]])
// OGCG: [[ENTRY:.*]]:
// OGCG: %[[A_ADDR:.*]] = alloca i32
// OGCG: %[[B_ADDR:.*]] = alloca i32
// OGCG: %[[C_ADDR:.*]] = alloca i32
// OGCG: %[[D_ADDR:.*]] = alloca i32
// OGCG: %[[X:.*]] = alloca i8
// OGCG: store i32 %[[ARG0]], ptr %[[A_ADDR]]
// OGCG: store i32 %[[ARG1]], ptr %[[B_ADDR]]
// OGCG: store i32 %[[ARG2]], ptr %[[C_ADDR]]
// OGCG: store i32 %[[ARG3]], ptr %[[D_ADDR]]
// OGCG: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[CMP1:.*]] = icmp eq i32 %[[A_VAL]], %[[B_VAL]]
// OGCG: br i1 %[[CMP1]], label %[[AND_TRUE:.+]], label %[[AND_MERGE:.+]]
// OGCG: [[AND_TRUE]]:
// OGCG: %[[C_VAL:.*]] = load i32, ptr %[[C_ADDR]]
// OGCG: %[[D_VAL:.*]] = load i32, ptr %[[D_ADDR]]
// OGCG: %[[CMP2:.*]] = icmp eq i32 %[[C_VAL]], %[[D_VAL]]
// OGCG: br label %[[AND_MERGE:.+]]
// OGCG: [[AND_MERGE]]:
// OGCG: %[[AND_PHI:.*]] = phi i1 [ false, %[[ENTRY]] ], [ %[[CMP2]], %[[AND_TRUE]] ]
// OGCG: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8
// OGCG: store i8 %[[ZEXT_AND]], ptr %[[X]]
// OGCG: %[[A_VAL2:.*]] = load i32, ptr %[[A_ADDR]]
// OGCG: %[[B_VAL2:.*]] = load i32, ptr %[[B_ADDR]]
// OGCG: %[[CMP3:.*]] = icmp eq i32 %[[A_VAL2]], %[[B_VAL2]]
// OGCG: br i1 %[[CMP3]], label %[[OR_MERGE:.+]], label %[[OR_FALSE:.+]]
// OGCG: [[OR_FALSE]]:
// OGCG: %[[C_VAL2:.*]] = load i32, ptr %[[C_ADDR]]
// OGCG: %[[D_VAL2:.*]] = load i32, ptr %[[D_ADDR]]
// OGCG: %[[CMP4:.*]] = icmp eq i32 %[[C_VAL2]], %[[D_VAL2]]
// OGCG: br label %[[OR_MERGE]]
// OGCG: [[OR_MERGE]]:
// OGCG: %[[OR_PHI:.*]] = phi i1 [ true, %[[AND_MERGE]] ], [ %[[CMP4]], %[[OR_FALSE]] ]
// OGCG: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8
// OGCG: store i8 %[[ZEXT_OR]], ptr %[[X]]
// OGCG: ret void