Implements isMemcpyEquivalentSpecialMember in CIR codegen so that trivial copy/move constructors and defaulted union copy/move ops emit a cir.copy directly instead of making a real constructor call. The logic is shared with OG codegen by moving the implementation into ASTContext, where it also gains the pointer field protection (PFP) check that was previously missing in CIR.
122 lines
5.0 KiB
C++
122 lines
5.0 KiB
C++
// 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 -fno-elide-constructors -fclangir -emit-cir %s -o %t-noelide.cir
|
|
// RUN: FileCheck --input-file=%t-noelide.cir %s --check-prefix=CIR-NOELIDE
|
|
// 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-prefix=LLVM
|
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
|
|
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
|
|
|
|
// There are no LLVM and OGCG tests with -fno-elide-constructors because the
|
|
// lowering isn't of interest for this test. We just need to see that the
|
|
// copy constructor is elided without -fno-elide-constructors but not with it.
|
|
|
|
struct S {
|
|
S();
|
|
int a;
|
|
int b;
|
|
};
|
|
|
|
struct S f1() {
|
|
S s;
|
|
return s;
|
|
}
|
|
|
|
// CIR: cir.func{{.*}} @_Z2f1v() -> !rec_S
|
|
// CIR-NEXT: %[[RETVAL:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["__retval", init]
|
|
// CIR-NEXT: cir.call @_ZN1SC1Ev(%[[RETVAL]]) : (!cir.ptr<!rec_S> {{.*}}) -> ()
|
|
// CIR-NEXT: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr<!rec_S>, !rec_S
|
|
// CIR-NEXT: cir.return %[[RET]]
|
|
|
|
// CIR-NOELIDE: cir.func{{.*}} @_Z2f1v() -> !rec_S
|
|
// CIR-NOELIDE-NEXT: %[[RETVAL:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["__retval"]
|
|
// CIR-NOELIDE-NEXT: %[[S:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s", init]
|
|
// CIR-NOELIDE-NEXT: cir.call @_ZN1SC1Ev(%[[S]]) : (!cir.ptr<!rec_S> {{.*}}) -> ()
|
|
// CIR-NOELIDE-NEXT: cir.copy %[[S]] to %[[RETVAL]] : !cir.ptr<!rec_S>
|
|
// CIR-NOELIDE-NEXT: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr<!rec_S>, !rec_S
|
|
// CIR-NOELIDE-NEXT: cir.return %[[RET]]
|
|
|
|
// FIXME: Update this when calling convetnion lowering is implemented.
|
|
// LLVM: define{{.*}} %struct.S @_Z2f1v()
|
|
// LLVM-NEXT: %[[RETVAL:.*]] = alloca %struct.S
|
|
// LLVM-NEXT: call void @_ZN1SC1Ev(ptr {{.*}} %[[RETVAL]])
|
|
// LLVM-NEXT: %[[RET:.*]] = load %struct.S, ptr %[[RETVAL]]
|
|
// LLVM-NEXT: ret %struct.S %[[RET]]
|
|
|
|
// OGCG: define{{.*}} i64 @_Z2f1v()
|
|
// OGCG-NEXT: entry:
|
|
// OGCG-NEXT: %[[RETVAL:.*]] = alloca %struct.S
|
|
// OGCG-NEXT: call void @_ZN1SC1Ev(ptr {{.*}} %[[RETVAL]])
|
|
// OGCG-NEXT: %[[RET:.*]] = load i64, ptr %[[RETVAL]]
|
|
// OGCG-NEXT: ret i64 %[[RET]]
|
|
|
|
struct NonTrivial {
|
|
~NonTrivial();
|
|
};
|
|
|
|
void maybeThrow();
|
|
|
|
NonTrivial test_nrvo() {
|
|
NonTrivial result;
|
|
maybeThrow();
|
|
return result;
|
|
}
|
|
|
|
// TODO(cir): Handle normal cleanup properly.
|
|
|
|
// CIR: cir.func {{.*}} @_Z9test_nrvov()
|
|
// CIR: %[[RESULT:.*]] = cir.alloca !rec_NonTrivial, !cir.ptr<!rec_NonTrivial>, ["__retval"]
|
|
// CIR: %[[NRVO_FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["nrvo"]
|
|
// CIR: %[[FALSE:.*]] = cir.const #false
|
|
// CIR: cir.store{{.*}} %[[FALSE]], %[[NRVO_FLAG]]
|
|
// CIR: cir.cleanup.scope {
|
|
// CIR: cir.call @_Z10maybeThrowv() : () -> ()
|
|
// CIR: %[[TRUE:.*]] = cir.const #true
|
|
// CIR: cir.store{{.*}} %[[TRUE]], %[[NRVO_FLAG]]
|
|
// CIR: %[[RET:.*]] = cir.load %[[RESULT]]
|
|
// CIR: cir.return %[[RET]]
|
|
// CIR: } cleanup normal {
|
|
// CIR: %[[NRVO_FLAG_VAL:.*]] = cir.load{{.*}} %[[NRVO_FLAG]]
|
|
// CIR: %[[NOT_NRVO_VAL:.*]] = cir.not %[[NRVO_FLAG_VAL]]
|
|
// CIR: cir.if %[[NOT_NRVO_VAL]] {
|
|
// CIR: cir.call @_ZN10NonTrivialD1Ev(%[[RESULT]])
|
|
// CIR: }
|
|
// CIR: cir.yield
|
|
// CIR: }
|
|
//
|
|
// TODO(cir): This is unreachable, but it really shouldn't be here. This is an
|
|
// artifact of us falling through to emitImplicitReturn().
|
|
// CIR: cir.trap
|
|
|
|
// LLVM: define {{.*}} %struct.NonTrivial @_Z9test_nrvov()
|
|
// LLVM: %[[RESULT:.*]] = alloca %struct.NonTrivial
|
|
// LLVM: %[[NRVO_FLAG:.*]] = alloca i8
|
|
// LLVM: store i8 0, ptr %[[NRVO_FLAG]]
|
|
// LLVM: call void @_Z10maybeThrowv()
|
|
// LLVM: store i8 1, ptr %[[NRVO_FLAG]]
|
|
// LLVM: %[[NRVO_VAL:.*]] = load i8, ptr %[[NRVO_FLAG]]
|
|
// LLVM: %[[NRVO_VAL_TRUNC:.*]] = trunc i8 %[[NRVO_VAL]] to i1
|
|
// LLVM: %[[NOT_NRVO_VAL:.*]] = xor i1 %[[NRVO_VAL_TRUNC]], true
|
|
// LLVM: br i1 %[[NOT_NRVO_VAL]], label %[[NRVO_UNUSED:.*]], label %[[NRVO_USED:.*]]
|
|
// LLVM: [[NRVO_UNUSED]]:
|
|
// LLVM: call void @_ZN10NonTrivialD1Ev(ptr {{.*}} %[[RESULT]])
|
|
// LLVM: br label %[[NRVO_USED]]
|
|
// LLVM: [[NRVO_USED]]:
|
|
// LLVM: %[[RET:.*]] = load %struct.NonTrivial, ptr %[[RESULT]]
|
|
// LLVM: ret %struct.NonTrivial %[[RET]]
|
|
|
|
// OGCG: define {{.*}} void @_Z9test_nrvov(ptr {{.*}} sret(%struct.NonTrivial) {{.*}} %[[RESULT:.*]])
|
|
// OGCG: %[[RESULT_ADDR:.*]] = alloca ptr
|
|
// OGCG: %[[NRVO_FLAG:.*]] = alloca i1, align 1
|
|
// OGCG: store ptr %[[RESULT]], ptr %[[RESULT_ADDR]]
|
|
// OGCG: store i1 false, ptr %[[NRVO_FLAG]]
|
|
// OGCG: call void @_Z10maybeThrowv()
|
|
// OGCG: store i1 true, ptr %[[NRVO_FLAG]]
|
|
// OGCG: %[[NRVO_VAL:.*]] = load i1, ptr %[[NRVO_FLAG]]
|
|
// OGCG: br i1 %[[NRVO_VAL]], label %[[SKIPDTOR:.*]], label %[[NRVO_UNUSED:.*]]
|
|
// OGCG: [[NRVO_UNUSED]]:
|
|
// OGCG: call void @_ZN10NonTrivialD1Ev(ptr {{.*}} %[[RESULT]])
|
|
// OGCG: br label %[[SKIPDTOR]]
|
|
// OGCG: [[SKIPDTOR]]:
|
|
// OGCG: ret void
|