C++ IR: guard against cycles in operand graph

This doesn't fix the underlying problem that for some reason there are
cycles in the operand graph on our snapshots of the Linux kernel, but it
ensures that the cycles don't lead to non-termination of
`ConstantAnalysis` and `ValueNumbering`.
This commit is contained in:
Jonas Jensen
2019-06-28 10:15:35 +02:00
parent 46d779248d
commit 39854a3f7b
3 changed files with 72 additions and 6 deletions

View File

@@ -8,15 +8,37 @@ private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not isInCycle(useInstr)
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not isInCycle(useInstr)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/** Gets a non-phi instruction that defines an operand of `instr`. */
private Instruction getNonPhiOperandDef(Instruction instr) {
result = Construction::getRegisterOperandDefinition(instr, _)
or
result = Construction::getMemoryOperandDefinition(instr, _, _)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
*
* If such cycles are present, either due to a programming error in the IR
* generation or due to a malformed database, it can cause infinite loops in
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
* better to remove these operands than to leave cycles in the operand graph.
*/
private predicate isInCycle(Instruction instr) {
getNonPhiOperandDef+(instr) = instr
}
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/

View File

@@ -8,15 +8,37 @@ private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not isInCycle(useInstr)
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not isInCycle(useInstr)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/** Gets a non-phi instruction that defines an operand of `instr`. */
private Instruction getNonPhiOperandDef(Instruction instr) {
result = Construction::getRegisterOperandDefinition(instr, _)
or
result = Construction::getMemoryOperandDefinition(instr, _, _)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
*
* If such cycles are present, either due to a programming error in the IR
* generation or due to a malformed database, it can cause infinite loops in
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
* better to remove these operands than to leave cycles in the operand graph.
*/
private predicate isInCycle(Instruction instr) {
getNonPhiOperandDef+(instr) = instr
}
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/

View File

@@ -8,15 +8,37 @@ private import semmle.code.cpp.ir.internal.OperandTag
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag)
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not isInCycle(useInstr)
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap)
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not isInCycle(useInstr)
} or
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/** Gets a non-phi instruction that defines an operand of `instr`. */
private Instruction getNonPhiOperandDef(Instruction instr) {
result = Construction::getRegisterOperandDefinition(instr, _)
or
result = Construction::getMemoryOperandDefinition(instr, _, _)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
*
* If such cycles are present, either due to a programming error in the IR
* generation or due to a malformed database, it can cause infinite loops in
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
* better to remove these operands than to leave cycles in the operand graph.
*/
private predicate isInCycle(Instruction instr) {
getNonPhiOperandDef+(instr) = instr
}
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/