https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/196592

This implements the ARM-specific CXXABI lowering details for pointers to member 
functions, including comparsion and cast to bool. This includes updates to 
several places that we had neglected to insert diagnostics saying that 
ARM-specific handling was needed.

>From e8295ed5937b0c34d6aa7b674945e3a30ff00978 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 7 May 2026 16:54:17 -0700
Subject: [PATCH] [CIR] Implement ARM-specific lowering for method pointers

This implements the ARM-specific CXXABI lowering details for pointers
to member functions, including comparsion and cast to bool. This includes
updates to several places that we had neglected to insert diagnostics
saying that ARM-specific handling was needed.
---
 .../TargetLowering/LowerItaniumCXXABI.cpp     | 115 +++++++--
 .../CodeGen/pointer-to-data-member-cmp.cpp    |   4 +
 .../CodeGen/pointer-to-member-func-cast.cpp   | 190 +++++++++------
 .../CodeGen/pointer-to-member-func-cmp.cpp    | 212 +++++++++-------
 .../CIR/CodeGen/pointer-to-member-func.cpp    | 227 +++++++++++-------
 5 files changed, 475 insertions(+), 273 deletions(-)

diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 8769975bdc948..636b65f514689 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<CIRCXXABI> 
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<LowerItaniumCXXABI>(
+        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<LowerItaniumCXXABI>(lm,
-                                                /*useARMMethodPtrABI=*/true);
+    return std::make_unique<LowerItaniumCXXABI>(
+        lm,
+        /*useARMMethodPtrABI=*/true,
+        /*use32BitVTableOffsetABI=*/true);
 
   case clang::TargetCXXABI::GenericItanium:
     return std::make_unique<LowerItaniumCXXABI>(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,11 +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);
+  mlir::Value adj = rawAdj;
   if (useARMMethodPtrABI) {
-    op.emitError("ARM method ptr abi NYI");
-    return;
+    adj =
+        cir::ShiftOp::create(locBuilder, ptrdiffCIRTy, adj, ptrdiffOne, false);
   }
 
   // Apply the adjustment to the 'this' pointer.
@@ -341,14 +356,14 @@ 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(rewriter, op.getLoc(), methodPtrField, ptrdiffOne);
+  mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq,
+                                             virtualBit, ptrdiffOne);
 
   assert(!cir::MissingFeatures::emitCFICheck());
   assert(!cir::MissingFeatures::emitVFEInfo());
@@ -371,11 +386,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 +481,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 +520,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 +529,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,6 +576,23 @@ 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));
+
+    // Compute (lhs.adj | rhs.adj) & 1 and test it against zero.
+    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
@@ -593,18 +637,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..adb28050881e9 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-X86-AFTER --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-ARM-AFTER 
--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<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo> -> !cir.bool
 
-// CIR-AFTER: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE
-// CIR-AFTER:   %[[FUNC:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!rec_anon_struct>, !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>, !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-ARM-AFTER:   %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CIR-ARM-AFTER:   %[[ADJ:.*]] = cir.extract_member %[[FUNC]][1] : 
!rec_anon_struct -> !s64i
+// CIR-ARM-AFTER:   %[[AND:.*]] = cir.and %[[ADJ]], %[[ONE]] : !s64i
+// CIR-ARM-AFTER:   %[[NOT_VIRTUAL:.*]] = cir.cmp ne %[[AND]], %[[NULL_VAL]] : 
!s64i
+// CIR-ARM-AFTER:   %[[TMP:.*]] = cir.or %[[BOOL_VAL]], %[[NOT_VIRTUAL]] : 
!cir.bool
+// CIR-X86-AFTER-NOT: cir.extract_member
+// CIR-X86-AFTER-NOT: cir.and
+// CIR-X86-AFTER-NOT: cir.cmp
+// CIR-X86-AFTER-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<void (Bar::*)()>(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<DerivedMemFunc>(ptr);
@@ -128,26 +168,30 @@ DerivedMemFunc base_to_derived(Base2MemFunc ptr) {
 // CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!cir.method<!cir.func<(!cir.ptr<!rec_Base2>, !s32i)> in !rec_Base2>>, 
!cir.method<!cir.func<(!cir.ptr<!rec_Base2>, !s32i)> in !rec_Base2>
 // CIR-BEFORE:   %{{.*}} = cir.derived_method %[[PTR]][16] : 
!cir.method<!cir.func<(!cir.ptr<!rec_Base2>, !s32i)> in !rec_Base2> -> 
!cir.method<!cir.func<(!cir.ptr<!rec_Derived>, !s32i)> in !rec_Derived>
 
-// CIR-AFTER: cir.func {{.*}} @_Z15base_to_derivedM5Base2FviE
-// CIR-AFTER:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!rec_anon_struct>, !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>, !rec_anon_struct
+// CIR-AFTER:       %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : 
!rec_anon_struct -> !s64i
+// CIR-X86-AFTER:   %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i
+// CIR-ARM-AFTER:   %[[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<Base1MemFunc>(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<Base2MemFunc>(ptr);
@@ -189,23 +239,27 @@ Base2MemFunc derived_to_base(DerivedMemFunc ptr) {
 // CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!cir.method<!cir.func<(!cir.ptr<!rec_Derived>, !s32i)> in 
!rec_Derived>>, !cir.method<!cir.func<(!cir.ptr<!rec_Derived>, !s32i)> in 
!rec_Derived>
 // CIR-BEFORE:   %{{.*}} = cir.base_method %[[PTR]][16] : 
!cir.method<!cir.func<(!cir.ptr<!rec_Derived>, !s32i)> in !rec_Derived> -> 
!cir.method<!cir.func<(!cir.ptr<!rec_Base2>, !s32i)> in !rec_Base2>
 
-// CIR-AFTER: cir.func {{.*}} @_Z15derived_to_baseM7DerivedFviE
-// CIR-AFTER:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!rec_anon_struct>, !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>, !rec_anon_struct
+// CIR-AFTER:       %[[OFFSET:.*]] = cir.extract_member %[[PTR]][1] : 
!rec_anon_struct -> !s64i
+// CIR-X86-AFTER:   %[[OFFSET_ADJ:.*]] = cir.const #cir.int<16> : !s64i
+// CIR-ARM-AFTER:   %[[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..e628628154429 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-X86-AFTER --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-ARM-AFTER 
--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<!cir.method<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo>>
 // CIR-BEFORE:   %[[RHS:.*]] = cir.load{{.*}} %1 : 
!cir.ptr<!cir.method<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo>>
 // CIR-BEFORE:   %[[CMP:.*]] = cir.cmp eq %[[LHS]], %[[RHS]] : 
!cir.method<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo>
 // CIR-BEFORE:   cir.store %[[CMP]], %{{.*}} : !cir.bool, !cir.ptr<!cir.bool>
 
-// CIR-AFTER: @_Z6cmp_eqM3FooFviES1_
-// CIR-AFTER:   %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr<!rec_anon_struct>, 
!rec_anon_struct
-// CIR-AFTER:   %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr<!rec_anon_struct>, 
!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>, !rec_anon_struct
+// CIR-AFTER:       %[[RHS:.*]] = cir.load{{.*}} %1 : 
!cir.ptr<!rec_anon_struct>, !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-X86-AFTER:   %[[TMP:.*]] = cir.or %[[PTR_NULL]], %[[ADJ_CMP]] : 
!cir.bool
+// CIR-ARM-AFTER:   %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR-ARM-AFTER:   %[[OR_ADJ:.*]] = cir.or %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i
+// CIR-ARM-AFTER:   %[[AND_ADJ:.*]] = cir.and %[[OR_ADJ]], %[[ONE]] : !s64i
+// CIR-ARM-AFTER:   %[[ADJ_CMP2:.*]] = cir.cmp eq %[[AND_ADJ]], %[[NULL]] : 
!s64i
+// CIR-ARM-AFTER:   %[[AND_PTR_NULL:.*]] = cir.and %[[PTR_NULL]], 
%[[ADJ_CMP2]] : !cir.bool
+// CIR-ARM-AFTER:   %[[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<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo>
 // CIR-BEFORE:   cir.store %[[CMP]], %{{.*}} : !cir.bool, !cir.ptr<!cir.bool>
 
-// CIR-AFTER: cir.func {{.*}} @_Z6cmp_neM3FooFviES1_
-// CIR-AFTER:   %[[LHS:.*]] = cir.load{{.*}} %0 : !cir.ptr<!rec_anon_struct>, 
!rec_anon_struct
-// CIR-AFTER:   %[[RHS:.*]] = cir.load{{.*}} %1 : !cir.ptr<!rec_anon_struct>, 
!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>, !rec_anon_struct
+// CIR-AFTER:       %[[RHS:.*]] = cir.load{{.*}} %1 : 
!cir.ptr<!rec_anon_struct>, !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-X86-AFTER:   %[[TMP:.*]] = cir.and %[[PTR_NULL]], %[[ADJ_CMP]] : 
!cir.bool
+// CIR-ARM-AFTER:   %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR-ARM-AFTER:   %[[OR_ADJ:.*]] = cir.or %[[LHS_ADJ]], %[[RHS_ADJ]] : !s64i
+// CIR-ARM-AFTER:   %[[AND_ADJ:.*]] = cir.and %[[OR_ADJ]], %[[ONE]] : !s64i
+// CIR-ARM-AFTER:   %[[ADJ_CMP2:.*]] = cir.cmp ne %[[AND_ADJ]], %[[NULL]] : 
!s64i
+// CIR-ARM-AFTER:   %[[AND_PTR_NULL:.*]] = cir.or %[[PTR_NULL]], %[[ADJ_CMP2]] 
: !cir.bool
+// CIR-ARM-AFTER:   %[[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..d060bcd3cfda0 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-X86-AFTER --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-ARM-AFTER 
--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<!cir.func<(!cir.ptr<!rec_Foo>, !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-X86-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-ARM-AFTER-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<vtable_offset = 0> : 
!cir.method<!cir.func<(!cir.ptr<!rec_Foo>, !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-X86-AFTER: cir.global external @m2_ptr = #cir.const_record<{#cir.int<1> 
: !s64i, #cir.int<0> : !s64i}> : !rec_anon_struct
+// CIR-ARM-AFTER: 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>, !s32i
 // CIR-BEFORE:   cir.call %[[CALLEE]](%[[THIS]], %[[ARG]]) : 
(!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>, 
!cir.ptr<!void> {{.*}}, !s32i {{.*}}) -> ()
 
-// CIR-AFTER: cir.func {{.*}} @_Z4callP3FooMS_FviEi
-// CIR-AFTER:   %[[OBJ:.*]] = cir.load{{.*}} %{{.*}} : 
!cir.ptr<!cir.ptr<!rec_Foo>>, !cir.ptr<!rec_Foo>
-// CIR-AFTER:   %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr<!rec_anon_struct>, 
!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<!rec_Foo> 
-> !cir.ptr<!void>
-// CIR-AFTER:   %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ]] : 
(!cir.ptr<!void>, !s64i) -> !cir.ptr<!void>
-// 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<!void> -> !cir.ptr<!cir.ptr<!s8i>>
-// CIR-AFTER:     %[[VTABLE:.*]] = cir.load %[[VTABLE_PTR]] : 
!cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
-// CIR-AFTER:     %[[OFFSET:.*]] = cir.sub %[[METHOD_PTR]], %[[VIRT_BIT]] : 
!s64i
-// CIR-AFTER:     %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], 
%[[OFFSET]] : (!cir.ptr<!s8i>, !s64i) -> !cir.ptr<!s8i>
-// CIR-AFTER:     %[[VIRTUAL_FN_PTR:.*]] = cir.cast bitcast %[[VTABLE_SLOT]] : 
!cir.ptr<!s8i> -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, 
!cir.ptr<!rec_Foo>, !s32i)>>>
-// CIR-AFTER:     %[[VIRTUAL_FN_PTR_LOAD:.*]] = cir.load %[[VIRTUAL_FN_PTR]] : 
!cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>>, 
!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
-// CIR-AFTER:     cir.yield %[[VIRTUAL_FN_PTR_LOAD]] : 
!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
-// CIR-AFTER:   }, false {
-// CIR-AFTER:     %[[CALLEE_PTR:.*]] = cir.cast int_to_ptr %[[METHOD_PTR]] : 
!s64i -> !cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
-// CIR-AFTER:     cir.yield %[[CALLEE_PTR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
-// CIR-AFTER:   }) : (!cir.bool) -> !cir.ptr<!cir.func<(!cir.ptr<!void>, 
!cir.ptr<!rec_Foo>, !s32i)>>
-// CIR-AFTER:   %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!s32i>, !s32i
-// CIR-AFTER:   cir.call %[[CALLEE]](%[[ADJUSTED_THIS]], %[[ARG]]) : 
(!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>, 
!cir.ptr<!void> {{.*}}, !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<!rec_Foo>>, !cir.ptr<!rec_Foo>
+// CIR-AFTER:      %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr<!rec_anon_struct>, 
!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-ARM-AFTER:  %[[ADJ_SHIFT:.*]] = cir.shift(right, %[[ADJ]] : !s64i, 
%[[ONE:.*]] : !s64i) -> !s64i
+// CIR-AFTER:      %[[THIS:.*]] = cir.cast bitcast %[[OBJ]] : 
!cir.ptr<!rec_Foo> -> !cir.ptr<!void>
+// CIR-X86-AFTER:  %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ]] 
: (!cir.ptr<!void>, !s64i) -> !cir.ptr<!void>
+// CIR-ARM-AFTER:  %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], 
%[[ADJ_SHIFT]] : (!cir.ptr<!void>, !s64i) -> !cir.ptr<!void>
+// CIR-AFTER:      %[[METHOD_PTR:.*]] = cir.extract_member %[[FUNC]][0] : 
!rec_anon_struct -> !s64i
+// CIR-X86-AFTER:  %[[VIRT_BIT_TEST:.*]] = cir.and %[[METHOD_PTR]], %[[ONE]] : 
!s64i
+// CIR-ARM-AFTER:  %[[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<!void> -> !cir.ptr<!cir.ptr<!s8i>>
+// CIR-AFTER:        %[[VTABLE:.*]] = cir.load %[[VTABLE_PTR]] : 
!cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
+// CIR-X86-AFTER:    %[[OFFSET:.*]] = cir.sub %[[METHOD_PTR]], %[[ONE]] : !s64i
+// CIR-X86-AFTER:    %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], 
%[[OFFSET]] : (!cir.ptr<!s8i>, !s64i) -> !cir.ptr<!s8i>
+// CIR-ARM-AFTER:    %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], 
%[[METHOD_PTR]] : (!cir.ptr<!s8i>, !s64i) -> !cir.ptr<!s8i>
+// CIR-AFTER:        %[[VIRTUAL_FN_PTR:.*]] = cir.cast bitcast 
%[[VTABLE_SLOT]] : !cir.ptr<!s8i> -> 
!cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>>
+// CIR-AFTER:        %[[VIRTUAL_FN_PTR_LOAD:.*]] = cir.load 
%[[VIRTUAL_FN_PTR]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, 
!cir.ptr<!rec_Foo>, !s32i)>>>, !cir.ptr<!cir.func<(!cir.ptr<!void>, 
!cir.ptr<!rec_Foo>, !s32i)>>
+// CIR-AFTER:        cir.yield %[[VIRTUAL_FN_PTR_LOAD]] : 
!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
+// CIR-AFTER:      }, false {
+// CIR-AFTER:        %[[CALLEE_PTR:.*]] = cir.cast int_to_ptr %[[METHOD_PTR]] 
: !s64i -> !cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
+// CIR-AFTER:        cir.yield %[[CALLEE_PTR]] : 
!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>
+// CIR-AFTER:      }) : (!cir.bool) -> !cir.ptr<!cir.func<(!cir.ptr<!void>, 
!cir.ptr<!rec_Foo>, !s32i)>>
+// CIR-AFTER:      %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!s32i>, 
!s32i
+// CIR-AFTER:      cir.call %[[CALLEE]](%[[ADJUSTED_THIS]], %[[ARG]]) : 
(!cir.ptr<!cir.func<(!cir.ptr<!void>, !cir.ptr<!rec_Foo>, !s32i)>>, 
!cir.ptr<!void> {{.*}}, !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:     }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to