diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index 8769975bdc948..3ed5ba4750a0c 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -33,10 +33,13 @@ namespace { class LowerItaniumCXXABI : public CIRCXXABI { protected: bool useARMMethodPtrABI; + bool use32BitVTableOffsetABI; public: - LowerItaniumCXXABI(LowerModule &lm, bool useARMMethodPtrABI = false) - : CIRCXXABI(lm), useARMMethodPtrABI(useARMMethodPtrABI) {} + LowerItaniumCXXABI(LowerModule &lm, bool useARMMethodPtrABI = false, + bool use32BitVTableOffsetABI = false) + : CIRCXXABI(lm), useARMMethodPtrABI(useARMMethodPtrABI), + use32BitVTableOffsetABI(use32BitVTableOffsetABI) {} /// Lower the given data member pointer type to its ABI type. The returned /// type is also a CIR type. @@ -128,12 +131,18 @@ std::unique_ptr createItaniumCXXABI(LowerModule &lm) { // include the other 32-bit ARM oddities: constructor/destructor return values // and array cookies. case clang::TargetCXXABI::GenericAArch64: + return std::make_unique( + lm, + /*useARMMethodPtrABI=*/true, + /*use32BitVTableOffsetABI=*/false); case clang::TargetCXXABI::AppleARM64: // TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits // from ARMCXXABI. We'll have to follow suit. assert(!cir::MissingFeatures::appleArm64CXXABI()); - return std::make_unique(lm, - /*useARMMethodPtrABI=*/true); + return std::make_unique( + lm, + /*useARMMethodPtrABI=*/true, + /*use32BitVTableOffsetABI=*/true); case clang::TargetCXXABI::GenericItanium: return std::make_unique(lm); @@ -247,7 +256,12 @@ mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant( // least significant bit of adj then makes exactly the same // discrimination as the least significant bit of ptr does for // Itanium. - llvm_unreachable("ARM method ptr abi NYI"); + assert(!cir::MissingFeatures::pointerAuthentication()); + auto ptr = + cir::IntAttr::get(ptrdiffCIRTy, attr.getVtableOffset().value()); + auto one = cir::IntAttr::get(ptrdiffCIRTy, 1); + return cir::ConstRecordAttr::get( + loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, one})); } // Itanium C++ ABI 2.3.2: @@ -321,12 +335,12 @@ void LowerItaniumCXXABI::lowerGetMethod( mlir::Value ptrdiffOne = cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1)); - mlir::Value adj = + mlir::Value rawAdj = cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 1); - if (useARMMethodPtrABI) { - op.emitError("ARM method ptr abi NYI"); - return; - } + mlir::Value adj = rawAdj; + if (useARMMethodPtrABI) + adj = cir::ShiftOp::create(locBuilder, ptrdiffCIRTy, adj, ptrdiffOne, + /*isLeftShift=*/false); // Apply the adjustment to the 'this' pointer. mlir::Type thisVoidPtrTy = @@ -341,14 +355,13 @@ void LowerItaniumCXXABI::lowerGetMethod( // points to a virtual function. mlir::Value methodPtrField = cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 0); - mlir::Value virtualBit = - cir::AndOp::create(rewriter, op.getLoc(), methodPtrField, ptrdiffOne); - mlir::Value isVirtual; + mlir::Value virtualBit; if (useARMMethodPtrABI) - llvm_unreachable("ARM method ptr abi NYI"); + virtualBit = cir::AndOp::create(locBuilder, rawAdj, ptrdiffOne); else - isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq, virtualBit, - ptrdiffOne); + virtualBit = cir::AndOp::create(locBuilder, methodPtrField, ptrdiffOne); + mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq, + virtualBit, ptrdiffOne); assert(!cir::MissingFeatures::emitCFICheck()); assert(!cir::MissingFeatures::emitVFEInfo()); @@ -371,11 +384,15 @@ void LowerItaniumCXXABI::lowerGetMethod( /*sync_scope=*/cir::SyncScopeKindAttr{}, /*mem_order=*/cir::MemOrderAttr()); - // Get the vtable offset. + // Apply the offset. + // On ARM64, to reserve extra space in virtual member function pointers, + // we only pay attention to the low 32 bits of the offset. mlir::Value vtableOffset = methodPtrField; - assert(!useARMMethodPtrABI && "ARM method ptr abi NYI"); - vtableOffset = cir::SubOp::create(b, loc, vtableOffset.getType(), - vtableOffset, ptrdiffOne); + if (!useARMMethodPtrABI) + vtableOffset = cir::SubOp::create(b, loc, vtableOffset.getType(), + vtableOffset, ptrdiffOne); + if (use32BitVTableOffsetABI) + llvm_unreachable("AppleARM64 method ptr abi NYI"); assert(!cir::MissingFeatures::emitCFICheck()); assert(!cir::MissingFeatures::emitVFEInfo()); @@ -462,11 +479,17 @@ LowerItaniumCXXABI::lowerDerivedDataMember(cir::DerivedDataMemberOp op, static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc, std::int64_t offset, bool isDerivedToBase, + bool useARMMethodPtrABI, LowerModule &lowerMod, mlir::OpBuilder &builder) { if (offset == 0) return loweredSrc; + // The this-adjustment is left-shifted by 1 on ARM, since the low bit of the + // adjustment field is used to encode whether the member function is virtual. + if (useARMMethodPtrABI) + offset <<= 1; + cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lowerMod); auto adjField = cir::ExtractMemberOp::create(builder, op->getLoc(), ptrdiffCIRTy, loweredSrc, 1); @@ -495,7 +518,8 @@ LowerItaniumCXXABI::lowerBaseMethod(cir::BaseMethodOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const { return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(), - /*isDerivedToBase=*/true, lm, builder); + /*isDerivedToBase=*/true, useARMMethodPtrABI, lm, + builder); } mlir::Value @@ -503,7 +527,8 @@ LowerItaniumCXXABI::lowerDerivedMethod(cir::DerivedMethodOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const { return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(), - /*isDerivedToBase=*/false, lm, builder); + /*isDerivedToBase=*/false, useARMMethodPtrABI, lm, + builder); } mlir::Value @@ -549,12 +574,43 @@ mlir::Value LowerItaniumCXXABI::lowerMethodCmp(cir::CmpOp op, return cir::OrOp::create(locBuilder, lhs.getType(), lhs, rhs); }; + // Null member function pointers on ARM clear the low bit of Adj, + // so the zero condition has to check that neither low bit is set. + if (useARMMethodPtrABI) { + mlir::Value one = + cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1)); + + // The low bit of the adjustment field is used to encode whether the member + // function is virtual, but the ARM ABI specifies that for null pointers + // this bit must be clear. Therefore, to test whether the member pointer is + // null, we need to check that bit. + // + // If we are performing an equality check, ptrCmpToNull indicates that both + // pointers are null (if they are equal -- we only actually test lhs). + // If we are performing an inequality check, ptrCmpToNull indicates that + // one of the pointers is not null. + // + // To apply the ARM-specific logic, if either virtual bit is set, they + // cannot both be null (equality case -- ptrCmpToNull &= orAdjAnd1CmpZero), + // and if either virtual bit is set, one of the pointers is not null + // (inequality case -- ptrCmpToNull |= orAdjAnd1CmpZero). + mlir::Value orAdj = create_or(lhsAdjField, rhsAdjField); + mlir::Value orAdjAnd1 = create_and(orAdj, one); + mlir::Value orAdjAnd1CmpZero = + cir::CmpOp::create(locBuilder, op.getKind(), orAdjAnd1, ptrdiffZero); + + if (op.getKind() == cir::CmpOpKind::eq) + ptrCmpToNull = create_and(ptrCmpToNull, orAdjAnd1CmpZero); + else + ptrCmpToNull = create_or(ptrCmpToNull, orAdjAnd1CmpZero); + } + mlir::Value result; if (op.getKind() == cir::CmpOpKind::eq) { // (lhs.ptr == null || lhs.adj == rhs.adj) && lhs.ptr == rhs.ptr result = create_and(ptrCmp, create_or(ptrCmpToNull, adjCmp)); } else { - // (lhs.ptr != null && lhs.adj != rhs.adj) || lhs.ptr != rhs.ptr + // lhs.ptr == rhs.ptr && (lhs.ptr == null || lhs.adj == rhs.adj) result = create_or(ptrCmp, create_and(ptrCmpToNull, adjCmp)); } @@ -593,18 +649,37 @@ LowerItaniumCXXABI::lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy, mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast( cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const { + mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder); + // Itanium C++ ABI 2.3.2: // // In the standard representation, a null member function pointer is // represented with ptr set to a null pointer. The value of adj is // unspecified for null member function pointers. cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm); - mlir::Value ptrdiffZero = cir::ConstantOp::create( - builder, op.getLoc(), cir::IntAttr::get(ptrdiffCIRTy, 0)); - mlir::Value ptrField = cir::ExtractMemberOp::create( - builder, op.getLoc(), ptrdiffCIRTy, loweredSrc, 0); - return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne, ptrField, - ptrdiffZero); + mlir::Value ptrdiffZero = + cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0)); + mlir::Value ptrField = + cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 0); + + mlir::Value result = + cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne, ptrField, ptrdiffZero); + + // On ARM, a member function pointer is also non-null if the low bit of 'adj' + // (the virtual bit) is set. + if (useARMMethodPtrABI) { + mlir::Value one = + cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1)); + mlir::Value adj = + cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 1); + mlir::Value virtualBit = + cir::AndOp::create(locBuilder, ptrdiffCIRTy, adj, one); + mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne, + virtualBit, ptrdiffZero); + result = cir::OrOp::create(locBuilder, result, isVirtual); + } + + return result; } static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc, diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member-cmp.cpp b/clang/test/CIR/CodeGen/pointer-to-data-member-cmp.cpp index dbc05e13c0733..fd1e615c351a8 100644 --- a/clang/test/CIR/CodeGen/pointer-to-data-member-cmp.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-data-member-cmp.cpp @@ -6,6 +6,10 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t-arm.cir 2> %t-arm-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-arm-before.cir %s +// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t-arm.cir %s + struct Foo { int a; }; diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp index 1da7fcb557903..d4fdc10fc601a 100644 --- a/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp @@ -1,10 +1,18 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir // RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s -// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-X86 --input-file=%t.cir %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-cir.ll -// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s +// RUN: FileCheck --input-file=%t-cir.ll --check-prefixes=LLVM,LLVM-X86 %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll -// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s +// RUN: FileCheck --input-file=%t.ll --check-prefixes=OGCG,OGCG-X86 %s + +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t-arm.cir 2> %t-arm-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-ARM --input-file=%t-arm.cir %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-arm-cir.ll +// RUN: FileCheck --input-file=%t-arm-cir.ll --check-prefixes=LLVM,LLVM-ARM %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t-arm.ll +// RUN: FileCheck --input-file=%t-arm.ll --check-prefixes=OGCG,OGCG-ARM %s struct Foo { void m1(int); @@ -23,16 +31,34 @@ bool memfunc_to_bool(void (Foo::*func)(int)) { // CIR-BEFORE: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE // CIR-BEFORE: %{{.*}} = cir.cast member_ptr_to_bool %{{.*}} : !cir.method, !s32i)> in !rec_Foo> -> !cir.bool -// CIR-AFTER: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE -// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[NULL_VAL:.*]] = cir.const #cir.int<0> : !s64i -// CIR-AFTER: %[[FUNC_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[BOOL_VAL:.*]] = cir.cmp ne %[[FUNC_PTR]], %[[NULL_VAL]] : !s64i +// CIR-AFTER: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE +// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[NULL_VAL:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER: %[[FUNC_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[BOOL_VAL:.*]] = cir.cmp ne %[[FUNC_PTR]], %[[NULL_VAL]] : !s64i +// CIR-AFTER-ARM: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i +// CIR-AFTER-ARM: %[[ADJ:.*]] = cir.extract_member %[[FUNC]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER-ARM: %[[AND:.*]] = cir.and %[[ADJ]], %[[ONE]] : !s64i +// CIR-AFTER-ARM: %[[NOT_VIRTUAL:.*]] = cir.cmp ne %[[AND]], %[[NULL_VAL]] : !s64i +// CIR-AFTER-ARM: %[[TMP:.*]] = cir.or %[[BOOL_VAL]], %[[NOT_VIRTUAL]] : !cir.bool +// CIR-AFTER-X86-NOT: cir.extract_member +// CIR-AFTER-X86-NOT: cir.and +// CIR-AFTER-X86-NOT: cir.cmp +// CIR-AFTER-X86-NOT: cir.or + +// LLVM: define {{.*}} i1 @_Z15memfunc_to_boolM3FooFviE +// LLVM: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[FUNC_PTR:.*]] = extractvalue { i64, i64 } %[[FUNC]], 0 +// LLVM: %[[BOOL_VAL:.*]] = icmp ne i64 %[[FUNC_PTR]], 0 +// LLVM-ARM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[FUNC]], 1 +// LLVM-ARM: %[[AND:.*]] = and i64 %[[ADJ]], 1 +// LLVM-ARM: %[[NOT_VIRTUAL:.*]] = icmp ne i64 %[[AND]], 0 +// LLVM-ARM: %[[TMP:.*]] = or i1 %[[BOOL_VAL]], %[[NOT_VIRTUAL]] +// LLVM-X86-NOT: extractvalue +// LLVM-X86-NOT: and +// LLVM-X86-NOT: icmp +// LLVM-X86-NOT: or i1 -// LLVM: define {{.*}} i1 @_Z15memfunc_to_boolM3FooFviE -// LLVM: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[FUNC_PTR:.*]] = extractvalue { i64, i64 } %[[FUNC]], 0 -// LLVM: %{{.*}} = icmp ne i64 %[[FUNC_PTR]], 0 // Note: OGCG uses an extra temporary for the function argument because it // composes it from coerced arguments. We'll do that in CIR too after @@ -43,7 +69,15 @@ bool memfunc_to_bool(void (Foo::*func)(int)) { // OGCG: store { i64, i64 } %[[FUNC_TMP]], ptr %[[FUNC_ADDR:.*]] // OGCG: %[[FUNC:.*]] = load { i64, i64 }, ptr %[[FUNC_ADDR]] // OGCG: %[[FUNC_PTR:.*]] = extractvalue { i64, i64 } %[[FUNC]], 0 -// OGCG: %{{.*}} = icmp ne i64 %[[FUNC_PTR]], 0 +// OGCG: %[[BOOL_VAL:.*]] = icmp ne i64 %[[FUNC_PTR]], 0 +// OGCG-ARM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[FUNC]], 1 +// OGCG-ARM: %[[AND:.*]] = and i64 %[[ADJ]], 1 +// OGCG-ARM: %[[NOT_VIRTUAL:.*]] = icmp ne i64 %[[AND]], 0 +// OGCG-ARM: %[[TMP:.*]] = or i1 %[[BOOL_VAL]], %[[NOT_VIRTUAL]] +// OGCG-X86-NOT: extractvalue +// OGCG-X86-NOT: and +// OGCG-X86-NOT: icmp +// OGCG-X86-NOT: or i1 auto memfunc_reinterpret(void (Foo::*func)(int)) -> void (Bar::*)() { return reinterpret_cast(func); @@ -64,11 +98,15 @@ auto memfunc_reinterpret(void (Foo::*func)(int)) -> void (Bar::*)() { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} { i64, i64 } @_Z19memfunc_reinterpretM3FooFviE -// OGCG: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} -// OGCG: store { i64, i64 } %[[FUNC]], ptr %[[RET_ADDR:.*]] -// OGCG: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] -// OGCG: ret { i64, i64 } %[[RET]] +// OGCG-X86: define {{.*}} { i64, i64 } @_Z19memfunc_reinterpretM3FooFviE +// OGCG-ARM: define {{.*}} [2 x i64] @_Z19memfunc_reinterpretM3FooFviE +// OGCG: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: store { i64, i64 } %[[FUNC]], ptr %[[FUNC_ADDR:[^,]+]] +// OGCG-X86: %[[RET:.*]] = load { i64, i64 }, ptr %[[FUNC_ADDR]] +// OGCG-ARM: %[[TMP:.*]] = load { i64, i64 }, ptr %[[FUNC_ADDR]] +// OGCG-ARM: store { i64, i64 } %[[TMP]], ptr %[[RET_ADDR:[^,]+]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[RET_ADDR]] +// OGCG: ret {{.*}} %[[RET]] struct Base1 { int x; @@ -114,11 +152,13 @@ DerivedMemFunc base_to_derived_zero_offset(Base1MemFunc ptr) { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} { i64, i64 } @_Z27base_to_derived_zero_offsetM5Base1FviE -// OGCG: %[[ARG_ADDR:.*]] = alloca { i64, i64 } -// OGCG: store { i64, i64 } %{{.*}}, ptr %[[ARG_ADDR]] -// OGCG: %[[RET:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] -// OGCG: ret { i64, i64 } %[[RET]] +// OGCG-X86: define {{.*}} { i64, i64 } @_Z27base_to_derived_zero_offsetM5Base1FviE +// OGCG-ARM: define {{.*}} [2 x i64] @_Z27base_to_derived_zero_offsetM5Base1FviE +// OGCG: %[[ARG_ADDR:.*]] = alloca { i64, i64 } +// OGCG: store { i64, i64 } %{{.*}}, ptr %[[ARG_ADDR]] +// OGCG-X86: %[[RET:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[ARG_ADDR]] +// OGCG: ret {{.*}} %[[RET]] DerivedMemFunc base_to_derived(Base2MemFunc ptr) { return static_cast(ptr); @@ -128,26 +168,30 @@ DerivedMemFunc base_to_derived(Base2MemFunc ptr) { // CIR-BEFORE: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !s32i)> in !rec_Base2>>, !cir.method, !s32i)> in !rec_Base2> // CIR-BEFORE: %{{.*}} = cir.derived_method %[[PTR]][16] : !cir.method, !s32i)> in !rec_Base2> -> !cir.method, !s32i)> in !rec_Derived> -// CIR-AFTER: cir.func {{.*}} @_Z15base_to_derivedM5Base2FviE -// CIR-AFTER: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i -// CIR-AFTER: %[[BINOP_KIND:.*]] = cir.add nsw %[[OFFSET]], %[[OFFSET_ADJ]] : !s64i -// CIR-AFTER: %{{.*}} = cir.insert_member %[[PTR]][1], %[[BINOP_KIND]] : !rec_anon_struct, !s64i - -// LLVM: define {{.*}} { i64, i64 } @_Z15base_to_derivedM5Base2FviE -// LLVM: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG]], 1 -// LLVM: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 16 -// LLVM: %{{.*}} = insertvalue { i64, i64 } %[[ARG]], i64 %[[ADJ_ADJ]], 1 - -// OGCG: define {{.*}} { i64, i64 } @_Z15base_to_derivedM5Base2FviE -// OGCG: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} -// OGCG: store { i64, i64 } %[[ARG]], ptr %[[ARG_ADDR:.*]] -// OGCG: %[[ARG1:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] -// OGCG: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG1]], 1 -// OGCG: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 16 -// OGCG: %{{.*}} = insertvalue { i64, i64 } %[[ARG1]], i64 %[[ADJ_ADJ]], 1 +// CIR-AFTER: cir.func {{.*}} @_Z15base_to_derivedM5Base2FviE +// CIR-AFTER: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER-X86: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i +// CIR-AFTER-ARM: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<32> : !s64i +// CIR-AFTER: %[[BINOP_KIND:.*]] = cir.add nsw %[[OFFSET]], %[[OFFSET_ADJ]] : !s64i +// CIR-AFTER: %{{.*}} = cir.insert_member %[[PTR]][1], %[[BINOP_KIND]] : !rec_anon_struct, !s64i + +// LLVM: define {{.*}} { i64, i64 } @_Z15base_to_derivedM5Base2FviE +// LLVM: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG]], 1 +// LLVM-X86: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 16 +// LLVM-ARM: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 32 +// LLVM: %{{.*}} = insertvalue { i64, i64 } %[[ARG]], i64 %[[ADJ_ADJ]], 1 + +// OGCG-X86: define {{.*}} { i64, i64 } @_Z15base_to_derivedM5Base2FviE +// OGCG-ARM: define {{.*}} [2 x i64] @_Z15base_to_derivedM5Base2FviE +// OGCG: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: store { i64, i64 } %[[ARG]], ptr %[[ARG_ADDR:.*]] +// OGCG: %[[ARG1:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] +// OGCG: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG1]], 1 +// OGCG-X86: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 16 +// OGCG-ARM: %[[ADJ_ADJ:.*]] = add nsw i64 %[[ADJ]], 32 +// OGCG: %{{.*}} = insertvalue { i64, i64 } %[[ARG1]], i64 %[[ADJ_ADJ]], 1 Base1MemFunc derived_to_base_zero_offset(DerivedMemFunc ptr) { return static_cast(ptr); @@ -175,11 +219,17 @@ Base1MemFunc derived_to_base_zero_offset(DerivedMemFunc ptr) { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} { i64, i64 } @_Z27derived_to_base_zero_offsetM7DerivedFviE -// OGCG: %[[ARG_ADDR:.*]] = alloca { i64, i64 } -// OGCG: store { i64, i64 } %{{.*}}, ptr %[[ARG_ADDR]] -// OGCG: %[[RET:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] -// OGCG: ret { i64, i64 } %[[RET]] +// OGCG-X86: define {{.*}} { i64, i64 } @_Z27derived_to_base_zero_offsetM7DerivedFviE +// OGCG-ARM: define {{.*}} [2 x i64] @_Z27derived_to_base_zero_offsetM7DerivedFviE +// OGCG-ARM: %[[RETVAL:.*]] = alloca { i64, i64 } +// OGCG: %[[ARG_ADDR:.*]] = alloca { i64, i64 } +// OGCG-ARM: %[[ARG_COERCE:.*]] = alloca { i64, i64 } +// OGCG: store { i64, i64 } %{{.*}}, ptr %[[ARG_ADDR]] +// OGCG-X86: %[[RET:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] +// OGCG-ARM: %[[TMP:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] +// OGCG-ARM: store { i64, i64 } %[[TMP]], ptr %[[RETVAL]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[RETVAL]] +// OGCG: ret {{.*}} %[[RET]] Base2MemFunc derived_to_base(DerivedMemFunc ptr) { return static_cast(ptr); @@ -189,23 +239,27 @@ Base2MemFunc derived_to_base(DerivedMemFunc ptr) { // CIR-BEFORE: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !s32i)> in !rec_Derived>>, !cir.method, !s32i)> in !rec_Derived> // CIR-BEFORE: %{{.*}} = cir.base_method %[[PTR]][16] : !cir.method, !s32i)> in !rec_Derived> -> !cir.method, !s32i)> in !rec_Base2> -// CIR-AFTER: cir.func {{.*}} @_Z15derived_to_baseM7DerivedFviE -// CIR-AFTER: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i -// CIR-AFTER: %[[BINOP_KIND:.*]] = cir.sub nsw %[[OFFSET]], %[[OFFSET_ADJ]] : !s64i -// CIR-AFTER: %{{.*}} = cir.insert_member %[[PTR]][1], %[[BINOP_KIND]] : !rec_anon_struct, !s64i - -// LLVM: define {{.*}} { i64, i64 } @_Z15derived_to_baseM7DerivedFviE -// LLVM: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG]], 1 -// LLVM: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 16 -// LLVM: %{{.*}} = insertvalue { i64, i64 } %[[ARG]], i64 %[[ADJ_ADJ]], 1 - -// OGCG: define {{.*}} { i64, i64 } @_Z15derived_to_baseM7DerivedFviE -// OGCG: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} -// OGCG: store { i64, i64 } %[[ARG]], ptr %[[ARG_ADDR:.*]] -// OGCG: %[[ARG1:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] -// OGCG: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG1]], 1 -// OGCG: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 16 -// OGCG: %{{.*}} = insertvalue { i64, i64 } %[[ARG1]], i64 %[[ADJ_ADJ]], 1 +// CIR-AFTER: cir.func {{.*}} @_Z15derived_to_baseM7DerivedFviE +// CIR-AFTER: %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER-X86: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i +// CIR-AFTER-ARM: %[[OFFSET_ADJ:.*]] = cir.const #cir.int<32> : !s64i +// CIR-AFTER: %[[BINOP_KIND:.*]] = cir.sub nsw %[[OFFSET]], %[[OFFSET_ADJ]] : !s64i +// CIR-AFTER: %{{.*}} = cir.insert_member %[[PTR]][1], %[[BINOP_KIND]] : !rec_anon_struct, !s64i + +// LLVM: define {{.*}} { i64, i64 } @_Z15derived_to_baseM7DerivedFviE +// LLVM: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG]], 1 +// LLVM-X86: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 16 +// LLVM-ARM: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 32 +// LLVM: %{{.*}} = insertvalue { i64, i64 } %[[ARG]], i64 %[[ADJ_ADJ]], 1 + +// OGCG-X86: define {{.*}} { i64, i64 } @_Z15derived_to_baseM7DerivedFviE +// OGCG-ARM: define {{.*}} [2 x i64] @_Z15derived_to_baseM7DerivedFviE +// OGCG: %[[ARG:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: store { i64, i64 } %[[ARG]], ptr %[[ARG_ADDR:.*]] +// OGCG: %[[ARG1:.*]] = load { i64, i64 }, ptr %[[ARG_ADDR]] +// OGCG: %[[ADJ:.*]] = extractvalue { i64, i64 } %[[ARG1]], 1 +// OGCG-X86: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 16 +// OGCG-ARM: %[[ADJ_ADJ:.*]] = sub nsw i64 %[[ADJ]], 32 +// OGCG: %{{.*}} = insertvalue { i64, i64 } %[[ARG1]], i64 %[[ADJ_ADJ]], 1 diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func-cmp.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func-cmp.cpp index 3707f03da3f7d..b7bcc31573c05 100644 --- a/clang/test/CIR/CodeGen/pointer-to-member-func-cmp.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-member-func-cmp.cpp @@ -1,10 +1,18 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir // RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s -// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-X86 --input-file=%t.cir %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-cir.ll -// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s +// RUN: FileCheck --input-file=%t-cir.ll --check-prefixes=LLVM,LLVM-X86 %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll -// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s +// RUN: FileCheck --check-prefixes=OGCG,OGCG-X86 --input-file=%t.ll %s + +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t-arm.cir 2> %t-arm-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-arm-before.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-ARM --input-file=%t-arm.cir %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-arm-cir.ll +// RUN: FileCheck --input-file=%t-arm-cir.ll --check-prefixes=LLVM,LLVM-ARM %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t-arm.ll +// RUN: FileCheck --check-prefixes=OGCG,OGCG-ARM --input-file=%t-arm.ll %s struct Foo { void m1(int); @@ -15,56 +23,72 @@ struct Foo { bool cmp_eq(void (Foo::*lhs)(int), void (Foo::*rhs)(int)) { return lhs == rhs; } - + // CIR-BEFORE: cir.func {{.*}} @_Z6cmp_eqM3FooFviES1_ // CIR-BEFORE: %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr, !s32i)> in !rec_Foo>> // CIR-BEFORE: %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr, !s32i)> in !rec_Foo>> // CIR-BEFORE: %[[CMP:.*]] = cir.cmp eq %[[LHS]], %[[RHS]] : !cir.method, !s32i)> in !rec_Foo> // CIR-BEFORE: cir.store %[[CMP]], %{{.*}} : !cir.bool, !cir.ptr -// CIR-AFTER: @_Z6cmp_eqM3FooFviES1_ -// CIR-AFTER: %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[NULL:.*]] = cir.const #cir.int<0> : !s64i -// CIR-AFTER: %[[LHS_PTR:.*]] = cir.extract_member %[[LHS]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[RHS_PTR:.*]] = cir.extract_member %[[RHS]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[PTR_CMP:.*]] = cir.cmp eq %[[LHS_PTR]], %[[RHS_PTR]] : !s64i -// CIR-AFTER: %[[PTR_NULL:.*]] = cir.cmp eq %[[LHS_PTR]], %[[NULL]] : !s64i -// CIR-AFTER: %[[LHS_ADJ:.*]] = cir.extract_member %[[LHS]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[RHS_ADJ:.*]] = cir.extract_member %[[RHS]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[ADJ_CMP:.*]] = cir.cmp eq %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i -// CIR-AFTER: %[[TMP:.*]] = cir.or %[[PTR_NULL]], %[[ADJ_CMP]] : !cir.bool -// CIR-AFTER: %[[RESULT:.*]] = cir.and %[[PTR_CMP]], %[[TMP]] : !cir.bool +// CIR-AFTER: @_Z6cmp_eqM3FooFviES1_ +// CIR-AFTER: %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[NULL:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER: %[[LHS_PTR:.*]] = cir.extract_member %[[LHS]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[RHS_PTR:.*]] = cir.extract_member %[[RHS]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[PTR_CMP:.*]] = cir.cmp eq %[[LHS_PTR]], %[[RHS_PTR]] : !s64i +// CIR-AFTER: %[[PTR_NULL:.*]] = cir.cmp eq %[[LHS_PTR]], %[[NULL]] : !s64i +// CIR-AFTER: %[[LHS_ADJ:.*]] = cir.extract_member %[[LHS]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[RHS_ADJ:.*]] = cir.extract_member %[[RHS]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[ADJ_CMP:.*]] = cir.cmp eq %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i +// CIR-AFTER-X86: %[[TMP:.*]] = cir.or %[[PTR_NULL]], %[[ADJ_CMP]] : !cir.bool +// CIR-AFTER-ARM: %[[ONE:.*]] = cir.const #cir.int<1> +// CIR-AFTER-ARM: %[[OR_ADJ:.*]] = cir.or %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i +// CIR-AFTER-ARM: %[[AND_ADJ:.*]] = cir.and %[[OR_ADJ]], %[[ONE]] : !s64i +// CIR-AFTER-ARM: %[[ADJ_CMP2:.*]] = cir.cmp eq %[[AND_ADJ]], %[[NULL]] : !s64i +// CIR-AFTER-ARM: %[[AND_PTR_NULL:.*]] = cir.and %[[PTR_NULL]], %[[ADJ_CMP2]] : !cir.bool +// CIR-AFTER-ARM: %[[TMP:.*]] = cir.or %[[AND_PTR_NULL]], %[[ADJ_CMP]] : !cir.bool +// CIR-AFTER: %[[RESULT:.*]] = cir.and %[[PTR_CMP]], %[[TMP]] : !cir.bool -// LLVM: define {{.*}} i1 @_Z6cmp_eqM3FooFviES1_ -// LLVM: %[[LHS:.*]] = load { i64, i64 }, ptr %{{.+}} -// LLVM: %[[RHS:.*]] = load { i64, i64 }, ptr %{{.+}} -// LLVM: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 -// LLVM: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 -// LLVM: %[[PTR_CMP:.*]] = icmp eq i64 %[[LHS_PTR]], %[[RHS_PTR]] -// LLVM: %[[PTR_NULL:.*]] = icmp eq i64 %[[LHS_PTR]], 0 -// LLVM: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 -// LLVM: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 -// LLVM: %[[ADJ_CMP:.*]] = icmp eq i64 %[[LHS_ADJ]], %[[RHS_ADJ]] -// LLVM: %[[TMP:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP]] -// LLVM: %[[RESULT:.*]] = and i1 %[[PTR_CMP]], %[[TMP]] +// LLVM: define {{.*}} i1 @_Z6cmp_eqM3FooFviES1_ +// LLVM: %[[LHS:.*]] = load { i64, i64 }, ptr %{{.+}} +// LLVM: %[[RHS:.*]] = load { i64, i64 }, ptr %{{.+}} +// LLVM: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 +// LLVM: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 +// LLVM: %[[PTR_CMP:.*]] = icmp eq i64 %[[LHS_PTR]], %[[RHS_PTR]] +// LLVM: %[[PTR_NULL:.*]] = icmp eq i64 %[[LHS_PTR]], 0 +// LLVM: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 +// LLVM: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 +// LLVM: %[[ADJ_CMP:.*]] = icmp eq i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// LLVM-X86: %[[TMP:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP]] +// LLVM-ARM: %[[OR_ADJ:.*]] = or i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// LLVM-ARM: %[[AND_ADJ:.*]] = and i64 %[[OR_ADJ]], 1 +// LLVM-ARM: %[[ADJ_CMP2:.*]] = icmp eq i64 %[[AND_ADJ]], 0 +// LLVM-ARM: %[[AND_PTR_NULL:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP2]] +// LLVM-ARM: %[[TMP:.*]] = or i1 %[[AND_PTR_NULL]], %[[ADJ_CMP]] +// LLVM: %[[RESULT:.*]] = and i1 %[[PTR_CMP]], %[[TMP]] -// OGCG: define {{.*}} i1 @_Z6cmp_eqM3FooFviES1_ -// OGCG: %[[LHS_TMP:.*]] = alloca { i64, i64 } -// OGCG: %[[RHS_TMP:.*]] = alloca { i64, i64 } -// OGCG: %[[LHS_ADDR:.*]] = alloca { i64, i64 } -// OGCG: %[[RHS_ADDR:.*]] = alloca { i64, i64 } -// OGCG: %[[LHS:.*]] = load { i64, i64 }, ptr %[[LHS_ADDR]] -// OGCG: %[[RHS:.*]] = load { i64, i64 }, ptr %[[RHS_ADDR]] -// OGCG: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 -// OGCG: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 -// OGCG: %[[PTR_CMP:.*]] = icmp eq i64 %[[LHS_PTR]], %[[RHS_PTR]] -// OGCG: %[[PTR_NULL:.*]] = icmp eq i64 %[[LHS_PTR]], 0 -// OGCG: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 -// OGCG: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 -// OGCG: %[[ADJ_CMP:.*]] = icmp eq i64 %[[LHS_ADJ]], %[[RHS_ADJ]] -// OGCG: %[[TMP:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP]] -// OGCG: %[[RESULT:.*]] = and i1 %[[PTR_CMP]], %[[TMP]] +// OGCG: define {{.*}} i1 @_Z6cmp_eqM3FooFviES1_ +// OGCG: %[[LHS_TMP:.*]] = alloca { i64, i64 } +// OGCG: %[[RHS_TMP:.*]] = alloca { i64, i64 } +// OGCG: %[[LHS_ADDR:.*]] = alloca { i64, i64 } +// OGCG: %[[RHS_ADDR:.*]] = alloca { i64, i64 } +// OGCG: %[[LHS:.*]] = load { i64, i64 }, ptr %[[LHS_ADDR]] +// OGCG: %[[RHS:.*]] = load { i64, i64 }, ptr %[[RHS_ADDR]] +// OGCG: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 +// OGCG: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 +// OGCG: %[[PTR_CMP:.*]] = icmp eq i64 %[[LHS_PTR]], %[[RHS_PTR]] +// OGCG: %[[PTR_NULL:.*]] = icmp eq i64 %[[LHS_PTR]], 0 +// OGCG: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 +// OGCG: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 +// OGCG: %[[ADJ_CMP:.*]] = icmp eq i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// OGCG-X86: %[[TMP:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP]] +// OGCG-ARM: %[[OR_ADJ:.*]] = or i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// OGCG-ARM: %[[AND_ADJ:.*]] = and i64 %[[OR_ADJ]], 1 +// OGCG-ARM: %[[ADJ_CMP2:.*]] = icmp eq i64 %[[AND_ADJ]], 0 +// OGCG-ARM: %[[AND_PTR_NULL:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP2]] +// OGCG-ARM: %[[TMP:.*]] = or i1 %[[AND_PTR_NULL]], %[[ADJ_CMP]] +// OGCG: %[[RESULT:.*]] = and i1 %[[PTR_CMP]], %[[TMP]] bool cmp_ne(void (Foo::*lhs)(int), void (Foo::*rhs)(int)) { return lhs != rhs; @@ -76,46 +100,62 @@ bool cmp_ne(void (Foo::*lhs)(int), void (Foo::*rhs)(int)) { // CIR-BEFORE: %[[CMP:.*]] = cir.cmp ne %[[LHS]], %[[RHS]] : !cir.method, !s32i)> in !rec_Foo> // CIR-BEFORE: cir.store %[[CMP]], %{{.*}} : !cir.bool, !cir.ptr -// CIR-AFTER: cir.func {{.*}} @_Z6cmp_neM3FooFviES1_ -// CIR-AFTER: %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[NULL:.*]] = cir.const #cir.int<0> : !s64i -// CIR-AFTER: %[[LHS_PTR:.*]] = cir.extract_member %[[LHS]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[RHS_PTR:.*]] = cir.extract_member %[[RHS]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[PTR_CMP:.*]] = cir.cmp ne %[[LHS_PTR]], %[[RHS_PTR]] : !s64i -// CIR-AFTER: %[[PTR_NULL:.*]] = cir.cmp ne %[[LHS_PTR]], %[[NULL]] : !s64i -// CIR-AFTER: %[[LHS_ADJ:.*]] = cir.extract_member %[[LHS]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[RHS_ADJ:.*]] = cir.extract_member %[[RHS]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[ADJ_CMP:.*]] = cir.cmp ne %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i -// CIR-AFTER: %[[TMP:.*]] = cir.and %[[PTR_NULL]], %[[ADJ_CMP]] : !cir.bool -// CIR-AFTER: %[[RESULT:.*]] = cir.or %[[PTR_CMP]], %[[TMP]] : !cir.bool +// CIR-AFTER: cir.func {{.*}} @_Z6cmp_neM3FooFviES1_ +// CIR-AFTER: %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[NULL:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER: %[[LHS_PTR:.*]] = cir.extract_member %[[LHS]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[RHS_PTR:.*]] = cir.extract_member %[[RHS]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[PTR_CMP:.*]] = cir.cmp ne %[[LHS_PTR]], %[[RHS_PTR]] : !s64i +// CIR-AFTER: %[[PTR_NULL:.*]] = cir.cmp ne %[[LHS_PTR]], %[[NULL]] : !s64i +// CIR-AFTER: %[[LHS_ADJ:.*]] = cir.extract_member %[[LHS]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[RHS_ADJ:.*]] = cir.extract_member %[[RHS]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[ADJ_CMP:.*]] = cir.cmp ne %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i +// CIR-AFTER-X86: %[[TMP:.*]] = cir.and %[[PTR_NULL]], %[[ADJ_CMP]] : !cir.bool +// CIR-AFTER-ARM: %[[ONE:.*]] = cir.const #cir.int<1> +// CIR-AFTER-ARM: %[[OR_ADJ:.*]] = cir.or %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i +// CIR-AFTER-ARM: %[[AND_ADJ:.*]] = cir.and %[[OR_ADJ]], %[[ONE]] : !s64i +// CIR-AFTER-ARM: %[[ADJ_CMP2:.*]] = cir.cmp ne %[[AND_ADJ]], %[[NULL]] : !s64i +// CIR-AFTER-ARM: %[[AND_PTR_NULL:.*]] = cir.or %[[PTR_NULL]], %[[ADJ_CMP2]] : !cir.bool +// CIR-AFTER-ARM: %[[TMP:.*]] = cir.and %[[AND_PTR_NULL]], %[[ADJ_CMP]] : !cir.bool +// CIR-AFTER: %[[RESULT:.*]] = cir.or %[[PTR_CMP]], %[[TMP]] : !cir.bool -// LLVM: define {{.*}} i1 @_Z6cmp_neM3FooFviES1_ -// LLVM: %[[LHS:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[RHS:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 -// LLVM: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 -// LLVM: %[[PTR_CMP:.*]] = icmp ne i64 %[[LHS_PTR]], %[[RHS_PTR]] -// LLVM: %[[PTR_NULL:.*]] = icmp ne i64 %[[LHS_PTR]], 0 -// LLVM: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 -// LLVM: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 -// LLVM: %[[ADJ_CMP:.*]] = icmp ne i64 %[[LHS_ADJ]], %[[RHS_ADJ]] -// LLVM: %[[TMP:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP]] -// LLVM: %[[RESULT:.*]] = or i1 %[[PTR_CMP]], %[[TMP]] +// LLVM: define {{.*}} i1 @_Z6cmp_neM3FooFviES1_ +// LLVM: %[[LHS:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[RHS:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 +// LLVM: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 +// LLVM: %[[PTR_CMP:.*]] = icmp ne i64 %[[LHS_PTR]], %[[RHS_PTR]] +// LLVM: %[[PTR_NULL:.*]] = icmp ne i64 %[[LHS_PTR]], 0 +// LLVM: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 +// LLVM: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 +// LLVM: %[[ADJ_CMP:.*]] = icmp ne i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// LLVM-X86: %[[TMP:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP]] +// LLVM-ARM: %[[OR_ADJ:.*]] = or i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// LLVM-ARM: %[[AND_ADJ:.*]] = and i64 %[[OR_ADJ]], 1 +// LLVM-ARM: %[[ADJ_CMP2:.*]] = icmp ne i64 %[[AND_ADJ]], 0 +// LLVM-ARM: %[[AND_PTR_NULL:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP2]] +// LLVM-ARM: %[[TMP:.*]] = and i1 %[[AND_PTR_NULL]], %[[ADJ_CMP]] +// LLVM: %[[RESULT:.*]] = or i1 %[[PTR_CMP]], %[[TMP]] -// OGCG: define {{.*}} i1 @_Z6cmp_neM3FooFviES1_ -// OGCG: %[[LHS_TMP:.*]] = alloca { i64, i64 } -// OGCG: %[[RHS_TMP:.*]] = alloca { i64, i64 } -// OGCG: %[[LHS_ADDR:.*]] = alloca { i64, i64 } -// OGCG: %[[RHS_ADDR:.*]] = alloca { i64, i64 } -// OGCG: %[[LHS:.*]] = load { i64, i64 }, ptr %[[LHS_ADDR]] -// OGCG: %[[RHS:.*]] = load { i64, i64 }, ptr %[[RHS_ADDR]] -// OGCG: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 -// OGCG: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 -// OGCG: %[[PTR_CMP:.*]] = icmp ne i64 %[[LHS_PTR]], %[[RHS_PTR]] -// OGCG: %[[PTR_NULL:.*]] = icmp ne i64 %[[LHS_PTR]], 0 -// OGCG: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 -// OGCG: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 -// OGCG: %[[ADJ_CMP:.*]] = icmp ne i64 %[[LHS_ADJ]], %[[RHS_ADJ]] -// OGCG: %[[TMP:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP]] -// OGCG: %[[RESULT:.*]] = or i1 %[[PTR_CMP]], %[[TMP]] +// OGCG: define {{.*}} i1 @_Z6cmp_neM3FooFviES1_ +// OGCG: %[[LHS_TMP:.*]] = alloca { i64, i64 } +// OGCG: %[[RHS_TMP:.*]] = alloca { i64, i64 } +// OGCG: %[[LHS_ADDR:.*]] = alloca { i64, i64 } +// OGCG: %[[RHS_ADDR:.*]] = alloca { i64, i64 } +// OGCG: %[[LHS:.*]] = load { i64, i64 }, ptr %[[LHS_ADDR]] +// OGCG: %[[RHS:.*]] = load { i64, i64 }, ptr %[[RHS_ADDR]] +// OGCG: %[[LHS_PTR:.*]] = extractvalue { i64, i64 } %[[LHS]], 0 +// OGCG: %[[RHS_PTR:.*]] = extractvalue { i64, i64 } %[[RHS]], 0 +// OGCG: %[[PTR_CMP:.*]] = icmp ne i64 %[[LHS_PTR]], %[[RHS_PTR]] +// OGCG: %[[PTR_NULL:.*]] = icmp ne i64 %[[LHS_PTR]], 0 +// OGCG: %[[LHS_ADJ:.*]] = extractvalue { i64, i64 } %[[LHS]], 1 +// OGCG: %[[RHS_ADJ:.*]] = extractvalue { i64, i64 } %[[RHS]], 1 +// OGCG: %[[ADJ_CMP:.*]] = icmp ne i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// OGCG-X86: %[[TMP:.*]] = and i1 %[[PTR_NULL]], %[[ADJ_CMP]] +// OGCG-ARM: %[[OR_ADJ:.*]] = or i64 %[[LHS_ADJ]], %[[RHS_ADJ]] +// OGCG-ARM: %[[AND_ADJ:.*]] = and i64 %[[OR_ADJ]], 1 +// OGCG-ARM: %[[ADJ_CMP2:.*]] = icmp ne i64 %[[AND_ADJ]], 0 +// OGCG-ARM: %[[AND_PTR_NULL:.*]] = or i1 %[[PTR_NULL]], %[[ADJ_CMP2]] +// OGCG-ARM: %[[TMP:.*]] = and i1 %[[AND_PTR_NULL]], %[[ADJ_CMP]] +// OGCG: %[[RESULT:.*]] = or i1 %[[PTR_CMP]], %[[TMP]] diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp index 1388e38882617..def822350a220 100644 --- a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp @@ -1,10 +1,21 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir // RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s -// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-X86 --input-file=%t.cir %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-cir.ll -// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s +// RUN: FileCheck --input-file=%t-cir.ll --check-prefixes=LLVM,LLVM-X86 %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll -// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s +// RUN: FileCheck --check-prefixes=OGCG,OGCG-X86 --input-file=%t.ll %s + +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t-arm.cir 2> %t-arm-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-arm-before.cir %s +// RUN: FileCheck --check-prefixes=CIR-AFTER,CIR-AFTER-ARM --input-file=%t-arm.cir %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-arm-cir.ll +// RUN: FileCheck --input-file=%t-arm-cir.ll --check-prefixes=LLVM,LLVM-ARM %s +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t-arm.ll +// RUN: FileCheck --check-prefixes=OGCG,OGCG-ARM --input-file=%t-arm.ll %s + +// FIXME: Some of the differences between LLVM (via CIR) and OGCG below are +// due to calling convention lowering being missing in the CIR path. struct Foo { void m1(int); @@ -16,23 +27,28 @@ struct Foo { void (Foo::*m1_ptr)(int) = &Foo::m1; // CIR-BEFORE: cir.global external @m1_ptr = #cir.method<@_ZN3Foo2m1Ei> : !cir.method, !s32i)> in !rec_Foo> -// CIR-AFTER-DAG: cir.global "private" constant cir_private @[[NONVIRT_RET:.*]] = #cir.const_record<{#cir.global_view<@_ZN3Foo2m1Ei> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct -// CIR-AFTER-DAG: cir.global "private" constant cir_private @[[VIRT_RET:.*]] = #cir.const_record<{#cir.int<9> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct -// CIR-AFTER-DAG: cir.global "private" constant cir_private @[[NULL_RET:.*]] = #cir.const_record<{#cir.int<0> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct -// CIR-AFTER: cir.global external @m1_ptr = #cir.const_record<{#cir.global_view<@_ZN3Foo2m1Ei> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct -// LLVM-DAG: @m1_ptr = global { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } -// LLVM-DAG: @[[NONVIRT_RET:.*]] = private constant { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } -// LLVM-DAG: @[[VIRT_RET:.*]] = private constant { i64, i64 } { i64 9, i64 0 } -// LLVM-DAG: @[[NULL_RET:.*]] = private constant { i64, i64 } zeroinitializer +// CIR-AFTER-DAG: cir.global "private" constant cir_private @[[NONVIRT_RET:.*]] = #cir.const_record<{#cir.global_view<@_ZN3Foo2m1Ei> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// CIR-AFTER-X86-DAG: cir.global "private" constant cir_private @[[VIRT_RET:.*]] = #cir.const_record<{#cir.int<9> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// CIR-AFTER-ARM-DAG: cir.global "private" constant cir_private @[[VIRT_RET:.*]] = #cir.const_record<{#cir.int<8> : !s64i, #cir.int<1> : !s64i}> : !rec_anon_struct +// CIR-AFTER-DAG: cir.global "private" constant cir_private @[[NULL_RET:.*]] = #cir.const_record<{#cir.int<0> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// CIR-AFTER: cir.global external @m1_ptr = #cir.const_record<{#cir.global_view<@_ZN3Foo2m1Ei> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// LLVM-DAG: @m1_ptr = global { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } +// LLVM-DAG: @[[NONVIRT_RET:.*]] = private constant { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } +// LLVM-X86-DAG: @[[VIRT_RET:.*]] = private constant { i64, i64 } { i64 9, i64 0 } +// LLVM-ARM-DAG: @[[VIRT_RET:.*]] = private constant { i64, i64 } { i64 8, i64 1 } +// LLVM-DAG: @[[NULL_RET:.*]] = private constant { i64, i64 } zeroinitializer // OGCG: @m1_ptr = global { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } // Global pointer to virtual method void (Foo::*m2_ptr)(int) = &Foo::m2; // CIR-BEFORE: cir.global external @m2_ptr = #cir.method : !cir.method, !s32i)> in !rec_Foo> -// CIR-AFTER: cir.global external @m2_ptr = #cir.const_record<{#cir.int<1> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct -// LLVM-DAG: @m2_ptr = global { i64, i64 } { i64 1, i64 0 } -// OGCG: @m2_ptr = global { i64, i64 } { i64 1, i64 0 } +// CIR-AFTER-X86: cir.global external @m2_ptr = #cir.const_record<{#cir.int<1> : !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct +// CIR-AFTER-ARM: cir.global external @m2_ptr = #cir.const_record<{#cir.int<0> : !s64i, #cir.int<1> : !s64i}> : !rec_anon_struct +// LLVM-X86-DAG: @m2_ptr = global { i64, i64 } { i64 1, i64 0 } +// LLVM-ARM-DAG: @m2_ptr = global { i64, i64 } { i64 0, i64 1 } +// OGCG-X86: @m2_ptr = global { i64, i64 } { i64 1, i64 0 } +// OGCG-ARM: @m2_ptr = global { i64, i64 } { i64 0, i64 1 } // Self-referencing PMF causes a null method. long (Foo::*pmf1)(int) = pmf1; @@ -65,8 +81,14 @@ auto make_non_virtual() -> void (Foo::*)(int) { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} { i64, i64 } @_Z16make_non_virtualv() -// OGCG: ret { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } +// OGCG-X86: define {{.*}} { i64, i64 } @_Z16make_non_virtualv() +// OGCG-X86: ret { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 } + +// OGCG-ARM: define {{.*}} [2 x i64] @_Z16make_non_virtualv() +// OGCG-ARM: %[[RETVAL:.*]] = alloca { i64, i64 } +// OGCG-ARM: store { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 }, ptr %[[RETVAL]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[RETVAL]] +// OGCG-ARM: ret [2 x i64] %[[RET]] auto make_virtual() -> void (Foo::*)(int) { return &Foo::m3; @@ -92,8 +114,12 @@ auto make_virtual() -> void (Foo::*)(int) { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} @_Z12make_virtualv() -// OGCG: ret { i64, i64 } { i64 9, i64 0 } +// OGCG: define {{.*}} @_Z12make_virtualv() +// OGCG-X86: ret { i64, i64 } { i64 9, i64 0 } +// OGCG-ARM: %[[RETVAL:.*]] = alloca { i64, i64 } +// OGCG-ARM: store { i64, i64 } { i64 8, i64 1 }, ptr %[[RETVAL]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[RETVAL]] +// OGCG-ARM: ret [2 x i64] %[[RET]] auto make_null() -> void (Foo::*)(int) { return nullptr; @@ -119,8 +145,12 @@ auto make_null() -> void (Foo::*)(int) { // LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RETVAL]] // LLVM: ret { i64, i64 } %[[RET]] -// OGCG: define {{.*}} @_Z9make_nullv() -// OGCG: ret { i64, i64 } zeroinitializer +// OGCG: define {{.*}} @_Z9make_nullv() +// OGCG-X86: ret { i64, i64 } zeroinitializer +// OGCG-ARM: %[[RETVAL:.*]] = alloca { i64, i64 } +// OGCG-ARM: store { i64, i64 } zeroinitializer, ptr %[[RETVAL]] +// OGCG-ARM: %[[RET:.*]] = load [2 x i64], ptr %[[RETVAL]] +// OGCG-ARM: ret [2 x i64] %[[RET]] void call(Foo *obj, void (Foo::*func)(int), int arg) { (obj->*func)(arg); @@ -133,76 +163,87 @@ void call(Foo *obj, void (Foo::*func)(int), int arg) { // CIR-BEFORE: %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !s32i // CIR-BEFORE: cir.call %[[CALLEE]](%[[THIS]], %[[ARG]]) : (!cir.ptr, !cir.ptr, !s32i)>>, !cir.ptr {{.*}}, !s32i {{.*}}) -> () -// CIR-AFTER: cir.func {{.*}} @_Z4callP3FooMS_FviEi -// CIR-AFTER: %[[OBJ:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr>, !cir.ptr -// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr, !rec_anon_struct -// CIR-AFTER: %[[VIRT_BIT:.*]] = cir.const #cir.int<1> : !s64i -// CIR-AFTER: %[[ADJ:.*]] = cir.extract_member %[[FUNC]][1] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[THIS:.*]] = cir.cast bitcast %[[OBJ]] : !cir.ptr -> !cir.ptr -// CIR-AFTER: %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ]] : (!cir.ptr, !s64i) -> !cir.ptr -// CIR-AFTER: %[[METHOD_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i -// CIR-AFTER: %[[VIRT_BIT_TEST:.*]] = cir.and %[[METHOD_PTR]], %[[VIRT_BIT]] : !s64i -// CIR-AFTER: %[[IS_VIRTUAL:.*]] = cir.cmp eq %[[VIRT_BIT_TEST]], %[[VIRT_BIT]] : !s64i -// CIR-AFTER: %[[CALLEE:.*]] = cir.ternary(%[[IS_VIRTUAL]], true { -// CIR-AFTER: %[[VTABLE_PTR:.*]] = cir.cast bitcast %[[ADJUSTED_THIS]] : !cir.ptr -> !cir.ptr> -// CIR-AFTER: %[[VTABLE:.*]] = cir.load %[[VTABLE_PTR]] : !cir.ptr>, !cir.ptr -// CIR-AFTER: %[[OFFSET:.*]] = cir.sub %[[METHOD_PTR]], %[[VIRT_BIT]] : !s64i -// CIR-AFTER: %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], %[[OFFSET]] : (!cir.ptr, !s64i) -> !cir.ptr -// CIR-AFTER: %[[VIRTUAL_FN_PTR:.*]] = cir.cast bitcast %[[VTABLE_SLOT]] : !cir.ptr -> !cir.ptr, !cir.ptr, !s32i)>>> -// CIR-AFTER: %[[VIRTUAL_FN_PTR_LOAD:.*]] = cir.load %[[VIRTUAL_FN_PTR]] : !cir.ptr, !cir.ptr, !s32i)>>>, !cir.ptr, !cir.ptr, !s32i)>> -// CIR-AFTER: cir.yield %[[VIRTUAL_FN_PTR_LOAD]] : !cir.ptr, !cir.ptr, !s32i)>> -// CIR-AFTER: }, false { -// CIR-AFTER: %[[CALLEE_PTR:.*]] = cir.cast int_to_ptr %[[METHOD_PTR]] : !s64i -> !cir.ptr, !cir.ptr, !s32i)>> -// CIR-AFTER: cir.yield %[[CALLEE_PTR]] : !cir.ptr, !cir.ptr, !s32i)>> -// CIR-AFTER: }) : (!cir.bool) -> !cir.ptr, !cir.ptr, !s32i)>> -// CIR-AFTER: %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !s32i -// CIR-AFTER: cir.call %[[CALLEE]](%[[ADJUSTED_THIS]], %[[ARG]]) : (!cir.ptr, !cir.ptr, !s32i)>>, !cir.ptr {{.*}}, !s32i {{.*}}) -> () - -// LLVM: define {{.*}} @_Z4callP3FooMS_FviEi -// LLVM: %[[OBJ:.*]] = load ptr, ptr %{{.*}} -// LLVM: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}} -// LLVM: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1 -// LLVM: %[[ADJUSTED_THIS:.*]] = getelementptr i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]] -// LLVM: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0 -// LLVM: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1 -// LLVM: %[[IS_VIRTUAL:.*]] = icmp eq i64 %[[VIRT_BIT]], 1 -// LLVM: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]] -// LLVM: [[HANDLE_VIRTUAL]]: -// LLVM: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]] -// LLVM: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1 -// LLVM: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]] -// LLVM: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]] -// LLVM: br label %[[CONTINUE:.*]] -// LLVM: [[HANDLE_NON_VIRTUAL]]: -// LLVM: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr -// LLVM: br label %[[CONTINUE]] -// LLVM: [[CONTINUE]]: -// LLVM: %[[CALLEE_PTR:.*]] = phi ptr [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ], [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ] -// LLVM: %[[ARG:.*]] = load i32, ptr %{{.+}} -// LLVM: call void %[[CALLEE_PTR]](ptr {{.*}} %[[ADJUSTED_THIS]], i32 {{.*}} %[[ARG]]) -// LLVM: } - -// OGCG: define {{.*}} @_Z4callP3FooMS_FviEi -// OGCG: %[[OBJ:.*]] = load ptr, ptr %{{.*}} -// OGCG: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}} -// OGCG: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1 -// OGCG: %[[ADJUSTED_THIS:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]] -// OGCG: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0 -// OGCG: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1 -// OGCG: %[[IS_VIRTUAL:.*]] = icmp ne i64 %[[VIRT_BIT]], 0 -// OGCG: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]] -// OGCG: [[HANDLE_VIRTUAL]]: -// OGCG: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]] -// OGCG: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1 -// OGCG: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]] -// OGCG: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]] -// OGCG: br label %[[CONTINUE:.*]] -// OGCG: [[HANDLE_NON_VIRTUAL]]: -// OGCG: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr -// OGCG: br label %[[CONTINUE]] -// OGCG: [[CONTINUE]]: -// OGCG: %[[CALLEE_PTR:.*]] = phi ptr [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ], [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ] -// OGCG: %[[ARG:.*]] = load i32, ptr %{{.+}} -// OGCG: call void %[[CALLEE_PTR]](ptr {{.*}} %[[ADJUSTED_THIS]], i32 {{.*}} %[[ARG]]) -// OGCG: } - +// CIR-AFTER: cir.func {{.*}} @_Z4callP3FooMS_FviEi +// CIR-AFTER: %[[OBJ:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr>, !cir.ptr +// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr, !rec_anon_struct +// CIR-AFTER: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i +// CIR-AFTER: %[[ADJ:.*]] = cir.extract_member %[[FUNC]][1] : !rec_anon_struct -> !s64i +// CIR-AFTER-ARM: %[[ADJ_SHIFT:.*]] = cir.shift(right, %[[ADJ]] : !s64i, %[[ONE:.*]] : !s64i) -> !s64i +// CIR-AFTER: %[[THIS:.*]] = cir.cast bitcast %[[OBJ]] : !cir.ptr -> !cir.ptr +// CIR-AFTER-X86: %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER-ARM: %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ_SHIFT]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER: %[[METHOD_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER-X86: %[[VIRT_BIT_TEST:.*]] = cir.and %[[METHOD_PTR]], %[[ONE]] : !s64i +// CIR-AFTER-ARM: %[[VIRT_BIT_TEST:.*]] = cir.and %[[ADJ]], %[[ONE]] : !s64i +// CIR-AFTER: %[[IS_VIRTUAL:.*]] = cir.cmp eq %[[VIRT_BIT_TEST]], %[[ONE]] : !s64i +// CIR-AFTER: %[[CALLEE:.*]] = cir.ternary(%[[IS_VIRTUAL]], true { +// CIR-AFTER: %[[VTABLE_PTR:.*]] = cir.cast bitcast %[[ADJUSTED_THIS]] : !cir.ptr -> !cir.ptr> +// CIR-AFTER: %[[VTABLE:.*]] = cir.load %[[VTABLE_PTR]] : !cir.ptr>, !cir.ptr +// CIR-AFTER-X86: %[[OFFSET:.*]] = cir.sub %[[METHOD_PTR]], %[[ONE]] : !s64i +// CIR-AFTER-X86: %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], %[[OFFSET]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER-ARM: %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], %[[METHOD_PTR]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER: %[[VIRTUAL_FN_PTR:.*]] = cir.cast bitcast %[[VTABLE_SLOT]] : !cir.ptr -> !cir.ptr, !cir.ptr, !s32i)>>> +// CIR-AFTER: %[[VIRTUAL_FN_PTR_LOAD:.*]] = cir.load %[[VIRTUAL_FN_PTR]] : !cir.ptr, !cir.ptr, !s32i)>>>, !cir.ptr, !cir.ptr, !s32i)>> +// CIR-AFTER: cir.yield %[[VIRTUAL_FN_PTR_LOAD]] : !cir.ptr, !cir.ptr, !s32i)>> +// CIR-AFTER: }, false { +// CIR-AFTER: %[[CALLEE_PTR:.*]] = cir.cast int_to_ptr %[[METHOD_PTR]] : !s64i -> !cir.ptr, !cir.ptr, !s32i)>> +// CIR-AFTER: cir.yield %[[CALLEE_PTR]] : !cir.ptr, !cir.ptr, !s32i)>> +// CIR-AFTER: }) : (!cir.bool) -> !cir.ptr, !cir.ptr, !s32i)>> +// CIR-AFTER: %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr, !s32i +// CIR-AFTER: cir.call %[[CALLEE]](%[[ADJUSTED_THIS]], %[[ARG]]) : (!cir.ptr, !cir.ptr, !s32i)>>, !cir.ptr {{.*}}, !s32i {{.*}}) -> () + +// LLVM: define {{.*}} @_Z4callP3FooMS_FviEi +// LLVM: %[[OBJ:.*]] = load ptr, ptr %{{.*}} +// LLVM: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1 +// LLVM-X86: %[[ADJUSTED_THIS:.*]] = getelementptr i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]] +// LLVM-ARM: %[[THIS_ADJ_SHIFT:.*]] = ashr i64 %[[THIS_ADJ]], 1 +// LLVM-ARM: %[[ADJUSTED_THIS:.*]] = getelementptr i8, ptr %[[OBJ]], i64 %[[THIS_ADJ_SHIFT]] +// LLVM: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0 +// LLVM-ARM: %[[VIRT_BIT:.*]] = and i64 %[[THIS_ADJ]], 1 +// LLVM-X86: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1 +// LLVM: %[[IS_VIRTUAL:.*]] = icmp eq i64 %[[VIRT_BIT]], 1 +// LLVM: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]] +// LLVM: [[HANDLE_VIRTUAL]]: +// LLVM: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]] +// LLVM-X86: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1 +// LLVM-X86: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]] +// LLVM-ARM: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[PTR_FIELD]] +// LLVM: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]] +// LLVM: br label %[[CONTINUE:.*]] +// LLVM: [[HANDLE_NON_VIRTUAL]]: +// LLVM: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr +// LLVM: br label %[[CONTINUE]] +// LLVM: [[CONTINUE]]: +// LLVM: %[[CALLEE_PTR:.*]] = phi ptr [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ], [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ] +// LLVM: %[[ARG:.*]] = load i32, ptr %{{.+}} +// LLVM: call void %[[CALLEE_PTR]](ptr {{.*}} %[[ADJUSTED_THIS]], i32 {{.*}} %[[ARG]]) +// LLVM: } + +// OGCG: define {{.*}} @_Z4callP3FooMS_FviEi +// OGCG: %[[OBJ:.*]] = load ptr, ptr %{{.*}} +// OGCG: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1 +// OGCG-X86: %[[ADJUSTED_THIS:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]] +// OGCG-ARM: %[[THIS_ADJ_SHIFT:.*]] = ashr i64 %[[THIS_ADJ]], 1 +// OGCG-ARM: %[[ADJUSTED_THIS:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i64 %[[THIS_ADJ_SHIFT]] +// OGCG: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0 +// OGCG-X86: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1 +// OGCG-ARM: %[[VIRT_BIT:.*]] = and i64 %[[THIS_ADJ]], 1 +// OGCG: %[[IS_VIRTUAL:.*]] = icmp ne i64 %[[VIRT_BIT]], 0 +// OGCG: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]] +// OGCG: [[HANDLE_VIRTUAL]]: +// OGCG: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]] +// OGCG-X86: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1 +// OGCG-X86: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]] +// OGCG-ARM: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[PTR_FIELD]] +// OGCG: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]] +// OGCG: br label %[[CONTINUE:.*]] +// OGCG: [[HANDLE_NON_VIRTUAL]]: +// OGCG: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr +// OGCG: br label %[[CONTINUE]] +// OGCG: [[CONTINUE]]: +// OGCG: %[[CALLEE_PTR:.*]] = phi ptr [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ], [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ] +// OGCG: %[[ARG:.*]] = load i32, ptr %{{.+}} +// OGCG: call void %[[CALLEE_PTR]](ptr {{.*}} %[[ADJUSTED_THIS]], i32 {{.*}} %[[ARG]]) +// OGCG: }