C++: Stop caching raw IR construction predicates

These predicates are only used within the new single IR stage, so there's no need to cache them beyond that. RA diffs are trivial. Where previously many of the predicate on `Instruction` were inline wrappers around cached predicates from `IRConstruction`, now the predicates from `IRConstruction` get inlined into the `Instruction` predicates, and the `Instruction` predicates get materialized. The net amount of work is the same, but now it's not getting cached unnecessarily.
This commit is contained in:
Dave Bartolomeo
2020-06-17 09:47:48 -04:00
parent 8e977dc6bf
commit e85cc0b0c6

View File

@@ -169,234 +169,213 @@ module Raw {
} }
} }
import Cached class TStageInstruction = TRawInstruction;
cached predicate hasInstruction(TRawInstruction instr) { any() }
private module Cached {
class TStageInstruction = TRawInstruction;
cached predicate hasModeledMemoryResult(Instruction instruction) { none() }
predicate hasInstruction(TRawInstruction instr) { any() }
cached predicate hasConflatedMemoryResult(Instruction instruction) {
predicate hasModeledMemoryResult(Instruction instruction) { none() } instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
}
cached Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
predicate hasConflatedMemoryResult(Instruction instruction) { result =
instruction instanceof AliasedDefinitionInstruction getInstructionTranslatedElement(instruction)
or .getInstructionRegisterOperand(getInstructionTag(instruction), tag)
instruction.getOpcode() instanceof Opcode::InitializeNonLocal }
}
cached Instruction getMemoryOperandDefinition(
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) { Instruction instruction, MemoryOperandTag tag, Overlap overlap
result = ) {
getInstructionTranslatedElement(instruction) none()
.getInstructionRegisterOperand(getInstructionTag(instruction), tag) }
}
cached /** Gets a non-phi instruction that defines an operand of `instr`. */
Instruction getMemoryOperandDefinition( private Instruction getNonPhiOperandDef(Instruction instr) {
Instruction instruction, MemoryOperandTag tag, Overlap overlap result = getRegisterOperandDefinition(instr, _)
) { or
none() result = getMemoryOperandDefinition(instr, _, _)
} }
/** Gets a non-phi instruction that defines an operand of `instr`. */ /**
private Instruction getNonPhiOperandDef(Instruction instr) { * Gets a non-phi instruction that defines an operand of `instr` but only if
result = getRegisterOperandDefinition(instr, _) * both `instr` and the result have neighbor on the other side of the edge
or * between them. This is a necessary condition for being in a cycle, and it
result = getMemoryOperandDefinition(instr, _, _) * removes about two thirds of the tuples that would otherwise be in this
} * predicate.
*/
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
result = getNonPhiOperandDef(instr) and
exists(getNonPhiOperandDef(result)) and
instr = getNonPhiOperandDef(_)
}
/** /**
* Gets a non-phi instruction that defines an operand of `instr` but only if * Holds if `instr` is part of a cycle in the operand graph that doesn't go
* both `instr` and the result have neighbor on the other side of the edge * through a phi instruction and therefore should be impossible.
* between them. This is a necessary condition for being in a cycle, and it *
* removes about two thirds of the tuples that would otherwise be in this * If such cycles are present, either due to a programming error in the IR
* predicate. * 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
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) { * better to remove these operands than to leave cycles in the operand graph.
result = getNonPhiOperandDef(instr) and */
exists(getNonPhiOperandDef(result)) and pragma[noopt]
instr = getNonPhiOperandDef(_) predicate isInCycle(Instruction instr) {
} instr instanceof Instruction and
getNonPhiOperandDefOfIntermediate+(instr) = instr
}
/** CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
* Holds if `instr` is part of a cycle in the operand graph that doesn't go // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
* through a phi instruction and therefore should be impossible. // the result type of the load.
* tag instanceof LoadOperandTag and
* If such cycles are present, either due to a programming error in the IR result = instruction.(LoadInstruction).getResultLanguageType()
* generation or due to a malformed database, it can cause infinite loops in or
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's not instruction instanceof LoadInstruction and
* better to remove these operands than to leave cycles in the operand graph. result =
*/ getInstructionTranslatedElement(instruction)
pragma[noopt] .getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
cached }
predicate isInCycle(Instruction instr) {
instr instanceof Instruction and
getNonPhiOperandDefOfIntermediate+(instr) = instr
}
cached Instruction getPhiOperandDefinition(
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as ) {
// the result type of the load. none()
tag instanceof LoadOperandTag and }
result = instruction.(LoadInstruction).getResultLanguageType()
or
not instruction instanceof LoadInstruction and
result =
getInstructionTranslatedElement(instruction)
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
}
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
Instruction getPhiOperandDefinition(
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
) {
none()
}
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() } result =
getInstructionTranslatedElement(instruction)
.getInstructionSuccessor(getInstructionTag(instruction), kind)
}
cached /**
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
result = * `targetInstruction` is a back edge under the condition that
getInstructionTranslatedElement(instruction) * `requiredAncestor` is an ancestor of `sourceElement`.
.getInstructionSuccessor(getInstructionTag(instruction), kind) */
} private predicate backEdgeCandidate(
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
/** Instruction targetInstruction, EdgeKind kind
* Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`--> ) {
* `targetInstruction` is a back edge under the condition that // While loop:
* `requiredAncestor` is an ancestor of `sourceElement`. // Any edge from within the body of the loop to the condition of the loop
*/ // is a back edge. This includes edges from `continue` and the fall-through
private predicate backEdgeCandidate( // edge(s) after the last instruction(s) in the body.
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor, exists(TranslatedWhileStmt s |
Instruction targetInstruction, EdgeKind kind targetInstruction = s.getFirstConditionInstruction() and
) { targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
// While loop: requiredAncestor = s.getBody()
// Any edge from within the body of the loop to the condition of the loop )
// is a back edge. This includes edges from `continue` and the fall-through or
// edge(s) after the last instruction(s) in the body. // Do-while loop:
exists(TranslatedWhileStmt s | // The back edge should be the edge(s) from the condition to the
targetInstruction = s.getFirstConditionInstruction() and // body. This ensures that it's the back edge that will be pruned in a `do
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and // { ... } while (0)` statement. Note that all `continue` statements in a
// do-while loop produce forward edges.
exists(TranslatedDoStmt s |
targetInstruction = s.getBody().getFirstInstruction() and
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
requiredAncestor = s.getCondition()
)
or
// For loop:
// Any edge from within the body or update of the loop to the condition of
// the loop is a back edge. When there is no loop update expression, this
// includes edges from `continue` and the fall-through edge(s) after the
// last instruction(s) in the body. A for loop may not have a condition, in
// which case `getFirstConditionInstruction` returns the body instead.
exists(TranslatedForStmt s |
targetInstruction = s.getFirstConditionInstruction() and
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
(
requiredAncestor = s.getUpdate()
or
not exists(s.getUpdate()) and
requiredAncestor = s.getBody() requiredAncestor = s.getBody()
) )
or )
// Do-while loop: or
// The back edge should be the edge(s) from the condition to the // Range-based for loop:
// body. This ensures that it's the back edge that will be pruned in a `do // Any edge from within the update of the loop to the condition of
// { ... } while (0)` statement. Note that all `continue` statements in a // the loop is a back edge.
// do-while loop produce forward edges. exists(TranslatedRangeBasedForStmt s |
exists(TranslatedDoStmt s | targetInstruction = s.getCondition().getFirstInstruction() and
targetInstruction = s.getBody().getFirstInstruction() and targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and requiredAncestor = s.getUpdate()
requiredAncestor = s.getCondition() )
) }
or
// For loop:
// Any edge from within the body or update of the loop to the condition of
// the loop is a back edge. When there is no loop update expression, this
// includes edges from `continue` and the fall-through edge(s) after the
// last instruction(s) in the body. A for loop may not have a condition, in
// which case `getFirstConditionInstruction` returns the body instead.
exists(TranslatedForStmt s |
targetInstruction = s.getFirstConditionInstruction() and
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
(
requiredAncestor = s.getUpdate()
or
not exists(s.getUpdate()) and
requiredAncestor = s.getBody()
)
)
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 |
targetInstruction = s.getCondition().getFirstInstruction() and
targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
requiredAncestor = s.getUpdate()
)
}
private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) { private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
backEdgeCandidate(jumpSource, _, _, _, _) and backEdgeCandidate(jumpSource, _, _, _, _) and
ancestor = jumpSource ancestor = jumpSource
or or
// For performance, we don't want a fastTC here // For performance, we don't want a fastTC here
jumpSourceHasAncestor(jumpSource, ancestor.getAChild()) jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
} }
cached Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) { exists(
exists( TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor |
| backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and jumpSourceHasAncestor(sourceElement, requiredAncestor) and
jumpSourceHasAncestor(sourceElement, requiredAncestor) and instruction = sourceElement.getInstruction(sourceTag)
instruction = sourceElement.getInstruction(sourceTag) )
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
// source and target are both inside a macro will be seen as having the
// same location for source and target, so we conservatively assume that
// such a `goto` creates a back edge.
exists(TranslatedElement s, GotoStmt goto |
not isStrictlyForwardGoto(goto) and
goto = s.getAST() and
exists(InstructionTag tag |
result = s.getInstructionSuccessor(tag, kind) and
instruction = s.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
// source and target are both inside a macro will be seen as having the
// same location for source and target, so we conservatively assume that
// such a `goto` creates a back edge.
exists(TranslatedElement s, GotoStmt goto |
not isStrictlyForwardGoto(goto) and
goto = s.getAST() and
exists(InstructionTag tag |
result = s.getInstructionSuccessor(tag, kind) and
instruction = s.getInstruction(tag)
)
)
}
/** Holds if `goto` jumps strictly forward in the program text. */ /** Holds if `goto` jumps strictly forward in the program text. */
private predicate isStrictlyForwardGoto(GotoStmt goto) { private predicate isStrictlyForwardGoto(GotoStmt goto) {
goto.getLocation().isBefore(goto.getTarget().getLocation()) goto.getLocation().isBefore(goto.getTarget().getLocation())
} }
cached Locatable getInstructionAST(TStageInstruction instr) {
Locatable getInstructionAST(TStageInstruction instr) { result = getInstructionTranslatedElement(instr).getAST()
result = getInstructionTranslatedElement(instr).getAST() }
}
cached CppType getInstructionResultType(TStageInstruction instr) {
CppType getInstructionResultType(TStageInstruction instr) { exists(TranslatedElement element, InstructionTag tag |
exists(TranslatedElement element, InstructionTag tag | instructionOrigin(instr, element, tag) and
instructionOrigin(instr, element, tag) and element.hasInstruction(_, tag, result)
element.hasInstruction(_, tag, result) )
) }
}
cached Opcode getInstructionOpcode(TStageInstruction instr) {
Opcode getInstructionOpcode(TStageInstruction instr) { exists(TranslatedElement element, InstructionTag tag |
exists(TranslatedElement element, InstructionTag tag | instructionOrigin(instr, element, tag) and
instructionOrigin(instr, element, tag) and element.hasInstruction(result, tag, _)
element.hasInstruction(result, tag, _) )
) }
}
cached IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) { result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
result.getFunction() = getInstructionTranslatedElement(instr).getFunction() }
}
cached Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) { exists(TranslatedElement element, InstructionTag tag |
exists(TranslatedElement element, InstructionTag tag | instructionOrigin(instruction, element, tag) and
instructionOrigin(instruction, element, tag) and result = element.getPrimaryInstructionForSideEffect(tag)
result = element.getPrimaryInstructionForSideEffect(tag) )
)
}
} }
import CachedForDebugging import CachedForDebugging