Skip to content

Commit

Permalink
Merge pull request #16301 from MathiasVP/proper-coroutine-cfg-support-2
Browse files Browse the repository at this point in the history
C++: Implement proper coroutine support in IR
  • Loading branch information
MathiasVP authored Apr 23, 2024
2 parents b2f0994 + c175560 commit 553c09a
Show file tree
Hide file tree
Showing 10 changed files with 2,790 additions and 1,595 deletions.
36 changes: 36 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,24 @@ class CoAwaitExpr extends UnaryOperation, @co_await {
override string getOperator() { result = "co_await" }

override int getPrecedence() { result = 16 }

/**
* Gets the Boolean expression that is used to decide if the enclosing
* coroutine should be suspended.
*/
Expr getAwaitReady() { result = this.getChild(1) }

/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
Expr getAwaitResume() { result = this.getChild(2) }

/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
Expr getAwaitSuspend() { result = this.getChild(3) }
}

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

override int getPrecedence() { result = 2 }

/**
* Gets the Boolean expression that is used to decide if the enclosing
* coroutine should be suspended.
*/
Expr getAwaitReady() { result = this.getChild(1) }

/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
Expr getAwaitResume() { result = this.getChild(2) }

/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
Expr getAwaitSuspend() { result = this.getChild(3) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ newtype TInstructionTag =
ImplicitDestructorTag(int index) {
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
}
} or
CoAwaitBranchTag()

class InstructionTag extends TInstructionTag {
final string toString() { result = getInstructionTagId(this) }
Expand Down Expand Up @@ -186,6 +187,8 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = BoolConversionCompareTag() and result = "BoolConvComp"
or
tag = ResultCopyTag() and result = "ResultCopy"
or
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
or
tag = CatchTag() and result = "Catch"
Expand Down Expand Up @@ -263,4 +266,6 @@ string getInstructionTagId(TInstructionTag tag) {
exists(int index |
tag = ImplicitDestructorTag(index) and result = "ImplicitDestructor(" + index + ")"
)
or
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -1259,9 +1259,7 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
expr instanceof NotExpr or
expr instanceof ComplementExpr or
expr instanceof UnaryPlusExpr or
expr instanceof UnaryMinusExpr or
expr instanceof CoAwaitExpr or
expr instanceof CoYieldExpr
expr instanceof UnaryMinusExpr
}

final override Instruction getFirstInstruction(EdgeKind kind) {
Expand Down Expand Up @@ -1301,19 +1299,153 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
expr instanceof UnaryPlusExpr and result instanceof Opcode::CopyValue
or
expr instanceof UnaryMinusExpr and result instanceof Opcode::Negate
or
// TODO: Use a new opcode to represent "awaiting the value"
expr instanceof CoAwaitExpr and result instanceof Opcode::CopyValue
or
// TODO: Use a new opcode to represent "awaiting the value"
expr instanceof CoYieldExpr and result instanceof Opcode::CopyValue
}

private TranslatedExpr getOperand() {
result = getTranslatedExpr(expr.(UnaryOperation).getOperand().getFullyConverted())
}
}

/**
* IR translation of a `co_await` or `co_yield` expression.
*
* The translation of `x = co_await ...` is essentially:
* ```cpp
* if (!awaiter.await_ready()) {
* awaiter.await_suspend();
* }
* x = awaiter.await_resume();
* ```
* where `awaiter` is an object constructed from programmer-supplied
* input, and for IR construction purposes these are resolved by the C/C++
* front-end.
*
* See https://en.cppreference.com/w/cpp/language/coroutines#co_await for the
* specification on how `awaiter` is obtained.
*/
abstract private class TranslatedCoExpr extends TranslatedNonConstantExpr {
/** Gets the operand of this operation. */
abstract Expr getOperand();

/**
* Gets the expression that decides if the enclosing coroutine should be
* suspended.
*/
abstract Expr getAwaitReady();

/**
* Gets the expression that is evaluated when the enclosing coroutine is
* suspended.
*/
abstract Expr getAwaitSuspend();

/**
* Gets the expression that represents the resume point if the enclosing
* coroutine was suspended.
*/
abstract Expr getAwaitResume();

final override Instruction getFirstInstruction(EdgeKind kind) {
result = this.getTranslatedOperand().getFirstInstruction(kind)
}

override Instruction getALastInstructionInternal() {
result = this.getTranslatedAwaitResume().getALastInstruction()
}

final override TranslatedElement getChildInternal(int id) {
id = 0 and result = this.getTranslatedOperand()
or
id = 1 and result = this.getTranslatedAwaitReady()
or
id = 2 and result = this.getTranslatedAwaitResume()
or
id = 3 and result = this.getTranslatedAwaitSuspend()
}

final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = CoAwaitBranchTag() and
(
kind instanceof TrueEdge and
result = this.getTranslatedAwaitResume().getFirstInstruction(any(GotoEdge goto))
or
kind instanceof FalseEdge and
result = this.getTranslatedAwaitSuspend().getFirstInstruction(any(GotoEdge goto))
)
}

override Instruction getResult() { result = this.getTranslatedAwaitResume().getResult() }

final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
child = this.getTranslatedOperand() and
result = this.getTranslatedAwaitReady().getFirstInstruction(kind)
or
child = this.getTranslatedAwaitReady() and
kind instanceof GotoEdge and
result = this.getInstruction(CoAwaitBranchTag())
or
child = this.getTranslatedAwaitSuspend() and
result = this.getTranslatedAwaitResume().getFirstInstruction(kind)
or
child = this.getTranslatedAwaitResume() and
result = this.getParent().getChildSuccessor(this, kind)
}

override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CoAwaitBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
resultType = getVoidType()
}

override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = CoAwaitBranchTag() and
operandTag instanceof ConditionOperandTag and
result = this.getTranslatedAwaitReady().getResult()
}

private TranslatedExpr getTranslatedOperand() {
result = getTranslatedExpr(this.getOperand().getFullyConverted())
}

private TranslatedExpr getTranslatedAwaitReady() {
result = getTranslatedExpr(this.getAwaitReady().getFullyConverted())
}

private TranslatedExpr getTranslatedAwaitResume() {
result = getTranslatedExpr(this.getAwaitResume().getFullyConverted())
}

private TranslatedExpr getTranslatedAwaitSuspend() {
result = getTranslatedExpr(this.getAwaitSuspend().getFullyConverted())
}
}

/** IR translation of `co_await`. */
class TranslatedCoAwaitExpr extends TranslatedCoExpr {
override CoAwaitExpr expr;

final override Expr getOperand() { result = expr.getOperand() }

final override Expr getAwaitReady() { result = expr.getAwaitReady() }

final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }

final override Expr getAwaitResume() { result = expr.getAwaitResume() }
}

/** IR translation of `co_yield`. */
class TranslatedCoYieldxpr extends TranslatedCoExpr {
override CoYieldExpr expr;

final override Expr getOperand() { result = expr.getOperand() }

final override Expr getAwaitReady() { result = expr.getAwaitReady() }

final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }

final override Expr getAwaitResume() { result = expr.getAwaitResume() }
}

abstract class TranslatedConversion extends TranslatedNonConstantExpr {
override Conversion expr;

Expand Down
Loading

0 comments on commit 553c09a

Please sign in to comment.