Skip to content

Commit be7444b

Browse files
authored
[CIR] Implement static lambda invoker (#160137)
This adds support for handling static lambda invokers.
1 parent ca43e76 commit be7444b

File tree

8 files changed

+322
-3
lines changed

8 files changed

+322
-3
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ struct MissingFeatures {
214214
static bool ehCleanupScopeRequiresEHCleanup() { return false; }
215215
static bool ehCleanupBranchFixups() { return false; }
216216
static bool ehstackBranches() { return false; }
217+
static bool emitBranchThroughCleanup() { return false; }
217218
static bool emitCheckedInBoundsGEP() { return false; }
218219
static bool emitCondLikelihoodViaExpectIntrinsic() { return false; }
219220
static bool emitLifetimeMarkers() { return false; }

clang/lib/CIR/CodeGen/CIRGenCall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class ReturnValueSlot {
256256
ReturnValueSlot() = default;
257257
ReturnValueSlot(Address addr) : addr(addr) {}
258258

259+
bool isNull() const { return !addr.isValid(); }
259260
Address getValue() const { return addr; }
260261
};
261262

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,86 @@ void CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) {
778778
s->getStmtClassName());
779779
}
780780

781+
void CIRGenFunction::emitForwardingCallToLambda(
782+
const CXXMethodDecl *callOperator, CallArgList &callArgs) {
783+
// Get the address of the call operator.
784+
const CIRGenFunctionInfo &calleeFnInfo =
785+
cgm.getTypes().arrangeCXXMethodDeclaration(callOperator);
786+
cir::FuncOp calleePtr = cgm.getAddrOfFunction(
787+
GlobalDecl(callOperator), cgm.getTypes().getFunctionType(calleeFnInfo));
788+
789+
// Prepare the return slot.
790+
const FunctionProtoType *fpt =
791+
callOperator->getType()->castAs<FunctionProtoType>();
792+
QualType resultType = fpt->getReturnType();
793+
ReturnValueSlot returnSlot;
794+
795+
// We don't need to separately arrange the call arguments because
796+
// the call can't be variadic anyway --- it's impossible to forward
797+
// variadic arguments.
798+
799+
// Now emit our call.
800+
CIRGenCallee callee =
801+
CIRGenCallee::forDirect(calleePtr, GlobalDecl(callOperator));
802+
RValue rv = emitCall(calleeFnInfo, callee, returnSlot, callArgs);
803+
804+
// If necessary, copy the returned value into the slot.
805+
if (!resultType->isVoidType() && returnSlot.isNull()) {
806+
if (getLangOpts().ObjCAutoRefCount && resultType->isObjCRetainableType())
807+
cgm.errorNYI(callOperator->getSourceRange(),
808+
"emitForwardingCallToLambda: ObjCAutoRefCount");
809+
emitReturnOfRValue(*currSrcLoc, rv, resultType);
810+
} else {
811+
cgm.errorNYI(callOperator->getSourceRange(),
812+
"emitForwardingCallToLambda: return slot is not null");
813+
}
814+
}
815+
816+
void CIRGenFunction::emitLambdaDelegatingInvokeBody(const CXXMethodDecl *md) {
817+
const CXXRecordDecl *lambda = md->getParent();
818+
819+
// Start building arguments for forwarding call
820+
CallArgList callArgs;
821+
822+
QualType lambdaType = getContext().getCanonicalTagType(lambda);
823+
QualType thisType = getContext().getPointerType(lambdaType);
824+
Address thisPtr =
825+
createMemTemp(lambdaType, getLoc(md->getSourceRange()), "unused.capture");
826+
callArgs.add(RValue::get(thisPtr.getPointer()), thisType);
827+
828+
// Add the rest of the parameters.
829+
for (auto *param : md->parameters())
830+
emitDelegateCallArg(callArgs, param, param->getBeginLoc());
831+
832+
const CXXMethodDecl *callOp = lambda->getLambdaCallOperator();
833+
// For a generic lambda, find the corresponding call operator specialization
834+
// to which the call to the static-invoker shall be forwarded.
835+
if (lambda->isGenericLambda()) {
836+
assert(md->isFunctionTemplateSpecialization());
837+
const TemplateArgumentList *tal = md->getTemplateSpecializationArgs();
838+
FunctionTemplateDecl *callOpTemplate =
839+
callOp->getDescribedFunctionTemplate();
840+
void *InsertPos = nullptr;
841+
FunctionDecl *correspondingCallOpSpecialization =
842+
callOpTemplate->findSpecialization(tal->asArray(), InsertPos);
843+
assert(correspondingCallOpSpecialization);
844+
callOp = cast<CXXMethodDecl>(correspondingCallOpSpecialization);
845+
}
846+
emitForwardingCallToLambda(callOp, callArgs);
847+
}
848+
849+
void CIRGenFunction::emitLambdaStaticInvokeBody(const CXXMethodDecl *md) {
850+
if (md->isVariadic()) {
851+
// Codgen for LLVM doesn't emit code for this as well, it says:
852+
// FIXME: Making this work correctly is nasty because it requires either
853+
// cloning the body of the call operator or making the call operator
854+
// forward.
855+
cgm.errorNYI(md->getSourceRange(), "emitLambdaStaticInvokeBody: variadic");
856+
}
857+
858+
emitLambdaDelegatingInvokeBody(md);
859+
}
860+
781861
void CIRGenFunction::destroyCXXObject(CIRGenFunction &cgf, Address addr,
782862
QualType type) {
783863
const auto *record = type->castAsCXXRecordDecl();

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
577577
getCIRGenModule().errorNYI(bodyRange, "CUDA kernel");
578578
} else if (isa<CXXMethodDecl>(funcDecl) &&
579579
cast<CXXMethodDecl>(funcDecl)->isLambdaStaticInvoker()) {
580-
getCIRGenModule().errorNYI(bodyRange, "Lambda static invoker");
580+
// The lambda static invoker function is special, because it forwards or
581+
// clones the body of the function call operator (but is actually
582+
// static).
583+
emitLambdaStaticInvokeBody(cast<CXXMethodDecl>(funcDecl));
581584
} else if (funcDecl->isDefaulted() && isa<CXXMethodDecl>(funcDecl) &&
582585
(cast<CXXMethodDecl>(funcDecl)->isCopyAssignmentOperator() ||
583586
cast<CXXMethodDecl>(funcDecl)->isMoveAssignmentOperator())) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,8 @@ class CIRGenFunction : public CIRGenTypeCache {
12741274

12751275
mlir::Value emitPromotedValue(mlir::Value result, QualType promotionType);
12761276

1277+
void emitReturnOfRValue(mlir::Location loc, RValue rv, QualType ty);
1278+
12771279
/// Emit the computation of the specified expression of scalar type.
12781280
mlir::Value emitScalarExpr(const clang::Expr *e);
12791281

@@ -1293,6 +1295,9 @@ class CIRGenFunction : public CIRGenTypeCache {
12931295

12941296
mlir::LogicalResult emitForStmt(const clang::ForStmt &s);
12951297

1298+
void emitForwardingCallToLambda(const CXXMethodDecl *lambdaCallOperator,
1299+
CallArgList &callArgs);
1300+
12961301
/// Emit the computation of the specified expression of complex type,
12971302
/// returning the result.
12981303
mlir::Value emitComplexExpr(const Expr *e);
@@ -1355,6 +1360,9 @@ class CIRGenFunction : public CIRGenTypeCache {
13551360
mlir::LogicalResult emitLabel(const clang::LabelDecl &d);
13561361
mlir::LogicalResult emitLabelStmt(const clang::LabelStmt &s);
13571362

1363+
void emitLambdaDelegatingInvokeBody(const CXXMethodDecl *md);
1364+
void emitLambdaStaticInvokeBody(const CXXMethodDecl *md);
1365+
13581366
mlir::LogicalResult emitIfStmt(const clang::IfStmt &s);
13591367

13601368
/// Emit code to compute the specified expression,

clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,11 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
488488
auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc);
489489
// This should emit a branch through the cleanup block if one exists.
490490
builder.create<cir::BrOp>(loc, retBlock);
491+
assert(!cir::MissingFeatures::emitBranchThroughCleanup());
491492
if (ehStack.stable_begin() != currentCleanupStackDepth)
492493
cgm.errorNYI(s.getSourceRange(), "return with cleanup stack");
494+
495+
// Insert the new block to continue codegen after branch to ret block.
493496
builder.createBlock(builder.getBlock()->getParent());
494497

495498
return mlir::success();
@@ -1041,3 +1044,21 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {
10411044

10421045
return res;
10431046
}
1047+
1048+
void CIRGenFunction::emitReturnOfRValue(mlir::Location loc, RValue rv,
1049+
QualType ty) {
1050+
if (rv.isScalar()) {
1051+
builder.createStore(loc, rv.getValue(), returnValue);
1052+
} else if (rv.isAggregate()) {
1053+
LValue dest = makeAddrLValue(returnValue, ty);
1054+
LValue src = makeAddrLValue(rv.getAggregateAddress(), ty);
1055+
emitAggregateCopy(dest, src, ty, getOverlapForReturnValue());
1056+
} else {
1057+
cgm.errorNYI(loc, "emitReturnOfRValue: complex return type");
1058+
}
1059+
mlir::Block *retBlock = curLexScope->getOrCreateRetBlock(*this, loc);
1060+
assert(!cir::MissingFeatures::emitBranchThroughCleanup());
1061+
builder.create<cir::BrOp>(loc, retBlock);
1062+
if (ehStack.stable_begin() != currentCleanupStackDepth)
1063+
cgm.errorNYI(loc, "return with cleanup stack");
1064+
}

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,8 +1941,14 @@ mlir::LogicalResult CIRToLLVMUnaryOpLowering::matchAndRewrite(
19411941
// Pointer unary operations: + only. (++ and -- of pointers are implemented
19421942
// with cir.ptr_stride, not cir.unary.)
19431943
if (mlir::isa<cir::PointerType>(elementType)) {
1944-
return op.emitError()
1945-
<< "Unary operation on pointer types is not yet implemented";
1944+
switch (op.getKind()) {
1945+
case cir::UnaryOpKind::Plus:
1946+
rewriter.replaceOp(op, adaptor.getInput());
1947+
return mlir::success();
1948+
default:
1949+
op.emitError() << "Unknown pointer unary operation during CIR lowering";
1950+
return mlir::failure();
1951+
}
19461952
}
19471953

19481954
return op.emitError() << "Unary operation has unsupported type: "
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
7+
8+
// We declare anonymous record types to represent lambdas. Rather than trying to
9+
// to match the declarations, we establish variables for these when they are used.
10+
11+
int g3() {
12+
auto* fn = +[](int const& i) -> int { return i; };
13+
auto task = fn(3);
14+
return task;
15+
}
16+
17+
// The order of these functions is different in OGCG.
18+
19+
// OGCG: define dso_local noundef i32 @_Z2g3v()
20+
// OGCG: %[[FN_PTR:.*]] = alloca ptr
21+
// OGCG: %[[REF_TMP:.*]] = alloca %[[REC_LAM_G3:.*]]
22+
// OGCG: %[[TASK:.*]] = alloca i32
23+
// OGCG: %[[REF_TMP1:.*]] = alloca i32
24+
// OGCG: %[[CALL:.*]] = call {{.*}} ptr @"_ZZ2g3vENK3$_0cvPFiRKiEEv"(ptr {{.*}} %[[REF_TMP]])
25+
// OGCG: store ptr %[[CALL]], ptr %[[FN_PTR]]
26+
// OGCG: %[[FN:.*]] = load ptr, ptr %[[FN_PTR]]
27+
// OGCG: store i32 3, ptr %[[REF_TMP1]]
28+
// OGCG: %[[CALL2:.*]] = call {{.*}} i32 %[[FN]](ptr {{.*}} %[[REF_TMP1]])
29+
// OGCG: store i32 %[[CALL2]], ptr %[[TASK]]
30+
// OGCG: %[[RESULT:.*]] = load i32, ptr %[[TASK]]
31+
// OGCG: ret i32 %[[RESULT]]
32+
33+
// OGCG: define internal noundef ptr @"_ZZ2g3vENK3$_0cvPFiRKiEEv"(ptr {{.*}} %[[THIS_ARG:.*]])
34+
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
35+
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
36+
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
37+
// OGCG: ret ptr @"_ZZ2g3vEN3$_08__invokeERKi"
38+
39+
// lambda operator()
40+
// CIR: cir.func lambda internal private dso_local @_ZZ2g3vENK3$_0clERKi(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3:.*]]> {{.*}}, %[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}})
41+
// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<![[REC_LAM_G3]]>, !cir.ptr<!cir.ptr<![[REC_LAM_G3]]>>, ["this", init]
42+
// CIR: %[[REF_I_ALLOCA:.*]] = cir.alloca {{.*}} ["i", init, const]
43+
// CIR: %[[RETVAL:.*]] = cir.alloca {{.*}} ["__retval"]
44+
// CIR: cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
45+
// CIR: cir.store %[[REF_I_ARG]], %[[REF_I_ALLOCA]]
46+
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ALLOCA]]
47+
// CIR: %[[REF_I:.*]] = cir.load %[[REF_I_ALLOCA]]
48+
// CIR: %[[I:.*]] = cir.load{{.*}} %[[REF_I]]
49+
// CIR: cir.store %[[I]], %[[RETVAL]]
50+
// CIR: %[[RET:.*]] = cir.load %[[RETVAL]]
51+
// CIR: cir.return %[[RET]]
52+
53+
// LLVM: define internal i32 @"_ZZ2g3vENK3$_0clERKi"(ptr %[[THIS_ARG:.*]], ptr %[[REF_I_ARG:.*]]) {
54+
// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr
55+
// LLVM: %[[REF_I_ALLOCA:.*]] = alloca ptr
56+
// LLVM: %[[RETVAL:.*]] = alloca i32
57+
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]]
58+
// LLVM: store ptr %[[REF_I_ARG]], ptr %[[REF_I_ALLOCA]]
59+
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
60+
// LLVM: %[[REF_I:.*]] = load ptr, ptr %[[REF_I_ALLOCA]]
61+
// LLVM: %[[I:.*]] = load i32, ptr %[[REF_I]]
62+
// LLVM: store i32 %[[I]], ptr %[[RETVAL]]
63+
// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]]
64+
// LLVM: ret i32 %[[RET]]
65+
66+
// In OGCG, the _ZZ2g3vENK3$_0clERKi function is emitted after _ZZ2g3vEN3$_08__invokeERKi, see below.
67+
68+
// lambda invoker
69+
// CIR: cir.func internal private dso_local @_ZZ2g3vEN3$_08__invokeERKi(%[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}}) -> !s32i {
70+
// CIR: %[[REF_I_ALLOCA:.*]] = cir.alloca {{.*}} ["i", init, const]
71+
// CIR: %[[RETVAL:.*]] = cir.alloca {{.*}} ["__retval"]
72+
// CIR: %[[LAM_ALLOCA:.*]] = cir.alloca ![[REC_LAM_G3]], !cir.ptr<![[REC_LAM_G3]]>, ["unused.capture"]
73+
// CIR: cir.store %[[REF_I_ARG]], %[[REF_I_ALLOCA]]
74+
// CIR: %[[REF_I:.*]] = cir.load{{.*}} %[[REF_I_ALLOCA]]
75+
// CIR: %[[LAM_RESULT:.*]] = cir.call @_ZZ2g3vENK3$_0clERKi(%2, %3) : (!cir.ptr<![[REC_LAM_G3]]>, !cir.ptr<!s32i>) -> !s32i
76+
// CIR: cir.store{{.*}} %[[LAM_RESULT]], %[[RETVAL]]
77+
// CIR: %[[RET:.*]] = cir.load %[[RETVAL]]
78+
// CIR: cir.return %[[RET]]
79+
80+
// LLVM: define internal i32 @"_ZZ2g3vEN3$_08__invokeERKi"(ptr %[[REF_I_ARG:.*]]) {
81+
// LLVM: %[[REF_I_ALLOCA:.*]] = alloca ptr
82+
// LLVM: %[[RETVAL:.*]] = alloca i32
83+
// LLVM: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_G3:.*]],
84+
// LLVM: store ptr %[[REF_I_ARG]], ptr %[[REF_I_ALLOCA]]
85+
// LLVM: %[[REF_I:.*]] = load ptr, ptr %[[REF_I_ALLOCA]]
86+
// LLVM: %[[LAM_RESULT:.*]] = call i32 @"_ZZ2g3vENK3$_0clERKi"(ptr %[[LAM_ALLOCA]], ptr %[[REF_I]])
87+
// LLVM: store i32 %[[LAM_RESULT]], ptr %[[RETVAL]]
88+
// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]]
89+
// LLVM: ret i32 %[[RET]]
90+
91+
// In OGCG, the _ZZ2g3vEN3$_08__invokeERKi function is emitted after _ZN1A3barEv, see below.
92+
93+
// lambda operator int (*)(int const&)()
94+
// CIR: cir.func internal private dso_local @_ZZ2g3vENK3$_0cvPFiRKiEEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3]]> {{.*}}) -> !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>> {
95+
// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<![[REC_LAM_G3]]>, !cir.ptr<!cir.ptr<![[REC_LAM_G3]]>>, ["this", init]
96+
// CIR: %[[RETVAL:.*]] = cir.alloca !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>>, ["__retval"]
97+
// CIR: cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
98+
// CIR: %[[THIS:.*]] = cir.load %[[THIS_ALLOCA]]
99+
// CIR: %[[INVOKER:.*]] = cir.get_global @_ZZ2g3vEN3$_08__invokeERKi : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>
100+
// CIR: cir.store %[[INVOKER]], %[[RETVAL]]
101+
// CIR: %[[RET:.*]] = cir.load %[[RETVAL]]
102+
// CIR: cir.return %[[RET]]
103+
104+
// LLVM: define internal ptr @"_ZZ2g3vENK3$_0cvPFiRKiEEv"(ptr %[[THIS_ARG:.*]]) {
105+
// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr
106+
// LLVM: %[[RETVAL:.*]] = alloca ptr
107+
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ALLOCA]]
108+
// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
109+
// LLVM: store ptr @"_ZZ2g3vEN3$_08__invokeERKi", ptr %[[RETVAL]]
110+
// LLVM: %[[RET:.*]] = load ptr, ptr %[[RETVAL]]
111+
// LLVM: ret ptr %[[RET]]
112+
113+
// In OGCG, the _ZZ2g3vENK3$_0cvPFiRKiEEv function is emitted just after the _Z2g3v function, see above.
114+
115+
// CIR: cir.func{{.*}} @_Z2g3v() -> !s32i {
116+
// CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
117+
// CIR: %[[FN_ADDR:.*]] = cir.alloca !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>>, ["fn", init]
118+
// CIR: %[[TASK:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["task", init]
119+
120+
// 1. Use `operator int (*)(int const&)()` to retrieve the fnptr to `__invoke()`.
121+
// CIR: %[[SCOPE_RET:.*]] = cir.scope {
122+
// CIR: %[[LAM_ALLOCA:.*]] = cir.alloca ![[REC_LAM_G3]], !cir.ptr<![[REC_LAM_G3]]>, ["ref.tmp0"]
123+
// CIR: %[[OPERATOR_RESULT:.*]] = cir.call @_ZZ2g3vENK3$_0cvPFiRKiEEv(%[[LAM_ALLOCA]]){{.*}}
124+
// CIR: %[[PLUS:.*]] = cir.unary(plus, %[[OPERATOR_RESULT]])
125+
// CIR: cir.yield %[[PLUS]]
126+
// CIR: }
127+
128+
// 2. Load ptr to `__invoke()`.
129+
// CIR: cir.store{{.*}} %[[SCOPE_RET]], %[[FN_ADDR]]
130+
// CIR: %[[SCOPE_RET2:.*]] = cir.scope {
131+
// CIR: %[[REF_TMP1:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp1", init]
132+
// CIR: %[[FN:.*]] = cir.load{{.*}} %[[FN_ADDR]]
133+
// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
134+
// CIR: cir.store{{.*}} %[[THREE]], %[[REF_TMP1]]
135+
136+
// 3. Call `__invoke()`, which effectively executes `operator()`.
137+
// CIR: %[[RESULT:.*]] = cir.call %[[FN]](%[[REF_TMP1]])
138+
// CIR: cir.yield %[[RESULT]]
139+
// CIR: }
140+
141+
// CIR: cir.store{{.*}} %[[SCOPE_RET2]], %[[TASK]]
142+
// CIR: %[[TASK_RET:.*]] = cir.load{{.*}} %[[TASK]]
143+
// CIR: cir.store{{.*}} %[[TASK_RET]], %[[RETVAL]]
144+
// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]]
145+
// CIR: cir.return %[[RET]]
146+
// CIR: }
147+
148+
// LLVM: define dso_local i32 @_Z2g3v() {
149+
// LLVM: %[[LAM_ALLOCA:.*]] = alloca %[[REC_LAM_G3]]
150+
// LLVM: %[[REF_TMP1:.*]] = alloca i32
151+
// LLVM: %[[RETVAL:.*]] = alloca i32
152+
// LLVM: %[[FN_PTR:.*]] = alloca ptr
153+
// LLVM: %[[TASK:.*]] = alloca i32
154+
// LLVM: br label %[[SCOPE_BB0:.*]]
155+
156+
// LLVM: [[SCOPE_BB0]]:
157+
// LLVM: %[[OPERATOR_RESULT:.*]] = call ptr @"_ZZ2g3vENK3$_0cvPFiRKiEEv"(ptr %[[LAM_ALLOCA]])
158+
// LLVM: br label %[[SCOPE_BB1:.*]]
159+
160+
// LLVM: [[SCOPE_BB1]]:
161+
// LLVM: %[[TMP0:.*]] = phi ptr [ %[[OPERATOR_RESULT]], %[[SCOPE_BB0]] ]
162+
// LLVM: store ptr %[[TMP0]], ptr %[[FN_PTR]]
163+
// LLVM: br label %[[SCOPE_BB2:.*]]
164+
165+
// LLVM: [[SCOPE_BB2]]:
166+
// LLVM: %[[FN:.*]] = load ptr, ptr %[[FN_PTR]]
167+
// LLVM: store i32 3, ptr %[[REF_TMP1]]
168+
// LLVM: %[[RESULT:.*]] = call i32 %[[FN]](ptr %[[REF_TMP1]])
169+
// LLVM: br label %[[RET_BB:.*]]
170+
171+
// LLVM: [[RET_BB]]:
172+
// LLVM: %[[TMP1:.*]] = phi i32 [ %[[RESULT]], %[[SCOPE_BB2]] ]
173+
// LLVM: store i32 %[[TMP1]], ptr %[[TASK]]
174+
// LLVM: %[[TMP2:.*]] = load i32, ptr %[[TASK]]
175+
// LLVM: store i32 %[[TMP2]], ptr %[[RETVAL]]
176+
// LLVM: %[[RET:.*]] = load i32, ptr %[[RETVAL]]
177+
// LLVM: ret i32 %[[RET]]
178+
179+
// The definition for _Z2g3v in OGCG is first among the functions for the g3 test, see above.
180+
181+
// The functions below are emitted later in OGCG, see above for the corresponding LLVM checks.
182+
183+
// OGCG: define internal noundef i32 @"_ZZ2g3vEN3$_08__invokeERKi"(ptr {{.*}} %[[I_ARG:.*]])
184+
// OGCG: %[[I_ADDR:.*]] = alloca ptr
185+
// OGCG: %[[UNUSED_CAPTURE:.*]] = alloca %[[REC_LAM_G3:.*]]
186+
// OGCG: store ptr %[[I_ARG]], ptr %[[I_ADDR]]
187+
// OGCG: %[[I_PTR:.*]] = load ptr, ptr %[[I_ADDR]]
188+
// OGCG: %[[CALL:.*]] = call {{.*}} i32 @"_ZZ2g3vENK3$_0clERKi"(ptr {{.*}} %[[UNUSED_CAPTURE]], ptr {{.*}} %[[I_PTR]])
189+
// OGCG: ret i32 %[[CALL]]
190+
191+
// OGCG: define internal noundef i32 @"_ZZ2g3vENK3$_0clERKi"(ptr {{.*}} %[[THIS_ARG:.*]], ptr {{.*}} %[[I_ARG:.*]])
192+
// OGCG: %[[THIS_ADDR:.*]] = alloca ptr
193+
// OGCG: %[[I_ADDR:.*]] = alloca ptr
194+
// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
195+
// OGCG: store ptr %[[I_ARG]], ptr %[[I_ADDR]]
196+
// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
197+
// OGCG: %[[I_PTR:.*]] = load ptr, ptr %[[I_ADDR]]
198+
// OGCG: %[[I:.*]] = load i32, ptr %[[I_PTR]]
199+
// OGCG: ret i32 %[[I]]

0 commit comments

Comments
 (0)