https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/197252
>From 583ac7a8f7cd7432301bd5d01b325e62edd43dea Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 12 May 2026 09:18:56 -0700 Subject: [PATCH 1/2] [CIR] Lower builtin_launder This patch introduces a cir.launder operation to represent a call to __builtin_launder, which is an optimization barrier. This cir.launder lowers directly to the LLVM-IR launder.invariant.group. This patch also moves the existing check to see if a type needs laundering to QualType from classic-codegen so it can be shared, however the CodeGenOpt check is still left in codegen/duplicated between the two implementations. --- clang/include/clang/AST/TypeBase.h | 4 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 26 ++++ clang/lib/AST/Type.cpp | 32 +++++ clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 13 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 8 ++ clang/lib/CodeGen/CGBuiltin.cpp | 30 +---- clang/test/CIR/CodeGen/launder.cpp | 119 ++++++++++++++++++ 7 files changed, 202 insertions(+), 30 deletions(-) create mode 100644 clang/test/CIR/CodeGen/launder.cpp diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index b2887bcc36246..e90aa9fb09012 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -1160,6 +1160,10 @@ class QualType { /// Returns true if it is a OverflowBehaviorType of Trap kind. bool isTrapType() const; + /// Returns true if this type requires laundering by checking if it is a + /// dynamic class type, or contains a subobject which is a dynamic class type. + bool requiresBuiltinLaunder(const ASTContext &Context) const; + // Don't promise in the API that anything besides 'const' can be // easily added. diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 9d9aaec1b275a..6415b19058397 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -8178,4 +8178,30 @@ def CIR_MemChrOp : CIR_Op<"libc.memchr"> { }]; } +//===----------------------------------------------------------------------===// +// LaunderOp +//===----------------------------------------------------------------------===// + +def CIR_LaunderOp : CIR_Op<"launder", [SameOperandsAndResultType]> { + let summary = "Launder operation"; + let description = [{ + This operation represents a call to 'launder' in C++, + which acts as an optimization boundary that breaks type invariance. + + Example: + ``` + %0 = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>> + %1 = cir.load align(8) %1 : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S> + %2 = cir.launder(%1) : !cir.ptr<!rec_S> + ``` + }]; + + let arguments = (ins CIR_PointerType:$arg); + let results = (outs CIR_PointerType:$result); + + let assemblyFormat = [{ + `(` $arg `)` `:` qualified(type($result)) attr-dict + }]; +} + #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index ca13f8f4fcfee..0c987bbf6d731 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5558,6 +5558,38 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { return DK_none; } +static bool +requiresBuiltinLaunderImpl(const ASTContext &Context, QualType Ty, + llvm::SmallPtrSetImpl<const Decl *> &Seen) { + if (const auto *Arr = Context.getAsArrayType(Ty)) + Ty = Context.getBaseElementType(Arr); + + const auto *Record = Ty->getAsCXXRecordDecl(); + if (!Record) + return false; + + // We've already checked this type, or are in the process of checking it. + if (!Seen.insert(Record).second) + return false; + + assert(Record->hasDefinition() && + "Incomplete types should already be diagnosed"); + + if (Record->isDynamicClass()) + return true; + + for (FieldDecl *F : Record->fields()) { + if (requiresBuiltinLaunderImpl(Context, F->getType(), Seen)) + return true; + } + return false; +} + +bool QualType::requiresBuiltinLaunder(const ASTContext &Context) const { + llvm::SmallPtrSet<const Decl *, 16> Seen; + return requiresBuiltinLaunderImpl(Context, *this, Seen); +} + bool MemberPointerType::isSugared() const { CXXRecordDecl *D1 = getMostRecentCXXRecordDecl(), *D2 = getQualifier().getAsRecordDecl(); diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 868bca404949b..b302f31860047 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -1932,7 +1932,18 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, cir::UnreachableOp::create(builder, loc); return RValue::get(nullptr); } - case Builtin::BI__builtin_launder: + case Builtin::BI__builtin_launder: { + const Expr *arg = e->getArg(0); + QualType argTy = arg->getType()->getPointeeType(); + mlir::Value ptr = emitScalarExpr(arg); + + if (cgm.getCodeGenOpts().StrictVTablePointers && + argTy.requiresBuiltinLaunder(cgm.getASTContext())) { + mlir::Location loc = getLoc(e->getExprLoc()); + ptr = cir::LaunderOp::create(builder, loc, ptr).getResult(); + } + return RValue::get(ptr); + } case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_sub: case Builtin::BI__sync_fetch_and_or: diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index dca079eff1752..c6027d08a5879 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -5083,6 +5083,14 @@ mlir::LogicalResult CIRToLLVMMemChrOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMLaunderOpLowering::matchAndRewrite( + cir::LaunderOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp<mlir::LLVM::LaunderInvariantGroupOp>( + op, adaptor.getArg()); + return mlir::success(); +} + std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() { return std::make_unique<ConvertCIRToLLVMPass>(); } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index f29e27818d7ec..877cfb0e4d1d0 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2449,40 +2449,12 @@ EmitCheckedMixedSignMultiply(CodeGenFunction &CGF, const clang::Expr *Op1, return RValue::get(Overflow); } -static bool -TypeRequiresBuiltinLaunderImp(const ASTContext &Ctx, QualType Ty, - llvm::SmallPtrSetImpl<const Decl *> &Seen) { - if (const auto *Arr = Ctx.getAsArrayType(Ty)) - Ty = Ctx.getBaseElementType(Arr); - - const auto *Record = Ty->getAsCXXRecordDecl(); - if (!Record) - return false; - - // We've already checked this type, or are in the process of checking it. - if (!Seen.insert(Record).second) - return false; - - assert(Record->hasDefinition() && - "Incomplete types should already be diagnosed"); - - if (Record->isDynamicClass()) - return true; - - for (FieldDecl *F : Record->fields()) { - if (TypeRequiresBuiltinLaunderImp(Ctx, F->getType(), Seen)) - return true; - } - return false; -} - /// Determine if the specified type requires laundering by checking if it is a /// dynamic class type or contains a subobject which is a dynamic class type. static bool TypeRequiresBuiltinLaunder(CodeGenModule &CGM, QualType Ty) { if (!CGM.getCodeGenOpts().StrictVTablePointers) return false; - llvm::SmallPtrSet<const Decl *, 16> Seen; - return TypeRequiresBuiltinLaunderImp(CGM.getContext(), Ty, Seen); + return Ty.requiresBuiltinLaunder(CGM.getContext()); } RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) { diff --git a/clang/test/CIR/CodeGen/launder.cpp b/clang/test/CIR/CodeGen/launder.cpp new file mode 100644 index 0000000000000..b76be5c8e6adb --- /dev/null +++ b/clang/test/CIR/CodeGen/launder.cpp @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +// __builtin_launder doesn't actually DO anything unless fstrict-vtable-pointers +// is enabled, so test here to make sure we get that switch correctly done. +// RUN: %clang_cc1 -fstrict-vtable-pointers -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR-STRICT +// RUN: %clang_cc1 -fstrict-vtable-pointers -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM-STRICT +// RUN: %clang_cc1 -fstrict-vtable-pointers -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG-STRICT + + +struct Base {}; + +struct Derived : virtual Base {}; + +struct VirtMem { + Derived d; +}; + +auto use_derived(Derived *d) { + return __builtin_launder(d); +} +// CIR-LABEL: cir.func{{.*}}@_Z11use_derivedP7Derived +// CIR: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["d", init] +// CIR: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["__retval"] +// CIR: %[[ARG_LOAD:.*]] = cir.load align(8) %[[ARG_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> +// CIR: cir.store %[[ARG_LOAD]], %[[RET_ALLOCA]] : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>> +// CIR: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> +// CIR: cir.return %[[RET]] : !cir.ptr<!rec_Derived> +// +// LLVM-LABEL: define {{.*}}@_Z11use_derivedP7Derived +// LLVM: %[[ARG_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// LLVM: store ptr %[[ARG_LOAD]], ptr %[[RET_ALLOCA]] +// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM: ret ptr %[[RET]] +// +// OGCG-LABEL: define {{.*}}@_Z11use_derivedP7Derived +// OGCG: %[[ARG_ALLOCA:.*]] = alloca ptr +// OGCG: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// OGCG: ret ptr %[[ARG_LOAD]] +// +// CIR-STRICT-LABEL: cir.func{{.*}}@_Z11use_derivedP7Derived +// CIR-STRICT: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["d", init] +// CIR-STRICT: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["__retval"] +// CIR-STRICT: %[[LOAD_ARG:.*]] = cir.load align(8) %[[ARG_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> +// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder(%[[LOAD_ARG]]) : !cir.ptr<!rec_Derived> +// CIR-STRICT: cir.store %[[LAUNDER]], %[[RET_ALLOCA]] : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>> +// CIR-STRICT: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> +// CIR-STRICT: cir.return %[[RET]] : !cir.ptr<!rec_Derived> +// +// LLVM-STRICT-LABEL: define {{.*}}@_Z11use_derivedP7Derived +// LLVM-STRICT: %[[ARG_ALLOCA:.*]] = alloca ptr +// LLVM-STRICT: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM-STRICT: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// LLVM-STRICT: %[[LAUNDER:.*]] = call ptr @llvm.launder.invariant.group.p0(ptr %[[ARG_LOAD]]) +// LLVM-STRICT: store ptr %[[LAUNDER]], ptr %[[RET_ALLOCA]] +// LLVM-STRICT: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM-STRICT: ret ptr %[[RET]] +// +// +// OGCG-STRICT-LABEL: define {{.*}}@_Z11use_derivedP7Derived +// OGCG-STRICT: %[[ARG_ALLOCA:.*]] = alloca ptr +// OGCG-STRICT: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// OGCG-STRICT: %[[LAUNDER:.*]] = call ptr @llvm.launder.invariant.group.p0(ptr %[[ARG_LOAD]]) +// OGCG-STRICT: ret ptr %[[LAUNDER]] + +auto use_vm(VirtMem *vm) { + return __builtin_launder(vm); +} +// CIR-LABEL: cir.func {{.*}}@_Z6use_vmP7VirtMem +// CIR: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["vm", init] +// CIR: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["__retval"] +// CIR: cir.store %[[ARG_LOAD]], %[[RET_ALLOCA]] : !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>> +// CIR: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtMem>>, !cir.ptr<!rec_VirtMem> +// CIR: cir.return %[[RET]] : !cir.ptr<!rec_VirtMem> +// +// LLVM-LABEL: define {{.*}}@_Z6use_vmP7VirtMem +// LLVM: %[[ARG_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// LLVM: store ptr %[[ARG_LOAD]], ptr %[[RET_ALLOCA]] +// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM: ret ptr %[[RET]] +// +// OGCG-LABEL: define {{.*}}@_Z6use_vmP7VirtMem +// OGCG: %[[ARG_ALLOCA:.*]] = alloca ptr +// OGCG: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// OGCG: ret ptr %[[ARG_LOAD]] +// +// CIR-STRICT-LABEL: cir.func {{.*}}@_Z6use_vmP7VirtMem +// CIR-STRICT: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["vm", init] +// CIR-STRICT: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["__retval"] +// CIR-STRICT: %[[LOAD_ARG:.*]] = cir.load align(8) %[[ARG_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtMem>>, !cir.ptr<!rec_VirtMem> +// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder(%[[LOAD_ARG]]) : !cir.ptr<!rec_VirtMem> +// CIR-STRICT: cir.store %[[LAUNDER]], %[[RET_ALLOCA]] : !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>> +// CIR-STRICT: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtMem>>, !cir.ptr<!rec_VirtMem> +// CIR-STRICT: cir.return %[[RET]] : !cir.ptr<!rec_VirtMem> +// +// LLVM-STRICT-LABEL: define {{.*}}@_Z6use_vmP7VirtMem +// LLVM-STRICT: %[[ARG_ALLOCA:.*]] = alloca ptr +// LLVM-STRICT: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM-STRICT: %[[ARG_LOAD:.*]] = load ptr, ptr %[[ARG_ALLOCA]] +// LLVM-STRICT: %[[LAUNDER:.*]] = call ptr @llvm.launder.invariant.group.p0(ptr %[[ARG_LOAD]]) +// LLVM-STRICT: store ptr %[[LAUNDER]], ptr %[[RET_ALLOCA]] +// LLVM-STRICT: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM-STRICT: ret ptr %[[RET]] +// +// OGCG-STRICT-LABEL: define {{.*}}@_Z6use_vmP7VirtMem +// OGCG-STRICT: %[[ARG_ALLOCA:.*]] = alloca ptr +// OGCG-STRICT: %[[LAUNDER:.*]] = call ptr @llvm.launder.invariant.group.p0(ptr %[[ARG_LOAD]]) +// OGCG-STRICT: ret ptr %[[LAUNDER]] >From d856f0b5bc0c0d46fa6a0fed2b612f28c3865802 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 12 May 2026 13:56:38 -0700 Subject: [PATCH 2/2] Change assembly format --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +- clang/test/CIR/CodeGen/launder.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 6415b19058397..1e51637a00a7d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -8200,7 +8200,7 @@ def CIR_LaunderOp : CIR_Op<"launder", [SameOperandsAndResultType]> { let results = (outs CIR_PointerType:$result); let assemblyFormat = [{ - `(` $arg `)` `:` qualified(type($result)) attr-dict + $arg `:` qualified(type($result)) attr-dict }]; } diff --git a/clang/test/CIR/CodeGen/launder.cpp b/clang/test/CIR/CodeGen/launder.cpp index b76be5c8e6adb..397459c0a7a24 100644 --- a/clang/test/CIR/CodeGen/launder.cpp +++ b/clang/test/CIR/CodeGen/launder.cpp @@ -51,7 +51,7 @@ auto use_derived(Derived *d) { // CIR-STRICT: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["d", init] // CIR-STRICT: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["__retval"] // CIR-STRICT: %[[LOAD_ARG:.*]] = cir.load align(8) %[[ARG_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> -// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder(%[[LOAD_ARG]]) : !cir.ptr<!rec_Derived> +// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder %[[LOAD_ARG]] : !cir.ptr<!rec_Derived> // CIR-STRICT: cir.store %[[LAUNDER]], %[[RET_ALLOCA]] : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>> // CIR-STRICT: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived> // CIR-STRICT: cir.return %[[RET]] : !cir.ptr<!rec_Derived> @@ -99,7 +99,7 @@ auto use_vm(VirtMem *vm) { // CIR-STRICT: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["vm", init] // CIR-STRICT: %[[RET_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>>, ["__retval"] // CIR-STRICT: %[[LOAD_ARG:.*]] = cir.load align(8) %[[ARG_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtMem>>, !cir.ptr<!rec_VirtMem> -// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder(%[[LOAD_ARG]]) : !cir.ptr<!rec_VirtMem> +// CIR-STRICT: %[[LAUNDER:.*]] = cir.launder %[[LOAD_ARG]] : !cir.ptr<!rec_VirtMem> // CIR-STRICT: cir.store %[[LAUNDER]], %[[RET_ALLOCA]] : !cir.ptr<!rec_VirtMem>, !cir.ptr<!cir.ptr<!rec_VirtMem>> // CIR-STRICT: %[[RET:.*]] = cir.load %[[RET_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtMem>>, !cir.ptr<!rec_VirtMem> // CIR-STRICT: cir.return %[[RET]] : !cir.ptr<!rec_VirtMem> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
