Skip to content

Commit 553c09a

Browse files
authored
Merge pull request #16301 from MathiasVP/proper-coroutine-cfg-support-2
C++: Implement proper coroutine support in IR
2 parents b2f0994 + c175560 commit 553c09a

File tree

10 files changed

+2790
-1595
lines changed

10 files changed

+2790
-1595
lines changed

cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,24 @@ class CoAwaitExpr extends UnaryOperation, @co_await {
13381338
override string getOperator() { result = "co_await" }
13391339

13401340
override int getPrecedence() { result = 16 }
1341+
1342+
/**
1343+
* Gets the Boolean expression that is used to decide if the enclosing
1344+
* coroutine should be suspended.
1345+
*/
1346+
Expr getAwaitReady() { result = this.getChild(1) }
1347+
1348+
/**
1349+
* Gets the expression that represents the resume point if the enclosing
1350+
* coroutine was suspended.
1351+
*/
1352+
Expr getAwaitResume() { result = this.getChild(2) }
1353+
1354+
/**
1355+
* Gets the expression that is evaluated when the enclosing coroutine is
1356+
* suspended.
1357+
*/
1358+
Expr getAwaitSuspend() { result = this.getChild(3) }
13411359
}
13421360

13431361
/**
@@ -1352,6 +1370,24 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
13521370
override string getOperator() { result = "co_yield" }
13531371

13541372
override int getPrecedence() { result = 2 }
1373+
1374+
/**
1375+
* Gets the Boolean expression that is used to decide if the enclosing
1376+
* coroutine should be suspended.
1377+
*/
1378+
Expr getAwaitReady() { result = this.getChild(1) }
1379+
1380+
/**
1381+
* Gets the expression that represents the resume point if the enclosing
1382+
* coroutine was suspended.
1383+
*/
1384+
Expr getAwaitResume() { result = this.getChild(2) }
1385+
1386+
/**
1387+
* Gets the expression that is evaluated when the enclosing coroutine is
1388+
* suspended.
1389+
*/
1390+
Expr getAwaitSuspend() { result = this.getChild(3) }
13551391
}
13561392

13571393
/**

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ newtype TInstructionTag =
8989
ImplicitDestructorTag(int index) {
9090
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
9191
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
92-
}
92+
} or
93+
CoAwaitBranchTag()
9394

9495
class InstructionTag extends TInstructionTag {
9596
final string toString() { result = getInstructionTagId(this) }
@@ -186,6 +187,8 @@ string getInstructionTagId(TInstructionTag tag) {
186187
or
187188
tag = BoolConversionCompareTag() and result = "BoolConvComp"
188189
or
190+
tag = ResultCopyTag() and result = "ResultCopy"
191+
or
189192
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
190193
or
191194
tag = CatchTag() and result = "Catch"
@@ -263,4 +266,6 @@ string getInstructionTagId(TInstructionTag tag) {
263266
exists(int index |
264267
tag = ImplicitDestructorTag(index) and result = "ImplicitDestructor(" + index + ")"
265268
)
269+
or
270+
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
266271
}

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 141 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,9 +1259,7 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
12591259
expr instanceof NotExpr or
12601260
expr instanceof ComplementExpr or
12611261
expr instanceof UnaryPlusExpr or
1262-
expr instanceof UnaryMinusExpr or
1263-
expr instanceof CoAwaitExpr or
1264-
expr instanceof CoYieldExpr
1262+
expr instanceof UnaryMinusExpr
12651263
}
12661264

12671265
final override Instruction getFirstInstruction(EdgeKind kind) {
@@ -1301,19 +1299,153 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
13011299
expr instanceof UnaryPlusExpr and result instanceof Opcode::CopyValue
13021300
or
13031301
expr instanceof UnaryMinusExpr and result instanceof Opcode::Negate
1304-
or
1305-
// TODO: Use a new opcode to represent "awaiting the value"
1306-
expr instanceof CoAwaitExpr and result instanceof Opcode::CopyValue
1307-
or
1308-
// TODO: Use a new opcode to represent "awaiting the value"
1309-
expr instanceof CoYieldExpr and result instanceof Opcode::CopyValue
13101302
}
13111303

13121304
private TranslatedExpr getOperand() {
13131305
result = getTranslatedExpr(expr.(UnaryOperation).getOperand().getFullyConverted())
13141306
}
13151307
}
13161308

1309+
/**
1310+
* IR translation of a `co_await` or `co_yield` expression.
1311+
*
1312+
* The translation of `x = co_await ...` is essentially:
1313+
* ```cpp
1314+
* if (!awaiter.await_ready()) {
1315+
* awaiter.await_suspend();
1316+
* }
1317+
* x = awaiter.await_resume();
1318+
* ```
1319+
* where `awaiter` is an object constructed from programmer-supplied
1320+
* input, and for IR construction purposes these are resolved by the C/C++
1321+
* front-end.
1322+
*
1323+
* See https://en.cppreference.com/w/cpp/language/coroutines#co_await for the
1324+
* specification on how `awaiter` is obtained.
1325+
*/
1326+
abstract private class TranslatedCoExpr extends TranslatedNonConstantExpr {
1327+
/** Gets the operand of this operation. */
1328+
abstract Expr getOperand();
1329+
1330+
/**
1331+
* Gets the expression that decides if the enclosing coroutine should be
1332+
* suspended.
1333+
*/
1334+
abstract Expr getAwaitReady();
1335+
1336+
/**
1337+
* Gets the expression that is evaluated when the enclosing coroutine is
1338+
* suspended.
1339+
*/
1340+
abstract Expr getAwaitSuspend();
1341+
1342+
/**
1343+
* Gets the expression that represents the resume point if the enclosing
1344+
* coroutine was suspended.
1345+
*/
1346+
abstract Expr getAwaitResume();
1347+
1348+
final override Instruction getFirstInstruction(EdgeKind kind) {
1349+
result = this.getTranslatedOperand().getFirstInstruction(kind)
1350+
}
1351+
1352+
override Instruction getALastInstructionInternal() {
1353+
result = this.getTranslatedAwaitResume().getALastInstruction()
1354+
}
1355+
1356+
final override TranslatedElement getChildInternal(int id) {
1357+
id = 0 and result = this.getTranslatedOperand()
1358+
or
1359+
id = 1 and result = this.getTranslatedAwaitReady()
1360+
or
1361+
id = 2 and result = this.getTranslatedAwaitResume()
1362+
or
1363+
id = 3 and result = this.getTranslatedAwaitSuspend()
1364+
}
1365+
1366+
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
1367+
tag = CoAwaitBranchTag() and
1368+
(
1369+
kind instanceof TrueEdge and
1370+
result = this.getTranslatedAwaitResume().getFirstInstruction(any(GotoEdge goto))
1371+
or
1372+
kind instanceof FalseEdge and
1373+
result = this.getTranslatedAwaitSuspend().getFirstInstruction(any(GotoEdge goto))
1374+
)
1375+
}
1376+
1377+
override Instruction getResult() { result = this.getTranslatedAwaitResume().getResult() }
1378+
1379+
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
1380+
child = this.getTranslatedOperand() and
1381+
result = this.getTranslatedAwaitReady().getFirstInstruction(kind)
1382+
or
1383+
child = this.getTranslatedAwaitReady() and
1384+
kind instanceof GotoEdge and
1385+
result = this.getInstruction(CoAwaitBranchTag())
1386+
or
1387+
child = this.getTranslatedAwaitSuspend() and
1388+
result = this.getTranslatedAwaitResume().getFirstInstruction(kind)
1389+
or
1390+
child = this.getTranslatedAwaitResume() and
1391+
result = this.getParent().getChildSuccessor(this, kind)
1392+
}
1393+
1394+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
1395+
tag = CoAwaitBranchTag() and
1396+
opcode instanceof Opcode::ConditionalBranch and
1397+
resultType = getVoidType()
1398+
}
1399+
1400+
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
1401+
tag = CoAwaitBranchTag() and
1402+
operandTag instanceof ConditionOperandTag and
1403+
result = this.getTranslatedAwaitReady().getResult()
1404+
}
1405+
1406+
private TranslatedExpr getTranslatedOperand() {
1407+
result = getTranslatedExpr(this.getOperand().getFullyConverted())
1408+
}
1409+
1410+
private TranslatedExpr getTranslatedAwaitReady() {
1411+
result = getTranslatedExpr(this.getAwaitReady().getFullyConverted())
1412+
}
1413+
1414+
private TranslatedExpr getTranslatedAwaitResume() {
1415+
result = getTranslatedExpr(this.getAwaitResume().getFullyConverted())
1416+
}
1417+
1418+
private TranslatedExpr getTranslatedAwaitSuspend() {
1419+
result = getTranslatedExpr(this.getAwaitSuspend().getFullyConverted())
1420+
}
1421+
}
1422+
1423+
/** IR translation of `co_await`. */
1424+
class TranslatedCoAwaitExpr extends TranslatedCoExpr {
1425+
override CoAwaitExpr expr;
1426+
1427+
final override Expr getOperand() { result = expr.getOperand() }
1428+
1429+
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
1430+
1431+
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
1432+
1433+
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
1434+
}
1435+
1436+
/** IR translation of `co_yield`. */
1437+
class TranslatedCoYieldxpr extends TranslatedCoExpr {
1438+
override CoYieldExpr expr;
1439+
1440+
final override Expr getOperand() { result = expr.getOperand() }
1441+
1442+
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
1443+
1444+
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
1445+
1446+
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
1447+
}
1448+
13171449
abstract class TranslatedConversion extends TranslatedNonConstantExpr {
13181450
override Conversion expr;
13191451

0 commit comments

Comments
 (0)