C++: IR support for range-based for loops

IR construction was missing support for C++ 11 range-based `for` loops. The extractor generates ASTs for the compiler-generated implementation already, so I had enough information to generate IR. I've expanded on some of the predicates in `RangeBasedForStmt` to access the desugared information.

One complication was that the `DeclStmt`s for the compiler-generated variables seem to have results for `getDeclaration()` but not for `getDeclarationEntry()`. This required handling these slightly differently than we do for other `DeclStmt`s.

The flow for range-based `for` is actually easier than for a regular `for`, because all three components (init, condition, and update) are always present.
This commit is contained in:
Dave Bartolomeo
2019-05-29 14:40:29 -07:00
parent a782585d74
commit aff85c5b24
8 changed files with 492 additions and 4 deletions

View File

@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.internal.OperandTag
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
private import TranslatedElement
private import TranslatedExpr
private import TranslatedStmt
@@ -159,6 +160,23 @@ cached private module Cached {
)
)
or
// Range-based for loop:
// Any edge from within the update of the loop to the condition of
// the loop is a back edge.
exists(TranslatedRangeBasedForStmt s, TranslatedCondition condition |
s instanceof TranslatedRangeBasedForStmt and
condition = s.getCondition() and
result = condition.getFirstInstruction() and
exists(TranslatedElement inUpdate, InstructionTag tag |
result = inUpdate.getInstructionSuccessor(tag, kind) and
exists(TranslatedElement update |
update = s.getUpdate() |
inUpdate = update.getAChild*()
) and
instruction = inUpdate.getInstruction(tag)
)
)
or
// Goto statement:
// As a conservative approximation, any edge out of `goto` is a back edge
// unless it goes strictly forward in the program text. A `goto` whose

View File

@@ -191,6 +191,44 @@ class TranslatedVariableDeclarationEntry extends TranslatedVariableDeclaration,
}
}
/**
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
* `var`.
*/
TranslatedRangeBasedForVariableDeclaration getTranslatedRangeBasedForVariableDeclaration(
LocalVariable var) {
result.getVariable() = var
}
/**
* Represents the IR translation of a compiler-generated variable in a range-based `for` loop.
*/
class TranslatedRangeBasedForVariableDeclaration extends TranslatedVariableDeclaration,
TTranslatedRangeBasedForVariableDeclaration {
RangeBasedForStmt forStmt;
LocalVariable var;
TranslatedRangeBasedForVariableDeclaration() {
this = TTranslatedRangeBasedForVariableDeclaration(forStmt, var)
}
override string toString() {
result = var.toString()
}
override Locatable getAST() {
result = var
}
override Function getFunction() {
result = forStmt.getEnclosingFunction()
}
override LocalVariable getVariable() {
result = var
}
}
TranslatedConditionDecl getTranslatedConditionDecl(ConditionDeclExpr expr) {
result.getAST() = expr
}

View File

@@ -359,6 +359,16 @@ newtype TTranslatedElement =
declStmt.getADeclarationEntry() = entry
)
} or
// A compiler-generated variable to implement a range-based for loop. These don't have a
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
TTranslatedRangeBasedForVariableDeclaration(RangeBasedForStmt forStmt, LocalVariable var) {
translateStmt(forStmt) and
(
var = forStmt.getRangeVariable() or
var = forStmt.getBeginEndDeclaration().getADeclaration() or
var = forStmt.getVariable()
)
} or
// An allocator call in a `new` or `new[]` expression
TTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
not ignoreExpr(newExpr)

View File

@@ -630,6 +630,105 @@ class TranslatedForStmt extends TranslatedLoop {
}
}
/**
* The IR translation of a range-based `for` loop.
* Note that this class does not extend `TranslatedLoop`. This is because the "body" of the
* range-based `for` loop consists of the per-iteration variable declaration followed by the
* user-written body statement. It is easier to handle the control flow of the loop separately,
* rather than synthesizing a single body or complicating the interface of `TranslatedLoop`.
*/
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
override RangeBasedForStmt stmt;
override TranslatedElement getChild(int id) {
id = 0 and result = getRangeVariableDeclaration() or
id = 1 and result = getBeginVariableDeclaration() or
id = 2 and result = getEndVariableDeclaration() or
id = 3 and result = getCondition() or
id = 4 and result = getUpdate() or
id = 5 and result = getVariableDeclaration() or
id = 6 and result = getBody()
}
override Instruction getFirstInstruction() {
result = getRangeVariableDeclaration().getFirstInstruction()
}
override Instruction getChildSuccessor(TranslatedElement child) {
(
child = getRangeVariableDeclaration() and
result = getBeginVariableDeclaration().getFirstInstruction()
) or
(
child = getBeginVariableDeclaration() and
result = getEndVariableDeclaration().getFirstInstruction()
) or
(
child = getEndVariableDeclaration() and
result = getCondition().getFirstInstruction()
) or
(
child = getVariableDeclaration() and
result = getBody().getFirstInstruction()
) or
(
child = getBody() and
result = getUpdate().getFirstInstruction()
) or
(
child = getUpdate() and
result = getCondition().getFirstInstruction()
)
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
boolean isGLValue) {
none()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
none()
}
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
child = getCondition() and result = getVariableDeclaration().getFirstInstruction()
}
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
child = getCondition() and result = getParent().getChildSuccessor(this)
}
private TranslatedRangeBasedForVariableDeclaration getRangeVariableDeclaration() {
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getRangeVariable())
}
private TranslatedRangeBasedForVariableDeclaration getBeginVariableDeclaration() {
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getBeginVariable())
}
private TranslatedRangeBasedForVariableDeclaration getEndVariableDeclaration() {
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getEndVariable())
}
// Public for getInstructionBackEdgeSuccessor
final TranslatedCondition getCondition() {
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
}
// Public for getInstructionBackEdgeSuccessor
final TranslatedExpr getUpdate() {
result = getTranslatedExpr(stmt.getUpdate().getFullyConverted())
}
private TranslatedRangeBasedForVariableDeclaration getVariableDeclaration() {
result = getTranslatedRangeBasedForVariableDeclaration(stmt.getVariable())
}
private TranslatedStmt getBody() {
result = getTranslatedStmt(stmt.getStmt())
}
}
class TranslatedJumpStmt extends TranslatedStmt {
override JumpStmt stmt;

View File

@@ -675,7 +675,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
* ```
* the result is `int x`.
*/
Variable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() }
LocalVariable getVariable() { result = getChild(4).(DeclStmt).getADeclaration() }
/**
* Gets the expression giving the range to iterate over.
@@ -689,7 +689,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
Expr getRange() { result = getRangeVariable().getInitializer().getExpr() }
/** Gets the compiler-generated `__range` variable after desugaring. */
Variable getRangeVariable() {
LocalVariable getRangeVariable() {
result = getChild(0).(DeclStmt).getADeclaration()
}
@@ -709,6 +709,16 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
*/
DeclStmt getBeginEndDeclaration() { result = this.getChild(1) }
/** Gets the compiler-generated `__begin` variable after desugaring. */
LocalVariable getBeginVariable() {
result = getBeginEndDeclaration().getDeclaration(0)
}
/** Gets the compiler-generated `__end` variable after desugaring. */
LocalVariable getEndVariable() {
result = getBeginEndDeclaration().getDeclaration(1)
}
/**
* Gets the compiler-generated `++__begin` which is the update
* expression of this for statement after desugaring. It will
@@ -718,8 +728,8 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
Expr getUpdate() { result = this.getChild(3) }
/** Gets the compiler-generated `__begin` variable after desugaring. */
Variable getAnIterationVariable() {
result = getUpdate().getAChild().(VariableAccess).getTarget()
LocalVariable getAnIterationVariable() {
result = getBeginVariable()
}
}