From 76b4db341132ac0e024e5f28e50cecd363e67846 Mon Sep 17 00:00:00 2001 From: ghehg Date: Mon, 18 Nov 2024 10:57:18 -0800 Subject: [PATCH] Support __builtin_launder except in the case of -fstrict-vtable-pointers --- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 49 +++- clang/test/CIR/CodeGen/builtins.cpp | 289 ++++++++++++++++++++++ 3 files changed, 337 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 8f56f0726f8a..547b3f05731c 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -230,6 +230,7 @@ struct MissingFeatures { static bool emitEmptyRecordCheck() { return false; } static bool isPPC_FP128Ty() { return false; } static bool emitBinaryAtomicPostHasInvert() { return false; } + static bool createLaunderInvariantGroup() { return false; } // Inline assembly static bool asmGoto() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 5d80e746e542..c9463e612c01 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -330,6 +330,43 @@ static mlir::Value MakeAtomicCmpXchgValue(CIRGenFunction &cgf, return returnBool ? op.getResult(1) : op.getResult(0); } +static bool +typeRequiresBuiltinLaunderImp(const ASTContext &ctx, QualType ty, + llvm::SmallPtrSetImpl &seen) { + if (const auto *arr = ctx.getAsArrayType(ty)) + ty = ctx.getBaseElementType(arr); + + const auto *record = ty->getAsCXXRecordDecl(); + if (!record) + return false; + + // We've already checked this type, or are in the process of checking it. + if (!seen.insert(record).second) + return false; + + assert(record->hasDefinition() && + "Incomplete types should already be diagnosed"); + + if (record->isDynamicClass()) + return true; + + for (FieldDecl *fld : record->fields()) { + if (typeRequiresBuiltinLaunderImp(ctx, fld->getType(), seen)) + return true; + } + return false; +} + +/// Determine if the specified type requires laundering by checking if it is a +/// dynamic class type or contains a subobject which is a dynamic class type. +static bool typeRequiresBuiltinLaunder(clang::CIRGen::CIRGenModule &cgm, + QualType ty) { + if (!cgm.getCodeGenOpts().StrictVTablePointers) + return false; + llvm::SmallPtrSet seen; + return typeRequiresBuiltinLaunderImp(cgm.getASTContext(), ty, seen); +} + RValue CIRGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) { auto src = emitScalarExpr(E->getArg(0)); auto shiftAmt = emitScalarExpr(E->getArg(1)); @@ -1625,8 +1662,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm_unreachable("BI__builtin_setjmp NYI"); case Builtin::BI__builtin_longjmp: llvm_unreachable("BI__builtin_longjmp NYI"); - case Builtin::BI__builtin_launder: - llvm_unreachable("BI__builtin_launder NYI"); + case Builtin::BI__builtin_launder: { + const clang::Expr *arg = E->getArg(0); + clang::QualType argTy = arg->getType()->getPointeeType(); + mlir::Value ptr = emitScalarExpr(arg); + if (typeRequiresBuiltinLaunder(CGM, argTy)) { + assert(!MissingFeatures::createLaunderInvariantGroup()); + llvm_unreachable(" launder.invariant.group NYI "); + } + return RValue::get(ptr); + } case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_sub: diff --git a/clang/test/CIR/CodeGen/builtins.cpp b/clang/test/CIR/CodeGen/builtins.cpp index c5f1ed5e0aff..504ec13da6ee 100644 --- a/clang/test/CIR/CodeGen/builtins.cpp +++ b/clang/test/CIR/CodeGen/builtins.cpp @@ -116,3 +116,292 @@ extern "C" void *test_frame_address(void) { // LLVM-LABEL: @test_frame_address // LLVM: {{%.*}} = call ptr @llvm.frameaddress.p0(i32 1) } + +// Following block of tests are for __builtin_launder +// FIXME: Once we fully __builtin_launder by allowing -fstrict-vtable-pointers, +// we should move following block of tests to a separate file. +namespace launder_test { +//===----------------------------------------------------------------------===// +// Positive Cases +//===----------------------------------------------------------------------===// + +struct TestVirtualFn { + virtual void foo() {} +}; + +// CIR-LABEL: test_builtin_launder_virtual_fn +// LLVM: define{{.*}} void @test_builtin_launder_virtual_fn(ptr [[P:%.*]]) +extern "C" void test_builtin_launder_virtual_fn(TestVirtualFn *p) { + // CIR: cir.return + + // LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8 + // LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 + // LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}} + // LLVM-NEXT: ret void + TestVirtualFn *d = __builtin_launder(p); +} + +struct TestPolyBase : TestVirtualFn { +}; + +// CIR-LABEL: test_builtin_launder_poly_base +// LLVM: define{{.*}} void @test_builtin_launder_poly_base(ptr [[P:%.*]]) +extern "C" void test_builtin_launder_poly_base(TestPolyBase *p) { + // CIR: cir.return + + // LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8 + // LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 + // LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}} + // LLVM-NEXT: ret void + TestPolyBase *d = __builtin_launder(p); +} + +struct TestBase {}; +struct TestVirtualBase : virtual TestBase {}; + +// CIR-LABEL: test_builtin_launder_virtual_base +// LLVM: define{{.*}} void @test_builtin_launder_virtual_base(ptr [[P:%.*]]) +extern "C" void test_builtin_launder_virtual_base(TestVirtualBase *p) { + TestVirtualBase *d = __builtin_launder(p); + + // CIR: cir.return + + // LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8 + // LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 + // LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}} + // LLVM-NEXT: ret void +} + +//===----------------------------------------------------------------------===// +// Negative Cases +//===----------------------------------------------------------------------===// + +// CIR-LABEL: test_builtin_launder_ommitted_one +// LLVM: define{{.*}} void @test_builtin_launder_ommitted_one(ptr [[P:%.*]]) +extern "C" void test_builtin_launder_ommitted_one(int *p) { + int *d = __builtin_launder(p); + + // CIR: cir.return + + // LLVM-NEXT: [[P_ADDR:%.*]] = alloca ptr, i64 1, align 8 + // LLVM-NEXT: [[D:%.*]] = alloca ptr, i64 1, align 8 + // LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8 + // LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 + // LLVM-NEXT: store ptr [[TMP0]], ptr [[D]] + // LLVM-NEXT: ret void +} + +struct TestNoInvariant { + int x; +}; + +// CIR-LABEL: test_builtin_launder_ommitted_two +// LLVM: define{{.*}} void @test_builtin_launder_ommitted_two(ptr [[P:%.*]]) +extern "C" void test_builtin_launder_ommitted_two(TestNoInvariant *p) { + TestNoInvariant *d = __builtin_launder(p); + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM-NEXT: [[P_ADDR:%.*]] = alloca ptr, i64 1, align 8 + // LLVM-NEXT: [[D:%.*]] = alloca ptr, i64 1, align 8 + // LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8 + // LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 + // LLVM-NEXT: store ptr [[TMP0]], ptr [[D]] + // LLVM-NEXT: ret void +} + +struct TestVirtualMember { + TestVirtualFn member; +}; + +// CIR-LABEL: test_builtin_launder_virtual_member +// LLVM: define{{.*}} void @test_builtin_launder_virtual_member +extern "C" void test_builtin_launder_virtual_member(TestVirtualMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestVirtualMember *d = __builtin_launder(p); +} + +struct TestVirtualMemberDepth2 { + TestVirtualMember member; +}; + +// CIR-LABEL: test_builtin_launder_virtual_member_depth_2 +// LLVM: define{{.*}} void @test_builtin_launder_virtual_member_depth_2 +extern "C" void test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestVirtualMemberDepth2 *d = __builtin_launder(p); +} + +struct TestVirtualReferenceMember { + TestVirtualFn &member; +}; + +// CIR-LABEL: test_builtin_launder_virtual_reference_member +// LLVM: define{{.*}} void @test_builtin_launder_virtual_reference_member +extern "C" void test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestVirtualReferenceMember *d = __builtin_launder(p); +} + +struct TestRecursiveMember { + TestRecursiveMember() : member(*this) {} + TestRecursiveMember &member; +}; + +// CIR-LABEL: test_builtin_launder_recursive_member +// LLVM: define{{.*}} void @test_builtin_launder_recursive_member +extern "C" void test_builtin_launder_recursive_member(TestRecursiveMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestRecursiveMember *d = __builtin_launder(p); +} + +struct TestVirtualRecursiveMember { + TestVirtualRecursiveMember() : member(*this) {} + TestVirtualRecursiveMember &member; + virtual void foo(); +}; + +// CIR-LABEL: test_builtin_launder_virtual_recursive_member +// LLVM: define{{.*}} void @test_builtin_launder_virtual_recursive_member +extern "C" void test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestVirtualRecursiveMember *d = __builtin_launder(p); +} + +// CIR-LABEL: test_builtin_launder_array +// LLVM: define{{.*}} void @test_builtin_launder_array +extern "C" void test_builtin_launder_array(TestVirtualFn (&Arr)[5]) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestVirtualFn *d = __builtin_launder(Arr); +} + +// CIR-LABEL: test_builtin_launder_array_nested +// LLVM: define{{.*}} void @test_builtin_launder_array_nested +extern "C" void test_builtin_launder_array_nested(TestVirtualFn (&Arr)[5][2]) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + using RetTy = TestVirtualFn(*)[2]; + RetTy d = __builtin_launder(Arr); +} + +// CIR-LABEL: test_builtin_launder_array_no_invariant +// LLVM: define{{.*}} void @test_builtin_launder_array_no_invariant +extern "C" void test_builtin_launder_array_no_invariant(TestNoInvariant (&Arr)[5]) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + TestNoInvariant *d = __builtin_launder(Arr); +} + +// CIR-LABEL: test_builtin_launder_array_nested_no_invariant +// LLVM: define{{.*}} void @test_builtin_launder_array_nested_no_invariant +extern "C" void test_builtin_launder_array_nested_no_invariant(TestNoInvariant (&Arr)[5][2]) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + using RetTy = TestNoInvariant(*)[2]; + RetTy d = __builtin_launder(Arr); +} + +template +struct WithMember { + Member mem; +}; + +template struct WithMember; + +// CIR-LABEL: test_builtin_launder_member_array +// LLVM: define{{.*}} void @test_builtin_launder_member_array +extern "C" void test_builtin_launder_member_array(WithMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} + +template struct WithMember; + +// CIR-LABEL: test_builtin_launder_member_array_nested +// LLVM: define{{.*}} void @test_builtin_launder_member_array_nested +extern "C" void test_builtin_launder_member_array_nested(WithMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} + +template struct WithMember; + +// CIR-LABEL: test_builtin_launder_member_array_no_invariant +// LLVM: define{{.*}} void @test_builtin_launder_member_array_no_invariant +extern "C" void test_builtin_launder_member_array_no_invariant(WithMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} + +template struct WithMember; + +// CIR-LABEL: test_builtin_launder_member_array_nested_no_invariant +// LLVM: define{{.*}} void @test_builtin_launder_member_array_nested_no_invariant +extern "C" void test_builtin_launder_member_array_nested_no_invariant(WithMember *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} + +template +struct WithBase : T {}; + +template struct WithBase; + +// CIR-LABEL: test_builtin_launder_base_no_invariant +// LLVM: define{{.*}} void @test_builtin_launder_base_no_invariant +extern "C" void test_builtin_launder_base_no_invariant(WithBase *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} + +template struct WithBase; + +// CIR-LABEL: test_builtin_launder_base +// LLVM: define{{.*}} void @test_builtin_launder_base +extern "C" void test_builtin_launder_base(WithBase *p) { + // CIR: cir.return + + // LLVM-NOT: llvm.launder.invariant.group + // LLVM: ret void + auto *d = __builtin_launder(p); +} +}