Files
llvm-project/clang/test/CIR/CodeGen/three-way-cmp.cpp
Erich Keane c3d7f77a2b [CIR] Lower PsuedoObjectExpr LValues (#192108)
This ends up being pretty much copy/paste from classic-codegen, so it
doesn't have anything particularly novel.

I DID switch the return type of the helper function to be a variant
instead of a manually-put-together pair, and switched to range-for, but
otherwise it should be identical.

However, I was uanble to reproduce a few of the branches, so NYIs were
left in place until we can figure them out. At least some of them are
going to be for RValue versions.
2026-04-15 05:59:49 -07:00

289 lines
18 KiB
C++

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
// RUN: FileCheck %s --input-file=%t-before.cir --check-prefix=BEFORE,BOTH
// RUN: FileCheck %s --input-file=%t.cir --check-prefix=AFTER
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER,BOTH
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-llvm %s -o %t.ll 2>&1
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s -o %t-og.ll 2>&1
// RUN: FileCheck --input-file=%t-og.ll %s -check-prefix=OGCG
#include "./Inputs/std-compare.h"
// BEFORE: #cmpinfo_partial_ltn1eq0gt1unn127 = #cir.cmp3way_info<partial, lt = -1, eq = 0, gt = 1, unordered = -127>
// BEFORE: #cmpinfo_strong_ltn1eq0gt1 = #cir.cmp3way_info<strong, lt = -1, eq = 0, gt = 1>
// BEFORE: !rec_std3A3A__13A3Apartial_ordering = !cir.record<class "std::__1::partial_ordering" {!s8i}>
// BEFORE: !rec_std3A3A__13A3Astrong_ordering = !cir.record<class "std::__1::strong_ordering" {!s8i}>
auto three_way_strong(int x, int y) {
return x <=> y;
}
// BEFORE: cir.func {{.*}} @_Z16three_way_strongii
// BEFORE: %{{.+}} = cir.cmp3way #cmpinfo_strong_ltn1eq0gt1 %{{.+}}, %{{.+}} : !s32i -> !s8i
// BEFORE: }
// AFTER: cir.func {{.*}} @_Z16three_way_strongii{{.*}}
// AFTER: %[[LHS:.*]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i{{.*}}
// AFTER-NEXT: %[[RHS:.*]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i{{.*}}
// AFTER-NEXT: %[[LT:.*]] = cir.const #cir.int<-1> : !s8i{{.*}}
// AFTER-NEXT: %[[EQ:.*]] = cir.const #cir.int<0> : !s8i{{.*}}
// AFTER-NEXT: %[[GT:.*]] = cir.const #cir.int<1> : !s8i{{.*}}
// AFTER-NEXT: %[[CMP_LT:.*]] = cir.cmp lt %[[LHS]], %[[RHS]] : !s32i{{.*}}
// AFTER-NEXT: %[[SELECT_1:.*]] = cir.select if %[[CMP_LT]] then %[[LT]] else %[[GT]] : (!cir.bool, !s8i, !s8i) -> !s8i{{.*}}
// AFTER-NEXT: %[[CMP_EQ:.*]] = cir.cmp eq %[[LHS]], %[[RHS]] : !s32i{{.*}}
// AFTER-NEXT: %[[SELECT_2:.*]] = cir.select if %[[CMP_EQ]] then %[[EQ]] else %[[SELECT_1]] : (!cir.bool, !s8i, !s8i) -> !s8i{{.*}}
// AFTER: %{{.+}} = cir.load %{{.+}} : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, !rec_std3A3A__13A3Astrong_ordering{{.*}}
// AFTER-NEXT: cir.return %{{.+}} : !rec_std3A3A__13A3Astrong_ordering{{.*}}
// LLVM: %[[LHS:.*]] = load i32, ptr %{{.*}}, align 4
// LLVM-NEXT: %[[RHS:.*]] = load i32, ptr %{{.*}}, align 4
// LLVM-NEXT: %[[CMP_LT:.*]] = icmp slt i32 %[[LHS]], %[[RHS]]
// LLVM-NEXT: %[[SEL_LT_GT:.*]] = select i1 %[[CMP_LT]], i8 -1, i8 1
// LLVM-NEXT: %[[CMP_EQ:.*]] = icmp eq i32 %[[LHS]], %[[RHS]]
// LLVM-NEXT: %[[RES:.*]] = select i1 %[[CMP_EQ]], i8 0, i8 %[[SEL_LT_GT]]
// OGCG: %[[LHS:.*]] = load i32, ptr %{{.*}}, align 4
// OGCG-NEXT: %[[RHS:.*]] = load i32, ptr %{{.*}}, align 4
// OGCG-NEXT: %[[CMP_LT:.*]] = icmp slt i32 %[[LHS]], %[[RHS]]
// OGCG-NEXT: %[[SEL_EQ_LT:.*]] = select i1 %[[CMP_LT]], i8 -1, i8 1
// OGCG-NEXT: %[[CMP_EQ:.*]] = icmp eq i32 %[[LHS]], %[[RHS]]
// OGCG-NEXT: %[[RES:.*]] = select i1 %[[CMP_EQ]], i8 0, i8 %[[SEL_EQ_LT]]
auto three_way_partial(float x, float y) {
return x <=> y;
}
// BEFORE: cir.func {{.*}} @_Z17three_way_partialff
// BEFORE: %{{.+}} = cir.cmp3way #cmpinfo_partial_ltn1eq0gt1unn127 %{{.+}}, %{{.+}} : !cir.float -> !s8i
// BEFORE: }
// AFTER: cir.func {{.*}} @_Z17three_way_partialff{{.*}}
// AFTER: %[[LHS:.*]] = cir.load align(4) %{{.+}} : !cir.ptr<!cir.float>, !cir.float{{.*}}
// AFTER-NEXT: %[[RHS:.*]] = cir.load align(4) %{{.+}} : !cir.ptr<!cir.float>, !cir.float{{.*}}
// AFTER-NEXT: %[[LT:.*]] = cir.const #cir.int<-1> : !s8i{{.*}}
// AFTER-NEXT: %[[EQ:.*]] = cir.const #cir.int<0> : !s8i{{.*}}
// AFTER-NEXT: %[[GT:.*]] = cir.const #cir.int<1> : !s8i{{.*}}
// AFTER-NEXT: %[[UNORDERED:.*]] = cir.const #cir.int<-127> : !s8i{{.*}}
// AFTER-NEXT: %[[CMP_EQ:.*]] = cir.cmp eq %[[LHS]], %[[RHS]] : !cir.float{{.*}}
// AFTER-NEXT: %[[SELECT_1:.*]] = cir.select if %[[CMP_EQ]] then %[[EQ]] else %[[UNORDERED]] : (!cir.bool, !s8i, !s8i) -> !s8i{{.*}}
// AFTER-NEXT: %[[CMP_GT:.*]] = cir.cmp gt %[[LHS]], %[[RHS]] : !cir.float{{.*}}
// AFTER-NEXT: %[[SELECT_2:.*]] = cir.select if %[[CMP_GT]] then %[[GT]] else %[[SELECT_1]] : (!cir.bool, !s8i, !s8i) -> !s8i{{.*}}
// AFTER-NEXT: %[[CMP_LT:.*]] = cir.cmp lt %[[LHS]], %[[RHS]] : !cir.float{{.*}}
// AFTER-NEXT: %[[SELECT_3:.*]] = cir.select if %[[CMP_LT]] then %[[LT]] else %[[SELECT_2]] : (!cir.bool, !s8i, !s8i) -> !s8i{{.*}}
// AFTER: %{{.+}} = cir.load %{{.+}} : !cir.ptr<!rec_std3A3A__13A3Apartial_ordering>, !rec_std3A3A__13A3Apartial_ordering{{.*}}
// AFTER-NEXT: cir.return %{{.+}} : !rec_std3A3A__13A3Apartial_ordering{{.*}}
// LLVM: %[[LHS:.*]] = load float, ptr %{{.*}}, align 4
// LLVM: %[[RHS:.*]] = load float, ptr %{{.*}}, align 4
// LLVM: %[[CMP_EQ:.*]] = fcmp oeq float %[[LHS]], %[[RHS]]
// LLVM: %[[SEL_EQ_UN:.*]] = select i1 %[[CMP_EQ]], i8 0, i8 -127
// LLVM: %[[CMP_GT:.*]] = fcmp ogt float %[[LHS]], %[[RHS]]
// LLVM: %[[SEL_GT_EQUN:.*]] = select i1 %[[CMP_GT]], i8 1, i8 %[[SEL_EQ_UN]]
// LLVM: %[[CMP_LT:.*]] = fcmp olt float %[[LHS]], %[[RHS]]
// LLVM: %[[RES:.*]] = select i1 %[[CMP_LT]], i8 -1, i8 %[[SEL_GT_EQUN]]
// OGCG: %[[LHS:.*]] = load float, ptr %{{.*}}, align 4
// OGCG: %[[RHS:.*]] = load float, ptr %{{.*}}, align 4
// OGCG: %[[CMP_EQ:.*]] = fcmp oeq float %[[LHS]], %[[RHS]]
// OGCG: %[[SEL_EQ_UN:.*]] = select i1 %[[CMP_EQ]], i8 0, i8 -127
// OGCG: %[[CMP_GT:.*]] = fcmp ogt float %[[LHS]], %[[RHS]]
// OGCG: %[[SEL_GT_EQUN:.*]] = select i1 %[[CMP_GT]], i8 1, i8 %[[SEL_EQ_UN]]
// OGCG: %[[CMP_LT:.*]] = fcmp olt float %[[LHS]], %[[RHS]]
// OGCG: %[[RES:.*]] = select i1 %[[CMP_LT]], i8 -1, i8 %[[SEL_GT_EQUN]]
struct Member {
bool operator==(const Member&) const;
bool operator<(const Member&) const;
};
struct HasMember {
Member m;
std::strong_ordering operator<=>(const HasMember&) const = default;
};
void use_pseudo_ordering(HasMember m1, HasMember m2) {
// BOTH: cir.func {{.*}}@_ZNK9HasMemberssERKS_(%{{.*}}: !cir.ptr<!rec_HasMember>{{.*}}, %{{.*}}: !cir.ptr<!rec_HasMember>{{.*}}) -> !rec_std3A3A__13A3Astrong_ordering
// BOTH: %[[LHS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasMember>, !cir.ptr<!cir.ptr<!rec_HasMember>>, ["this", init]
// BOTH: %[[RHS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasMember>, !cir.ptr<!cir.ptr<!rec_HasMember>>, ["", init, const]
// BOTH: %[[RET_ALLOCA:.*]] = cir.alloca !rec_std3A3A__13A3Astrong_ordering, !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, ["__retval"]
// BOTH: %[[LHS_LOAD:.*]] = cir.load deref %[[LHS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_HasMember>>, !cir.ptr<!rec_HasMember>
// BOTH: cir.scope {
// BOTH: %[[CMP_RES:.*]] = cir.alloca !rec_std3A3A__13A3Astrong_ordering, !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, ["cmp", init]
// BOTH: %[[CMP_TEMP:.*]] = cir.alloca !rec_std3A3A__13A3Astrong_ordering, !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, ["agg.tmp0"]
// BOTH: %[[LHS_MEMBER:.*]] = cir.cast bitcast %[[LHS_LOAD]] : !cir.ptr<!rec_HasMember> -> !cir.ptr<!rec_Member>
// BOTH: %[[RHS_LOAD:.*]] = cir.load %[[RHS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_HasMember>>, !cir.ptr<!rec_HasMember>
// BOTH: %[[RHS_MEMBER:.*]] = cir.cast bitcast %[[RHS_LOAD]] : !cir.ptr<!rec_HasMember> -> !cir.ptr<!rec_Member>
// BOTH: %[[EQ_RES:.*]] = cir.call @_ZNK6MembereqERKS_(%[[LHS_MEMBER]], %[[RHS_MEMBER]]) : (!cir.ptr<!rec_Member> {{.*}}, !cir.ptr<!rec_Member> {{.*}})
// BOTH: %[[TOP_TERN_RES:.*]] = cir.ternary(%[[EQ_RES]], true {
// BOTH: %[[EQ_GLOB:.*]] = cir.get_global @_ZNSt3__115strong_ordering5equalE : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.yield %[[EQ_GLOB]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: }, false {
// BOTH: %[[LT_RES:.*]] = cir.call @_ZNK6MemberltERKS_(%[[LHS_MEMBER]], %[[RHS_MEMBER]]) : (!cir.ptr<!rec_Member> {{.*}}, !cir.ptr<!rec_Member> {{.*}}) -> (!cir.bool {{.*}})
// BOTH: %[[TERN_LT_RES:.*]] = cir.ternary(%[[LT_RES]], true {
// BOTH: %[[LT_GLOB:.*]] = cir.get_global @_ZNSt3__115strong_ordering4lessE : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.yield %[[LT_GLOB]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: }, false {
// BOTH: %[[GT_GLOB:.*]] = cir.get_global @_ZNSt3__115strong_ordering7greaterE : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.yield %[[GT_GLOB]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: }) : (!cir.bool) -> !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.yield %[[TERN_LT_RES]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: }) : (!cir.bool) -> !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.copy %[[TOP_TERN_RES]] to %[[CMP_RES]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.copy %[[CMP_RES]] to %[[CMP_TEMP]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: %[[UNSPEC_TEMP:.*]] = cir.const #cir.const_record<{#cir.int<0> : !s64i, #cir.int<0> : !s64i}>
// BOTH: %[[CMP_TEMP_LOAD:.*]] = cir.load {{.*}}%[[CMP_TEMP]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, !rec_std3A3A__13A3Astrong_ordering
// BOTH: %[[SO_NE_RES:.*]] = cir.call @_ZNSt3__1neENS_15strong_orderingEMNS_19_CmpUnspecifiedTypeEFvvE(%14, %[[UNSPEC_TEMP]])
// BOTH: cir.if %[[SO_NE_RES]] {
// BOTH: cir.copy %[[CMP_RES]] to %[[RET_ALLOCA]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: %[[RET_LOAD:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, !rec_std3A3A__13A3Astrong_ordering
// BOTH: cir.return %[[RET_LOAD]] : !rec_std3A3A__13A3Astrong_ordering
// BOTH: }
// BOTH: }
// BOTH: %[[EQ_GLOB:.*]] = cir.get_global @_ZNSt3__115strong_ordering5equalE : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: cir.copy %[[EQ_GLOB]] to %[[RET_ALLOCA]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
// BOTH: %[[RET_LOAD:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, !rec_std3A3A__13A3Astrong_ordering
// BOTH: cir.return %[[RET_LOAD]] : !rec_std3A3A__13A3Astrong_ordering
// BOTH: cir.func {{.*}} @_Z19use_pseudo_ordering9HasMemberS_(%[[M1:.*]]: !rec_HasMember{{.*}}, %[[M2:.*]]: !rec_HasMember{{.*}})
// BOTH: %[[M1_ALLOCA:.*]] = cir.alloca !rec_HasMember, !cir.ptr<!rec_HasMember>, ["m1", init]
// BOTH: %[[M2_ALLOCA:.*]] = cir.alloca !rec_HasMember, !cir.ptr<!rec_HasMember>, ["m2", init] {alignment = 1 : i64}
// BOTH: %[[G_ALLOCA:.*]] = cir.alloca !rec_std3A3A__13A3Astrong_ordering, !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>, ["g", init]
// BOTH: %[[CALL_RES:.*]] = cir.call @_ZNK9HasMemberssERKS_(%[[M1_ALLOCA]], %[[M2_ALLOCA]]) : (!cir.ptr<!rec_HasMember> {{.*}}, !cir.ptr<!rec_HasMember> {{.*}}) -> !rec_std3A3A__13A3Astrong_ordering
// BOTH: cir.store {{.*}}%[[CALL_RES]], %[[G_ALLOCA]] : !rec_std3A3A__13A3Astrong_ordering, !cir.ptr<!rec_std3A3A__13A3Astrong_ordering>
std::strong_ordering g = (m1 <=> m2);
// LLVM: define {{.*}} @_ZNK9HasMemberssERKS_(ptr {{.*}}, ptr {{.*}})
// LLVM: %[[TMP_SO:.*]] = alloca %"class.std::__1::strong_ordering"
// LLVM: %[[RET_ALLOCA:.*]] = alloca %"class.std::__1::strong_ordering"
// LLVM: %[[LHS_ALLOCA:.*]] = alloca ptr
// LLVM: %[[RHS_ALLOCA:.*]] = alloca ptr
// LLVM: %[[TMP_SO2:.*]] = alloca %"class.std::__1::strong_ordering"
// LLVM: %[[LHS_LOAD:.*]] = load ptr, ptr %[[LHS_ALLOCA]]
//
// LLVM: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
// LLVM: %[[EQ_RES:.*]] = call noundef i1 @_ZNK6MembereqERKS_(ptr {{.*}}%[[LHS_LOAD]], ptr {{.*}}%[[RHS_LOAD]])
// LLVM: br i1 %[[EQ_RES]], label %[[EQ_TRUE:.*]], label %[[EQ_FALSE:.*]]
//
// LLVM: [[EQ_TRUE]]:
// LLVM: br label %20
//
// LLVM: [[EQ_FALSE]]:
// LLVM: %[[LT_RES:.*]] = call noundef i1 @_ZNK6MemberltERKS_(ptr {{.*}}%[[LHS_LOAD]], ptr {{.*}}%[[RHS_LOAD]])
// LLVM: br i1 %[[LT_RES]], label %[[LT_TRUE:.*]], label %[[LT_FALSE:.*]]
//
// LLVM: [[LT_TRUE]]:
// LLVM: br label %[[AFTER_LT:.*]]
//
// LLVM: [[LT_FALSE]]:
// LLVM: br label %[[AFTER_LT]]
//
// LLVM: [[AFTER_LT]]:
// LLVM: %[[LT_RES_PHI:.*]] = phi ptr [ @_ZNSt3__115strong_ordering7greaterE, %[[LT_FALSE]] ], [ @_ZNSt3__115strong_ordering4lessE, %[[LT_TRUE]] ]
// LLVM: br label %[[AFTER_LT_CTD:.*]]
//
// LLVM: [[AFTER_LT_CTD]]:
// LLVM: br label %[[AFTER_CMPS:.*]]
//
// LLVM: [[AFTER_CMPS]]:
// LLVM: %[[CMP_RES:.*]] = phi ptr [ %[[LT_RES_PHI]], %[[AFTER_LT_CTD]] ], [ @_ZNSt3__115strong_ordering5equalE, %[[EQ_TRUE]] ]
// LLVM: br label %[[AFTER_CMPS_CTD:.*]]
//
// LLVM: [[AFTER_CMPS_CTD]]:
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[TMP_SO]], ptr %[[CMP_RES]], i64 1, i1 false)
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[RET_ALLOCA]], ptr %[[TMP_SO]], i64 1, i1 false)
// LLVM: %[[RET_LOAD:.*]] = load %"class.std::__1::strong_ordering", ptr %[[RET_ALLOCA]]
// LLVM: %[[SO_NE_RES:.*]] = call noundef i1 @_ZNSt3__1neENS_15strong_orderingEMNS_19_CmpUnspecifiedTypeEFvvE(%"class.std::__1::strong_ordering" %[[RET_LOAD]],
// LLVM: br i1 %[[SO_NE_RES]], label %[[SO_NE_RES_TRUE:.*]], label %[[SO_NE_RES_FALSE:.*]]
//
// LLVM: [[SO_NE_RES_TRUE]]:
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[TMP_SO2]], ptr %[[TMP_SO]], i64 1, i1 false)
// LLVM: %[[TMP_SO2_LOAD:.*]] = load %"class.std::__1::strong_ordering", ptr %[[TMP_SO2]]
// LLVM: ret %"class.std::__1::strong_ordering" %[[TMP_SO2_LOAD]]
//
// LLVM: [[SO_NE_RES_FALSE]]:
// LLVM: br label %[[SO_NE_RES_FALSE_CTD:.*]]
//
// LLVM: [[SO_NE_RES_FALSE_CTD]]:
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr %[[TMP_SO2]], ptr @_ZNSt3__115strong_ordering5equalE, i64 1, i1 false)
// LLVM: %[[TMP_SO2_LOAD:.*]] = load %"class.std::__1::strong_ordering", ptr %[[TMP_SO2]]
// LLVM: ret %"class.std::__1::strong_ordering" %[[TMP_SO2_LOAD]]
// LLVM: }
// LLVM: define {{.*}}void @_Z19use_pseudo_ordering9HasMemberS_(%struct.HasMember %{{.*}}, %struct.HasMember %{{.*}}) #0 {
// LLVM: %[[M1_ALLOCA:.*]] = alloca %struct.HasMember
// LLVM: %[[M2_ALLOCA:.*]] = alloca %struct.HasMember
// LLVM: %[[G_ALLOCA:.*]] = alloca %"class.std::__1::strong_ordering"
// LLVM: %[[CALL_RES:.*]] = call %"class.std::__1::strong_ordering" @_ZNK9HasMemberssERKS_(ptr {{.*}}%[[M1_ALLOCA]], ptr {{.*}}%[[M2_ALLOCA]])
// LLVM: store %"class.std::__1::strong_ordering" %[[CALL_RES]], ptr %[[G_ALLOCA]]
// LLVM: ret void
// LLVM: }
// OGCG: define {{.*}}void @_Z19use_pseudo_ordering9HasMemberS_()
// OGCG: %[[M1_ALLOCA:.*]] = alloca %struct.HasMember
// OGCG: %[[M2_ALLOCA:.*]] = alloca %struct.HasMember
// OGCG: %[[G_ALLOCA:.*]] = alloca %"class.std::__1::strong_ordering"
// OGCG: %[[CALL_RES:.*]] = call i8 @_ZNK9HasMemberssERKS_(ptr {{.*}}%[[M1_ALLOCA]], ptr {{.*}}%[[M2_ALLOCA]])
// OGCG: %[[G_COERCE:.*]] = getelementptr inbounds nuw %"class.std::__1::strong_ordering", ptr %[[G_ALLOCA]], i32 0, i32 0
// OGCG: store i8 %[[CALL_RES]], ptr %[[G_COERCE]]
// OGCG: ret void
// OGCG: }
//
// OGCG: define {{.*}}i8 @_ZNK9HasMemberssERKS_(ptr {{.*}}, ptr {{.*}})
// OGCG: %[[RET_ALLOCA:.*]] = alloca %"class.std::__1::strong_ordering"
// OGCG: %[[LHS_ALLOCA:.*]] = alloca ptr
// OGCG: %[[RHS_ALLOCA:.*]] = alloca ptr
// OGCG: %[[CMP_RES:.*]] = alloca %"class.std::__1::strong_ordering"
// OGCG: %[[CMP_TEMP:.*]] = alloca %"class.std::__1::strong_ordering"
// OGCG: %[[COERCE_ALLOCA:.*]] = alloca { i64, i64 }
// OGCG: %[[LHS_LOAD:.*]] = load ptr, ptr %[[LHS_ALLOCA]]
// OGCG: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
// OGCG: %[[EQ_RES:.*]] = call {{.*}}i1 @_ZNK6MembereqERKS_(ptr {{.*}}%[[LHS_LOAD]], ptr {{.*}}%[[RHS_LOAD]])
// OGCG: br i1 %[[EQ_RES]], label %[[EQ_TRUE:.*]], label %[[EQ_FALSE:.*]]
//
// OGCG: [[EQ_TRUE]]:
// OGCG: br label %[[AFTER_COND:.*]]
//
// OGCG: [[EQ_FALSE]]:
// OGCG: %[[LT_RES:.*]] = call {{.*}}i1 @_ZNK6MemberltERKS_(ptr {{.*}}%[[LHS_LOAD]], ptr {{.*}}%[[RHS_LOAD]])
// OGCG: br i1 %[[LT_RES]], label %[[LT_TRUE:.*]], label %[[LT_FALSE:.*]]
//
// OGCG: [[LT_TRUE]]:
// OGCG: br label %[[AFTER_LT:.*]]
//
// OGCG: [[LT_FALSE]]:
// OGCG: br label %[[AFTER_LT]]
//
// OGCG: [[AFTER_LT]]:
// OGCG: %[[AFTER_LT_RES:.*]] = phi ptr [ @_ZNSt3__115strong_ordering4lessE, %[[LT_TRUE]] ], [ @_ZNSt3__115strong_ordering7greaterE, %[[LT_FALSE]] ]
// OGCG: br label %cond.end5
//
// OGCG: [[AFTER_COND]]:
// OGCG: %[[MEM_EQ_RES:.*]] = phi ptr [ @_ZNSt3__115strong_ordering5equalE, %[[EQ_TRUE]] ], [ %[[AFTER_LT_RES]], %[[AFTER_LT]] ]
// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[CMP_RES]], ptr align 1 %[[MEM_EQ_RES]], i64 1, i1 false)
// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[CMP_TEMP]], ptr align 1 %[[CMP_RES]], i64 1, i1 false)
// OGCG: %[[UNSPEC_TEMP:.*]] = getelementptr inbounds nuw %"class.std::__1::strong_ordering", ptr %[[CMP_TEMP]], i32 0, i32 0
// OGCG: %[[UNSPEC_LOAD:.*]] = load i8, ptr %[[UNSPEC_TEMP]]
// OGCG: store { i64, i64 } zeroinitializer, ptr %[[COERCE_ALLOCA]]
// OGCG: %[[COERCE_L_GEP:.*]] = getelementptr inbounds nuw { i64, i64 }, ptr %[[COERCE_ALLOCA]], i32 0, i32 0
// OGCG: %[[COERCE_L:.*]] = load i64, ptr %[[COERCE_L_GEP]]
// OGCG: %[[COERCE_R_GEP:.*]] = getelementptr inbounds nuw { i64, i64 }, ptr %[[COERCE_ALLOCA]], i32 0, i32 1
// OGCG: %[[COERCE_R:.*]] = load i64, ptr %[[COERCE_R_GEP]]
// OGCG: %[[UNSPEC_RES:.*]] = call noundef zeroext i1 @_ZNSt3__1neENS_15strong_orderingEMNS_19_CmpUnspecifiedTypeEFvvE(i8 %[[UNSPEC_LOAD]], i64 %[[COERCE_L]], i64 %[[COERCE_R]])
// OGCG: br i1 %[[UNSPEC_RES]], label %[[UNSPEC_TRUE:.*]], label %[[UNSPEC_FALSE:.*]]
//
// OGCG: [[UNSPEC_TRUE]]:
// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[RET_ALLOCA]], ptr align 1 %[[CMP_RES]], i64 1, i1 false)
// OGCG: br label %[[RETURN_BLOCK:.*]]
//
// OGCG: [[UNSPEC_FALSE]]:
// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[RET_ALLOCA]], ptr align 1 @_ZNSt3__115strong_ordering5equalE, i64 1, i1 false)
// OGCG: br label %[[RETURN_BLOCK]]
//
// OGCG: [[RETURN_BLOCK]]:
// OGCG: %[[GEP_RET:.*]] = getelementptr inbounds nuw %"class.std::__1::strong_ordering", ptr %[[RET_ALLOCA]], i32 0, i32 0
// OGCG: %[[GEP_RET_LOAD:.*]] = load i8, ptr %[[GEP_RET]]
// OGCG: ret i8 %[[GEP_RET_LOAD]]
}