diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 34ee0b8b29a0c..ee23b87b9bfb5 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2267,6 +2267,10 @@ def CIR_FuncOp : CIR_Op<"func", [ The function linkage information is specified by `linkage`, as defined by `GlobalLinkageKind` attribute. + The `lambda` translates to a C++ `operator()` that implements a lambda, this + allow callsites to make certain assumptions about the real function nature + when writing analysis. + The `no_proto` keyword is used to identify functions that were declared without a prototype and, consequently, may contain calls with invalid arguments and undefined behavior. @@ -2289,6 +2293,7 @@ def CIR_FuncOp : CIR_Op<"func", [ let arguments = (ins SymbolNameAttr:$sym_name, CIR_VisibilityAttr:$global_visibility, TypeAttrOf:$function_type, + UnitAttr:$lambda, UnitAttr:$no_proto, UnitAttr:$dso_local, DefaultValuedAttr(curFuncDecl)->getParent(); + const CXXRecordDecl *rd = cast(curCodeDecl)->getParent(); const CXXRecordDecl *base = cast(gd.getDecl())->getParent(); uint64_t subVTTIndex; diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index aab7e2745f30f..9145b50f33d35 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -461,7 +461,8 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { llvm::StringRef fieldName = field->getName(); unsigned fieldIndex; - assert(!cir::MissingFeatures::lambdaFieldToName()); + if (cgm.lambdaFieldToName.count(field)) + fieldName = cgm.lambdaFieldToName[field]; if (rec->isUnion()) fieldIndex = field->getFieldIndex(); @@ -476,8 +477,16 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { // If this is a reference field, load the reference right now. if (fieldType->isReferenceType()) { - cgm.errorNYI(field->getSourceRange(), "emitLValueForField: reference type"); - return LValue(); + assert(!cir::MissingFeatures::opTBAA()); + LValue refLVal = makeAddrLValue(addr, fieldType, fieldBaseInfo); + if (recordCVR & Qualifiers::Volatile) + refLVal.getQuals().addVolatile(); + addr = emitLoadOfReference(refLVal, getLoc(field->getSourceRange()), + &fieldBaseInfo); + + // Qualifiers on the struct don't apply to the referencee. + recordCVR = 0; + fieldType = fieldType->getPointeeType(); } if (field->hasAttr()) { @@ -619,6 +628,38 @@ static cir::FuncOp emitFunctionDeclPointer(CIRGenModule &cgm, GlobalDecl gd) { return cgm.getAddrOfFunction(gd); } +static LValue emitCapturedFieldLValue(CIRGenFunction &cgf, const FieldDecl *fd, + mlir::Value thisValue) { + return cgf.emitLValueForLambdaField(fd, thisValue); +} + +/// Given that we are currently emitting a lambda, emit an l-value for +/// one of its members. +/// +LValue CIRGenFunction::emitLValueForLambdaField(const FieldDecl *field, + mlir::Value thisValue) { + bool hasExplicitObjectParameter = false; + const auto *methD = dyn_cast_if_present(curCodeDecl); + LValue lambdaLV; + if (methD) { + hasExplicitObjectParameter = methD->isExplicitObjectMemberFunction(); + assert(methD->getParent()->isLambda()); + assert(methD->getParent() == field->getParent()); + } + if (hasExplicitObjectParameter) { + cgm.errorNYI(field->getSourceRange(), "ExplicitObjectMemberFunction"); + } else { + QualType lambdaTagType = + getContext().getCanonicalTagType(field->getParent()); + lambdaLV = makeNaturalAlignAddrLValue(thisValue, lambdaTagType); + } + return emitLValueForField(lambdaLV, field); +} + +LValue CIRGenFunction::emitLValueForLambdaField(const FieldDecl *field) { + return emitLValueForLambdaField(field, cxxabiThisValue); +} + static LValue emitFunctionDeclLValue(CIRGenFunction &cgf, const Expr *e, GlobalDecl gd) { const FunctionDecl *fd = cast(gd.getDecl()); @@ -645,6 +686,57 @@ static LValue emitFunctionDeclLValue(CIRGenFunction &cgf, const Expr *e, AlignmentSource::Decl); } +/// Determine whether we can emit a reference to \p vd from the current +/// context, despite not necessarily having seen an odr-use of the variable in +/// this context. +/// TODO(cir): This could be shared with classic codegen. +static bool canEmitSpuriousReferenceToVariable(CIRGenFunction &cgf, + const DeclRefExpr *e, + const VarDecl *vd) { + // For a variable declared in an enclosing scope, do not emit a spurious + // reference even if we have a capture, as that will emit an unwarranted + // reference to our capture state, and will likely generate worse code than + // emitting a local copy. + if (e->refersToEnclosingVariableOrCapture()) + return false; + + // For a local declaration declared in this function, we can always reference + // it even if we don't have an odr-use. + if (vd->hasLocalStorage()) { + return vd->getDeclContext() == + dyn_cast_or_null(cgf.curCodeDecl); + } + + // For a global declaration, we can emit a reference to it if we know + // for sure that we are able to emit a definition of it. + vd = vd->getDefinition(cgf.getContext()); + if (!vd) + return false; + + // Don't emit a spurious reference if it might be to a variable that only + // exists on a different device / target. + // FIXME: This is unnecessarily broad. Check whether this would actually be a + // cross-target reference. + if (cgf.getLangOpts().OpenMP || cgf.getLangOpts().CUDA || + cgf.getLangOpts().OpenCL) { + return false; + } + + // We can emit a spurious reference only if the linkage implies that we'll + // be emitting a non-interposable symbol that will be retained until link + // time. + switch (cgf.cgm.getCIRLinkageVarDefinition(vd, /*IsConstant=*/false)) { + case cir::GlobalLinkageKind::ExternalLinkage: + case cir::GlobalLinkageKind::LinkOnceODRLinkage: + case cir::GlobalLinkageKind::WeakODRLinkage: + case cir::GlobalLinkageKind::InternalLinkage: + case cir::GlobalLinkageKind::PrivateLinkage: + return true; + default: + return false; + } +} + LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) { const NamedDecl *nd = e->getDecl(); QualType ty = e->getType(); @@ -652,6 +744,32 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) { assert(e->isNonOdrUse() != NOUR_Unevaluated && "should not emit an unevaluated operand"); + if (const auto *vd = dyn_cast(nd)) { + // Global Named registers access via intrinsics only + if (vd->getStorageClass() == SC_Register && vd->hasAttr() && + !vd->isLocalVarDecl()) { + cgm.errorNYI(e->getSourceRange(), + "emitDeclRefLValue: Global Named registers access"); + return LValue(); + } + + if (e->isNonOdrUse() == NOUR_Constant && + (vd->getType()->isReferenceType() || + !canEmitSpuriousReferenceToVariable(*this, e, vd))) { + cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: NonOdrUse"); + return LValue(); + } + + // Check for captured variables. + if (e->refersToEnclosingVariableOrCapture()) { + vd = vd->getCanonicalDecl(); + if (FieldDecl *fd = lambdaCaptureFields.lookup(vd)) + return emitCapturedFieldLValue(*this, fd, cxxabiThisValue); + assert(!cir::MissingFeatures::cgCapturedStmtInfo()); + assert(!cir::MissingFeatures::openMP()); + } + } + if (const auto *vd = dyn_cast(nd)) { // Checks for omitted feature handling assert(!cir::MissingFeatures::opAllocaStaticLocal()); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index dc34d2b3baa8d..5615960ea5247 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -99,6 +99,7 @@ class AggExprEmitter : public StmtVisitor { assert(!cir::MissingFeatures::aggValueSlotDestructedFlag()); Visit(e->getSubExpr()); } + void VisitLambdaExpr(LambdaExpr *e); // Stubs -- These should be moved up when they are implemented. void VisitCastExpr(CastExpr *e) { @@ -239,9 +240,6 @@ class AggExprEmitter : public StmtVisitor { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCXXInheritedCtorInitExpr"); } - void VisitLambdaExpr(LambdaExpr *e) { - cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitLambdaExpr"); - } void VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *e) { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCXXStdInitializerListExpr"); @@ -495,8 +493,10 @@ void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { if (isa(e)) return; - if (type->isReferenceType()) - cgf.cgm.errorNYI("emitInitializationToLValue ReferenceType"); + if (type->isReferenceType()) { + RValue rv = cgf.emitReferenceBindingToExpr(e); + return cgf.emitStoreThroughLValue(rv, lv); + } switch (cgf.getEvaluationKind(type)) { case cir::TEK_Complex: @@ -550,6 +550,47 @@ void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc, cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType()); } +void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) { + CIRGenFunction::SourceLocRAIIObject loc{cgf, cgf.getLoc(e->getSourceRange())}; + AggValueSlot slot = ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType()); + [[maybe_unused]] LValue slotLV = + cgf.makeAddrLValue(slot.getAddress(), e->getType()); + + // We'll need to enter cleanup scopes in case any of the element + // initializers throws an exception or contains branch out of the expressions. + assert(!cir::MissingFeatures::opScopeCleanupRegion()); + + for (auto [curField, capture, captureInit] : llvm::zip( + e->getLambdaClass()->fields(), e->captures(), e->capture_inits())) { + // Pick a name for the field. + llvm::StringRef fieldName = curField->getName(); + if (capture.capturesVariable()) { + assert(!curField->isBitField() && "lambdas don't have bitfield members!"); + ValueDecl *v = capture.getCapturedVar(); + fieldName = v->getName(); + cgf.cgm.lambdaFieldToName[curField] = fieldName; + } else if (capture.capturesThis()) { + cgf.cgm.lambdaFieldToName[curField] = "this"; + } else { + cgf.cgm.errorNYI(e->getSourceRange(), "Unhandled capture kind"); + cgf.cgm.lambdaFieldToName[curField] = "unhandled-capture-kind"; + } + + // Emit initialization + LValue lv = + cgf.emitLValueForFieldInitialization(slotLV, curField, fieldName); + if (curField->hasCapturedVLAType()) + cgf.cgm.errorNYI(e->getSourceRange(), "lambda captured VLA type"); + + emitInitializationToLValue(captureInit, lv); + + // Push a destructor if necessary. + if ([[maybe_unused]] QualType::DestructionKind DtorKind = + curField->getType().isDestructedType()) + cgf.cgm.errorNYI(e->getSourceRange(), "lambda with destructed field"); + } +} + void AggExprEmitter::VisitCallExpr(const CallExpr *e) { if (e->getCallReturnType(cgf.getContext())->isReferenceType()) { cgf.cgm.errorNYI(e->getSourceRange(), "reference return type"); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index e2181b8222aa2..f43a0e60c9f5b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -405,6 +405,7 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, curFn = fn; const Decl *d = gd.getDecl(); + curCodeDecl = d; const auto *fd = dyn_cast_or_null(d); curFuncDecl = d->getNonClosureContext(); @@ -457,7 +458,36 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, const auto *md = cast(d); if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) { - cgm.errorNYI(loc, "lambda call operator"); + // We're in a lambda. + curFn.setLambda(true); + + // Figure out the captures. + md->getParent()->getCaptureFields(lambdaCaptureFields, + lambdaThisCaptureField); + if (lambdaThisCaptureField) { + // If the lambda captures the object referred to by '*this' - either by + // value or by reference, make sure CXXThisValue points to the correct + // object. + + // Get the lvalue for the field (which is a copy of the enclosing object + // or contains the address of the enclosing object). + LValue thisFieldLValue = + emitLValueForLambdaField(lambdaThisCaptureField); + if (!lambdaThisCaptureField->getType()->isPointerType()) { + // If the enclosing object was captured by value, just use its + // address. Sign this pointer. + cxxThisValue = thisFieldLValue.getPointer(); + } else { + // Load the lvalue pointed to by the field, since '*this' was captured + // by reference. + cxxThisValue = + emitLoadOfLValue(thisFieldLValue, SourceLocation()).getValue(); + } + } + for (auto *fd : md->getParent()->fields()) { + if (fd->hasCapturedVLAType()) + cgm.errorNYI(loc, "lambda captured VLA type"); + } } else { // Not in a lambda; just use 'this' from the method. // FIXME: Should we generate a new load for each use of 'this'? The fast diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 42f7f401555ca..ecf919396c878 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -73,6 +73,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// Tracks function scope overall cleanup handling. EHScopeStack ehStack; + llvm::DenseMap + lambdaCaptureFields; + clang::FieldDecl *lambdaThisCaptureField = nullptr; + /// CXXThisDecl - When generating code for a C++ member function, /// this will hold the implicit 'this' declaration. ImplicitParamDecl *cxxabiThisDecl = nullptr; @@ -91,6 +95,8 @@ class CIRGenFunction : public CIRGenTypeCache { // Holds the Decl for the current outermost non-closure context const clang::Decl *curFuncDecl = nullptr; + /// This is the inner-most code context, which includes blocks. + const clang::Decl *curCodeDecl = nullptr; /// The function for which code is currently being generated. cir::FuncOp curFn; @@ -1377,6 +1383,10 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitLValueForBitField(LValue base, const FieldDecl *field); LValue emitLValueForField(LValue base, const clang::FieldDecl *field); + LValue emitLValueForLambdaField(const FieldDecl *field); + LValue emitLValueForLambdaField(const FieldDecl *field, + mlir::Value thisValue); + /// Like emitLValueForField, excpet that if the Field is a reference, this /// will return the address of the reference and not the address of the value /// stored in the reference. diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 95a7ac0648bb7..073e8d96b773b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -121,6 +121,12 @@ class CIRGenModule : public CIRGenTypeCache { mlir::Operation *lastGlobalOp = nullptr; + /// Keep a map between lambda fields and names, this needs to be per module + /// since lambdas might get generated later as part of defered work, and since + /// the pointers are supposed to be uniqued, should be fine. Revisit this if + /// it ends up taking too much memory. + llvm::DenseMap lambdaFieldToName; + /// Tell the consumer that this variable has been instantiated. void handleCXXStaticMemberVarInstantiation(VarDecl *vd); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 24aef693024f7..02d6d526c5590 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1548,11 +1548,14 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { llvm::SMLoc loc = parser.getCurrentLocation(); mlir::Builder &builder = parser.getBuilder(); + mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name); mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name); mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name); mlir::StringAttr visibilityNameAttr = getGlobalVisibilityAttrName(state.name); mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); + if (::mlir::succeeded(parser.parseOptionalKeyword(lambdaNameAttr.strref()))) + state.addAttribute(lambdaNameAttr, parser.getBuilder().getUnitAttr()); if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded()) state.addAttribute(noProtoNameAttr, parser.getBuilder().getUnitAttr()); @@ -1660,6 +1663,9 @@ mlir::Region *cir::FuncOp::getCallableRegion() { } void cir::FuncOp::print(OpAsmPrinter &p) { + if (getLambda()) + p << " lambda"; + if (getNoProto()) p << " no_proto"; diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp new file mode 100644 index 0000000000000..033adc60be1ed --- /dev/null +++ b/clang/test/CIR/CodeGen/lambda.cpp @@ -0,0 +1,486 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// We declare anonymous record types to represent lambdas. Rather than trying to +// to match the declarations, we establish variables for these when they are used. + +void fn() { + auto a = [](){}; + a(); +} + +// CIR: cir.func lambda internal private dso_local @_ZZ2fnvENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) +// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: cir.store %[[THIS_ARG]], %[[THIS]] +// CIR: cir.load %[[THIS]] +// CIR: cir.return + +// CIR: cir.func dso_local @_Z2fnv() +// CIR: %[[A:.*]] = cir.alloca ![[REC_LAM_FN_A]], !cir.ptr, ["a"] +// CIR: cir.call @_ZZ2fnvENK3$_0clEv(%[[A]]) + +// LLVM: define internal void @"_ZZ2fnvENK3$_0clEv"(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: ret void + +// FIXME: parameter attributes should be emitted +// LLVM: define {{.*}} void @_Z2fnv() +// LLVM: [[A:%.*]] = alloca %[[REC_LAM_FN_A:.*]], i64 1, align 1 +// LLVM: call void @"_ZZ2fnvENK3$_0clEv"(ptr [[A]]) +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z2fnv() +// OGCG: %[[A:.*]] = alloca %[[REC_LAM_FN_A:.*]] +// OGCG: call void @"_ZZ2fnvENK3$_0clEv"(ptr {{.*}} %[[A]]) +// OGCG: ret void + +// OGCG: define internal void @"_ZZ2fnvENK3$_0clEv"(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ADDR:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// OGCG: ret void + +void l0() { + int i; + auto a = [&](){ i = i + 1; }; + a(); +} + +// CIR: cir.func lambda internal private dso_local @_ZZ2l0vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] {alignment = 8 : i64} +// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "i"} +// CIR: %[[I_ADDR:.*]] = cir.load %[[I_ADDR_ADDR]] +// CIR: %[[I:.*]] = cir.load align(4) %[[I_ADDR]] +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR: %[[I_PLUS_ONE:.*]] = cir.binop(add, %[[I]], %[[ONE]]) nsw +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "i"} +// CIR: %[[I_ADDR:.*]] = cir.load %[[I_ADDR_ADDR]] +// CIR: cir.store{{.*}} %[[I_PLUS_ONE]], %[[I_ADDR]] +// CIR: cir.return + +// CIR: cir.func {{.*}} @_Z2l0v() +// CIR: %[[I:.*]] = cir.alloca !s32i, !cir.ptr, ["i"] +// CIR: %[[A:.*]] = cir.alloca ![[REC_LAM_L0_A]], !cir.ptr, ["a", init] +// CIR: %[[I_ADDR:.*]] = cir.get_member %[[A]][0] {name = "i"} +// CIR: cir.store{{.*}} %[[I]], %[[I_ADDR]] +// CIR: cir.call @_ZZ2l0vENK3$_0clEv(%[[A]]) +// CIR: cir.return + +// LLVM: define internal void @"_ZZ2l0vENK3$_0clEv"(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: %[[I_ADDR_ADDR:.*]] = getelementptr %[[REC_LAM_L0_A:.*]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// LLVM: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// LLVM: %[[ADD:.*]] = add nsw i32 %[[I]], 1 +// LLVM: %[[I_ADDR_ADDR:.*]] = getelementptr %[[REC_LAM_L0_A]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// LLVM: store i32 %[[ADD]], ptr %[[I_ADDR]] +// LLVM: ret void + +// LLVM: define {{.*}} void @_Z2l0v() +// LLVM: %[[I:.*]] = alloca i32 +// LLVM: %[[A:.*]] = alloca %[[REC_LAM_L0_A]] +// LLVM: %[[I_ADDR:.*]] = getelementptr %[[REC_LAM_L0_A]], ptr %[[A]], i32 0, i32 0 +// LLVM: store ptr %[[I]], ptr %[[I_ADDR]] +// LLVM: call void @"_ZZ2l0vENK3$_0clEv"(ptr %[[A]]) +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z2l0v() +// OGCG: %[[I:.*]] = alloca i32 +// OGCG: %[[A:.*]] = alloca %[[REC_LAM_L0_A:.*]], +// OGCG: %[[I_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_L0_A]], ptr %[[A]], i32 0, i32 0 +// OGCG: store ptr %[[I]], ptr %[[I_ADDR]] +// OGCG: call void @"_ZZ2l0vENK3$_0clEv"(ptr {{.*}} %[[A]]) +// OGCG: ret void + +// OGCG: define internal void @"_ZZ2l0vENK3$_0clEv"(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ADDR:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// OGCG: %[[I_ADDR_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_L0_A]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// OGCG: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// OGCG: %[[ADD:.*]] = add nsw i32 %[[I]], 1 +// OGCG: %[[I_ADDR_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_L0_A]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// OGCG: store i32 %[[ADD]], ptr %[[I_ADDR]] +// OGCG: ret void + +auto g() { + int i = 12; + return [&] { + i += 100; + return i; + }; +} + +// CIR: cir.func dso_local @_Z1gv() -> ![[REC_LAM_G:.*]] { +// CIR: %[[RETVAL:.*]] = cir.alloca ![[REC_LAM_G]], !cir.ptr, ["__retval"] +// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["i", init] +// CIR: %[[TWELVE:.*]] = cir.const #cir.int<12> : !s32i +// CIR: cir.store{{.*}} %[[TWELVE]], %[[I_ADDR]] : !s32i, !cir.ptr +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[RETVAL]][0] {name = "i"} : !cir.ptr -> !cir.ptr> +// CIR: cir.store{{.*}} %[[I_ADDR]], %[[I_ADDR_ADDR]] : !cir.ptr, !cir.ptr> +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] : !cir.ptr, ![[REC_LAM_G]] +// CIR: cir.return %[[RET]] : ![[REC_LAM_G]] + +// Note: In this case, OGCG returns a pointer to the 'i' field of the lambda, +// whereas CIR and LLVM return the lambda itself. + +// LLVM: define dso_local %[[REC_LAM_G:.*]] @_Z1gv() +// LLVM: %[[RETVAL:.*]] = alloca %[[REC_LAM_G]] +// LLVM: %[[I:.*]] = alloca i32 +// LLVM: store i32 12, ptr %[[I]] +// LLVM: %[[I_ADDR:.*]] = getelementptr %[[REC_LAM_G]], ptr %[[RETVAL]], i32 0, i32 0 +// LLVM: store ptr %[[I]], ptr %[[I_ADDR]] +// LLVM: %[[RET:.*]] = load %[[REC_LAM_G]], ptr %[[RETVAL]] +// LLVM: ret %[[REC_LAM_G]] %[[RET]] + +// OGCG: define dso_local ptr @_Z1gv() +// OGCG: %[[RETVAL:.*]] = alloca %[[REC_LAM_G:.*]], +// OGCG: %[[I:.*]] = alloca i32 +// OGCG: store i32 12, ptr %[[I]] +// OGCG: %[[I_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_G]], ptr %[[RETVAL]], i32 0, i32 0 +// OGCG: store ptr %[[I]], ptr %[[I_ADDR]] +// OGCG: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %[[REC_LAM_G]], ptr %[[RETVAL]], i32 0, i32 0 +// OGCG: %[[RET:.*]] = load ptr, ptr %[[COERCE_DIVE]] +// OGCG: ret ptr %[[RET]] + +auto g2() { + int i = 12; + auto lam = [&] { + i += 100; + return i; + }; + return lam; +} + +// Should be same as above because of NRVO +// CIR: cir.func dso_local @_Z2g2v() -> ![[REC_LAM_G2:.*]] { +// CIR: %[[RETVAL:.*]] = cir.alloca ![[REC_LAM_G2]], !cir.ptr, ["__retval", init] +// CIR: %[[I_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["i", init] +// CIR: %[[TWELVE:.*]] = cir.const #cir.int<12> : !s32i +// CIR: cir.store{{.*}} %[[TWELVE]], %[[I_ADDR]] : !s32i, !cir.ptr +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[RETVAL]][0] {name = "i"} : !cir.ptr -> !cir.ptr> +// CIR: cir.store{{.*}} %[[I_ADDR]], %[[I_ADDR_ADDR]] : !cir.ptr, !cir.ptr> +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] : !cir.ptr, ![[REC_LAM_G2]] +// CIR: cir.return %[[RET]] : ![[REC_LAM_G2]] + +// LLVM: define dso_local %[[REC_LAM_G:.*]] @_Z2g2v() +// LLVM: %[[RETVAL:.*]] = alloca %[[REC_LAM_G]] +// LLVM: %[[I:.*]] = alloca i32 +// LLVM: store i32 12, ptr %[[I]] +// LLVM: %[[I_ADDR:.*]] = getelementptr %[[REC_LAM_G]], ptr %[[RETVAL]], i32 0, i32 0 +// LLVM: store ptr %[[I]], ptr %[[I_ADDR]] +// LLVM: %[[RET:.*]] = load %[[REC_LAM_G]], ptr %[[RETVAL]] +// LLVM: ret %[[REC_LAM_G]] %[[RET]] + +// OGCG: define dso_local ptr @_Z2g2v() +// OGCG: %[[RETVAL:.*]] = alloca %[[REC_LAM_G2:.*]], +// OGCG: %[[I:.*]] = alloca i32 +// OGCG: store i32 12, ptr %[[I]] +// OGCG: %[[I_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_G2]], ptr %[[RETVAL]], i32 0, i32 0 +// OGCG: store ptr %[[I]], ptr %[[I_ADDR]] +// OGCG: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %[[REC_LAM_G2]], ptr %[[RETVAL]], i32 0, i32 0 +// OGCG: %[[RET:.*]] = load ptr, ptr %[[COERCE_DIVE]] +// OGCG: ret ptr %[[RET]] + +int f() { + return g2()(); +} + +// CIR:cir.func lambda internal private dso_local @_ZZ2g2vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) -> !s32i +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR: %[[ONE_HUNDRED:.*]] = cir.const #cir.int<100> : !s32i +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "i"} +// CIR: %[[I_ADDR:.*]] = cir.load %[[I_ADDR_ADDR]] +// CIR: %[[I:.*]] = cir.load{{.*}} %[[I_ADDR]] +// CIR: %[[I_PLUS_ONE_HUNDRED:.*]] = cir.binop(add, %[[I]], %[[ONE_HUNDRED]]) nsw : !s32i +// CIR: cir.store{{.*}} %[[I_PLUS_ONE_HUNDRED]], %[[I_ADDR]] : !s32i, !cir.ptr +// CIR: %[[I_ADDR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "i"} +// CIR: %[[I_ADDR:.*]] = cir.load %[[I_ADDR_ADDR]] +// CIR: %[[I:.*]] = cir.load{{.*}} %[[I_ADDR]] +// CIR: cir.store{{.*}} %[[I]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// CIR: cir.func dso_local @_Z1fv() -> !s32i +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: %[[SCOPE_RET:.*]] = cir.scope { +// CIR: %[[TMP:.*]] = cir.alloca ![[REC_LAM_G2]], !cir.ptr, ["ref.tmp0"] +// CIR: %[[G2:.*]] = cir.call @_Z2g2v() : () -> ![[REC_LAM_G2]] +// CIR: cir.store{{.*}} %[[G2]], %[[TMP]] +// CIR: %[[RESULT:.*]] = cir.call @_ZZ2g2vENK3$_0clEv(%[[TMP]]) +// CIR: cir.yield %[[RESULT]] +// CIR: } +// CIR: cir.store{{.*}} %[[SCOPE_RET]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// LLVM: define internal i32 @"_ZZ2g2vENK3$_0clEv"(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[I_ALLOCA:.*]] = alloca i32 +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: %[[I_ADDR_ADDR:.*]] = getelementptr %[[REC_LAM_G2:.*]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// LLVM: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// LLVM: %[[ADD:.*]] = add nsw i32 %[[I]], 100 +// LLVM: store i32 %[[ADD]], ptr %[[I_ADDR]] +// LLVM: %[[I_ADDR_ADDR:.*]] = getelementptr %[[REC_LAM_G2]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// LLVM: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// LLVM: store i32 %[[I]], ptr %[[I_ALLOCA]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[I_ALLOCA]] +// LLVM: ret i32 %[[RET]] + +// LLVM: define {{.*}} i32 @_Z1fv() +// LLVM: %[[TMP:.*]] = alloca %[[REC_LAM_G2]] +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: br label %[[SCOPE_BB:.*]] +// LLVM: [[SCOPE_BB]]: +// LLVM: %[[G2:.*]] = call %[[REC_LAM_G2]] @_Z2g2v() +// LLVM: store %[[REC_LAM_G2]] %[[G2]], ptr %[[TMP]] +// LLVM: %[[RESULT:.*]] = call i32 @"_ZZ2g2vENK3$_0clEv"(ptr %[[TMP]]) +// LLVM: br label %[[RET_BB:.*]] +// LLVM: [[RET_BB]]: +// LLVM: %[[RETPHI:.*]] = phi i32 [ %[[RESULT]], %[[SCOPE_BB]] ] +// LLVM: store i32 %[[RETPHI]], ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET]] + +// The order of these functions is reversed in OGCG. + +// OGCG: define {{.*}} i32 @_Z1fv() +// OGCG: %[[TMP:.*]] = alloca %[[REC_LAM_G2]] +// OGCG: %[[RESULT:.*]] = call ptr @_Z2g2v() +// OGCG: %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %[[REC_LAM_G2]], ptr %[[TMP]], i32 0, i32 0 +// OGCG: store ptr %[[RESULT]], ptr %[[COERCE_DIVE]] +// OGCG: %[[RET:.*]] = call {{.*}} i32 @"_ZZ2g2vENK3$_0clEv"(ptr {{.*}} %[[TMP]]) +// OGCG: ret i32 %[[RET]] + +// OGCG: define internal noundef i32 @"_ZZ2g2vENK3$_0clEv"(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[I_ADDR_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_G2]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// OGCG: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// OGCG: %[[ADD:.*]] = add nsw i32 %[[I]], 100 +// OGCG: store i32 %[[ADD]], ptr %[[I_ADDR]] +// OGCG: %[[I_ADDR_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_G2]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[I_ADDR:.*]] = load ptr, ptr %[[I_ADDR_ADDR]] +// OGCG: %[[I:.*]] = load i32, ptr %[[I_ADDR]] +// OGCG: ret i32 %[[I]] + +struct A { + int a = 111; + int foo() { return [*this] { return a; }(); } + int bar() { return [this] { return a; }(); } +}; + +// This function gets emitted before the lambdas in OGCG. + +// OGCG: define {{.*}} i32 @_Z17test_lambda_this1v +// OGCG: %[[A_THIS:.*]] = alloca %struct.A +// OGCG: call void @_ZN1AC1Ev(ptr {{.*}} %[[A_THIS]]) +// OGCG: call noundef i32 @_ZN1A3fooEv(ptr {{.*}} %[[A_THIS]]) +// OGCG: call noundef i32 @_ZN1A3barEv(ptr {{.*}} %[[A_THIS]]) + +// lambda operator() in foo() +// CIR: cir.func lambda comdat linkonce_odr @_ZZN1A3fooEvENKUlvE_clEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]] +// CIR: %[[STRUCT_A:.*]] = cir.get_member %[[THIS]][0] {name = "this"} +// CIR: %[[A_A_ADDR:.*]] = cir.get_member %[[STRUCT_A]][0] {name = "a"} +// CIR: %[[A_A:.*]] = cir.load{{.*}} %[[A_A_ADDR]] +// CIR: cir.store{{.*}} %[[A_A]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// LLVM: define linkonce_odr i32 @_ZZN1A3fooEvENKUlvE_clEv(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: %[[PTR_A:.*]] = getelementptr %[[REC_LAM_A:.*]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[A_A_ADDR:.*]] = getelementptr %struct.A, ptr %[[PTR_A]], i32 0, i32 0 +// LLVM: %[[A_A:.*]] = load i32, ptr %[[A_A_ADDR]] +// LLVM: store i32 %[[A_A]], ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET]] + +// The function above is defined after _ZN1A3barEv in OGCG, see below. + +// A::foo() +// CIR: cir.func {{.*}} @_ZN1A3fooEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) -> !s32i +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS]] = cir.load deref %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr +// CIR: %[[SCOPE_RET:.*]] = cir.scope { +// CIR: %[[LAM_ADDR:.*]] = cir.alloca ![[REC_LAM_A]], !cir.ptr, ["ref.tmp0"] +// CIR: %[[STRUCT_A:.*]] = cir.get_member %[[LAM_ADDR]][0] {name = "this"} : !cir.ptr -> !cir.ptr +// CIR: cir.call @_ZN1AC1ERKS_(%[[STRUCT_A]], %[[THIS]]){{.*}} : (!cir.ptr, !cir.ptr){{.*}} -> () +// CIR: %[[LAM_RET:.*]] = cir.call @_ZZN1A3fooEvENKUlvE_clEv(%[[LAM_ADDR]]) +// CIR: cir.yield %[[LAM_RET]] +// CIR: } +// CIR: cir.store{{.*}} %[[SCOPE_RET]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// LLVM: define linkonce_odr i32 @_ZN1A3fooEv(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_A]] +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: br label %[[SCOPE_BB:.*]] +// LLVM: [[SCOPE_BB]]: +// LLVM: %[[STRUCT_A:.*]] = getelementptr %[[REC_LAM_A]], ptr %[[LAM_ALLOCA]], i32 0, i32 0 +// LLVM: call void @_ZN1AC1ERKS_(ptr %[[STRUCT_A]], ptr %[[THIS]]) +// LLVM: %[[LAM_RET:.*]] = call i32 @_ZZN1A3fooEvENKUlvE_clEv(ptr %[[LAM_ALLOCA]]) +// LLVM: br label %[[RET_BB:.*]] +// LLVM: [[RET_BB]]: +// LLVM: %[[RETPHI:.*]] = phi i32 [ %[[LAM_RET]], %[[SCOPE_BB]] ] +// LLVM: store i32 %[[RETPHI]], ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET]] + +// OGCG: define linkonce_odr noundef i32 @_ZN1A3fooEv(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_A:.*]], +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[STRUCT_A:.*]] = getelementptr inbounds nuw %[[REC_LAM_A]], ptr %[[LAM_ALLOCA]], i32 0, i32 0 +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}} %[[STRUCT_A]], ptr {{.*}} %[[THIS]], i64 4, i1 false) +// OGCG: %[[LAM_RET:.*]] = call noundef i32 @_ZZN1A3fooEvENKUlvE_clEv(ptr {{.*}} %[[LAM_ALLOCA]]) +// OGCG: ret i32 %[[LAM_RET]] + +// lambda operator() in bar() +// CIR: cir.func {{.*}} @_ZZN1A3barEvENKUlvE_clEv(%[[THIS_ARG2:.*]]: !cir.ptr {{.*}}) -> !s32i +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS:.*]] = cir.load{{.*}} %[[THIS_ADDR]] +// CIR: %[[STRUCT_A_ADDR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "this"} +// CIR: %[[STRUCT_A_ADDR:.*]] = cir.load{{.*}} %[[STRUCT_A_ADDR_ADDR]] +// CIR: %[[A_A_ADDR:.*]] = cir.get_member %[[STRUCT_A_ADDR]][0] {name = "a"} +// CIR: %[[A_A:.*]] = cir.load{{.*}} %[[A_A_ADDR]] +// CIR: cir.store{{.*}} %[[A_A]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// LLVM: define linkonce_odr i32 @_ZZN1A3barEvENKUlvE_clEv(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: %[[STRUCT_A_ADDRR_ADDR:.*]] = getelementptr %[[REC_LAM_PTR_A:.*]], ptr %[[THIS]], i32 0, i32 0 +// LLVM: %[[STRUCT_A_ADDR:.*]] = load ptr, ptr %[[STRUCT_A_ADDRR_ADDR]] +// LLVM: %[[A_A_ADDR:.*]] = getelementptr %struct.A, ptr %[[STRUCT_A_ADDR]], i32 0, i32 0 +// LLVM: %[[A_A:.*]] = load i32, ptr %[[A_A_ADDR]] +// LLVM: store i32 %[[A_A]], ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET]] + +// The function above is defined after _ZZN1A3fooEvENKUlvE_clEv in OGCG, see below. + +// A::bar() +// CIR: cir.func {{.*}} @_ZN1A3barEv(%[[THIS_ARG:.*]]: !cir.ptr {{.*}}) -> !s32i +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["this", init] +// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR: cir.store %[[THIS_ARG]], %[[THIS_ADDR]] +// CIR: %[[THIS]] = cir.load %[[THIS_ADDR]] : !cir.ptr>, !cir.ptr +// CIR: %[[SCOPE_RET:.*]] = cir.scope { +// CIR: %[[LAM_ADDR:.*]] = cir.alloca ![[REC_LAM_PTR_A]], !cir.ptr, ["ref.tmp0"] +// CIR: %[[A_ADDR_ADDR:.*]] = cir.get_member %[[LAM_ADDR]][0] {name = "this"} : !cir.ptr -> !cir.ptr> +// CIR: cir.store{{.*}} %[[THIS]], %[[A_ADDR_ADDR]] +// CIR: %[[LAM_RET:.*]] = cir.call @_ZZN1A3barEvENKUlvE_clEv(%[[LAM_ADDR]]) +// CIR: cir.yield %[[LAM_RET]] +// CIR: } +// CIR: cir.store{{.*}} %[[SCOPE_RET]], %[[RETVAL]] +// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]] +// CIR: cir.return %[[RET]] + +// LLVM: define linkonce_odr i32 @_ZN1A3barEv(ptr %[[THIS_ARG:.*]]) +// LLVM: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_PTR_A]] +// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// LLVM: br label %[[SCOPE_BB:.*]] +// LLVM: [[SCOPE_BB]]: +// LLVM: %[[A_ADDR_ADDR:.*]] = getelementptr %[[REC_LAM_PTR_A]], ptr %[[LAM_ALLOCA]], i32 0, i32 0 +// LLVM: store ptr %[[THIS]], ptr %[[A_ADDR_ADDR]] +// LLVM: %[[LAM_RET:.*]] = call i32 @_ZZN1A3barEvENKUlvE_clEv(ptr %[[LAM_ALLOCA]]) +// LLVM: br label %[[RET_BB:.*]] +// LLVM: [[RET_BB]]: +// LLVM: %[[RETPHI:.*]] = phi i32 [ %[[LAM_RET]], %[[SCOPE_BB]] ] +// LLVM: store i32 %[[RETPHI]], ptr %[[RETVAL]] +// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET]] + +// OGCG: define linkonce_odr noundef i32 @_ZN1A3barEv(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_PTR_A:.*]], +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[STRUCT_A:.*]] = getelementptr inbounds nuw %[[REC_LAM_PTR_A]], ptr %[[LAM_ALLOCA]], i32 0, i32 0 +// OGCG: store ptr %[[THIS]], ptr %[[STRUCT_A]] +// OGCG: %[[LAM_RET:.*]] = call noundef i32 @_ZZN1A3barEvENKUlvE_clEv(ptr {{.*}} %[[LAM_ALLOCA]]) +// OGCG: ret i32 %[[LAM_RET]] + +// OGCG: define linkonce_odr noundef i32 @_ZZN1A3fooEvENKUlvE_clEv(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[PTR_A:.*]] = getelementptr inbounds nuw %[[REC_LAM_A]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[A_A_ADDR:.*]] = getelementptr inbounds nuw %struct.A, ptr %[[PTR_A]], i32 0, i32 0 +// OGCG: %[[A_A:.*]] = load i32, ptr %[[A_A_ADDR]] +// OGCG: ret i32 %[[A_A]] + +// OGCG: define linkonce_odr noundef i32 @_ZZN1A3barEvENKUlvE_clEv(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]] +// OGCG: %[[A_ADDR_ADDR:.*]] = getelementptr inbounds nuw %[[REC_LAM_PTR_A]], ptr %[[THIS]], i32 0, i32 0 +// OGCG: %[[A_ADDR:.*]] = load ptr, ptr %[[A_ADDR_ADDR]] +// OGCG: %[[A_A_ADDR:.*]] = getelementptr inbounds nuw %struct.A, ptr %[[A_ADDR]], i32 0, i32 0 +// OGCG: %[[A_A:.*]] = load i32, ptr %[[A_A_ADDR]] +// OGCG: ret i32 %[[A_A]] + +int test_lambda_this1(){ + struct A clsA; + int x = clsA.foo(); + int y = clsA.bar(); + return x+y; +} + +// CIR: cir.func {{.*}} @_Z17test_lambda_this1v +// CIR: cir.call @_ZN1AC1Ev(%[[A_THIS:.*]]){{.*}} : (!cir.ptr) -> () +// CIR: cir.call @_ZN1A3fooEv(%[[A_THIS]]){{.*}} : (!cir.ptr) -> !s32i +// CIR: cir.call @_ZN1A3barEv(%[[A_THIS]]){{.*}} : (!cir.ptr) -> !s32i + +// LLVM: define {{.*}} i32 @_Z17test_lambda_this1v +// LLVM: %[[A_THIS:.*]] = alloca %struct.A +// LLVM: call void @_ZN1AC1Ev(ptr %[[A_THIS]]) +// LLVM: call i32 @_ZN1A3fooEv(ptr %[[A_THIS]]) +// LLVM: call i32 @_ZN1A3barEv(ptr %[[A_THIS]]) + +// The function above is define before lambda operator() in foo() in OGCG, see above.