mirror of
https://github.com/github/codeql.git
synced 2026-01-17 08:24:46 +01:00
Merge pull request #15838 from michaelnebel/csharp/deleteirqueries
C#: Remove IR queries.
This commit is contained in:
@@ -88,123 +88,46 @@
|
||||
"IR Instruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -252,8 +175,7 @@
|
||||
],
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"SSA PrintAliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
@@ -268,44 +190,28 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -333,38 +239,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRFunctionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
@@ -498,4 +372,4 @@
|
||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Most queries should operate on the aliased SSA IR, so that's what we expose
|
||||
* publicly as the "IR".
|
||||
*/
|
||||
|
||||
import implementation.unaliased_ssa.IR
|
||||
@@ -1 +0,0 @@
|
||||
import implementation.IRConfiguration
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name IR Consistency Check
|
||||
* @description Performs consistency checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind table
|
||||
* @id cs/ir-consistency-check
|
||||
*/
|
||||
|
||||
import implementation.raw.IRConsistency
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name Print IR
|
||||
* @description Outputs a representation of the IR graph
|
||||
* @id cs/print-ir
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import implementation.unaliased_ssa.PrintIR
|
||||
@@ -1 +0,0 @@
|
||||
import implementation.unaliased_ssa.PrintIR
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Temporary file that has stubs for various functionalities in the IR conversion.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
class ArrayInitWithMod extends ArrayInitializer {
|
||||
predicate isInitialized(int entry) { entry in [0 .. this.getNumberOfElements() - 1] }
|
||||
|
||||
predicate isValueInitialized(int elementIndex) {
|
||||
this.isInitialized(elementIndex) and
|
||||
not exists(this.getElement(elementIndex))
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectInitializerMod extends ObjectInitializer {
|
||||
private predicate isInitialized(Field field) {
|
||||
not field.isReadOnly() and // TODO: Is this the only instance whena field can not be init?
|
||||
this.getAMemberInitializer().getTargetVariable() = field
|
||||
}
|
||||
|
||||
predicate isValueInitialized(Field field) {
|
||||
this.isInitialized(field) and
|
||||
not field = this.getAMemberInitializer().getInitializedMember()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: See if we need to adapt this for C#
|
||||
abstract class SideEffectFunction extends Callable {
|
||||
/**
|
||||
* Holds if the function never reads from memory that was defined before entry to the function.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverReadsMemory();
|
||||
|
||||
/**
|
||||
* Holds if the function never writes to memory that remains allocated after the function
|
||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||
* from a pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverWritesMemory();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import implementation.unaliased_ssa.gvn.ValueNumbering
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* Provides classes that specify the conditions under which control flows along a given edge.
|
||||
*/
|
||||
|
||||
private import internal.EdgeKindInternal
|
||||
|
||||
private newtype TEdgeKind =
|
||||
TGotoEdge() or // Single successor (including fall-through)
|
||||
TTrueEdge() or // 'true' edge of conditional branch
|
||||
TFalseEdge() or // 'false' edge of conditional branch
|
||||
TExceptionEdge() or // Thrown exception
|
||||
TDefaultEdge() or // 'default' label of switch
|
||||
TCaseEdge(string minValue, string maxValue) {
|
||||
// Case label of switch
|
||||
Language::hasCaseEdge(minValue, maxValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the kind of an edge in the IR control flow graph. Each
|
||||
* `Instruction` or `IRBlock` has at most one successor of any single
|
||||
* `EdgeKind`.
|
||||
*/
|
||||
abstract class EdgeKind extends TEdgeKind {
|
||||
/** Gets a textual representation of this edge kind. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A "goto" edge, representing the unconditional successor of an `Instruction`
|
||||
* or `IRBlock`.
|
||||
*/
|
||||
class GotoEdge extends EdgeKind, TGotoEdge {
|
||||
final override string toString() { result = "Goto" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "true" edge, representing the successor of a conditional branch when the
|
||||
* condition is non-zero.
|
||||
*/
|
||||
class TrueEdge extends EdgeKind, TTrueEdge {
|
||||
final override string toString() { result = "True" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "false" edge, representing the successor of a conditional branch when the
|
||||
* condition is zero.
|
||||
*/
|
||||
class FalseEdge extends EdgeKind, TFalseEdge {
|
||||
final override string toString() { result = "False" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An "exception" edge, representing the successor of an instruction when that
|
||||
* instruction's evaluation throws an exception.
|
||||
*/
|
||||
class ExceptionEdge extends EdgeKind, TExceptionEdge {
|
||||
final override string toString() { result = "Exception" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "default" edge, representing the successor of a `Switch` instruction when
|
||||
* none of the case values matches the condition value.
|
||||
*/
|
||||
class DefaultEdge extends EdgeKind, TDefaultEdge {
|
||||
final override string toString() { result = "Default" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A "case" edge, representing the successor of a `Switch` instruction when the
|
||||
* the condition value matches a corresponding `case` label.
|
||||
*/
|
||||
class CaseEdge extends EdgeKind, TCaseEdge {
|
||||
string minValue;
|
||||
string maxValue;
|
||||
|
||||
CaseEdge() { this = TCaseEdge(minValue, maxValue) }
|
||||
|
||||
final override string toString() {
|
||||
if minValue = maxValue
|
||||
then result = "Case[" + minValue + "]"
|
||||
else result = "Case[" + minValue + ".." + maxValue + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the smallest value of the switch expression for which control will flow along this edge.
|
||||
*/
|
||||
final string getMinValue() { result = minValue }
|
||||
|
||||
/**
|
||||
* Gets the largest value of the switch expression for which control will flow along this edge.
|
||||
*/
|
||||
final string getMaxValue() { result = maxValue }
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicates to access the single instance of each `EdgeKind` class.
|
||||
*/
|
||||
module EdgeKind {
|
||||
/**
|
||||
* Gets the single instance of the `GotoEdge` class.
|
||||
*/
|
||||
GotoEdge gotoEdge() { result = TGotoEdge() }
|
||||
|
||||
/**
|
||||
* Gets the single instance of the `TrueEdge` class.
|
||||
*/
|
||||
TrueEdge trueEdge() { result = TTrueEdge() }
|
||||
|
||||
/**
|
||||
* Gets the single instance of the `FalseEdge` class.
|
||||
*/
|
||||
FalseEdge falseEdge() { result = TFalseEdge() }
|
||||
|
||||
/**
|
||||
* Gets the single instance of the `ExceptionEdge` class.
|
||||
*/
|
||||
ExceptionEdge exceptionEdge() { result = TExceptionEdge() }
|
||||
|
||||
/**
|
||||
* Gets the single instance of the `DefaultEdge` class.
|
||||
*/
|
||||
DefaultEdge defaultEdge() { result = TDefaultEdge() }
|
||||
|
||||
/**
|
||||
* Gets the `CaseEdge` representing a `case` label with the specified lower and upper bounds.
|
||||
* For example:
|
||||
* ```
|
||||
* switch (x) {
|
||||
* case 1: // Edge kind is `caseEdge("1", "1")`
|
||||
* return x;
|
||||
* case 2...8: // Edge kind is `caseEdge("2", "8")`
|
||||
* return x - 1;
|
||||
* default: // Edge kind is `defaultEdge()`
|
||||
* return 0;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
CaseEdge caseEdge(string minValue, string maxValue) { result = TCaseEdge(minValue, maxValue) }
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Module used to configure the IR generation process.
|
||||
*/
|
||||
|
||||
private import internal.IRConfigurationInternal
|
||||
|
||||
private newtype TIRConfiguration = MkIRConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which functions have IR generated for them.
|
||||
*/
|
||||
class IRConfiguration extends TIRConfiguration {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "IRConfiguration" }
|
||||
|
||||
/**
|
||||
* Holds if IR should be created for function `func`. By default, holds for all functions.
|
||||
*/
|
||||
predicate shouldCreateIRForFunction(Language::Declaration func) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the strings used as part of an IR dump should be generated for function `func`.
|
||||
*
|
||||
* This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number
|
||||
* of debug strings for IR that will not be dumped. We still generate the actual IR for these
|
||||
* functions, however, to preserve the results of any interprocedural analysis.
|
||||
*/
|
||||
predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { any() }
|
||||
}
|
||||
|
||||
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control what escape analysis is used when generating SSA.
|
||||
*/
|
||||
class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "IREscapeAnalysisConfiguration" }
|
||||
|
||||
/**
|
||||
* Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is
|
||||
* built assuming that no variable's address ever escapes.
|
||||
*/
|
||||
predicate useSoundEscapeAnalysis() { none() }
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
/**
|
||||
* Minimal, language-neutral type system for the IR.
|
||||
*/
|
||||
|
||||
private import internal.IRTypeInternal
|
||||
|
||||
cached
|
||||
private newtype TIRType =
|
||||
TIRVoidType() or
|
||||
TIRUnknownType() or
|
||||
TIRErrorType() { Language::hasErrorType() } or
|
||||
TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or
|
||||
TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or
|
||||
TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or
|
||||
TIRFloatingPointType(int byteSize, int base, Language::TypeDomain domain) {
|
||||
Language::hasFloatingPointType(byteSize, base, domain)
|
||||
} or
|
||||
TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
|
||||
TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
|
||||
TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
|
||||
Language::hasOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`.
|
||||
* The interface to `IRType` and its subclasses is the same across all languages for which the IR
|
||||
* is supported, so analyses that expect to be used for multiple languages should generally use
|
||||
* `IRType` rather than a language-specific type.
|
||||
*
|
||||
* Many types from the language-specific type system will map to a single canonical `IRType`. Two
|
||||
* types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++,
|
||||
* all pointer types map to the same instance of `IRAddressType`.
|
||||
*/
|
||||
class IRType extends TIRType {
|
||||
/** Gets a textual representation of this type. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this `IRType`. This string is often the same as the
|
||||
* result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness.
|
||||
*/
|
||||
string getIdentityString() { result = this.toString() }
|
||||
|
||||
/**
|
||||
* Gets the size of the type, in bytes, if known.
|
||||
*
|
||||
* This will hold for all `IRType` objects except `IRUnknownType`.
|
||||
*/
|
||||
// This predicate is overridden with `pragma[noinline]` in every leaf subclass.
|
||||
// This allows callers to ask for things like _the_ floating-point type of
|
||||
// size 4 without getting a join that first finds all types of size 4 and
|
||||
// _then_ restricts them to floating-point types.
|
||||
int getByteSize() { none() }
|
||||
|
||||
/**
|
||||
* Gets a single instance of `LanguageType` that maps to this `IRType`.
|
||||
*/
|
||||
Language::LanguageType getCanonicalLanguageType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unknown type. Generally used to represent results and operands that access an unknown set of
|
||||
* memory locations, such as the side effects of a function call.
|
||||
*/
|
||||
class IRUnknownType extends IRType, TIRUnknownType {
|
||||
final override string toString() { result = "unknown" }
|
||||
|
||||
final override int getByteSize() { none() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A void type, which has no values. Used to represent the result type of an instruction that does
|
||||
* not produce a result.
|
||||
*/
|
||||
class IRVoidType extends IRType, TIRVoidType {
|
||||
final override string toString() { result = "void" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalVoidType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error type. Used when an error in the source code prevents the extractor from determining the
|
||||
* proper type.
|
||||
*/
|
||||
class IRErrorType extends IRType, TIRErrorType {
|
||||
final override string toString() { result = "error" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalErrorType()
|
||||
}
|
||||
}
|
||||
|
||||
private class IRSizedType extends IRType {
|
||||
int byteSize;
|
||||
|
||||
IRSizedType() {
|
||||
this = TIRBooleanType(byteSize) or
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _) or
|
||||
this = TIRAddressType(byteSize) or
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean type, which can hold the values `true` (non-zero) or `false` (zero).
|
||||
*/
|
||||
class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
final override string toString() { result = "bool" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalBooleanType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
IRNumericType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize, _, _)
|
||||
}
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
|
||||
*/
|
||||
class IRIntegerType extends IRNumericType {
|
||||
IRIntegerType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize)
|
||||
}
|
||||
|
||||
/** Holds if this integer type is signed. */
|
||||
predicate isSigned() { none() }
|
||||
|
||||
/** Holds if this integer type is unsigned. */
|
||||
predicate isUnsigned() { none() }
|
||||
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
|
||||
// overridden only in the leaf classes.
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalSignedIntegerType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
|
||||
override predicate isSigned() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnsignedIntegerType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
|
||||
override predicate isUnsigned() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type.
|
||||
*/
|
||||
class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
|
||||
final private int base;
|
||||
final private Language::TypeDomain domain;
|
||||
|
||||
IRFloatingPointType() { this = TIRFloatingPointType(_, base, domain) }
|
||||
|
||||
final override string toString() {
|
||||
result = this.getDomainPrefix() + this.getBaseString() + byteSize.toString()
|
||||
}
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFloatingPointType(byteSize, base, domain)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
|
||||
/** Gets the numeric base of the type. Can be either 2 (binary) or 10 (decimal). */
|
||||
final int getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Gets the type domain of the type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
|
||||
*/
|
||||
final Language::TypeDomain getDomain() { result = domain }
|
||||
|
||||
private string getBaseString() {
|
||||
base = 2 and result = "float"
|
||||
or
|
||||
base = 10 and result = "decimal"
|
||||
}
|
||||
|
||||
private string getDomainPrefix() {
|
||||
domain instanceof Language::RealDomain and result = ""
|
||||
or
|
||||
domain instanceof Language::ComplexDomain and result = "c"
|
||||
or
|
||||
domain instanceof Language::ImaginaryDomain and result = "i"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of data. Used to represent pointers, references,
|
||||
* and lvalues, include those that are garbage collected.
|
||||
*
|
||||
* The address of a function is represented by the separate `IRFunctionAddressType`.
|
||||
*/
|
||||
class IRAddressType extends IRSizedType, TIRAddressType {
|
||||
final override string toString() { result = "addr" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalAddressType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of code. Used to represent function pointers,
|
||||
* function references, and the target of a direct function call.
|
||||
*/
|
||||
class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType {
|
||||
final override string toString() { result = "func" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFunctionAddressType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type with known size that does not fit any of the other kinds of type. Used to represent
|
||||
* classes, structs, unions, fixed-size arrays, pointers-to-member, and more.
|
||||
*/
|
||||
class IROpaqueType extends IRSizedType, TIROpaqueType {
|
||||
Language::OpaqueTypeTag tag;
|
||||
|
||||
IROpaqueType() { this = TIROpaqueType(tag, byteSize) }
|
||||
|
||||
final override string toString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}"
|
||||
}
|
||||
|
||||
final override string getIdentityString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}"
|
||||
}
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "tag" that differentiates this type from other incompatible opaque types that have the
|
||||
* same size.
|
||||
*/
|
||||
final Language::OpaqueTypeTag getTag() { result = tag }
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
* Query predicates used to check invariants that should hold for all `IRType` objects. To run all
|
||||
* consistency queries for the IR, including the ones below, run
|
||||
* "semmle/code/cpp/IR/IRConsistency.ql".
|
||||
*/
|
||||
module IRTypeConsistency {
|
||||
/**
|
||||
* Holds if the type has no result for `IRType.getCanonicalLanguageType()`.
|
||||
*/
|
||||
query predicate missingCanonicalLanguageType(IRType type, string message) {
|
||||
not exists(type.getCanonicalLanguageType()) and
|
||||
message = "Type does not have a canonical `LanguageType`"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type has more than one result for `IRType.getCanonicalLanguageType()`.
|
||||
*/
|
||||
query predicate multipleCanonicalLanguageTypes(IRType type, string message) {
|
||||
strictcount(type.getCanonicalLanguageType()) > 1 and
|
||||
message =
|
||||
"Type has multiple canonical `LanguageType`s: " +
|
||||
concat(type.getCanonicalLanguageType().toString(), ", ")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type has no result for `LanguageType.getIRType()`.
|
||||
*/
|
||||
query predicate missingIRType(Language::LanguageType type, string message) {
|
||||
not exists(type.getIRType()) and
|
||||
message = "`LanguageType` does not have a corresponding `IRType`."
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type has more than one result for `LanguageType.getIRType()`.
|
||||
*/
|
||||
query predicate multipleIRTypes(Language::LanguageType type, string message) {
|
||||
strictcount(type.getIRType()) > 1 and
|
||||
message =
|
||||
"`LanguageType` " + type + " has multiple `IRType`s: " +
|
||||
concat(type.getIRType().toString(), ", ")
|
||||
}
|
||||
|
||||
import Language::LanguageTypeConsistency
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Provides classes that describe how a particular `Instruction` or its operands access memory.
|
||||
*/
|
||||
|
||||
private import IRConfiguration
|
||||
|
||||
private newtype TMemoryAccessKind =
|
||||
TIndirectMemoryAccess() or
|
||||
TBufferMemoryAccess() or
|
||||
TEntireAllocationMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TNonLocalMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
TChiPartialMemoryAccess()
|
||||
|
||||
/**
|
||||
* Describes the set of memory locations memory accessed by a memory operand or
|
||||
* memory result.
|
||||
*/
|
||||
class MemoryAccessKind extends TMemoryAccessKind {
|
||||
/** Gets a textual representation of this access kind. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the operand or result accesses memory pointed to by the `AddressOperand` on the
|
||||
* same instruction.
|
||||
*/
|
||||
predicate usesAddressOperand() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory at the address specified by the `AddressOperand` on the
|
||||
* same instruction.
|
||||
*/
|
||||
class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
override string toString() { result = "indirect" }
|
||||
|
||||
final override predicate usesAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory starting at the address specified by the `AddressOperand`
|
||||
* on the same instruction, accessing a number of consecutive elements given by the
|
||||
* `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMemoryAccess extends MemoryAccessKind, TBufferMemoryAccess {
|
||||
override string toString() { result = "buffer" }
|
||||
|
||||
final override predicate usesAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or results accesses all memory in the contiguous allocation that contains the address
|
||||
* specified by the `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class EntireAllocationMemoryAccess extends MemoryAccessKind, TEntireAllocationMemoryAccess {
|
||||
override string toString() { result = "alloc" }
|
||||
|
||||
final override predicate usesAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result accesses all memory whose address has escaped.
|
||||
*/
|
||||
class EscapedMemoryAccess extends MemoryAccessKind, TEscapedMemoryAccess {
|
||||
override string toString() { result = "escaped" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result access all memory whose address has escaped, other than data on the stack
|
||||
* frame of the current function.
|
||||
*/
|
||||
class NonLocalMemoryAccess extends MemoryAccessKind, TNonLocalMemoryAccess {
|
||||
override string toString() { result = "nonlocal" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a Phi operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class PhiMemoryAccess extends MemoryAccessKind, TPhiMemoryAccess {
|
||||
override string toString() { result = "phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiTotal operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
|
||||
override string toString() { result = "chi(total)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a ChiPartial operand, which accesses the same memory as its
|
||||
* definition.
|
||||
*/
|
||||
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
|
||||
override string toString() { result = "chi(partial)" }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Defines the public interface to temporary variable tags, which describe the reason a particular
|
||||
* `IRTempVariable` was generated.
|
||||
*/
|
||||
|
||||
private import internal.TempVariableTagInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
/**
|
||||
* A reason that a particular IR temporary variable was generated. For example, it could be
|
||||
* generated to hold the return value of a function, or to hold the result of a `?:` operator
|
||||
* computed on each branch. The set of possible `TempVariableTag`s is language-dependent.
|
||||
*/
|
||||
class TempVariableTag extends TTempVariableTag {
|
||||
/** Gets a textual representation of this tag. */
|
||||
string toString() { result = getTempVariableTagId(this) }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import IRConfiguration
|
||||
|
||||
/**
|
||||
* Overrides the default IR configuration to use sound escape analysis, instead of assuming that
|
||||
* variable addresses never escape.
|
||||
*/
|
||||
class SoundEscapeAnalysisConfiguration extends IREscapeAnalysisConfiguration {
|
||||
override predicate useSoundEscapeAnalysis() { any() }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Provides a stub implementation of the required aliased SSA interface until we implement aliased
|
||||
* SSA construction for C#.
|
||||
*/
|
||||
|
||||
private import IRFunctionBase
|
||||
private import TInstruction
|
||||
|
||||
module Ssa {
|
||||
class MemoryLocation = boolean;
|
||||
|
||||
predicate hasPhiInstruction(TRawInstruction blockStartInstr, MemoryLocation memoryLocation) {
|
||||
none()
|
||||
}
|
||||
|
||||
predicate hasChiInstruction(TRawInstruction primaryInstruction) { none() }
|
||||
|
||||
predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() }
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
|
||||
*/
|
||||
|
||||
private import IRFunctionBaseInternal
|
||||
|
||||
private newtype TIRFunction =
|
||||
TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
|
||||
TVarInitIRFunction(Language::Variable var) { IRConstruction::Raw::varHasIRFunc(var) }
|
||||
|
||||
/**
|
||||
* The IR for a function. This base class contains only the predicates that are the same between all
|
||||
* phases of the IR. Each instantiation of `IRFunction` extends this class.
|
||||
*/
|
||||
class IRFunctionBase extends TIRFunction {
|
||||
Language::Declaration decl;
|
||||
|
||||
IRFunctionBase() {
|
||||
this = TFunctionIRFunction(decl)
|
||||
or
|
||||
this = TVarInitIRFunction(decl)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = "IR: " + decl.toString() }
|
||||
|
||||
/** Gets the function whose IR is represented. */
|
||||
final Language::Declaration getFunction() { result = decl }
|
||||
|
||||
/** Gets the location of the function. */
|
||||
final Language::Location getLocation() { result = decl.getLocation() }
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
@@ -1,300 +0,0 @@
|
||||
/**
|
||||
* Defines the set of possible `OperandTag`s, which are used to identify the role each `Operand`
|
||||
* plays in the evaluation of its `Instruction`.
|
||||
*/
|
||||
|
||||
private import OperandTagInternal
|
||||
|
||||
private newtype TOperandTag =
|
||||
TAddressOperand() or
|
||||
TBufferSizeOperand() or
|
||||
TSideEffectOperand() or
|
||||
TLoadOperand() or
|
||||
TStoreValueOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
TRightOperand() or
|
||||
TConditionOperand() or
|
||||
TCallTargetOperand() or
|
||||
TThisArgumentOperand() or
|
||||
TPositionalArgumentOperand(int argIndex) { Language::hasPositionalArgIndex(argIndex) } or
|
||||
TChiTotalOperand() or
|
||||
TChiPartialOperand() or
|
||||
TAsmOperand(int index) { Language::hasAsmOperandIndex(index) }
|
||||
|
||||
/**
|
||||
* Identifies the kind of operand on an instruction. Each `Instruction` has at
|
||||
* most one operand of any single `OperandTag`. The set of `OperandTag`s used by
|
||||
* an `Instruction` is determined by the instruction's opcode.
|
||||
*/
|
||||
abstract class OperandTag extends TOperandTag {
|
||||
/** Gets a textual representation of this operand tag */
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets an integer that represents where this this operand will appear in the operand list of an
|
||||
* instruction when the IR is printed.
|
||||
*/
|
||||
abstract int getSortOrder();
|
||||
|
||||
/**
|
||||
* Gets a label that will appear before the operand when the IR is printed.
|
||||
*/
|
||||
final string getLabel() {
|
||||
if this.alwaysPrintLabel() then result = this.getId() + ":" else result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an identifier that uniquely identifies this operand within its instruction.
|
||||
*/
|
||||
abstract string getId();
|
||||
|
||||
/**
|
||||
* Holds if the operand should always be prefixed with its label in the dump of its instruction.
|
||||
*/
|
||||
predicate alwaysPrintLabel() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
abstract class MemoryOperandTag extends OperandTag { }
|
||||
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
abstract class RegisterOperandTag extends OperandTag { }
|
||||
|
||||
/**
|
||||
* A memory operand whose type may be different from the result type of its definition instruction.
|
||||
*/
|
||||
abstract class TypedOperandTag extends MemoryOperandTag { }
|
||||
|
||||
// Note: individual subtypes are listed in the order that the operands should
|
||||
// appear in the operand list of the instruction when the IR is printed.
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`, `InitializeParameter`, `IndirectReadSideEffect`).
|
||||
*/
|
||||
class AddressOperandTag extends RegisterOperandTag, TAddressOperand {
|
||||
final override string toString() { result = "Address" }
|
||||
|
||||
final override int getSortOrder() { result = 0 }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = "&" }
|
||||
}
|
||||
|
||||
AddressOperandTag addressOperand() { result = TAddressOperand() }
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
|
||||
final override string toString() { result = "BufferSize" }
|
||||
|
||||
final override int getSortOrder() { result = 1 }
|
||||
|
||||
final override string getId() { result = "size" }
|
||||
}
|
||||
|
||||
BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
|
||||
|
||||
/**
|
||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||
*/
|
||||
class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand {
|
||||
final override string toString() { result = "SideEffect" }
|
||||
|
||||
final override int getSortOrder() { result = 2 }
|
||||
|
||||
final override string getId() { result = "side_effect" }
|
||||
}
|
||||
|
||||
SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() }
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
*/
|
||||
class LoadOperandTag extends TypedOperandTag, TLoadOperand {
|
||||
final override string toString() { result = "Load" }
|
||||
|
||||
final override int getSortOrder() { result = 3 }
|
||||
|
||||
final override string getId() { result = "load" }
|
||||
}
|
||||
|
||||
LoadOperandTag loadOperand() { result = TLoadOperand() }
|
||||
|
||||
/**
|
||||
* The source value operand of a `Store` instruction.
|
||||
*/
|
||||
class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand {
|
||||
final override string toString() { result = "StoreValue" }
|
||||
|
||||
final override int getSortOrder() { result = 4 }
|
||||
|
||||
final override string getId() { result = "store" }
|
||||
}
|
||||
|
||||
StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() }
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
|
||||
*/
|
||||
class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand {
|
||||
final override string toString() { result = "Unary" }
|
||||
|
||||
final override int getSortOrder() { result = 5 }
|
||||
|
||||
final override string getId() { result = "unary" }
|
||||
}
|
||||
|
||||
UnaryOperandTag unaryOperand() { result = TUnaryOperand() }
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperandTag extends RegisterOperandTag, TLeftOperand {
|
||||
final override string toString() { result = "Left" }
|
||||
|
||||
final override int getSortOrder() { result = 6 }
|
||||
|
||||
final override string getId() { result = "left" }
|
||||
}
|
||||
|
||||
LeftOperandTag leftOperand() { result = TLeftOperand() }
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperandTag extends RegisterOperandTag, TRightOperand {
|
||||
final override string toString() { result = "Right" }
|
||||
|
||||
final override int getSortOrder() { result = 7 }
|
||||
|
||||
final override string getId() { result = "right" }
|
||||
}
|
||||
|
||||
RightOperandTag rightOperand() { result = TRightOperand() }
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperandTag extends RegisterOperandTag, TConditionOperand {
|
||||
final override string toString() { result = "Condition" }
|
||||
|
||||
final override int getSortOrder() { result = 8 }
|
||||
|
||||
final override string getId() { result = "cond" }
|
||||
}
|
||||
|
||||
ConditionOperandTag conditionOperand() { result = TConditionOperand() }
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand {
|
||||
final override string toString() { result = "CallTarget" }
|
||||
|
||||
final override int getSortOrder() { result = 10 }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = "func" }
|
||||
}
|
||||
|
||||
CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() }
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
abstract class ArgumentOperandTag extends RegisterOperandTag { }
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand {
|
||||
ThisArgumentOperandTag() { this = TThisArgumentOperand() }
|
||||
|
||||
final override string toString() { result = "Arg(this)" }
|
||||
|
||||
final override int getSortOrder() { result = 11 }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = "this" }
|
||||
}
|
||||
|
||||
ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() }
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperandTag() { this = TPositionalArgumentOperand(argIndex) }
|
||||
|
||||
final override string toString() { result = "Arg(" + argIndex + ")" }
|
||||
|
||||
final override int getSortOrder() { result = 12 + argIndex }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final int getArgIndex() { result = argIndex }
|
||||
|
||||
final override string getId() { result = argIndex.toString() }
|
||||
}
|
||||
|
||||
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
abstract class ChiOperandTag extends MemoryOperandTag { }
|
||||
|
||||
class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand {
|
||||
final override string toString() { result = "ChiTotal" }
|
||||
|
||||
final override int getSortOrder() { result = 13 }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = "total" }
|
||||
}
|
||||
|
||||
ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() }
|
||||
|
||||
class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand {
|
||||
final override string toString() { result = "ChiPartial" }
|
||||
|
||||
final override int getSortOrder() { result = 14 }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = "partial" }
|
||||
}
|
||||
|
||||
ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() }
|
||||
|
||||
class AsmOperandTag extends RegisterOperandTag, TAsmOperand {
|
||||
int index;
|
||||
|
||||
AsmOperandTag() { this = TAsmOperand(index) }
|
||||
|
||||
final override string toString() { result = "AsmOperand(" + index + ")" }
|
||||
|
||||
final override int getSortOrder() { result = 15 + index }
|
||||
|
||||
final override predicate alwaysPrintLabel() { any() }
|
||||
|
||||
final override string getId() { result = index.toString() }
|
||||
}
|
||||
|
||||
AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) }
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -1,23 +0,0 @@
|
||||
private import TIRVariableInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
newtype TIRVariable =
|
||||
TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Declaration func) {
|
||||
Construction::hasUserVariable(func, var, type)
|
||||
} or
|
||||
TIRTempVariable(
|
||||
Language::Declaration func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasTempVariable(func, ast, tag, type)
|
||||
} or
|
||||
TIRDynamicInitializationFlag(
|
||||
Language::Declaration func, Language::Variable var, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasDynamicInitializationFlag(func, var, type)
|
||||
} or
|
||||
TIRStringLiteral(
|
||||
Language::Declaration func, Language::AST ast, Language::LanguageType type,
|
||||
Language::StringLiteral literal
|
||||
) {
|
||||
Construction::hasStringLiteral(func, ast, type, literal)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction::Raw as Construction
|
||||
private import experimental.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
module TempVariableTag = TempVariableTag_;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
private import TInstructionInternal
|
||||
private import IRFunctionBase
|
||||
private import TInstructionImports as Imports
|
||||
private import Imports::IRType
|
||||
private import Imports::Opcode
|
||||
|
||||
/**
|
||||
* An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
|
||||
* branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
|
||||
* instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
|
||||
* `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
|
||||
* all of the branches that can appear in that particular stage. The public `Instruction` class for
|
||||
* each phase extends the `TStageInstruction` type for that stage.
|
||||
*/
|
||||
cached
|
||||
newtype TInstruction =
|
||||
TRawInstruction(
|
||||
IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
|
||||
) {
|
||||
IRConstruction::Raw::hasInstruction(tag1, tag2)
|
||||
} or
|
||||
TRawUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
IRConstruction::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TUnaliasedSsaPhiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
UnaliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TUnaliasedSsaChiInstruction(TRawInstruction primaryInstruction) { none() } or
|
||||
TUnaliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
UnaliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
} or
|
||||
TAliasedSsaPhiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
AliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation)
|
||||
} or
|
||||
TAliasedSsaChiInstruction(TRawInstruction primaryInstruction) {
|
||||
AliasedSsa::Ssa::hasChiInstruction(primaryInstruction)
|
||||
} or
|
||||
TAliasedSsaUnreachedInstruction(IRFunctionBase irFunc) {
|
||||
AliasedSsa::Ssa::hasUnreachedInstruction(irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSsaInstructions {
|
||||
class TPhiInstruction = TUnaliasedSsaPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TUnaliasedSsaPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TRawInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) { none() }
|
||||
|
||||
class TChiInstruction = TUnaliasedSsaChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TUnaliasedSsaChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TUnaliasedSsaUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TUnaliasedSsaUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
|
||||
* aliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSsaInstructions {
|
||||
class TPhiInstruction = TAliasedSsaPhiInstruction or TUnaliasedSsaPhiInstruction;
|
||||
|
||||
TPhiInstruction phiInstruction(
|
||||
TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation
|
||||
) {
|
||||
result = TAliasedSsaPhiInstruction(blockStartInstr, memoryLocation)
|
||||
}
|
||||
|
||||
TPhiInstruction reusedPhiInstruction(TRawInstruction blockStartInstr) {
|
||||
result = TUnaliasedSsaPhiInstruction(blockStartInstr, _)
|
||||
}
|
||||
|
||||
class TChiInstruction = TAliasedSsaChiInstruction;
|
||||
|
||||
TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
|
||||
result = TAliasedSsaChiInstruction(primaryInstruction)
|
||||
}
|
||||
|
||||
class TUnreachedInstruction = TAliasedSsaUnreachedInstruction;
|
||||
|
||||
TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
|
||||
result = TAliasedSsaUnreachedInstruction(irFunc)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.implementation.Opcode as Opcode
|
||||
@@ -1,4 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import experimental.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSsa
|
||||
import AliasedSSAStub as AliasedSsa
|
||||
@@ -1,150 +0,0 @@
|
||||
private import TInstruction
|
||||
private import OperandTag
|
||||
private import experimental.ir.implementation.raw.internal.IRConstruction as RawConstruction
|
||||
private import experimental.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedConstruction
|
||||
private import experimental.ir.implementation.raw.IR as Raw
|
||||
private import experimental.ir.implementation.unaliased_ssa.IR as Unaliased
|
||||
private import experimental.ir.internal.Overlap
|
||||
|
||||
/**
|
||||
* Provides the newtype used to represent operands across all phases of the IR.
|
||||
*/
|
||||
private module Internal {
|
||||
/**
|
||||
* An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
|
||||
* type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
|
||||
* for operands computed by each stage of SSA construction (`T*PhiOperand` and
|
||||
* `TAliasedChiOperand`), and a placehold branch for operands that do not exist in a given
|
||||
* stage of IR construction (`TNoOperand`).
|
||||
*/
|
||||
cached
|
||||
newtype TOperand =
|
||||
// RAW
|
||||
TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
|
||||
defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not RawConstruction::isInCycle(useInstr) and
|
||||
strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
// Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
|
||||
TNoOperand() { none() } or
|
||||
// Can be "removed" later when there's unreachable code
|
||||
// These operands can be reused across all three stages. They just get different defs.
|
||||
TNonSsaMemoryOperand(Raw::Instruction useInstr, MemoryOperandTag tag) {
|
||||
// Has no definition in raw but will get definitions later
|
||||
useInstr.getOpcode().hasOperand(tag)
|
||||
} or
|
||||
TUnaliasedPhiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reexports some branches from `TOperand` so they can be used in stage modules without importing
|
||||
* `TOperand` itself.
|
||||
*/
|
||||
private module Shared {
|
||||
class TRegisterOperand = Internal::TRegisterOperand;
|
||||
|
||||
/**
|
||||
* Returns the register operand with the specified parameters.
|
||||
*/
|
||||
TRegisterOperand registerOperand(
|
||||
TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr
|
||||
) {
|
||||
result = Internal::TRegisterOperand(useInstr, tag, defInstr)
|
||||
}
|
||||
|
||||
class TNonSsaMemoryOperand = Internal::TNonSsaMemoryOperand;
|
||||
|
||||
/**
|
||||
* Returns the non-Phi memory operand with the specified parameters.
|
||||
*/
|
||||
TNonSsaMemoryOperand nonSsaMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
|
||||
result = Internal::TNonSsaMemoryOperand(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* raw IR stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module RawOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TNoOperand;
|
||||
|
||||
class TChiOperand = Internal::TNoOperand;
|
||||
|
||||
class TNonPhiMemoryOperand = TNonSsaMemoryOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
|
||||
Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
|
||||
Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(Raw::Instruction useInstr, ChiOperandTag tag) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSsaOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TUnaliasedPhiOperand;
|
||||
|
||||
class TChiOperand = Internal::TNoOperand;
|
||||
|
||||
class TNonPhiMemoryOperand = TNonSsaMemoryOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand reusedPhiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import experimental.ir.internal.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
module TempVariableTag = TempVariableTag_;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Provides classes that describe the Intermediate Representation (IR) of the program.
|
||||
*
|
||||
* The IR is a representation of the semantics of the program, with very little dependence on the
|
||||
* syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
|
||||
* and `++i` all have the same semantic effect, but appear in the AST as three different types of
|
||||
* `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
|
||||
* operations similar to:
|
||||
*
|
||||
* ```
|
||||
* r1(int*) = VariableAddress[i] // Compute the address of variable `i`
|
||||
* r2(int) = Load &:r1, m0 // Load the value of `i`
|
||||
* r3(int) = Constant[1] // An integer constant with the value `1`
|
||||
* r4(int) = Add r2, r3 // Add `1` to the value of `i`
|
||||
* r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
|
||||
* ```
|
||||
*
|
||||
* This allows IR-based analysis to focus on the fundamental operations, rather than having to be
|
||||
* concerned with the various ways of expressing those operations in source code.
|
||||
*
|
||||
* The key classes in the IR are:
|
||||
*
|
||||
* - `IRFunction` - Contains the IR for an entire function definition, including all of that
|
||||
* function's `Instruction`s, `IRBlock`s, and `IRVariables`.
|
||||
* - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
|
||||
* performed, the operands that produce the inputs to that operation, and the type of the result
|
||||
* of the operation. Control flows from an `Instruction` to one of a set of successor
|
||||
* `Instruction`s.
|
||||
* - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
|
||||
* represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
|
||||
* a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
|
||||
* that produces its value (its "definition").
|
||||
* - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
|
||||
* created for each variable directly accessed by the function. In addition, `IRVariable`s are
|
||||
* created to represent certain temporary storage locations that do not have explicitly declared
|
||||
* variables in the source code, such as the return value of the function.
|
||||
* - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
|
||||
* sequence of instructions such that control flow can only enter the block at the first
|
||||
* instruction, and can only leave the block from the last instruction.
|
||||
* - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
|
||||
* is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
|
||||
* be represented as the `IRType` `uint4`, a four-byte unsigned integer.
|
||||
*/
|
||||
|
||||
import IRFunction
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* A class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "IRPropertyProvider" }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified operand.
|
||||
*/
|
||||
string getOperandProperty(Operand operand, string key) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction `instr` should be included when printing
|
||||
* the IR instructions.
|
||||
*/
|
||||
predicate shouldPrintInstruction(Instruction instr) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the operand `operand` should be included when printing the an
|
||||
* instruction's operand list.
|
||||
*/
|
||||
predicate shouldPrintOperand(Operand operand) { any() }
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
/**
|
||||
* Provides classes describing basic blocks in the IR of a function.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
* sequence.
|
||||
*
|
||||
* This class does not contain any members that query the predecessor or successor edges of the
|
||||
* block. This allows different classes that extend `IRBlockBase` to expose different subsets of
|
||||
* edges (e.g. ignoring unreachable edges).
|
||||
*
|
||||
* Most consumers should use the class `IRBlock`.
|
||||
*/
|
||||
class IRBlockBase extends TIRBlock {
|
||||
/** Gets a textual representation of this block. */
|
||||
final string toString() { result = getFirstInstruction(this).toString() }
|
||||
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the zero-based index of the block within its function.
|
||||
*
|
||||
* This predicate is used by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `index`th non-`Phi` instruction in this block.
|
||||
*/
|
||||
final Instruction getInstruction(int index) { result = getInstruction(this, index) }
|
||||
|
||||
/**
|
||||
* Get the `Phi` instructions that appear at the start of this block.
|
||||
*/
|
||||
final PhiInstruction getAPhiInstruction() {
|
||||
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction in this block. This includes `Phi` instructions.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
result = this.getInstruction(_) or
|
||||
result = this.getAPhiInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first non-`Phi` instruction in this block.
|
||||
*/
|
||||
final Instruction getFirstInstruction() { result = getFirstInstruction(this) }
|
||||
|
||||
/**
|
||||
* Gets the last instruction in this block.
|
||||
*/
|
||||
final Instruction getLastInstruction() {
|
||||
result = this.getInstruction(this.getInstructionCount() - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of non-`Phi` instructions in this block.
|
||||
*/
|
||||
final int getInstructionCount() { result = getInstructionCount(this) }
|
||||
|
||||
/**
|
||||
* Gets the `IRFunction` that contains this block.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() {
|
||||
result = getFirstInstruction(this).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Function` that contains this block.
|
||||
*/
|
||||
final Language::Declaration getEnclosingFunction() {
|
||||
result = getFirstInstruction(this).getEnclosingFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block with additional information about its predecessor and successor edges. Each edge
|
||||
* corresponds to the control flow between the last instruction of one block and the first
|
||||
* instruction of another block.
|
||||
*/
|
||||
class IRBlock extends IRBlockBase {
|
||||
/**
|
||||
* Gets a block to which control flows directly from this block.
|
||||
*/
|
||||
final IRBlock getASuccessor() { blockSuccessor(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a block from which control flows directly to this block.
|
||||
*/
|
||||
final IRBlock getAPredecessor() { blockSuccessor(result, this) }
|
||||
|
||||
/**
|
||||
* Gets the block to which control flows directly from this block along an edge of kind `kind`.
|
||||
*/
|
||||
final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) }
|
||||
|
||||
/**
|
||||
* Gets the block to which control flows directly from this block along a back edge of kind
|
||||
* `kind`.
|
||||
*/
|
||||
final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) }
|
||||
|
||||
/**
|
||||
* Holds if this block immediately dominates `block`.
|
||||
*
|
||||
* Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B`
|
||||
* is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) }
|
||||
|
||||
/**
|
||||
* Holds if this block strictly dominates `block`.
|
||||
*
|
||||
* Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B`
|
||||
* are not the same block.
|
||||
*/
|
||||
final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) }
|
||||
|
||||
/**
|
||||
* Holds if this block dominates `block`.
|
||||
*
|
||||
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
|
||||
* block `B` must pass through block `A`. A block always dominates itself.
|
||||
*/
|
||||
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the dominance frontier of this block.
|
||||
*
|
||||
* The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock dominanceFrontier() {
|
||||
this.getASuccessor() = result and
|
||||
not this.immediatelyDominates(result)
|
||||
or
|
||||
exists(IRBlock prev | result = prev.dominanceFrontier() |
|
||||
this.immediatelyDominates(prev) and
|
||||
not this.immediatelyDominates(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postDominanceFrontier() {
|
||||
this.getAPredecessor() = result and
|
||||
not this.immediatelyPostDominates(result)
|
||||
or
|
||||
exists(IRBlock prev | result = prev.postDominanceFrontier() |
|
||||
this.immediatelyPostDominates(prev) and
|
||||
not this.immediatelyPostDominates(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
final predicate isReachableFromFunctionEntry() {
|
||||
this = this.getEnclosingIRFunction().getEntryBlock() or
|
||||
this.getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
not adjacentInBlock(_, instr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
// - i2 must be the only successor of i1
|
||||
i2 = unique(Instruction i | i = i1.getASuccessor()) and
|
||||
// - i1 must be the only predecessor of i2
|
||||
i1 = unique(Instruction i | i.getASuccessor() = i2) and
|
||||
// - The edge between the two must be a GotoEdge. We just check that one
|
||||
// exists since we've already checked that it's unique.
|
||||
exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
|
||||
// - The edge must not be a back edge. This means we get the same back edges
|
||||
// in the basic-block graph as we do in the raw CFG.
|
||||
not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
|
||||
// This predicate could be simplified to remove one of the `unique`s if we
|
||||
// were willing to rely on the CFG being well-formed and thus never having
|
||||
// more than one successor to an instruction that has a `GotoEdge` out of it.
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) }
|
||||
|
||||
/** Holds if `i` is the `index`th instruction the block starting with `first`. */
|
||||
private Instruction getInstructionFromFirst(Instruction first, int index) =
|
||||
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached
|
||||
Instruction getInstruction(TIRBlock block, int index) {
|
||||
result = getInstructionFromFirst(getFirstInstruction(block), index)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) }
|
||||
|
||||
cached
|
||||
predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 }
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
backEdgeSuccessorRaw(pred, succ, kind)
|
||||
or
|
||||
// See the QLDoc on `backEdgeSuccessorRaw`.
|
||||
exists(TIRBlock pred2 |
|
||||
// Joining with `blockIdentity` is a performance trick to get
|
||||
// `forwardEdgeRaw` on the RHS of a join, where it's fast.
|
||||
blockIdentity(pred, pred2) and
|
||||
forwardEdgeRaw+(pred, pred2)
|
||||
) and
|
||||
blockSuccessor(pred, succ, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an edge from `pred` to `succ` that is not a back edge.
|
||||
*/
|
||||
private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) {
|
||||
exists(EdgeKind kind |
|
||||
blockSuccessor(pred, succ, kind) and
|
||||
not backEdgeSuccessorRaw(pred, succ, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `kind`-edge from `pred` to `succ` is a back edge according to
|
||||
* `Construction`.
|
||||
*
|
||||
* There could be loops of non-back-edges if there is a flaw in the IR
|
||||
* construction or back-edge detection, and this could cause non-termination
|
||||
* of subsequent analysis. To prevent that, a subsequent predicate further
|
||||
* classifies all edges as back edges if they are involved in a loop of
|
||||
* non-back-edges.
|
||||
*/
|
||||
private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) }
|
||||
|
||||
cached
|
||||
predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name Raw IR Consistency Check
|
||||
* @description Performs consistency checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind table
|
||||
* @id cs/raw-ir-consistency-check
|
||||
*/
|
||||
|
||||
import IRConsistency
|
||||
@@ -1,549 +0,0 @@
|
||||
private import IR
|
||||
import InstructionConsistency // module is below
|
||||
import IRTypeConsistency // module is in IRType.qll
|
||||
import internal.IRConsistencyImports
|
||||
|
||||
module InstructionConsistency {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(LanguageDebug::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
|
||||
IRFunction getIRFunction() { result = irFunc }
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate nonUniqueIRVariable(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(VariableInstruction vi, IRVariable v1, IRVariable v2 |
|
||||
instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2
|
||||
) and
|
||||
message =
|
||||
"Variable instruction '" + instr.toString() +
|
||||
"' has multiple associated variables, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::VariableAddress and
|
||||
not instr instanceof VariableInstruction and
|
||||
message =
|
||||
"Variable address instruction '" + instr.toString() +
|
||||
"' has no associated variable, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Provides the class `IRFunction`, which represents the Intermediate Representation for the
|
||||
* definition of a function.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
/**
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final EnterFunctionInstruction getEnterFunctionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exit point for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ExitFunctionInstruction getExitFunctionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single return instruction for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets the variable used to hold the return value of this function. If this
|
||||
* function does not return a value, this predicate does not hold.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets the block containing the entry point of this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock getEntryBlock() {
|
||||
result.getFirstInstruction() = this.getEnterFunctionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all instructions in this function.
|
||||
*/
|
||||
final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets all blocks in this function.
|
||||
*/
|
||||
final IRBlock getABlock() { result.getEnclosingIRFunction() = this }
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/**
|
||||
* Provides classes that represent variables accessed by the IR.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
import IRFunction
|
||||
private import internal.IRVariableImports as Imports
|
||||
import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
/**
|
||||
* A variable referenced by the IR for a function.
|
||||
*
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
class IRVariable extends TIRVariable {
|
||||
Language::Declaration func;
|
||||
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
* literals, but could also apply to `const` global and static variables.
|
||||
*/
|
||||
predicate isReadOnly() { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
final Language::Type getType() { this.getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = this.getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { none() }
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
Language::AST getAst() { none() }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
/**
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
string getUniqueId() { none() }
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
*/
|
||||
final Language::Location getLocation() { result = this.getAst().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the IR for the function that references this variable.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
||||
|
||||
/**
|
||||
* Gets the function that references this variable.
|
||||
*/
|
||||
final Language::Declaration getEnclosingFunction() { result = func }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
final override string toString() { result = this.getVariable().toString() }
|
||||
|
||||
final override Language::AST getAst() { result = var }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated override Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = this.getVariable().toString() + " " + this.getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
*/
|
||||
Language::Variable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
class IRAutomaticVariable extends IRVariable {
|
||||
IRAutomaticVariable() {
|
||||
exists(Language::Variable var |
|
||||
this = TIRUserVariable(var, _, func) and
|
||||
Language::isVariableAutomatic(var)
|
||||
)
|
||||
or
|
||||
this = TIRTempVariable(func, _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||
* namespace-scope variables, static fields, and static local variables.
|
||||
*/
|
||||
class IRStaticUserVariable extends IRUserVariable {
|
||||
override Language::StaticVariable var;
|
||||
|
||||
IRStaticUserVariable() { not Language::isVariableAutomatic(var) }
|
||||
|
||||
final override Language::StaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
class IRGeneratedVariable extends IRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAst() { result = ast }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated override Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
override string toString() { result = this.getBaseString() + this.getLocationString() }
|
||||
|
||||
override string getUniqueId() { none() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets a string containing the source code location of the AST that generated this variable.
|
||||
*
|
||||
* This is used by debugging and printing code only.
|
||||
*/
|
||||
final string getLocationString() {
|
||||
result =
|
||||
ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the string that is combined with the location of the variable to generate the string
|
||||
* representation of this variable.
|
||||
*
|
||||
* This is used by debugging and printing code only.
|
||||
*/
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
TempVariableTag tag;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "tag" object that differentiates this temporary variable from other temporary
|
||||
* variables generated for the same AST.
|
||||
*/
|
||||
final TempVariableTag getTag() { result = tag }
|
||||
|
||||
override string getBaseString() { result = "#temp" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the return value of a function.
|
||||
*/
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
IRReturnVariable() { tag = ReturnValueTempVar() }
|
||||
|
||||
final override string toString() { result = "#return" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() { tag = ThrowTempVar() }
|
||||
|
||||
final override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
|
||||
final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
|
||||
final override int getIndex() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||
* a read-only global variable.
|
||||
*/
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
|
||||
final override predicate isReadOnly() { any() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "String: " + this.getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#string" }
|
||||
|
||||
/**
|
||||
* Gets the AST of the string literal represented by this `IRStringLiteral`.
|
||||
*/
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||
}
|
||||
|
||||
final override string toString() { result = var.toString() + "#init" }
|
||||
|
||||
/**
|
||||
* Gets variable whose initialization is guarded by this flag.
|
||||
*/
|
||||
final Language::Variable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result =
|
||||
"Init: " + this.getVariable().toString() + " " + this.getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,499 +0,0 @@
|
||||
/**
|
||||
* Provides classes that represent the input values of IR instructions.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
private import Imports::TOperand
|
||||
private import internal.OperandInternal
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
|
||||
* of `TOperand` that are used in this stage.
|
||||
*/
|
||||
private class TStageOperand =
|
||||
TRegisterOperand or TNonSsaMemoryOperand or TPhiOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* A known location. Testing `loc instanceof KnownLocation` will account for non existing locations, as
|
||||
* opposed to testing `not loc isntanceof UnknownLocation`
|
||||
*/
|
||||
private class KnownLocation extends Language::Location {
|
||||
KnownLocation() { not this instanceof Language::UnknownLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
|
||||
* (the defining instruction) in another instruction (the use instruction)
|
||||
*/
|
||||
class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
|
||||
or
|
||||
exists(Instruction use | this = nonSsaMemoryOperand(use, _))
|
||||
or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
this = chiOperand(_, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Operand" }
|
||||
|
||||
/**
|
||||
* Gets the location of the source code for this operand.
|
||||
* By default this is where the operand is used, but some subclasses may override this
|
||||
* using `getAnyDef()` if it makes more sense.
|
||||
*/
|
||||
Language::Location getLocation() { result = this.getUse().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the function that contains this operand.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
Instruction getUse() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand. Unlike
|
||||
* `getDef`, this also has a result when `isDefinitionInexact` holds, which
|
||||
* means that the resulting instruction may only _partially_ or _potentially_
|
||||
* be the value of this operand.
|
||||
*/
|
||||
Instruction getAnyDef() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand. Unlike
|
||||
* `getAnyDef`, this also has no result when `isDefinitionInexact` holds,
|
||||
* which means that the resulting instruction must always be exactly the be
|
||||
* the value of this operand.
|
||||
*/
|
||||
final Instruction getDef() {
|
||||
result = this.getAnyDef() and
|
||||
this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overlap relationship between the operand's definition and its use.
|
||||
*/
|
||||
Overlap getDefinitionOverlap() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the result of the definition instruction does not exactly overlap this use.
|
||||
*/
|
||||
final predicate isDefinitionInexact() {
|
||||
not this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this operand on its use instruction.
|
||||
*/
|
||||
string getDumpId() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
|
||||
* result ID of the instruction consumed by the operand, plus a label identifying the operand
|
||||
* kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
final string getDumpString() {
|
||||
result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string containing the identifier of the definition of this use, or `m?` if the
|
||||
* definition is not modeled in SSA.
|
||||
*/
|
||||
private string getDefinitionId() {
|
||||
result = this.getAnyDef().getResultId()
|
||||
or
|
||||
not exists(this.getAnyDef()) and result = "m?"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is
|
||||
* an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is
|
||||
* the empty string.
|
||||
*/
|
||||
private string getInexactSpecifier() {
|
||||
if this.isDefinitionInexact() then result = "~" else result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order in which the operand should be sorted in the operand list.
|
||||
*/
|
||||
int getDumpSortOrder() { result = -1 }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = this.getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { this.getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getType()`.
|
||||
*/
|
||||
final predicate isGLValue() { this.getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
final int getSize() { result = this.getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
cached
|
||||
MemoryOperand() {
|
||||
this instanceof TNonSsaMemoryOperand or
|
||||
this instanceof TPhiOperand or
|
||||
this instanceof TChiOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this operand will not always read from every bit in the
|
||||
* memory location. This is most commonly used for memory accesses that may or may not actually
|
||||
* occur depending on runtime state (for example, the write side effect of an output parameter
|
||||
* that is not written to on all paths), or for accesses where the memory location is a
|
||||
* conservative estimate of the memory that might actually be accessed at runtime (for example,
|
||||
* the global side effects of a function call).
|
||||
*/
|
||||
predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
|
||||
* is `r1`.
|
||||
*/
|
||||
final AddressOperand getAddressOperand() {
|
||||
this.getMemoryAccess().usesAddressOperand() and
|
||||
result.getUse() = this.getUse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand {
|
||||
Instruction useInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = registerOperand(useInstr, tag, _) or
|
||||
this = nonSsaMemoryOperand(useInstr, tag) or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override Instruction getUse() { result = useInstr }
|
||||
|
||||
final override string getDumpLabel() { result = tag.getLabel() }
|
||||
|
||||
final override string getDumpId() { result = tag.getId() }
|
||||
|
||||
final override int getDumpSortOrder() { result = tag.getSortOrder() }
|
||||
|
||||
/**
|
||||
* Gets the `OperandTag` that specifies how this operand is used by its `Instruction`.
|
||||
*/
|
||||
final OperandTag getOperandTag() { result = tag }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
Instruction defInstr;
|
||||
|
||||
cached
|
||||
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
// most `RegisterOperands` have a more meaningful location at the definition
|
||||
// the only exception are specific cases of `ThisArgumentOperand`
|
||||
override Language::Location getLocation() { result = this.getAnyDef().getLocation() }
|
||||
|
||||
final override Instruction getAnyDef() { result = defInstr }
|
||||
|
||||
final override Overlap getDefinitionOverlap() {
|
||||
// All register results overlap exactly with their uses.
|
||||
result instanceof MustExactlyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory operand other than the operand of a `Phi` instruction.
|
||||
*/
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
|
||||
cached
|
||||
NonPhiMemoryOperand() {
|
||||
this = nonSsaMemoryOperand(useInstr, tag)
|
||||
or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
final override Instruction getAnyDef() {
|
||||
result = unique(Instruction defInstr | this.hasDefinition(defInstr, _))
|
||||
}
|
||||
|
||||
final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
|
||||
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
|
||||
not Construction::isInCycle(useInstr) and
|
||||
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the operand totally overlaps with its definition and consumes the
|
||||
* bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
|
||||
*/
|
||||
predicate getUsedInterval(int startBitOffset, int endBitOffset) {
|
||||
Construction::getUsedInterval(this, startBitOffset, endBitOffset)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory operand whose type may be different from the type of the result of its definition.
|
||||
*/
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class AddressOperand extends RegisterOperand {
|
||||
override AddressOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperand {
|
||||
override BufferSizeOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
*/
|
||||
class LoadOperand extends TypedOperand {
|
||||
override LoadOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of a `Store` instruction.
|
||||
*/
|
||||
class StoreValueOperand extends RegisterOperand {
|
||||
override StoreValueOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
|
||||
*/
|
||||
class UnaryOperand extends RegisterOperand {
|
||||
override UnaryOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends RegisterOperand {
|
||||
override LeftOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends RegisterOperand {
|
||||
override RightOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends RegisterOperand {
|
||||
override ConditionOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends RegisterOperand {
|
||||
override CallTargetOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit `this` argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
override ThisArgumentOperandTag tag;
|
||||
|
||||
// in most cases the def location makes more sense, but in some corner cases it
|
||||
// has an unknown location: in those cases we fall back to the use location
|
||||
override Language::Location getLocation() {
|
||||
if this.getAnyDef().getLocation() instanceof KnownLocation
|
||||
then result = this.getAnyDef().getLocation()
|
||||
else result = this.getUse().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override PositionalArgumentOperandTag tag;
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() { result = tag.getArgIndex() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing memory read as a side effect of evaluating another instruction.
|
||||
*/
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() {
|
||||
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
or
|
||||
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
final override PhiInstruction getUse() { result = useInstr }
|
||||
|
||||
final override Instruction getAnyDef() { result = defInstr }
|
||||
|
||||
final override Overlap getDefinitionOverlap() { result = overlap }
|
||||
|
||||
final override int getDumpSortOrder() {
|
||||
result = 11 + this.getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
final override string getDumpLabel() {
|
||||
result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
final override string getDumpId() {
|
||||
result = this.getPredecessorBlock().getDisplayIndex().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() { result = predecessorBlock }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends NonPhiMemoryOperand {
|
||||
override ChiTotalOperandTag tag;
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends NonPhiMemoryOperand {
|
||||
override ChiPartialOperandTag tag;
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name Print Raw IR
|
||||
* @description Outputs a representation of the Raw IR graph
|
||||
* @id cs/print-raw-ir
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import PrintIR
|
||||
@@ -1,342 +0,0 @@
|
||||
/**
|
||||
* Outputs a representation of the IR as a control flow graph.
|
||||
*
|
||||
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
|
||||
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
|
||||
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
|
||||
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
|
||||
* to dump.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import IR
|
||||
private import internal.PrintIRImports as Imports
|
||||
import Imports::IRConfiguration
|
||||
|
||||
private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which declarations are printed.
|
||||
*/
|
||||
class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
/** Gets a textual representation of this configuration. */
|
||||
string toString() { result = "PrintIRConfiguration" }
|
||||
|
||||
/**
|
||||
* Holds if the IR for `func` should be printed. By default, holds for all
|
||||
* functions, global and namespace variables, and static local variables.
|
||||
*/
|
||||
predicate shouldPrintDeclaration(Language::Declaration decl) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
|
||||
shouldPrintDeclaration(func)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintDeclaration(Language::Declaration decl) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl))
|
||||
}
|
||||
|
||||
private predicate shouldPrintInstruction(Instruction i) {
|
||||
exists(IRPropertyProvider provider | provider.shouldPrintInstruction(i))
|
||||
}
|
||||
|
||||
private predicate shouldPrintOperand(Operand operand) {
|
||||
exists(IRPropertyProvider provider | provider.shouldPrintOperand(operand))
|
||||
}
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties of an operand from any active property providers.
|
||||
*/
|
||||
private string getAdditionalOperandProperty(Operand operand, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string listing the properties of the operand and their corresponding values. If the
|
||||
* operand has no properties, this predicate has no result.
|
||||
*/
|
||||
private string getOperandPropertyListString(Operand operand) {
|
||||
result =
|
||||
strictconcat(string key, string value |
|
||||
value = getAdditionalOperandProperty(operand, key)
|
||||
|
|
||||
key + ":" + value, ", "
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string listing the properties of the operand and their corresponding values. The list is
|
||||
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
|
||||
* string.
|
||||
*/
|
||||
private string getOperandPropertyString(Operand operand) {
|
||||
result = "{" + getOperandPropertyListString(operand) + "}"
|
||||
or
|
||||
not exists(getOperandPropertyListString(operand)) and result = ""
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or
|
||||
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or
|
||||
TPrintableInstruction(Instruction instr) {
|
||||
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* A node to be emitted in the IR graph.
|
||||
*/
|
||||
abstract private class PrintableIRNode extends TPrintableIRNode {
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the location to be emitted for the node.
|
||||
*/
|
||||
abstract Language::Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets the label to be emitted for the node.
|
||||
*/
|
||||
abstract string getLabel();
|
||||
|
||||
/**
|
||||
* Gets the order in which the node appears in its parent node.
|
||||
*/
|
||||
abstract int getOrder();
|
||||
|
||||
/**
|
||||
* Gets the parent of this node.
|
||||
*/
|
||||
abstract PrintableIRNode getParent();
|
||||
|
||||
/**
|
||||
* Gets the kind of graph represented by this node ("graph" or "tree").
|
||||
*/
|
||||
string getGraphKind() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this node should always be rendered as text, even in a graphical
|
||||
* viewer.
|
||||
*/
|
||||
predicate forceText() { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the node property with the specified key.
|
||||
*/
|
||||
string getProperty(string key) {
|
||||
key = "semmle.label" and result = this.getLabel()
|
||||
or
|
||||
key = "semmle.order" and result = this.getOrder().toString()
|
||||
or
|
||||
key = "semmle.graphKind" and result = this.getGraphKind()
|
||||
or
|
||||
key = "semmle.forceText" and this.forceText() and result = "true"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing a `IRFunction` object.
|
||||
*/
|
||||
private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
|
||||
IRFunction irFunc;
|
||||
|
||||
PrintableIRFunction() { this = TPrintableIRFunction(irFunc) }
|
||||
|
||||
override string toString() { result = irFunc.toString() }
|
||||
|
||||
override Language::Location getLocation() { result = irFunc.getLocation() }
|
||||
|
||||
override string getLabel() {
|
||||
result = Imports::LanguageDebug::getIdentityString(irFunc.getFunction())
|
||||
}
|
||||
|
||||
override int getOrder() {
|
||||
this =
|
||||
rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location |
|
||||
location = orderedFunc.getIRFunction().getLocation()
|
||||
|
|
||||
orderedFunc
|
||||
order by
|
||||
location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(),
|
||||
orderedFunc.getLabel()
|
||||
)
|
||||
}
|
||||
|
||||
final override PrintableIRNode getParent() { none() }
|
||||
|
||||
final IRFunction getIRFunction() { result = irFunc }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing an `IRBlock` object.
|
||||
*/
|
||||
private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
IRBlock block;
|
||||
|
||||
PrintableIRBlock() { this = TPrintableIRBlock(block) }
|
||||
|
||||
override string toString() { result = this.getLabel() }
|
||||
|
||||
override Language::Location getLocation() { result = block.getLocation() }
|
||||
|
||||
override string getLabel() { result = "Block " + block.getDisplayIndex().toString() }
|
||||
|
||||
override int getOrder() { result = block.getDisplayIndex() }
|
||||
|
||||
final override string getGraphKind() { result = "tree" }
|
||||
|
||||
final override predicate forceText() { any() }
|
||||
|
||||
final override PrintableIRFunction getParent() {
|
||||
result.getIRFunction() = block.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() { result = block }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing an `Instruction`.
|
||||
*/
|
||||
private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
Instruction instr;
|
||||
|
||||
PrintableInstruction() { this = TPrintableInstruction(instr) }
|
||||
|
||||
override string toString() { result = instr.toString() }
|
||||
|
||||
override Language::Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
override string getLabel() {
|
||||
exists(IRBlock block |
|
||||
instr = block.getAnInstruction() and
|
||||
exists(
|
||||
string resultString, string operationString, string operandsString, int resultWidth,
|
||||
int operationWidth
|
||||
|
|
||||
resultString = instr.getResultString() and
|
||||
operationString = instr.getOperationString() and
|
||||
operandsString = this.getOperandsString() and
|
||||
columnWidths(block, resultWidth, operationWidth) and
|
||||
result =
|
||||
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
|
||||
operationString + getPaddingString(operationWidth - operationString.length()) + " : " +
|
||||
operandsString
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getOrder() { result = instr.getDisplayIndexInBlock() }
|
||||
|
||||
final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() }
|
||||
|
||||
final Instruction getInstruction() { result = instr }
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the operand list. This is the same as
|
||||
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
|
||||
* provided by active `IRPropertyProvider` instances.
|
||||
*/
|
||||
private string getOperandsString() {
|
||||
result =
|
||||
concat(Operand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
shouldPrintOperand(operand)
|
||||
|
|
||||
operand.getDumpString() + getOperandPropertyString(operand), ", "
|
||||
order by
|
||||
operand.getDumpSortOrder()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and
|
||||
operationWidth =
|
||||
max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length())
|
||||
}
|
||||
|
||||
private int maxColumnWidth() {
|
||||
result =
|
||||
max(Instruction instr, int width |
|
||||
width = instr.getResultString().length() or
|
||||
width = instr.getOperationString().length() or
|
||||
width = instr.getOperandsString().length()
|
||||
|
|
||||
width
|
||||
)
|
||||
}
|
||||
|
||||
private string getPaddingString(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " "
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` belongs to the output graph, and its property `key` has the given `value`.
|
||||
*/
|
||||
query predicate nodes(PrintableIRNode node, string key, string value) {
|
||||
value = node.getProperty(key)
|
||||
}
|
||||
|
||||
private int getSuccessorIndex(IRBlock pred, IRBlock succ) {
|
||||
succ =
|
||||
rank[result + 1](IRBlock aSucc, EdgeKind kind |
|
||||
aSucc = pred.getSuccessor(kind)
|
||||
|
|
||||
aSucc order by kind.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key`
|
||||
* has the given `value`.
|
||||
*/
|
||||
query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) {
|
||||
exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock |
|
||||
predBlock = pred.getBlock() and
|
||||
succBlock = succ.getBlock() and
|
||||
predBlock.getSuccessor(kind) = succBlock and
|
||||
(
|
||||
(
|
||||
key = "semmle.label" and
|
||||
if predBlock.getBackEdgeSuccessor(kind) = succBlock
|
||||
then value = kind.toString() + " (back edge)"
|
||||
else value = kind.toString()
|
||||
)
|
||||
or
|
||||
key = "semmle.order" and
|
||||
value = getSuccessorIndex(predBlock, succBlock).toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `parent` is the parent node of `child` in the output graph.
|
||||
*/
|
||||
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
|
||||
parent = child.getParent()
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import experimental.ir.internal.IntegerPartial
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
int getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt()
|
||||
or
|
||||
result = getBinaryInstructionValue(instr)
|
||||
or
|
||||
result = neg(getConstantValue(instr.(NegateInstruction).getUnary()))
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def)) and
|
||||
result = min(Instruction def | def = phi.getAnInput() | getConstantValueToPhi(def))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
int getConstantValueToPhi(Instruction def) {
|
||||
exists(PhiInstruction phi |
|
||||
result = getConstantValue(def) and
|
||||
def = phi.getAnInput()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) {
|
||||
left = getConstantValue(instr.getLeft()) and
|
||||
right = getConstantValue(instr.getRight())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||
exists(int left, int right |
|
||||
binaryInstructionOperands(instr, left, right) and
|
||||
(
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import experimental.ir.internal.IntegerConstant
|
||||
private import ConstantAnalysis
|
||||
import IR
|
||||
|
||||
private class ConstantAnalysisPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
key = "ConstantValue" and
|
||||
result = getValue(getConstantValue(instr)).toString()
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.implementation.raw.IR as IR
|
||||
@@ -1,17 +0,0 @@
|
||||
private import internal.ValueNumberingImports
|
||||
private import ValueNumbering
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1
|
||||
then result = vn.getDebugString()
|
||||
else result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
private import internal.ValueNumberingImports
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() { result = "GVN" }
|
||||
|
||||
final string getDebugString() {
|
||||
result = strictconcat(this.getAnInstruction().getResultId(), ", ")
|
||||
}
|
||||
|
||||
final Language::Location getLocation() {
|
||||
if
|
||||
exists(Instruction i |
|
||||
i = this.getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
|
||||
)
|
||||
then
|
||||
result =
|
||||
min(Language::Location l |
|
||||
l = this.getAnInstruction().getLocation() and not l instanceof Language::UnknownLocation
|
||||
|
|
||||
l
|
||||
order by
|
||||
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn()
|
||||
)
|
||||
else result instanceof Language::UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result =
|
||||
min(Instruction instr |
|
||||
instr = this.getAnInstruction()
|
||||
|
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Operand` whose definition is exact and has this value number.
|
||||
*/
|
||||
final Operand getAUse() { this = valueNumber(result.getDef()) }
|
||||
|
||||
final string getKind() {
|
||||
this instanceof TVariableAddressValueNumber and result = "VariableAddress"
|
||||
or
|
||||
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
|
||||
or
|
||||
this instanceof TConstantValueNumber and result = "Constant"
|
||||
or
|
||||
this instanceof TStringConstantValueNumber and result = "StringConstant"
|
||||
or
|
||||
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
|
||||
or
|
||||
this instanceof TBinaryValueNumber and result = "Binary"
|
||||
or
|
||||
this instanceof TPointerArithmeticValueNumber and result = "PointerArithmetic"
|
||||
or
|
||||
this instanceof TUnaryValueNumber and result = "Unary"
|
||||
or
|
||||
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
|
||||
or
|
||||
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
|
||||
or
|
||||
this instanceof TUniqueValueNumber and result = "Unique"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) { result = tvalueNumber(instr) }
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to the exact definition of `op`, if any.
|
||||
* Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumberOfOperand(Operand op) { result = tvalueNumberOfOperand(op) }
|
||||
@@ -1,3 +0,0 @@
|
||||
import experimental.ir.internal.Overlap
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import experimental.ir.implementation.unaliased_ssa.IR
|
||||
@@ -1,356 +0,0 @@
|
||||
private import ValueNumberingImports
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
|
||||
variableAddressValueNumber(_, irFunc, ast)
|
||||
} or
|
||||
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
TValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
TLoadTotalOverlapValueNumber(
|
||||
IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand
|
||||
) {
|
||||
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
|
||||
} or
|
||||
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*/
|
||||
class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
class LoadTotalOverlapInstruction extends LoadInstruction {
|
||||
LoadTotalOverlapInstruction() {
|
||||
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction
|
||||
or
|
||||
instr instanceof InitializeParameterInstruction
|
||||
or
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
instr instanceof FieldAddressInstruction
|
||||
or
|
||||
instr instanceof BinaryInstruction
|
||||
or
|
||||
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
|
||||
or
|
||||
instr instanceof PointerArithmeticInstruction
|
||||
or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
or
|
||||
instr instanceof LoadTotalOverlapInstruction
|
||||
}
|
||||
|
||||
private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
// count rather than strictcount to handle missing AST elements
|
||||
// separate instanceof and inline casts to avoid failed casts with a count of 0
|
||||
instr instanceof VariableAddressInstruction and
|
||||
count(instr.(VariableAddressInstruction).getIRVariable().getAst()) != 1
|
||||
or
|
||||
instr instanceof ConstantInstruction and
|
||||
count(instr.getResultIRType()) != 1
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
unique( | | instr.getIRVariable().getAst()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAst() = var
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(
|
||||
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryValueNumber0(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, boolean isLeft,
|
||||
TValueNumber valueNumber
|
||||
) {
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
(
|
||||
isLeft = true and
|
||||
tvalueNumber(instr.getLeft()) = valueNumber
|
||||
or
|
||||
isLeft = false and
|
||||
tvalueNumber(instr.getRight()) = valueNumber
|
||||
)
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber0(instr, irFunc, opcode, true, leftOperand) and
|
||||
binaryValueNumber0(instr, irFunc, opcode, false, rightOperand)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pointerArithmeticValueNumber0(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
|
||||
boolean isLeft, TValueNumber valueNumber
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getElementSize() = elementSize and
|
||||
(
|
||||
isLeft = true and
|
||||
tvalueNumber(instr.getLeft()) = valueNumber
|
||||
or
|
||||
isLeft = false and
|
||||
tvalueNumber(instr.getRight()) = valueNumber
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
|
||||
TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber0(instr, irFunc, opcode, elementSize, true, leftOperand) and
|
||||
pointerArithmeticValueNumber0(instr, irFunc, opcode, elementSize, false, rightOperand)
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate loadTotalOverlapValueNumber0(
|
||||
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber valueNumber,
|
||||
boolean isAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultIRType() = type and
|
||||
(
|
||||
isAddress = true and
|
||||
tvalueNumberOfOperand(instr.getSourceAddressOperand()) = valueNumber
|
||||
or
|
||||
isAddress = false and
|
||||
tvalueNumber(instr.getSourceValueOperand().getAnyDef()) = valueNumber
|
||||
)
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
|
||||
TValueNumber operand
|
||||
) {
|
||||
loadTotalOverlapValueNumber0(instr, irFunc, type, operand, true) and
|
||||
loadTotalOverlapValueNumber0(instr, irFunc, type, memOperand, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
(
|
||||
not numberableInstruction(instr)
|
||||
or
|
||||
filteredNumberableInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
cached
|
||||
TValueNumber tvalueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr)
|
||||
or
|
||||
exists(IRFunction irFunc |
|
||||
uniqueValueNumber(instr, irFunc) and
|
||||
result = TUniqueValueNumber(irFunc, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to the exact definition of `op`, if any.
|
||||
* Returns at most one result.
|
||||
*/
|
||||
TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef()) }
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(IRFunction irFunc |
|
||||
irFunc = instr.getEnclosingIRFunction() and
|
||||
(
|
||||
exists(Language::AST ast |
|
||||
variableAddressValueNumber(instr, irFunc, ast) and
|
||||
result = TVariableAddressValueNumber(irFunc, ast)
|
||||
)
|
||||
or
|
||||
exists(Language::AST var |
|
||||
initializeParameterValueNumber(instr, irFunc, var) and
|
||||
result = TInitializeParameterValueNumber(irFunc, var)
|
||||
)
|
||||
or
|
||||
exists(string value, IRType type |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(IRType type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Language::Field field, TValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
||||
|
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
|
||||
result =
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
|
||||
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
|
||||
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
|
||||
)
|
||||
or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.implementation.EdgeKind as EdgeKind
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguageDebug as LanguageDebug
|
||||
@@ -1,435 +0,0 @@
|
||||
import csharp
|
||||
import experimental.ir.implementation.raw.IR
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.internal.IRFunctionBase
|
||||
private import experimental.ir.implementation.internal.TInstruction
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.Overlap
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedStmt
|
||||
private import desugar.Foreach
|
||||
private import TranslatedFunction
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = TRawInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = TRawInstruction(_, result)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(
|
||||
Instruction instruction, TranslatedElement element, InstructionTag tag
|
||||
) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction;
|
||||
|
||||
/**
|
||||
* Provides the portion of the parameterized IR interface that is used to construct the initial
|
||||
* "raw" stage of the IR. The other stages of the IR do not expose these predicates.
|
||||
*/
|
||||
cached
|
||||
module Raw {
|
||||
class InstructionTag1 = TranslatedElement;
|
||||
|
||||
class InstructionTag2 = InstructionTag;
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Callable callable) { exists(getTranslatedFunction(callable)) }
|
||||
|
||||
cached
|
||||
predicate varHasIRFunc(Field field) { none() }
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Callable callable, Variable var, CSharpType type) {
|
||||
getTranslatedFunction(callable).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(
|
||||
Callable callable, Language::AST ast, TempVariableTag tag, CSharpType type
|
||||
) {
|
||||
exists(TranslatedElement element |
|
||||
element.getAst() = ast and
|
||||
callable = element.getFunction() and
|
||||
element.hasTempVariable(tag, type)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasStringLiteral(
|
||||
Callable callable, Language::AST ast, CSharpType type, StringLiteral literal
|
||||
) {
|
||||
literal = ast and
|
||||
literal.getEnclosingCallable() = callable and
|
||||
getTypeForPRValue(literal.getType()) = type
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasDynamicInitializationFlag(Callable callable, Language::Variable var, CSharpType type) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
exists(Expr converted, TranslatedExpr translatedExpr | result = converted.stripCasts() |
|
||||
translatedExpr = getTranslatedExpr(converted) and
|
||||
instruction = translatedExpr.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
(
|
||||
result = element.getInstructionVariable(tag) or
|
||||
result.(IRStringLiteral).getAst() = element.getInstructionStringLiteral(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionIndex(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
Callable getInstructionFunction(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionFunction(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionConstantValue(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionConstantValue(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
CSharpType getInstructionExceptionType(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionExceptionType(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) { none() }
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate getInstructionOpcode(Opcode opcode, TRawInstruction instr) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instr, element, tag) and
|
||||
element.hasInstruction(opcode, tag, _)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
IRFunctionBase getInstructionEnclosingIRFunction(TRawInstruction instr) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasInstruction(TRawInstruction instr) { any() }
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
overlap instanceof MustTotallyOverlap and
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionOperand(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the operand totally overlaps with its definition and consumes the
|
||||
* bit range `[startBitOffset, endBitOffset)`.
|
||||
*/
|
||||
cached
|
||||
predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() }
|
||||
|
||||
cached
|
||||
predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) { none() }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDef+(instr) = instr
|
||||
}
|
||||
|
||||
cached
|
||||
CSharpType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
if instruction instanceof LoadInstruction
|
||||
then result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
else
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionSuccessor(getInstructionTag(instruction), kind)
|
||||
}
|
||||
|
||||
// This predicate has pragma[noopt] because otherwise the `getAChild*` calls
|
||||
// get joined too early. The join order for the loop cases goes like this:
|
||||
// - Find all loops of that type (tens of thousands).
|
||||
// - Find all edges into the start of the loop (x 2).
|
||||
// - Restrict to edges that originate within the loop (/ 2).
|
||||
pragma[noopt]
|
||||
cached
|
||||
Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
// While loop:
|
||||
// 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
|
||||
// edge(s) after the last instruction(s) in the body.
|
||||
exists(TranslatedWhileStmt s |
|
||||
s instanceof TranslatedWhileStmt and
|
||||
result = s.getFirstConditionInstruction() and
|
||||
exists(TranslatedElement inBody, InstructionTag tag |
|
||||
result = inBody.getInstructionSuccessor(tag, kind) and
|
||||
exists(TranslatedElement body | body = s.getBody() | inBody = body.getAChild*()) and
|
||||
instruction = inBody.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Compiler generated foreach while loop:
|
||||
// Same as above
|
||||
exists(TranslatedForeachWhile s |
|
||||
result = s.getFirstInstruction() and
|
||||
exists(TranslatedElement inBody, InstructionTag tag |
|
||||
result = inBody.getInstructionSuccessor(tag, kind) and
|
||||
exists(TranslatedElement body | body = s.getBody() | inBody = body.getAChild*()) and
|
||||
instruction = inBody.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Do-while loop:
|
||||
// The back edge should be the edge(s) from the condition to the
|
||||
// body. This ensures that it's the back edge that will be pruned in a `do
|
||||
// { ... } while (0)` statement. Note that all `continue` statements in a
|
||||
// do-while loop produce forward edges.
|
||||
exists(TranslatedDoStmt s |
|
||||
s instanceof TranslatedDoStmt and
|
||||
exists(TranslatedStmt body | body = s.getBody() | result = body.getFirstInstruction()) and
|
||||
exists(TranslatedElement inCondition, InstructionTag tag |
|
||||
result = inCondition.getInstructionSuccessor(tag, kind) and
|
||||
exists(TranslatedElement condition | condition = s.getCondition() |
|
||||
inCondition = condition.getAChild*()
|
||||
) and
|
||||
instruction = inCondition.getInstruction(tag)
|
||||
)
|
||||
)
|
||||
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 |
|
||||
s instanceof TranslatedForStmt and
|
||||
result = s.getFirstConditionInstruction() and
|
||||
exists(TranslatedElement inLoop, InstructionTag tag |
|
||||
result = inLoop.getInstructionSuccessor(tag, kind) and
|
||||
exists(TranslatedElement bodyOrUpdate |
|
||||
bodyOrUpdate = s.getBody()
|
||||
or
|
||||
bodyOrUpdate = s.getUpdate(_)
|
||||
|
|
||||
inLoop = bodyOrUpdate.getAChild*()
|
||||
) and
|
||||
instruction = inLoop.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 |
|
||||
goto instanceof GotoStmt and
|
||||
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. */
|
||||
private predicate isStrictlyForwardGoto(GotoLabelStmt goto) {
|
||||
goto.getLocation().getFile() = goto.getTarget().getLocation().getFile() and
|
||||
goto.getLocation().getEndLine() < goto.getTarget().getLocation().getStartLine()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::AST getInstructionAst(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getAst()
|
||||
}
|
||||
|
||||
cached
|
||||
CSharpType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
ArrayAccess getInstructionArrayAccess(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.getInstructionArrayAccess(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionResultSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getPrimaryInstructionForSideEffect(tag)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate hasUnreachedInstruction(IRFunction func) { none() }
|
||||
|
||||
import CachedForDebugging
|
||||
|
||||
cached
|
||||
private module CachedForDebugging {
|
||||
cached
|
||||
string getTempVariableUniqueId(IRTempVariable var) {
|
||||
exists(TranslatedElement element |
|
||||
var = element.getTempVariable(_) and
|
||||
result = element.getId().toString() + ":" + getTempVariableTagId(var.getTag())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instruction, int key1, int key2) {
|
||||
key1 = getInstructionTranslatedElement(instruction).getId() and
|
||||
getInstructionTag(instruction) =
|
||||
rank[key2](InstructionTag tag, string tagId |
|
||||
tagId = getInstructionTagId(tag)
|
||||
|
|
||||
tag order by tagId
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionUniqueId(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction).getId() + ":" +
|
||||
getInstructionTagId(getInstructionTag(instruction))
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase
|
||||
@@ -1,3 +0,0 @@
|
||||
import experimental.ir.implementation.EdgeKind as EdgeKind
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
@@ -1,4 +0,0 @@
|
||||
import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import experimental.ir.implementation.IRConfiguration as IRConfiguration
|
||||
import IRConstruction::Raw as Raw
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import experimental.ir.internal.IRUtilities as IRUtilities
|
||||
import experimental.ir.internal.TempVariableTag as TTempVariableTag
|
||||
import experimental.ir.implementation.internal.TIRVariable as TIRVariable
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.ir.implementation.EdgeKind as EdgeKind
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import experimental.ir.implementation.Opcode as Opcode
|
||||
import experimental.ir.implementation.internal.OperandTag as OperandTag
|
||||
import experimental.ir.internal.Overlap as Overlap
|
||||
@@ -1,204 +0,0 @@
|
||||
import csharp
|
||||
import experimental.ir.Util
|
||||
|
||||
private predicate elementIsInitialized(int elementIndex) {
|
||||
exists(ArrayInitWithMod initList | initList.isInitialized(elementIndex))
|
||||
}
|
||||
|
||||
newtype TInstructionTag =
|
||||
OnlyInstructionTag() or // Single instruction (not including implicit Load)
|
||||
InitializeThisTag() or
|
||||
InitializerVariableAddressTag() or
|
||||
InitializerLoadStringTag() or
|
||||
InitializerStoreTag() or
|
||||
ZeroPadStringConstantTag() or
|
||||
ZeroPadStringElementIndexTag() or
|
||||
ZeroPadStringElementAddressTag() or
|
||||
ZeroPadStringStoreTag() or
|
||||
AssignOperationLoadTag() or
|
||||
AssignOperationConvertLeftTag() or
|
||||
AssignOperationOpTag() or
|
||||
AssignmentConvertRightTag() or
|
||||
AssignOperationConvertResultTag() or
|
||||
AssignmentStoreTag() or
|
||||
CrementLoadTag() or
|
||||
CrementConstantTag() or
|
||||
CrementOpTag() or
|
||||
CrementStoreTag() or
|
||||
EnterFunctionTag() or
|
||||
ReturnValueAddressTag() or
|
||||
ReturnTag() or
|
||||
ExitFunctionTag() or
|
||||
AliasedDefinitionTag() or
|
||||
AliasedUseTag() or
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
CallSideEffectTag() or
|
||||
AllocationSizeTag() or
|
||||
AllocationElementSizeTag() or
|
||||
AllocationExtentConvertTag() or
|
||||
ValueConditionConditionalBranchTag() or
|
||||
ConditionValueTrueTempAddressTag() or
|
||||
ConditionValueTrueConstantTag() or
|
||||
ConditionValueTrueStoreTag() or
|
||||
ConditionValueFalseTempAddressTag() or
|
||||
ConditionValueFalseConstantTag() or
|
||||
ConditionValueFalseStoreTag() or
|
||||
ConditionValueResultTempAddressTag() or
|
||||
ConditionValueResultLoadTag() or
|
||||
BoolConversionConstantTag() or
|
||||
BoolConversionCompareTag() or
|
||||
LoadTag() or // Implicit load due to lvalue-to-rvalue conversion
|
||||
AddressTag() or
|
||||
CatchTag() or
|
||||
ThrowTag() or
|
||||
UnwindTag() or
|
||||
InitializerUninitializedTag() or
|
||||
InitializerElementIndexTag(int elementIndex) { elementIsInitialized(elementIndex) } or
|
||||
InitializerElementAddressTag(int elementIndex) { elementIsInitialized(elementIndex) } or
|
||||
InitializerElementDefaultValueTag(int elementIndex) { elementIsInitialized(elementIndex) } or
|
||||
InitializerElementDefaultValueStoreTag(int elementIndex) { elementIsInitialized(elementIndex) } or
|
||||
// Added for C#
|
||||
NewObjTag() or
|
||||
// TODO: remove the need for indexing
|
||||
PointerAddTag(int index) { index in [0 .. 255] } or
|
||||
ElementsAddressTag(int index) { index in [0 .. 255] } or
|
||||
ConvertTag() or
|
||||
GeneratedNeqTag() or
|
||||
GeneratedConstantTag() or
|
||||
GeneratedBranchTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = "Tag" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unique string for the instruction tag. Primarily used for generating
|
||||
* instruction IDs to ensure stable IR dumps.
|
||||
*/
|
||||
string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and result = "Only" // Single instruction (not including implicit Load)
|
||||
or
|
||||
tag = InitializerVariableAddressTag() and result = "InitVarAddr"
|
||||
or
|
||||
tag = InitializerLoadStringTag() and result = "InitLoadStr"
|
||||
or
|
||||
tag = InitializerStoreTag() and result = "InitStore"
|
||||
or
|
||||
tag = InitializerUninitializedTag() and result = "InitUninit"
|
||||
or
|
||||
tag = ZeroPadStringConstantTag() and result = "ZeroPadConst"
|
||||
or
|
||||
tag = ZeroPadStringElementIndexTag() and result = "ZeroPadElemIndex"
|
||||
or
|
||||
tag = ZeroPadStringElementAddressTag() and result = "ZeroPadElemAddr"
|
||||
or
|
||||
tag = ZeroPadStringStoreTag() and result = "ZeroPadStore"
|
||||
or
|
||||
tag = AssignOperationLoadTag() and result = "AssignOpLoad"
|
||||
or
|
||||
tag = AssignOperationConvertLeftTag() and result = "AssignOpConvLeft"
|
||||
or
|
||||
tag = AssignmentConvertRightTag() and result = "AssignConvRight"
|
||||
or
|
||||
tag = AssignOperationOpTag() and result = "AssignOpOp"
|
||||
or
|
||||
tag = AssignOperationConvertResultTag() and result = "AssignOpConvRes"
|
||||
or
|
||||
tag = AssignmentStoreTag() and result = "AssignStore"
|
||||
or
|
||||
tag = CrementLoadTag() and result = "CrementLoad"
|
||||
or
|
||||
tag = CrementConstantTag() and result = "CrementConst"
|
||||
or
|
||||
tag = CrementOpTag() and result = "CrementOp"
|
||||
or
|
||||
tag = CrementStoreTag() and result = "CrementStore"
|
||||
or
|
||||
tag = EnterFunctionTag() and result = "EnterFunc"
|
||||
or
|
||||
tag = ReturnValueAddressTag() and result = "RetValAddr"
|
||||
or
|
||||
tag = ReturnTag() and result = "Ret"
|
||||
or
|
||||
tag = ExitFunctionTag() and result = "ExitFunc"
|
||||
or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef"
|
||||
or
|
||||
tag = AliasedUseTag() and result = "AliasedUse"
|
||||
or
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch"
|
||||
or
|
||||
tag = CallTargetTag() and result = "CallTarget"
|
||||
or
|
||||
tag = CallTag() and result = "Call"
|
||||
or
|
||||
tag = CallSideEffectTag() and result = "CallSideEffect"
|
||||
or
|
||||
tag = AllocationSizeTag() and result = "AllocSize"
|
||||
or
|
||||
tag = AllocationElementSizeTag() and result = "AllocElemSize"
|
||||
or
|
||||
tag = AllocationExtentConvertTag() and result = "AllocExtConv"
|
||||
or
|
||||
tag = ValueConditionConditionalBranchTag() and result = "ValCondCondBranch"
|
||||
or
|
||||
tag = ConditionValueTrueTempAddressTag() and result = "CondValTrueTempAddr"
|
||||
or
|
||||
tag = ConditionValueTrueConstantTag() and result = "CondValTrueConst"
|
||||
or
|
||||
tag = ConditionValueTrueStoreTag() and result = "CondValTrueStore"
|
||||
or
|
||||
tag = ConditionValueFalseTempAddressTag() and result = "CondValFalseTempAddr"
|
||||
or
|
||||
tag = ConditionValueFalseConstantTag() and result = "CondValFalseConst"
|
||||
or
|
||||
tag = ConditionValueFalseStoreTag() and result = "CondValFalseStore"
|
||||
or
|
||||
tag = ConditionValueResultTempAddressTag() and result = "CondValResTempAddr"
|
||||
or
|
||||
tag = ConditionValueResultLoadTag() and result = "CondValResLoad"
|
||||
or
|
||||
tag = BoolConversionConstantTag() and result = "BoolConvConst"
|
||||
or
|
||||
tag = BoolConversionCompareTag() and result = "BoolConvComp"
|
||||
or
|
||||
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
|
||||
or
|
||||
tag = CatchTag() and result = "Catch"
|
||||
or
|
||||
tag = ThrowTag() and result = "Throw"
|
||||
or
|
||||
tag = UnwindTag() and result = "Unwind"
|
||||
or
|
||||
// Added for C#
|
||||
tag = NewObjTag() and result = "NewObj"
|
||||
or
|
||||
tag = ElementsAddressTag(_) and result = "ElementsAddress"
|
||||
or
|
||||
tag = PointerAddTag(_) and result = "PointerAdd"
|
||||
or
|
||||
tag = ConvertTag() and result = "Convert"
|
||||
or
|
||||
tag = GeneratedNeqTag() and result = "GeneratedNEQTag"
|
||||
or
|
||||
tag = GeneratedConstantTag() and result = "GeneratedConstantTag"
|
||||
or
|
||||
tag = GeneratedBranchTag() and result = "GeneratedBranchTag"
|
||||
or
|
||||
tag = AddressTag() and result = "AddressTag"
|
||||
or
|
||||
exists(int index, string tagName |
|
||||
(
|
||||
tag = InitializerElementIndexTag(index) and tagName = "InitElemIndex"
|
||||
or
|
||||
tag = InitializerElementAddressTag(index) and tagName = "InitElemAddr"
|
||||
or
|
||||
tag = InitializerElementDefaultValueTag(index) and tagName = "InitElemDefVal"
|
||||
or
|
||||
tag = InitializerElementDefaultValueStoreTag(index) and tagName = "InitElemDefValStore"
|
||||
) and
|
||||
result = tagName + "(" + index + ")"
|
||||
)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import experimental.ir.implementation.IRType as IRType
|
||||
import experimental.ir.internal.Overlap as Overlap
|
||||
import experimental.ir.implementation.internal.OperandTag as OperandTag
|
||||
import experimental.ir.implementation.internal.TOperand as TOperand
|
||||
@@ -1,2 +0,0 @@
|
||||
private import experimental.ir.implementation.internal.TOperand
|
||||
import RawOperands
|
||||
@@ -1,2 +0,0 @@
|
||||
import experimental.ir.IRConfiguration as IRConfiguration
|
||||
import experimental.ir.internal.IRCSharpLanguageDebug as LanguageDebug
|
||||
@@ -1,102 +0,0 @@
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedCallBase
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The function can be a normal function
|
||||
* (e.g. `MethodCall`) or a constructor call (e.g. `ObjectCreation`). Notice that the
|
||||
* AST generated translated calls are tied to an expression (unlike compiler generated ones,
|
||||
* which can be attached to either a statement or an expression).
|
||||
*/
|
||||
abstract class TranslatedCall extends TranslatedExpr, TranslatedCallBase {
|
||||
final override Instruction getResult() { result = TranslatedCallBase.super.getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a direct function call. The call can be one of the following:
|
||||
* `MethodCall`, `LocalFunctionCall`, `AccessorCall`, `OperatorCall`.
|
||||
* Note that `DelegateCall`s are not treated here since they need to be desugared.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedNonConstantExpr, TranslatedCall {
|
||||
override Call expr;
|
||||
|
||||
TranslatedFunctionCall() {
|
||||
expr instanceof MethodCall or
|
||||
expr instanceof LocalFunctionCall or
|
||||
expr instanceof AccessorCall or
|
||||
expr instanceof OperatorCall
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = expr.getTarget()
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(expr.getArgument(index))
|
||||
}
|
||||
|
||||
override TranslatedExpr getQualifier() {
|
||||
expr instanceof QualifiableExpr and
|
||||
result = getTranslatedExpr(expr.(QualifiableExpr).getQualifier())
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
// since `ElementInitializer`s do not have a qualifier, the qualifier's result is retrieved
|
||||
// from the enclosing initialization context
|
||||
if expr.getParent() instanceof CollectionInitializer
|
||||
then result = getTranslatedExpr(expr.getParent()).(InitializationContext).getTargetAddress()
|
||||
else result = this.getQualifier().getResult()
|
||||
}
|
||||
|
||||
override Type getCallResultType() { result = expr.getTarget().getReturnType() }
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).neverReadsMemory()
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).neverWritesMemory()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call to a constructor or to a constructor initializer.
|
||||
* The qualifier of the call is obtained from the constructor call context.
|
||||
* Note that `DelegateCreation` is not present here, since the call to a delegate constructor is
|
||||
* compiler generated.
|
||||
*/
|
||||
class TranslatedConstructorCall extends TranslatedNonConstantExpr, TranslatedCall {
|
||||
override Call expr;
|
||||
|
||||
TranslatedConstructorCall() {
|
||||
expr instanceof ObjectCreation or
|
||||
expr instanceof ConstructorInitializer
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = expr.getTarget()
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(expr.getArgument(index))
|
||||
}
|
||||
|
||||
// The qualifier for a constructor call has already been generated
|
||||
// (the `NewObj` instruction)
|
||||
override TranslatedExpr getQualifier() { none() }
|
||||
|
||||
override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
exists(ConstructorCallContext context |
|
||||
context = this.getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import common.TranslatedConditionBase
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
TranslatedCondition getTranslatedCondition(Expr expr) { result.getExpr() = expr }
|
||||
|
||||
abstract class TranslatedCondition extends ConditionBase {
|
||||
Expr expr;
|
||||
|
||||
final override string toString() { result = expr.toString() }
|
||||
|
||||
final override Language::AST getAst() { result = expr }
|
||||
|
||||
final Expr getExpr() { result = expr }
|
||||
|
||||
final override Callable getFunction() { result = expr.getEnclosingCallable() }
|
||||
|
||||
final Type getResultType() { result = expr.getType() }
|
||||
}
|
||||
|
||||
abstract class TranslatedFlexibleCondition extends TranslatedCondition, ConditionContext,
|
||||
TTranslatedFlexibleCondition
|
||||
{
|
||||
TranslatedFlexibleCondition() { this = TTranslatedFlexibleCondition(expr) }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = this.getOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
abstract TranslatedCondition getOperand();
|
||||
}
|
||||
|
||||
class TranslatedParenthesisCondition extends TranslatedFlexibleCondition {
|
||||
override ParenthesizedExpr expr;
|
||||
|
||||
final override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
|
||||
final override TranslatedCondition getOperand() {
|
||||
result = getTranslatedCondition(expr.getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedNotCondition extends TranslatedFlexibleCondition {
|
||||
override LogicalNotExpr expr;
|
||||
|
||||
override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
override TranslatedCondition getOperand() { result = getTranslatedCondition(expr.getOperand()) }
|
||||
}
|
||||
|
||||
abstract class TranslatedNativeCondition extends TranslatedCondition, TTranslatedNativeCondition {
|
||||
TranslatedNativeCondition() { this = TTranslatedNativeCondition(expr) }
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
abstract class TranslatedBinaryLogicalOperation extends TranslatedNativeCondition, ConditionContext {
|
||||
override BinaryLogicalOperation expr;
|
||||
|
||||
final override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getLeftOperand()
|
||||
or
|
||||
id = 1 and result = this.getRightOperand()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = this.getLeftOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
final TranslatedCondition getLeftOperand() {
|
||||
result = getTranslatedCondition(expr.getLeftOperand())
|
||||
}
|
||||
|
||||
final TranslatedCondition getRightOperand() {
|
||||
result = getTranslatedCondition(expr.getRightOperand())
|
||||
}
|
||||
|
||||
final TranslatedCondition getAnOperand() {
|
||||
result = this.getLeftOperand() or
|
||||
result = this.getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedLogicalAndExpr extends TranslatedBinaryLogicalOperation {
|
||||
TranslatedLogicalAndExpr() { expr instanceof LogicalAndExpr }
|
||||
|
||||
override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getLeftOperand() and
|
||||
result = this.getRightOperand().getFirstInstruction()
|
||||
or
|
||||
child = this.getRightOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getAnOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedLogicalOrExpr extends TranslatedBinaryLogicalOperation {
|
||||
override LogicalOrExpr expr;
|
||||
|
||||
override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getAnOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getLeftOperand() and
|
||||
result = this.getRightOperand().getFirstInstruction()
|
||||
or
|
||||
child = this.getRightOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedValueCondition extends TranslatedCondition, ValueConditionBase,
|
||||
TTranslatedValueCondition
|
||||
{
|
||||
TranslatedValueCondition() { this = TTranslatedValueCondition(expr) }
|
||||
|
||||
override TranslatedExpr getValueExpr() { result = getTranslatedExpr(expr) }
|
||||
|
||||
override Instruction valueExprResult() { result = this.getValueExpr().getResult() }
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.internal.IRUtilities
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import common.TranslatedDeclarationBase
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedDeclaration` that represents the declaration
|
||||
* `entry`.
|
||||
*/
|
||||
TranslatedLocalDeclaration getTranslatedLocalDeclaration(LocalVariableDeclExpr declExpr) {
|
||||
result.getAst() = declExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a declaration within the body of a function.
|
||||
*/
|
||||
abstract class TranslatedLocalDeclaration extends TranslatedElement, TTranslatedDeclaration {
|
||||
LocalVariableDeclExpr expr;
|
||||
|
||||
TranslatedLocalDeclaration() { this = TTranslatedDeclaration(expr) }
|
||||
|
||||
final override Callable getFunction() { result = expr.getEnclosingCallable() }
|
||||
|
||||
final override string toString() { result = expr.toString() }
|
||||
|
||||
final override Language::AST getAst() { result = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the declaration of a local variable,
|
||||
* including its initialization, if any.
|
||||
*/
|
||||
class TranslatedLocalVariableDeclaration extends TranslatedLocalDeclaration,
|
||||
LocalVariableDeclarationBase, InitializationContext
|
||||
{
|
||||
LocalVariable var;
|
||||
|
||||
TranslatedLocalVariableDeclaration() { var = expr.getVariable() }
|
||||
|
||||
override Instruction getTargetAddress() {
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override LocalVariable getDeclVar() { result = var }
|
||||
|
||||
override Type getVarType() { result = getVariableType(this.getDeclVar()) }
|
||||
|
||||
override Type getTargetType() { result = getVariableType(var) }
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag()
|
||||
or
|
||||
this.hasUninitializedInstruction() and tag = InitializerStoreTag()
|
||||
) and
|
||||
result = getIRUserVariable(this.getFunction(), this.getDeclVar())
|
||||
}
|
||||
|
||||
override TranslatedInitialization getInitialization() {
|
||||
// First complex initializations
|
||||
if var.getInitializer() instanceof ArrayCreation
|
||||
then result = getTranslatedInitialization(var.getInitializer().(ArrayCreation).getInitializer())
|
||||
else
|
||||
if var.getInitializer() instanceof ObjectCreation
|
||||
then result = getTranslatedInitialization(var.getInitializer())
|
||||
else
|
||||
// then the simple variable initialization
|
||||
result = getTranslatedInitialization(var.getInitializer())
|
||||
}
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
import csharp
|
||||
import experimental.ir.implementation.raw.IR
|
||||
private import experimental.ir.IRConfiguration
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedFunction
|
||||
private import TranslatedStmt
|
||||
private import IRConstruction
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import desugar.Foreach
|
||||
private import desugar.Delegate
|
||||
private import desugar.Lock
|
||||
|
||||
ArrayType getArrayOfDim(int dim, Type type) {
|
||||
result.getRank() = dim and
|
||||
result.getElementType() = type
|
||||
}
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
result.getEnclosingFunction() = func
|
||||
}
|
||||
|
||||
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
result.getAst() = ast and
|
||||
result.getTag() = tag
|
||||
}
|
||||
|
||||
private predicate canCreateCompilerGeneratedElement(Element generatedBy, int nth) {
|
||||
generatedBy instanceof ForeachStmt and nth in [0 .. ForeachElements::noGeneratedElements() - 1]
|
||||
or
|
||||
generatedBy instanceof LockStmt and nth in [0 .. LockElements::noGeneratedElements() - 1]
|
||||
or
|
||||
generatedBy instanceof DelegateCreation and
|
||||
nth in [0 .. DelegateElements::noGeneratedElements(generatedBy) - 1]
|
||||
or
|
||||
generatedBy instanceof DelegateCall and
|
||||
nth in [0 .. DelegateElements::noGeneratedElements(generatedBy) - 1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "real" parent of `expr`. This predicate treats conversions as if
|
||||
* they were explicit nodes in the expression tree, rather than as implicit
|
||||
* nodes as in the regular AST representation.
|
||||
*/
|
||||
private Element getRealParent(Expr expr) { result = expr.getParent() }
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a constant of a type that can be replaced directly with
|
||||
* its value in the IR. This does not include address constants as we have no
|
||||
* means to express those as QL values.
|
||||
*/
|
||||
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
|
||||
|
||||
// Pulled out for performance. See
|
||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
||||
private predicate isOrphan(Expr expr) { not exists(getRealParent(expr)) }
|
||||
|
||||
/**
|
||||
* Holds if `expr` should be ignored for the purposes of IR generation due to
|
||||
* some property of `expr` or one of its ancestors.
|
||||
*/
|
||||
private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
// Ignore parentless expressions
|
||||
isOrphan(expr)
|
||||
or
|
||||
// Ignore the constants in SwitchCase, since their values are embedded in the
|
||||
// CaseEdge.
|
||||
getRealParent(expr) instanceof CaseStmt
|
||||
or
|
||||
// Ignore descendants of constant expressions, since we'll just substitute the
|
||||
// constant value.
|
||||
isIRConstant(getRealParent(expr))
|
||||
or
|
||||
// Ignore the local declaration done by a `ForeachStmt`
|
||||
// since we desugar it
|
||||
expr instanceof LocalVariableDeclExpr and
|
||||
expr.getParent() instanceof ForeachStmt
|
||||
or
|
||||
// recursive case
|
||||
ignoreExprAndDescendants(getRealParent(expr)) and
|
||||
// The two children of an `AssignOperation` should not be ignored, but since they are also
|
||||
// descendants of an orphan node (the expanded form of the `AssignOperation` is also retrieved by
|
||||
// the extractor, which is rooted in an AST node without parents) they would be
|
||||
not expr.getParent() instanceof AssignOperation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` (not including its descendants) should be ignored for the
|
||||
* purposes of IR generation.
|
||||
*/
|
||||
private predicate ignoreExprOnly(Expr expr) {
|
||||
not translateFunction(expr.getEnclosingCallable())
|
||||
or
|
||||
// Ignore size of arrays when translating
|
||||
expr.getParent() instanceof ArrayCreation and expr.hasValue()
|
||||
or
|
||||
// Ignore the child expression of a goto case stmt
|
||||
expr.getParent() instanceof GotoCaseStmt
|
||||
or
|
||||
// Ignore the expression (that is not a declaration)
|
||||
// that appears in a using block
|
||||
expr.getParent().(UsingBlockStmt).getExpr() = expr
|
||||
or
|
||||
// Ignore the `ThisAccess` when it is used as the qualifier for
|
||||
// a callable access (e.g. when a member callable is passed as a
|
||||
// parameter for a delegate creation expression)
|
||||
expr instanceof ThisAccess and
|
||||
expr.getParent() instanceof CallableAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` should be ignored for the purposes of IR generation.
|
||||
*/
|
||||
private predicate ignoreExpr(Expr expr) {
|
||||
ignoreExprOnly(expr) or
|
||||
ignoreExprAndDescendants(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `func` should be translated to IR.
|
||||
*/
|
||||
private predicate translateFunction(Callable callable) {
|
||||
// not isInvalidFunction(callable)
|
||||
exists(callable.getEntryPoint()) and
|
||||
callable.fromSource() and
|
||||
exists(IRConfiguration config | config.shouldCreateIRForFunction(callable))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `stmt` should be translated to IR.
|
||||
*/
|
||||
private predicate translateStmt(Stmt stmt) { translateFunction(stmt.getEnclosingCallable()) }
|
||||
|
||||
/**
|
||||
* Holds if `expr` is most naturally evaluated as control flow, rather than as
|
||||
* a value.
|
||||
*/
|
||||
private predicate isNativeCondition(Expr expr) {
|
||||
expr instanceof BinaryLogicalOperation and
|
||||
not isIRConstant(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` can be evaluated as either a condition or a value expression,
|
||||
* depending on context.
|
||||
*/
|
||||
private predicate isFlexibleCondition(Expr expr) {
|
||||
(
|
||||
expr instanceof ParenthesizedExpr or
|
||||
expr instanceof LogicalNotExpr
|
||||
) and
|
||||
usedAsCondition(expr) and
|
||||
not isIRConstant(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is used in a condition context, i.e. the Boolean result of
|
||||
* the expression is directly used to determine control flow.
|
||||
*/
|
||||
private predicate usedAsCondition(Expr expr) {
|
||||
exists(BinaryLogicalOperation op |
|
||||
op.getLeftOperand() = expr or
|
||||
op.getRightOperand() = expr
|
||||
)
|
||||
or
|
||||
exists(LoopStmt loop | loop.getCondition() = expr)
|
||||
or
|
||||
exists(IfStmt ifStmt | ifStmt.getCondition() = expr)
|
||||
or
|
||||
exists(ConditionalExpr condExpr | condExpr.getCondition() = expr)
|
||||
or
|
||||
exists(LogicalNotExpr notExpr |
|
||||
notExpr.getOperand() = expr and
|
||||
usedAsCondition(notExpr)
|
||||
)
|
||||
or
|
||||
exists(ParenthesizedExpr paren |
|
||||
paren.getExpr() = expr and
|
||||
usedAsCondition(paren)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we should have a `Load` instruction for `expr` when generating the IR.
|
||||
*/
|
||||
private predicate mayNeedLoad(Expr expr) {
|
||||
expr instanceof AssignableRead
|
||||
or
|
||||
// We need an extra load for the `PointerIndirectionExpr`
|
||||
expr instanceof PointerIndirectionExpr and
|
||||
// If the dereferencing happens on the lhs of an
|
||||
// assignment we shouldn't have a load instruction
|
||||
not exists(Assignment a | a.getLValue() = expr)
|
||||
}
|
||||
|
||||
predicate needsLoad(Expr expr) {
|
||||
mayNeedLoad(expr) and
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if we should ignore the `Load` instruction for `expr` when generating IR.
|
||||
*/
|
||||
private predicate ignoreLoad(Expr expr) {
|
||||
// No load needed for the qualifier of an array access,
|
||||
// since we use the instruction `ElementsAddress`
|
||||
// to get the address of the first element in an array
|
||||
expr = any(ArrayAccess aa).getQualifier()
|
||||
or
|
||||
// Indexer calls returns a reference or a value,
|
||||
// no need to load it
|
||||
expr instanceof IndexerCall
|
||||
or
|
||||
// No load is needed for the lvalue in an assignment such as:
|
||||
// Eg. `Object obj = oldObj`;
|
||||
expr = any(Assignment a).getLValue() and
|
||||
expr.getType() instanceof RefType
|
||||
or
|
||||
// Since the loads for a crement operation is handled by the translation
|
||||
// of the operation, we ignore the load here
|
||||
expr.getParent() instanceof MutatorOperation
|
||||
or
|
||||
// The `&` operator does not need a load, since the
|
||||
// address is the final value of the expression
|
||||
expr.getParent() instanceof AddressOfExpr
|
||||
or
|
||||
// A property access does not need a load since it is a call
|
||||
expr instanceof PropertyAccess
|
||||
or
|
||||
// If expr is a variable access used as the qualifier for a field access and
|
||||
// its target variable is a value type variable,
|
||||
// ignore the load since the address of a variable that is a value type is
|
||||
// given by a single `VariableAddress` instruction.
|
||||
expr = any(FieldAccess fa).getQualifier() and
|
||||
expr =
|
||||
any(VariableAccess va |
|
||||
va.getType().isValueType() and
|
||||
not va.getTarget() = any(Parameter p | p.isOutOrRef() or p.isIn())
|
||||
)
|
||||
or
|
||||
// If expr is passed as an `out,`ref` or `in` argument,
|
||||
// no load should take place since we pass the address, not the
|
||||
// value of the variable
|
||||
expr.(AssignableAccess).isOutOrRefArgument()
|
||||
or
|
||||
expr.(AssignableAccess).isInArgument()
|
||||
}
|
||||
|
||||
newtype TTranslatedElement =
|
||||
// An expression that is not being consumed as a condition
|
||||
TTranslatedValueExpr(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
not isNativeCondition(expr) and
|
||||
not isFlexibleCondition(expr)
|
||||
} or
|
||||
// A creation expression
|
||||
TTranslatedCreationExpr(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
(expr instanceof ObjectCreation or expr instanceof DelegateCreation)
|
||||
} or
|
||||
// A separate element to handle the lvalue-to-rvalue conversion step of an
|
||||
// expression.
|
||||
TTranslatedLoad(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
needsLoad(expr)
|
||||
} or
|
||||
// An expression most naturally translated as control flow.
|
||||
TTranslatedNativeCondition(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
isNativeCondition(expr)
|
||||
} or
|
||||
// An expression that can best be translated as control flow given the context
|
||||
// in which it is used.
|
||||
TTranslatedFlexibleCondition(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
isFlexibleCondition(expr)
|
||||
} or
|
||||
// An expression that is not naturally translated as control flow, but is
|
||||
// consumed in a condition context. This element adapts the original element
|
||||
// to the condition context.
|
||||
TTranslatedValueCondition(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
not isNativeCondition(expr) and
|
||||
not isFlexibleCondition(expr) and
|
||||
usedAsCondition(expr)
|
||||
} or
|
||||
// An expression that is naturally translated as control flow, but is used in
|
||||
// a context where a simple value is expected. This element adapts the
|
||||
// original condition to the value context.
|
||||
TTranslatedConditionValue(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
isNativeCondition(expr) and
|
||||
not usedAsCondition(expr)
|
||||
} or
|
||||
// An expression used as an initializer.
|
||||
TTranslatedInitialization(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
(
|
||||
// Because of their implementation in C#,
|
||||
// we deal with all the types of initialization separately.
|
||||
// First only simple local variable initialization (ie. `int x = 0`)
|
||||
exists(LocalVariableDeclAndInitExpr lvInit | lvInit.getInitializer() = expr)
|
||||
or
|
||||
// Then treat more complex ones
|
||||
expr instanceof ArrayInitializer
|
||||
or
|
||||
expr instanceof ObjectInitializer
|
||||
or
|
||||
expr = any(ThrowElement throwElement).getExpr()
|
||||
or
|
||||
expr = any(CollectionInitializer colInit).getAnElementInitializer()
|
||||
or
|
||||
expr = any(ReturnStmt returnStmt).getExpr()
|
||||
or
|
||||
expr = any(ArrayInitializer arrInit).getAnElement()
|
||||
)
|
||||
} or
|
||||
// The initialization of an array element via a member of an initializer list.
|
||||
TTranslatedExplicitElementInitialization(ArrayInitializer initList, int elementIndex) {
|
||||
not ignoreExpr(initList) and
|
||||
exists(initList.getElement(elementIndex))
|
||||
} or
|
||||
// The initialization of a base class from within a constructor.
|
||||
TTranslatedConstructorInitializer(ConstructorInitializer init) { not ignoreExpr(init) } or
|
||||
// A statement
|
||||
TTranslatedStmt(Stmt stmt) { translateStmt(stmt) } or
|
||||
// A function
|
||||
TTranslatedFunction(Callable callable) { translateFunction(callable) } or
|
||||
// A function parameter
|
||||
TTranslatedParameter(Parameter param) {
|
||||
exists(Callable func |
|
||||
func = param.getCallable() and
|
||||
translateFunction(func)
|
||||
)
|
||||
} or
|
||||
// A local declaration
|
||||
TTranslatedDeclaration(LocalVariableDeclExpr entry) {
|
||||
// foreach var decl and init is treated separately,
|
||||
// because foreach needs desugaring
|
||||
not ignoreExprAndDescendants(entry)
|
||||
} or
|
||||
// A compiler generated element, generated by `generatedBy` during the
|
||||
// desugaring process
|
||||
TTranslatedCompilerGeneratedElement(Element generatedBy, int index) {
|
||||
canCreateCompilerGeneratedElement(generatedBy, index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an AST node for which IR needs to be generated.
|
||||
*
|
||||
* In most cases, there is a single `TranslatedElement` for each AST node.
|
||||
* However, when a single AST node performs two separable operations (e.g.
|
||||
* a `VariableAccess` that is also a load), there may be multiple
|
||||
* `TranslatedElement` nodes for a single AST node.
|
||||
*/
|
||||
abstract class TranslatedElement extends TTranslatedElement {
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the AST node being translated.
|
||||
*/
|
||||
abstract Language::AST getAst();
|
||||
|
||||
/**
|
||||
* Get the first instruction to be executed in the evaluation of this element.
|
||||
*/
|
||||
abstract Instruction getFirstInstruction();
|
||||
|
||||
/**
|
||||
* Get the immediate child elements of this element.
|
||||
*/
|
||||
final TranslatedElement getAChild() { result = this.getChild(_) }
|
||||
|
||||
/**
|
||||
* Gets the immediate child element of this element. The `id` is unique
|
||||
* among all children of this element, but the values are not necessarily
|
||||
* consecutive.
|
||||
*/
|
||||
abstract TranslatedElement getChild(int id);
|
||||
|
||||
/**
|
||||
* Gets the an identifier string for the element. This id is unique within
|
||||
* the scope of the element's function.
|
||||
*/
|
||||
int getId() { result = this.getUniqueId() }
|
||||
|
||||
private TranslatedElement getChildByRank(int rankIndex) {
|
||||
result =
|
||||
rank[rankIndex + 1](TranslatedElement child, int id |
|
||||
child = this.getChild(id)
|
||||
|
|
||||
child order by id
|
||||
)
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
private int getDescendantCount() {
|
||||
result =
|
||||
1 + sum(TranslatedElement child | child = this.getChildByRank(_) | child.getDescendantCount())
|
||||
}
|
||||
|
||||
private int getUniqueId() {
|
||||
if not exists(this.getParent())
|
||||
then result = 0
|
||||
else
|
||||
exists(TranslatedElement parent |
|
||||
parent = this.getParent() and
|
||||
if this = parent.getChildByRank(0)
|
||||
then result = 1 + parent.getUniqueId()
|
||||
else
|
||||
exists(int childIndex, TranslatedElement previousChild |
|
||||
this = parent.getChildByRank(childIndex) and
|
||||
previousChild = parent.getChildByRank(childIndex - 1) and
|
||||
result = previousChild.getUniqueId() + previousChild.getDescendantCount()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element generates an instruction with opcode `opcode` and
|
||||
* result type `resultType`. `tag` must be unique for each instruction
|
||||
* generated from the same AST node (not just from the same
|
||||
* `TranslatedElement`).
|
||||
* If the instruction does not return a result, `resultType` should be
|
||||
* `VoidType`.
|
||||
*/
|
||||
abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType);
|
||||
|
||||
/**
|
||||
* Gets the `Function` that contains this element.
|
||||
*/
|
||||
abstract Callable getFunction();
|
||||
|
||||
/**
|
||||
* Gets the successor instruction of the instruction that was generated by
|
||||
* this element for tag `tag`. The successor edge kind is specified by `kind`.
|
||||
*/
|
||||
abstract Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind);
|
||||
|
||||
/**
|
||||
* Gets the successor instruction to which control should flow after the
|
||||
* child element specified by `child` has finished execution.
|
||||
*/
|
||||
abstract Instruction getChildSuccessor(TranslatedElement child);
|
||||
|
||||
/**
|
||||
* Gets the instruction to which control should flow if an exception is thrown
|
||||
* within this element. This will generally return first `catch` block of the
|
||||
* nearest enclosing `try`, or the `Unwind` instruction for the function if
|
||||
* there is no enclosing `try`.
|
||||
*/
|
||||
Instruction getExceptionSuccessorInstruction() {
|
||||
result = this.getParent().getExceptionSuccessorInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary instruction for the side effect instruction that was
|
||||
* generated by this element for tag `tag`.
|
||||
*/
|
||||
Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element generates a temporary variable with type `type`.
|
||||
* `tag` must be unique for each variable generated from the same AST node
|
||||
* (not just from the same `TranslatedElement`).
|
||||
*/
|
||||
predicate hasTempVariable(TempVariableTag tag, CSharpType type) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `FunctionInstruction`, gets the
|
||||
* `Function` for that instruction.
|
||||
*/
|
||||
Callable getInstructionFunction(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `VariableInstruction`, gets the
|
||||
* `IRVariable` for that instruction.
|
||||
*/
|
||||
IRVariable getInstructionVariable(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `FieldInstruction`, gets the
|
||||
* `Field` for that instruction.
|
||||
*/
|
||||
Field getInstructionField(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is an `IndexedElementInstruction`,
|
||||
* gets the `ArrayAccess` of that instruction.
|
||||
*/
|
||||
ArrayAccess getInstructionArrayAccess(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `ConstantValueInstruction`, gets
|
||||
* the constant value for that instruction.
|
||||
*/
|
||||
string getInstructionConstantValue(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `PointerArithmeticInstruction`,
|
||||
* gets the size of the type pointed to by the pointer.
|
||||
*/
|
||||
int getInstructionElementSize(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` has a result of type `UnknownType`,
|
||||
* gets the size of the result in bytes. If the result does not have a known
|
||||
* constant size, this predicate does not hold.
|
||||
*/
|
||||
int getInstructionResultSize(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `StringConstantInstruction`,
|
||||
* gets the `StringLiteral` for that instruction.
|
||||
*/
|
||||
StringLiteral getInstructionStringLiteral(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `CatchByTypeInstruction`,
|
||||
* gets the type of the exception to be caught.
|
||||
*/
|
||||
CSharpType getInstructionExceptionType(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is an `InheritanceConversionInstruction`,
|
||||
* gets the inheritance relationship for that instruction.
|
||||
*/
|
||||
predicate getInstructionInheritance(InstructionTag tag, Class baseClass, Class derivedClass) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result is consumed as an operand of the
|
||||
* instruction specified by `tag`, with the operand specified by `operandTag`.
|
||||
*/
|
||||
Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
|
||||
*/
|
||||
CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
|
||||
/**
|
||||
* Gets the instruction generated by this element with tag `tag`.
|
||||
*/
|
||||
final Instruction getInstruction(InstructionTag tag) {
|
||||
getInstructionTranslatedElement(result) = this and
|
||||
getInstructionTag(result) = tag
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the temporary variable generated by this element with tag `tag`.
|
||||
*/
|
||||
final IRTempVariable getTempVariable(TempVariableTag tag) {
|
||||
result.getAst() = this.getAst() and
|
||||
result.getTag() = tag and
|
||||
this.hasTempVariable(tag, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent element of this element.
|
||||
*/
|
||||
final TranslatedElement getParent() { result.getAChild() = this }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,333 +0,0 @@
|
||||
import csharp
|
||||
import experimental.ir.implementation.raw.IR
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.IRUtilities
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import TranslatedStmt
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` that represents function `callable`.
|
||||
*/
|
||||
TranslatedFunction getTranslatedFunction(Callable callable) { result.getAst() = callable }
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function. This is the root element for
|
||||
* all other elements associated with this function.
|
||||
*/
|
||||
class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
Callable callable;
|
||||
|
||||
TranslatedFunction() { this = TTranslatedFunction(callable) }
|
||||
|
||||
final override string toString() { result = callable.toString() }
|
||||
|
||||
final override Language::AST getAst() { result = callable }
|
||||
|
||||
/**
|
||||
* Gets the function being translated.
|
||||
*/
|
||||
final override Callable getFunction() { result = callable }
|
||||
|
||||
final override TranslatedElement getChild(int id) {
|
||||
id = -2 and result = this.getConstructorInitializer()
|
||||
or
|
||||
id = -1 and result = this.getBody()
|
||||
or
|
||||
result = this.getParameter(id)
|
||||
}
|
||||
|
||||
final private TranslatedConstructorInitializer getConstructorInitializer() {
|
||||
exists(ConstructorInitializer ci |
|
||||
ci = callable.getAChild() and
|
||||
result = getTranslatedConstructorInitializer(ci)
|
||||
)
|
||||
}
|
||||
|
||||
final private TranslatedStmt getBody() { result = getTranslatedStmt(callable.getBody()) }
|
||||
|
||||
final private TranslatedParameter getParameter(int index) {
|
||||
result = getTranslatedParameter(callable.getParameter(index))
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(EnterFunctionTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
result = this.getInstruction(AliasedDefinitionTag())
|
||||
or
|
||||
(
|
||||
tag = AliasedDefinitionTag() and
|
||||
if exists(this.getThisType())
|
||||
then result = this.getInstruction(InitializeThisTag())
|
||||
else
|
||||
if exists(this.getParameter(0))
|
||||
then result = this.getParameter(0).getFirstInstruction()
|
||||
else result = this.getBodyOrReturn()
|
||||
)
|
||||
or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
if exists(this.getParameter(0))
|
||||
then result = this.getParameter(0).getFirstInstruction()
|
||||
else
|
||||
if exists(this.getConstructorInitializer())
|
||||
then result = this.getConstructorInitializer().getFirstInstruction()
|
||||
else result = this.getBodyOrReturn()
|
||||
)
|
||||
or
|
||||
tag = ReturnValueAddressTag() and
|
||||
result = this.getInstruction(ReturnTag())
|
||||
or
|
||||
tag = ReturnTag() and
|
||||
result = this.getInstruction(AliasedUseTag())
|
||||
or
|
||||
tag = UnwindTag() and
|
||||
result = this.getInstruction(AliasedUseTag())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
result = this.getInstruction(ExitFunctionTag())
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
exists(int paramIndex |
|
||||
child = this.getParameter(paramIndex) and
|
||||
if exists(callable.getParameter(paramIndex + 1))
|
||||
then result = this.getParameter(paramIndex + 1).getFirstInstruction()
|
||||
else
|
||||
if exists(this.getConstructorInitializer())
|
||||
then result = this.getConstructorInitializer().getFirstInstruction()
|
||||
else result = this.getBodyOrReturn()
|
||||
)
|
||||
or
|
||||
child = this.getConstructorInitializer() and
|
||||
result = this.getBodyOrReturn()
|
||||
or
|
||||
child = this.getBody() and
|
||||
result = this.getReturnSuccessorInstruction()
|
||||
}
|
||||
|
||||
private Instruction getBodyOrReturn() {
|
||||
if exists(this.getBody())
|
||||
then result = this.getBody().getFirstInstruction()
|
||||
else result = this.getReturnSuccessorInstruction()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
opcode instanceof Opcode::EnterFunction and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType = getUnknownType()
|
||||
or
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
resultType = getTypeForGLValue(this.getThisType())
|
||||
or
|
||||
tag = ReturnValueAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
not this.getReturnType() instanceof VoidType and
|
||||
resultType = getTypeForGLValue(this.getReturnType())
|
||||
or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
resultType = getVoidType() and
|
||||
if this.getReturnType() instanceof VoidType
|
||||
then opcode instanceof Opcode::ReturnVoid
|
||||
else opcode instanceof Opcode::ReturnValue
|
||||
)
|
||||
or
|
||||
tag = UnwindTag() and
|
||||
opcode instanceof Opcode::Unwind and
|
||||
resultType = getVoidType() and
|
||||
(
|
||||
// Only generate the `Unwind` instruction if there is any exception
|
||||
// handling present in the function (compiler generated or not).
|
||||
exists(TryStmt try | try.getEnclosingCallable() = callable) or
|
||||
exists(ThrowStmt throw | throw.getEnclosingCallable() = callable)
|
||||
)
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
opcode instanceof Opcode::AliasedUse and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = ExitFunctionTag() and
|
||||
opcode instanceof Opcode::ExitFunction and
|
||||
resultType = getVoidType()
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getExceptionSuccessorInstruction() {
|
||||
result = this.getInstruction(UnwindTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = ReturnTag() and
|
||||
not this.getReturnType() instanceof VoidType and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ReturnValueAddressTag())
|
||||
}
|
||||
|
||||
final override CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = ReturnTag() and
|
||||
not this.getReturnType() instanceof VoidType and
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getTypeForPRValue(this.getReturnType())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = ReturnValueAddressTag() and
|
||||
result = this.getReturnVariable()
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = ReturnValueTempVar() and
|
||||
type = getTypeForPRValue(this.getReturnType()) and
|
||||
not this.getReturnType() instanceof VoidType
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction to which control should flow after a `return`
|
||||
* statement. In C#, this should be the instruction which generates `VariableAddress[#return]`.
|
||||
*/
|
||||
final Instruction getReturnSuccessorInstruction() {
|
||||
if this.getReturnType() instanceof VoidType
|
||||
then result = this.getInstruction(ReturnTag())
|
||||
else result = this.getInstruction(ReturnValueAddressTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable that represents the return value of this function.
|
||||
*/
|
||||
final IRReturnVariable getReturnVariable() {
|
||||
result = getIRTempVariable(callable, ReturnValueTempVar())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single `InitializeThis` instruction for this function. Holds only
|
||||
* if the function is an instance member function, constructor, or destructor.
|
||||
*/
|
||||
final Instruction getInitializeThisInstruction() {
|
||||
result = this.getInstruction(InitializeThisTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type pointed to by the `this` pointer for this function (i.e. `*this`).
|
||||
* Holds only if the function is an instance member function, constructor, or destructor.
|
||||
*/
|
||||
final Type getThisType() {
|
||||
// `callable` is a user declared member and it is not static
|
||||
callable instanceof Member and
|
||||
not callable.(Member).isStatic() and
|
||||
result = callable.getDeclaringType()
|
||||
or
|
||||
// `callable` is a compiler generated accessor
|
||||
callable instanceof Accessor and
|
||||
not callable.(Accessor).isStatic() and
|
||||
result = callable.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function defines or accesses variable `var` with type `type`. This includes all
|
||||
* parameters and local variables, plus any static fields that are directly accessed by the
|
||||
* function.
|
||||
*/
|
||||
final predicate hasUserVariable(Variable var, CSharpType type) {
|
||||
(
|
||||
var.(Member).isStatic() and
|
||||
exists(VariableAccess access |
|
||||
access.getTarget() = var and
|
||||
access.getEnclosingCallable() = callable
|
||||
)
|
||||
or
|
||||
var.(LocalScopeVariable).getCallable() = callable
|
||||
) and
|
||||
type = getTypeForPRValue(getVariableType(var))
|
||||
}
|
||||
|
||||
final private Type getReturnType() { result = callable.getReturnType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedParameter` that represents parameter `param`.
|
||||
*/
|
||||
TranslatedParameter getTranslatedParameter(Parameter param) { result.getAst() = param }
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function parameter, including the
|
||||
* initialization of that parameter with the incoming argument.
|
||||
*/
|
||||
class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
Parameter param;
|
||||
|
||||
TranslatedParameter() { this = TTranslatedParameter(param) }
|
||||
|
||||
final override string toString() { result = param.toString() }
|
||||
|
||||
final override Language::AST getAst() { result = param }
|
||||
|
||||
final override Callable getFunction() { result = param.getCallable() }
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = this.getInstruction(InitializerStoreTag())
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(param.getType())
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::InitializeParameter and
|
||||
resultType = getTypeForPRValue(param.getType())
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
(
|
||||
tag = InitializerStoreTag() or
|
||||
tag = InitializerVariableAddressTag()
|
||||
) and
|
||||
result = getIRUserVariable(this.getFunction(), param)
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
/**
|
||||
* Class that deals with variable initializations.
|
||||
* Separated from `TranslatedExpr` for clarity.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
private import IRInternal
|
||||
private import desugar.Delegate
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedInitialization` for the expression `expr`.
|
||||
*/
|
||||
TranslatedInitialization getTranslatedInitialization(Expr expr) { result.getExpr() = expr }
|
||||
|
||||
/**
|
||||
* Base class for any `TranslatedElement` that has an initialization as a child.
|
||||
* Provides the child with the address and type of the location to be
|
||||
* initialized.
|
||||
*/
|
||||
abstract class InitializationContext extends TranslatedElement {
|
||||
/**
|
||||
* Gets the instruction that produces the address of the location to be
|
||||
* initialized.
|
||||
*/
|
||||
abstract Instruction getTargetAddress();
|
||||
|
||||
/**
|
||||
* Gets the type of the location to be initialized.
|
||||
*/
|
||||
abstract Type getTargetType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of any initialization, whether from an
|
||||
* initializer list or from a direct initializer.
|
||||
*/
|
||||
abstract class TranslatedInitialization extends TranslatedElement, TTranslatedInitialization {
|
||||
Expr expr;
|
||||
|
||||
TranslatedInitialization() { this = TTranslatedInitialization(expr) }
|
||||
|
||||
final override string toString() { result = "init: " + expr.toString() }
|
||||
|
||||
final override Callable getFunction() { result = expr.getEnclosingCallable() }
|
||||
|
||||
final override Language::AST getAst() { result = expr }
|
||||
|
||||
/**
|
||||
* Gets the expression that is doing the initialization.
|
||||
*/
|
||||
final Expr getExpr() { result = expr }
|
||||
|
||||
/**
|
||||
* Gets the initialization context that describes the location being
|
||||
* initialized.
|
||||
*/
|
||||
final InitializationContext getContext() { result = this.getParent() }
|
||||
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingCallable())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of an initialization from an initializer list.
|
||||
*/
|
||||
abstract class TranslatedListInitialization extends TranslatedInitialization, InitializationContext {
|
||||
override Instruction getFirstInstruction() {
|
||||
result = this.getChild(0).getFirstInstruction()
|
||||
or
|
||||
not exists(this.getChild(0)) and result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
exists(int index |
|
||||
child = this.getChild(index) and
|
||||
if exists(this.getChild(index + 1))
|
||||
then result = this.getChild(index + 1).getFirstInstruction()
|
||||
else result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getTargetAddress() { result = this.getContext().getTargetAddress() }
|
||||
|
||||
override Type getTargetType() { result = this.getContext().getTargetType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of an initialization of an array from an
|
||||
* initializer list.
|
||||
*/
|
||||
class TranslatedArrayListInitialization extends TranslatedListInitialization {
|
||||
override ArrayInitializer expr;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
// The children are in initialization order
|
||||
result =
|
||||
rank[id + 1](TranslatedElementInitialization init |
|
||||
init.getInitList() = expr
|
||||
|
|
||||
init order by init.getElementIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of an initialization from a single initializer
|
||||
* expression, where the initialization is performed via bitwise copy.
|
||||
*/
|
||||
class TranslatedDirectInitialization extends TranslatedInitialization {
|
||||
TranslatedDirectInitialization() {
|
||||
not expr instanceof ArrayInitializer and
|
||||
not expr instanceof ObjectInitializer and
|
||||
not expr instanceof CollectionInitializer
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitializer() }
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = this.getInitializer().getFirstInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(this.getContext().getTargetType())
|
||||
or
|
||||
this.needsConversion() and
|
||||
tag = AssignmentConvertRightTag() and
|
||||
// For now only use `Opcode::Convert` to
|
||||
// crudely represent conversions. Could
|
||||
// be useful to represent the whole chain of conversions
|
||||
opcode instanceof Opcode::Convert and
|
||||
resultType = getTypeForPRValue(this.getContext().getTargetType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = InitializerStoreTag() and
|
||||
result = this.getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
this.needsConversion() and
|
||||
tag = AssignmentConvertRightTag() and
|
||||
result = this.getInstruction(InitializerStoreTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInitializer() and
|
||||
if this.needsConversion()
|
||||
then result = this.getInstruction(AssignmentConvertRightTag())
|
||||
else result = this.getInstruction(InitializerStoreTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getParent().(InitializationContext).getTargetAddress()
|
||||
or
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getContext().getTargetAddress()
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and
|
||||
result = this.getInitializer().getResult()
|
||||
)
|
||||
)
|
||||
or
|
||||
tag = AssignmentConvertRightTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getInitializer().getResult()
|
||||
or
|
||||
tag = AssignmentConvertRightTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getInstruction(NewObjTag())
|
||||
}
|
||||
|
||||
TranslatedExpr getInitializer() { result = getTranslatedExpr(expr) }
|
||||
|
||||
private predicate needsConversion() { expr.getType() != this.getContext().getTargetType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an element of an initializer list.
|
||||
*/
|
||||
abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
ArrayInitializer initList;
|
||||
|
||||
final override string toString() {
|
||||
result = initList.toString() + "[" + this.getElementIndex().toString() + "]"
|
||||
}
|
||||
|
||||
final override Language::AST getAst() { result = initList }
|
||||
|
||||
final override Callable getFunction() { result = initList.getEnclosingCallable() }
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(this.getElementIndexTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = this.getElementIndexTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType()
|
||||
or
|
||||
tag = this.getElementAddressTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getTypeForGLValue(this.getElementType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = this.getElementIndexTag() and
|
||||
result = this.getInstruction(this.getElementAddressTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = this.getElementAddressTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getParent().(InitializationContext).getTargetAddress()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(this.getElementIndexTag())
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
tag = this.getElementAddressTag() and
|
||||
result = Language::getTypeSize(this.getElementType())
|
||||
}
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = this.getElementIndexTag() and
|
||||
result = this.getElementIndex().toString()
|
||||
}
|
||||
|
||||
abstract int getElementIndex();
|
||||
|
||||
final InstructionTag getElementAddressTag() {
|
||||
result = InitializerElementAddressTag(this.getElementIndex())
|
||||
}
|
||||
|
||||
final InstructionTag getElementIndexTag() {
|
||||
result = InitializerElementIndexTag(this.getElementIndex())
|
||||
}
|
||||
|
||||
final ArrayInitializer getInitList() { result = initList }
|
||||
|
||||
final Type getElementType() { result = initList.getAnElement().getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of the initialization of an array element from
|
||||
* an explicit element in an initializer list.
|
||||
*/
|
||||
class TranslatedExplicitElementInitialization extends TranslatedElementInitialization,
|
||||
TTranslatedExplicitElementInitialization, InitializationContext
|
||||
{
|
||||
int elementIndex;
|
||||
|
||||
TranslatedExplicitElementInitialization() {
|
||||
this = TTranslatedExplicitElementInitialization(initList, elementIndex)
|
||||
}
|
||||
|
||||
override Instruction getTargetAddress() {
|
||||
result = this.getInstruction(this.getElementAddressTag())
|
||||
}
|
||||
|
||||
override Type getTargetType() { result = this.getElementType() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind)
|
||||
or
|
||||
tag = this.getElementAddressTag() and
|
||||
result = this.getInitialization().getFirstInstruction() and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
|
||||
|
||||
override int getElementIndex() { result = elementIndex }
|
||||
|
||||
TranslatedInitialization getInitialization() {
|
||||
result = getTranslatedInitialization(initList.getElement(elementIndex))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Possibly refactor into something simpler
|
||||
abstract class TranslatedConstructorCallFromConstructor extends TranslatedElement,
|
||||
ConstructorCallContext
|
||||
{
|
||||
Call call;
|
||||
|
||||
final override Language::AST getAst() { result = call }
|
||||
|
||||
final override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getConstructorCall()
|
||||
}
|
||||
|
||||
final override Callable getFunction() { result = call.getEnclosingCallable() }
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getConstructorCall() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final TranslatedExpr getConstructorCall() { result = getTranslatedExpr(call) }
|
||||
}
|
||||
|
||||
TranslatedConstructorInitializer getTranslatedConstructorInitializer(ConstructorInitializer ci) {
|
||||
result.getAst() = ci
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call from a constructor to a base class
|
||||
* constructor or another constructor in same class .
|
||||
*/
|
||||
// Review: do we need the conversion instructions in C#?
|
||||
class TranslatedConstructorInitializer extends TranslatedConstructorCallFromConstructor,
|
||||
TTranslatedConstructorInitializer
|
||||
{
|
||||
TranslatedConstructorInitializer() { this = TTranslatedConstructorInitializer(call) }
|
||||
|
||||
override string toString() { result = "constructor init: " + call.toString() }
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if this.needsConversion()
|
||||
then result = this.getInstruction(OnlyInstructionTag())
|
||||
else result = this.getConstructorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
this.needsConversion() and
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
resultType = getTypeForGLValue(call.getTarget().getDeclaringType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getConstructorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getReceiver() {
|
||||
if this.needsConversion()
|
||||
then result = this.getInstruction(OnlyInstructionTag())
|
||||
else result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getTranslatedFunction(this.getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
predicate needsConversion() {
|
||||
call.getTarget().getDeclaringType() != this.getFunction().getDeclaringType()
|
||||
}
|
||||
|
||||
override predicate getInstructionInheritance(
|
||||
InstructionTag tag, Class baseClass, Class derivedClass
|
||||
) {
|
||||
tag = OnlyInstructionTag() and
|
||||
baseClass = call.getTarget().getDeclaringType() and
|
||||
derivedClass = this.getFunction().getDeclaringType()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class that serves as a Base for classes that deal with the translation of calls
|
||||
* (both AST generated and compiler generated).
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import TranslatedExprBase
|
||||
|
||||
abstract class TranslatedCallBase extends TranslatedElement {
|
||||
final override TranslatedElement getChild(int id) {
|
||||
// We choose the child's id in the order of evaluation.
|
||||
// Note: some calls do need qualifiers, though instructions for them have already
|
||||
// been generated; eg. a constructor does not need to generate a qualifier,
|
||||
// though the `this` argument exists and is the result of the instruction
|
||||
// that allocated the new object. For those calls, `getQualifier()` should
|
||||
// be void.
|
||||
id = -1 and result = this.getQualifier()
|
||||
or
|
||||
result = this.getArgument(id)
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
if exists(this.getQualifier())
|
||||
then result = this.getQualifier().getFirstInstruction()
|
||||
else result = this.getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getTypeForPRValue(this.getCallResultType())
|
||||
or
|
||||
this.hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
(
|
||||
if this.hasWriteSideEffect()
|
||||
then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType = getUnknownType()
|
||||
) else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType = getUnknownType()
|
||||
)
|
||||
)
|
||||
or
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// Since the DB does not have a function type,
|
||||
// we just use the UnknownType
|
||||
resultType = getFunctionAddressType()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getQualifier() and
|
||||
result = this.getInstruction(CallTargetTag())
|
||||
or
|
||||
exists(int argIndex |
|
||||
child = this.getArgument(argIndex) and
|
||||
if exists(this.getArgument(argIndex + 1))
|
||||
then result = this.getArgument(argIndex + 1).getFirstInstruction()
|
||||
else result = this.getInstruction(CallTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
tag = CallTag() and
|
||||
if this.hasSideEffect()
|
||||
then result = this.getInstruction(CallSideEffectTag())
|
||||
else result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
or
|
||||
this.hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
or
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = CallTag() and
|
||||
(
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = this.getInstruction(CallTargetTag())
|
||||
or
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = this.getQualifierResult()
|
||||
or
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = this.getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = CallSideEffectTag() and
|
||||
this.hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
Instruction getResult() { result = this.getInstruction(CallTag()) }
|
||||
|
||||
/**
|
||||
* Gets the result type of the call.
|
||||
*/
|
||||
abstract Type getCallResultType();
|
||||
|
||||
/**
|
||||
* Holds if the call has a `this` argument.
|
||||
*/
|
||||
predicate hasQualifier() { exists(this.getQualifier()) }
|
||||
|
||||
/**
|
||||
* Gets the expr for the qualifier of the call.
|
||||
*/
|
||||
abstract TranslatedExprBase getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
* In general, this is just the result of `getQualifier()`, but it can be
|
||||
* overridden by a subclass for cases where there is a `this` argument that is
|
||||
* not computed from a child expression (e.g. a constructor call).
|
||||
*/
|
||||
abstract Instruction getQualifierResult();
|
||||
|
||||
/**
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument. We use `TranslatedExprBase` so that we can give both `TranslatedExpr` args,
|
||||
* in the case of AST generated arguments, or `TranslatedCompilerElement` args in the case of
|
||||
* compiler generated arguments.
|
||||
*/
|
||||
abstract TranslatedExprBase getArgument(int index);
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
* argument. Otherwise, returns the call instruction.
|
||||
*/
|
||||
final Instruction getFirstArgumentOrCallInstruction() {
|
||||
if this.hasArguments()
|
||||
then result = this.getArgument(0).getFirstInstruction()
|
||||
else result = this.getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
final predicate hasArguments() { exists(this.getArgument(0)) }
|
||||
|
||||
predicate hasReadSideEffect() { any() }
|
||||
|
||||
predicate hasWriteSideEffect() { any() }
|
||||
|
||||
private predicate hasSideEffect() { this.hasReadSideEffect() or this.hasWriteSideEffect() }
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
this.hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = this.getResult()
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Contains several abstract classes that serve as Bases.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedCondition
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* Represents the context of the condition, ie. provides
|
||||
* information about the instruction that follows a conditional branch.
|
||||
*/
|
||||
abstract class ConditionContext extends TranslatedElement {
|
||||
abstract Instruction getChildTrueSuccessor(ConditionBase child);
|
||||
|
||||
abstract Instruction getChildFalseSuccessor(ConditionBase child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that serves as a Base for the classes that deal with both the AST generated conditions
|
||||
* and the compiler generated ones (captures the common patterns).
|
||||
*/
|
||||
abstract class ConditionBase extends TranslatedElement {
|
||||
final ConditionContext getConditionContext() { result = this.getParent() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class that serves as a Base for the classes that deal with both the AST generated _value_ conditions
|
||||
* and the compiler generated ones (captures the common patterns).
|
||||
*/
|
||||
abstract class ValueConditionBase extends ConditionBase {
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getValueExpr() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getValueExpr().getFirstInstruction() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getValueExpr() and
|
||||
result = this.getInstruction(ValueConditionConditionalBranchTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.valueExprResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that represents the result of the value expression.
|
||||
*/
|
||||
abstract Instruction valueExprResult();
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedElements that represents the value expression.
|
||||
*/
|
||||
abstract TranslatedElement getValueExpr();
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class that serves as a Base for the classes that deal with both the AST
|
||||
* generated declarations and the compiler generated ones (captures the common patterns).
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.internal.IRUtilities
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedInitialization
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class LocalVariableDeclarationBase extends TranslatedElement {
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getVarAddress() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(this.getVarType())
|
||||
or
|
||||
this.hasUninitializedInstruction() and
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Uninitialized and
|
||||
resultType = getTypeForPRValue(this.getVarType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
if this.hasUninitializedInstruction()
|
||||
then result = this.getInstruction(InitializerStoreTag())
|
||||
else result = this.getInitialization().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
this.hasUninitializedInstruction() and
|
||||
kind instanceof GotoEdge and
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
result = this.getInitialization().getFirstInstruction()
|
||||
or
|
||||
not exists(this.getInitialization()) and result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInitialization() and result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
this.hasUninitializedInstruction() and
|
||||
tag = InitializerStoreTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getVarAddress()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the declaration should have an `Uninitialized` instruction.
|
||||
* Compiler generated elements should override this predicate and
|
||||
* make it empty, since we always initialize the vars declared during the
|
||||
* desugaring process.
|
||||
*/
|
||||
predicate hasUninitializedInstruction() {
|
||||
not exists(this.getInitialization()) or
|
||||
this.getInitialization() instanceof TranslatedListInitialization
|
||||
}
|
||||
|
||||
Instruction getVarAddress() { result = this.getInstruction(InitializerVariableAddressTag()) }
|
||||
|
||||
/**
|
||||
* Gets the declared variable. For compiler generated elements, this
|
||||
* should be empty (since we treat temp vars differently).
|
||||
*/
|
||||
abstract LocalVariable getDeclVar();
|
||||
|
||||
/**
|
||||
* Gets the type of the declared variable.
|
||||
*/
|
||||
abstract Type getVarType();
|
||||
|
||||
/**
|
||||
* Gets the initialization, if there is one.
|
||||
* For compiler generated elements we don't treat the initialization
|
||||
* as a different step, but do it during the declaration.
|
||||
*/
|
||||
abstract TranslatedElement getInitialization();
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class that serves as a Base for classes that deal with the translation of exprs
|
||||
* (both AST generated and compiler generated).
|
||||
*/
|
||||
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedExprBase extends TranslatedElement {
|
||||
/**
|
||||
* Gets the instruction that produces the result of the expression.
|
||||
*/
|
||||
abstract Instruction getResult();
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/**
|
||||
* Exposes several patterns for the compiler generated code, so as to improve code sharing between files that
|
||||
* deal with the desugaring process.
|
||||
* For example, we expose the `try ... finally` pattern, which is shared by the desugaring of both the
|
||||
* `ForeachStmt`, `UsingStmt` and `LockStmt`.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedFunction
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import internal.TranslatedCompilerGeneratedStmt
|
||||
private import internal.TranslatedCompilerGeneratedExpr
|
||||
private import internal.TranslatedCompilerGeneratedCondition
|
||||
private import internal.TranslatedCompilerGeneratedCall
|
||||
private import internal.TranslatedCompilerGeneratedElement
|
||||
private import internal.TranslatedCompilerGeneratedDeclaration
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* The general form of a compiler generated try stmt.
|
||||
* The concrete implementation needs to specify the body of the try and the
|
||||
* finally block.
|
||||
*/
|
||||
abstract class TranslatedCompilerGeneratedTry extends TranslatedCompilerGeneratedStmt {
|
||||
override Stmt generatedBy;
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getBody()
|
||||
or
|
||||
id = 1 and result = this.getFinally()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getBody().getFirstInstruction() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getBody() and result = this.getFinally().getFirstInstruction()
|
||||
or
|
||||
child = this.getFinally() and result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getExceptionSuccessorInstruction() {
|
||||
result = this.getParent().getExceptionSuccessorInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the finally block.
|
||||
*/
|
||||
abstract TranslatedElement getFinally();
|
||||
|
||||
/**
|
||||
* Gets the body of the try stmt.
|
||||
*/
|
||||
abstract TranslatedElement getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* The general form of a compiler generated constant expression.
|
||||
* The concrete implementation needs to specify the immediate operand that represents the constant.
|
||||
*/
|
||||
abstract class TranslatedCompilerGeneratedConstant extends TranslatedCompilerGeneratedExpr {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
opcode instanceof Opcode::Constant and
|
||||
tag = OnlyInstructionTag() and
|
||||
resultType = getTypeForPRValue(this.getResultType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The general form of a compiler generated block stmt.
|
||||
* The concrete implementation needs to specify the statements that
|
||||
* compose the block.
|
||||
*/
|
||||
abstract class TranslatedCompilerGeneratedBlock extends TranslatedCompilerGeneratedStmt {
|
||||
override TranslatedElement getChild(int id) { result = this.getStmt(id) }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getStmt(0).getFirstInstruction() }
|
||||
|
||||
abstract TranslatedElement getStmt(int index);
|
||||
|
||||
private int getStmtCount() { result = count(this.getStmt(_)) }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
exists(int index |
|
||||
child = this.getStmt(index) and
|
||||
if index = (this.getStmtCount() - 1)
|
||||
then result = this.getParent().getChildSuccessor(this)
|
||||
else result = this.getStmt(index + 1).getFirstInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The general form of a compiler generated if stmt.
|
||||
* The concrete implementation needs to specify the condition,
|
||||
* the body of the `then` and the body of the `else`.
|
||||
*/
|
||||
abstract class TranslatedCompilerGeneratedIfStmt extends TranslatedCompilerGeneratedStmt,
|
||||
ConditionContext
|
||||
{
|
||||
override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() }
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getCondition()
|
||||
or
|
||||
id = 1 and result = this.getThen()
|
||||
or
|
||||
id = 2 and result = this.getElse()
|
||||
}
|
||||
|
||||
abstract TranslatedCompilerGeneratedValueCondition getCondition();
|
||||
|
||||
abstract TranslatedCompilerGeneratedElement getThen();
|
||||
|
||||
abstract TranslatedCompilerGeneratedElement getElse();
|
||||
|
||||
private predicate hasElse() { exists(this.getElse()) }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getCondition() and
|
||||
result = this.getThen().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getCondition() and
|
||||
if this.hasElse()
|
||||
then result = this.getElse().getFirstInstruction()
|
||||
else result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(child = this.getThen() or child = this.getElse()) and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The general form of a compiler generated variable access.
|
||||
* The concrete implementation needs to specify the immediate
|
||||
* operand for the `VariableAddress` instruction and if the
|
||||
* access needs a `Load` instruction or not (eg. `ref` params do not)
|
||||
*/
|
||||
abstract class TranslatedCompilerGeneratedVariableAccess extends TranslatedCompilerGeneratedExpr {
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(AddressTag()) }
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
/**
|
||||
* Returns the type of the accessed variable. Can be overridden when the return
|
||||
* type is different than the type of the underlying variable.
|
||||
*/
|
||||
Type getVariableType() { result = this.getResultType() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
tag = AddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(this.getVariableType())
|
||||
or
|
||||
this.needsLoad() and
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(this.getVariableType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
this.needsLoad() and
|
||||
tag = LoadTag() and
|
||||
result = this.getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
(
|
||||
tag = AddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
if this.needsLoad()
|
||||
then result = this.getInstruction(LoadTag())
|
||||
else result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getResult() {
|
||||
if this.needsLoad()
|
||||
then result = this.getInstruction(LoadTag())
|
||||
else result = this.getInstruction(AddressTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
this.needsLoad() and
|
||||
tag = LoadTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(AddressTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the variable access should be followed by a `Load` instruction.
|
||||
*/
|
||||
abstract predicate needsLoad();
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/**
|
||||
* File that translates the desugaring of delegate creation and call expressions.
|
||||
* In particular, in the IR we explicitly allocate a new object and call the delegate's constructor when
|
||||
* creating a new one.
|
||||
* For the delegate call, we explicitly call the `Invoke` method.
|
||||
* More information about the internals:
|
||||
* https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs
|
||||
* This is a rough approximation which will need further refining.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedStmt
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedCondition
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import Common
|
||||
private import internal.TranslatedCompilerGeneratedCall
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase
|
||||
|
||||
/**
|
||||
* Module that exposes the functions needed for the translation of the delegate creation and call expressions.
|
||||
*/
|
||||
module DelegateElements {
|
||||
TranslatedDelegateConstructorCall getConstructor(DelegateCreation generatedBy) {
|
||||
result.getAst() = generatedBy
|
||||
}
|
||||
|
||||
TranslatedDelegateInvokeCall getInvoke(DelegateCall generatedBy) { result.getAst() = generatedBy }
|
||||
|
||||
int noGeneratedElements(Element generatedBy) {
|
||||
(
|
||||
generatedBy instanceof DelegateCreation or
|
||||
generatedBy instanceof DelegateCall
|
||||
) and
|
||||
result = 2
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the constructor call that happens as part of the delegate creation.
|
||||
*/
|
||||
private class TranslatedDelegateConstructorCall extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override DelegateCreation generatedBy;
|
||||
|
||||
TranslatedDelegateConstructorCall() { this = TTranslatedCompilerGeneratedElement(generatedBy, 0) }
|
||||
|
||||
final override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override TranslatedExpr getArgument(int index) {
|
||||
index = 0 and result = getTranslatedExpr(generatedBy.getArgument())
|
||||
}
|
||||
|
||||
override TranslatedExprBase getQualifier() { none() }
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
exists(ConstructorCallContext context |
|
||||
context = this.getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable internal |
|
||||
internal.getName() = generatedBy.getDelegateType().getName() and
|
||||
internal.isCompilerGenerated() and
|
||||
internal.getFile() = generatedBy.getFile() and
|
||||
result = internal
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the invoke call that happens as part of the desugaring of the delegate call.
|
||||
*/
|
||||
private class TranslatedDelegateInvokeCall extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override DelegateCall generatedBy;
|
||||
|
||||
TranslatedDelegateInvokeCall() { this = TTranslatedCompilerGeneratedElement(generatedBy, 1) }
|
||||
|
||||
final override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable internal |
|
||||
internal.getName() = "Invoke" and
|
||||
internal.isCompilerGenerated() and
|
||||
internal.getFile() = generatedBy.getFile() and
|
||||
result = internal
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedExprBase getQualifier() { result = getTranslatedExpr(generatedBy.getExpr()) }
|
||||
|
||||
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
|
||||
|
||||
override TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(generatedBy.getArgument(index))
|
||||
}
|
||||
}
|
||||
@@ -1,457 +0,0 @@
|
||||
/**
|
||||
* File that provides the desugaring of a `Foreach` stmt.
|
||||
* Since Roslyn rewrites it in quite a few ways,
|
||||
* for now only desugar it to a "canonical" form.
|
||||
* Also we only deal with foreach stmts where there is only
|
||||
* one declaration (see below).
|
||||
* For example the code:
|
||||
* ```csharp
|
||||
* foreach(var item in some_enumerable) {
|
||||
* // body
|
||||
* }
|
||||
* ```
|
||||
* gets desugared to:
|
||||
* ```csharp
|
||||
* Enumerator e = some_enumerable.GetEnumerator();
|
||||
* try
|
||||
* {
|
||||
* while(e.MoveNext())
|
||||
* {
|
||||
* int current = e.Current;
|
||||
* //body
|
||||
* }
|
||||
* }
|
||||
* finally
|
||||
* {
|
||||
* e.Dispose();
|
||||
* }
|
||||
* ```
|
||||
* More info about the desugaring process for `foreach` stmts:
|
||||
* https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
|
||||
* A TODO is to not call `Dispose` no matter what, but desugar the `finally` as an `AsExpr` (cast to IDisposable),
|
||||
* the call to `Dispose` being made only if the result of the `AsExpr` is not null.
|
||||
* This is a rough approximation which will need further refining.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedStmt
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import Common
|
||||
private import internal.TranslatedCompilerGeneratedStmt
|
||||
private import internal.TranslatedCompilerGeneratedCall
|
||||
private import internal.TranslatedCompilerGeneratedDeclaration
|
||||
private import internal.TranslatedCompilerGeneratedCondition
|
||||
private import internal.TranslatedCompilerGeneratedElement
|
||||
|
||||
/**
|
||||
* Module that exposes the functions needed for the translation of the `foreach` stmt.
|
||||
*/
|
||||
module ForeachElements {
|
||||
TranslatedForeachTry getTry(ForeachStmt generatedBy) { result.getAst() = generatedBy }
|
||||
|
||||
TranslatedForeachEnumerator getEnumDecl(ForeachStmt generatedBy) { result.getAst() = generatedBy }
|
||||
|
||||
int noGeneratedElements() { result = 13 }
|
||||
}
|
||||
|
||||
private class TranslatedForeachTry extends TranslatedCompilerGeneratedTry,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachTry() { this = TTranslatedCompilerGeneratedElement(generatedBy, 0) }
|
||||
|
||||
override TranslatedElement getFinally() {
|
||||
exists(TranslatedForeachFinally ff |
|
||||
ff.getAst() = generatedBy and
|
||||
result = ff
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedElement getBody() {
|
||||
exists(TranslatedForeachWhile fw |
|
||||
fw.getAst() = generatedBy and
|
||||
result = fw
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the finally block.
|
||||
*/
|
||||
private class TranslatedForeachFinally extends TranslatedCompilerGeneratedBlock,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachFinally() { this = TTranslatedCompilerGeneratedElement(generatedBy, 1) }
|
||||
|
||||
override TranslatedElement getStmt(int index) {
|
||||
index = 0 and
|
||||
exists(TranslatedForeachDispose fd |
|
||||
fd.getAst() = generatedBy and
|
||||
result = fd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The compiler generated while loop.
|
||||
* Note that this class is not private since it is needed in `IRConstruction.qll`,
|
||||
* to correctly mark which edges should be back edges.
|
||||
*/
|
||||
class TranslatedForeachWhile extends TranslatedCompilerGeneratedStmt, ConditionContext,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachWhile() { this = TTranslatedCompilerGeneratedElement(generatedBy, 2) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInit() and result = this.getBody().getFirstInstruction()
|
||||
or
|
||||
child = this.getBody() and result = this.getCondition().getFirstInstruction()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getCondition()
|
||||
or
|
||||
id = 1 and result = this.getInit()
|
||||
or
|
||||
id = 2 and result = this.getBody()
|
||||
}
|
||||
|
||||
final override Instruction getChildTrueSuccessor(ConditionBase child) {
|
||||
child = this.getCondition() and result = this.getInit().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override Instruction getChildFalseSuccessor(ConditionBase child) {
|
||||
child = this.getCondition() and result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
TranslatedStmt getBody() { result = getTranslatedStmt(generatedBy.getBody()) }
|
||||
|
||||
TranslatedElement getInit() {
|
||||
exists(TranslatedForeachIterVar iv |
|
||||
iv.getAst() = generatedBy and
|
||||
result = iv
|
||||
)
|
||||
}
|
||||
|
||||
ValueConditionBase getCondition() {
|
||||
exists(TranslatedForeachWhileCondition cond |
|
||||
cond.getAst() = generatedBy and
|
||||
result = cond
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to the `MoveNext` method, used as a condition for the while.
|
||||
*/
|
||||
private class TranslatedForeachMoveNext extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachMoveNext() { this = TTranslatedCompilerGeneratedElement(generatedBy, 3) }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
result = generatedBy.getMoveNext()
|
||||
}
|
||||
|
||||
override Type getCallResultType() { result instanceof BoolType }
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override TranslatedExprBase getQualifier() {
|
||||
exists(TranslatedMoveNextEnumAcc acc |
|
||||
acc.getAst() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to retrieve the enumerator.
|
||||
*/
|
||||
private class TranslatedForeachGetEnumerator extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachGetEnumerator() { this = TTranslatedCompilerGeneratedElement(generatedBy, 4) }
|
||||
|
||||
final override Type getCallResultType() {
|
||||
result = this.getInstructionFunction(CallTargetTag()).getReturnType()
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
result = generatedBy.getGetEnumerator()
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override TranslatedExprBase getQualifier() {
|
||||
result = getTranslatedExpr(generatedBy.getIterableExpr())
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to the getter method of the `Current` property of the enumerator.
|
||||
*/
|
||||
private class TranslatedForeachCurrent extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachCurrent() { this = TTranslatedCompilerGeneratedElement(generatedBy, 5) }
|
||||
|
||||
override Type getCallResultType() { result = generatedBy.getElementType() }
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override TranslatedExprBase getQualifier() {
|
||||
exists(TranslatedForeachCurrentEnumAcc acc |
|
||||
acc.getAst() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
result = generatedBy.getCurrent().getGetter()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to dispose (inside the finally block)
|
||||
*/
|
||||
private class TranslatedForeachDispose extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachDispose() { this = TTranslatedCompilerGeneratedElement(generatedBy, 6) }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
result = generatedBy.getDispose()
|
||||
}
|
||||
|
||||
final override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override TranslatedExprBase getQualifier() {
|
||||
exists(TranslatedForeachDisposeEnumAcc acc |
|
||||
acc.getAst() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition for the while, ie. a call to MoveNext.
|
||||
*/
|
||||
private class TranslatedForeachWhileCondition extends TranslatedCompilerGeneratedValueCondition,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachWhileCondition() { this = TTranslatedCompilerGeneratedElement(generatedBy, 7) }
|
||||
|
||||
override TranslatedCompilerGeneratedCall getValueExpr() {
|
||||
exists(TranslatedForeachMoveNext mn |
|
||||
mn.getAst() = generatedBy and
|
||||
result = mn
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction valueExprResult() { result = this.getValueExpr().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of the declaration that happens before the `try ... finally` block (the
|
||||
* declaration of the `temporary` enumerator variable)
|
||||
*/
|
||||
private class TranslatedForeachEnumerator extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachEnumerator() { this = TTranslatedCompilerGeneratedElement(generatedBy, 8) }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getTypeForPRValue(this.getInitialization().getCallResultType())
|
||||
}
|
||||
|
||||
override IRTempVariable getIRVariable() {
|
||||
result = getIRTempVariable(generatedBy, ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getInitialization() {
|
||||
exists(TranslatedForeachGetEnumerator ge |
|
||||
ge.getAst() = generatedBy and
|
||||
result = ge
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInitializationResult() { result = this.getInitialization().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of the declaration that's happening inside the body of the while.
|
||||
*/
|
||||
private class TranslatedForeachIterVar extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachIterVar() { this = TTranslatedCompilerGeneratedElement(generatedBy, 9) }
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = this.getIRVariable()
|
||||
}
|
||||
|
||||
override IRVariable getIRVariable() {
|
||||
result = getIRUserVariable(this.getFunction(), generatedBy.getAVariable())
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getInitialization() {
|
||||
exists(TranslatedForeachCurrent crtProp |
|
||||
crtProp.getAst() = generatedBy and
|
||||
result = crtProp
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInitializationResult() { result = this.getInitialization().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to `MoveNext`.
|
||||
*/
|
||||
private class TranslatedMoveNextEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedMoveNextEnumAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 10) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
|
||||
override Type getVariableType() {
|
||||
exists(TranslatedForeachGetEnumerator ge |
|
||||
ge.getAst() = generatedBy and
|
||||
result = ge.getCallResultType()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getTypeForPRValue(this.getVariableType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to the getter of the property `Current`.
|
||||
*/
|
||||
private class TranslatedForeachCurrentEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachCurrentEnumAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 11) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
|
||||
override Type getVariableType() {
|
||||
exists(TranslatedForeachGetEnumerator ge |
|
||||
ge.getAst() = generatedBy and
|
||||
result = ge.getCallResultType()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getTypeForPRValue(this.getVariableType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to `Dispose`.
|
||||
*/
|
||||
private class TranslatedForeachDisposeEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachDisposeEnumAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 12) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
|
||||
override Type getVariableType() {
|
||||
exists(TranslatedForeachGetEnumerator ge |
|
||||
ge.getAst() = generatedBy and
|
||||
result = ge.getCallResultType()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getTypeForPRValue(this.getVariableType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
/**
|
||||
* File that provides the desugaring of a `lock` stmt.
|
||||
* The statement:
|
||||
* ```csharp
|
||||
* lock (anExpr) ...
|
||||
* ```
|
||||
* gets desugared to:
|
||||
* ```csharp
|
||||
* SomeRefType lockedVar = anExpr;
|
||||
* bool __lockWasTaken = false;
|
||||
* try {
|
||||
* System.Threading.Monitor.Enter(lockedVar, ref __lockWasTaken);
|
||||
* ...
|
||||
* }
|
||||
* finally {
|
||||
* if (__lockWasTaken) System.Threading.Monitor.Exit(lockedVar);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.TempVariableTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedStmt
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
private import Common
|
||||
private import internal.TranslatedCompilerGeneratedStmt
|
||||
private import internal.TranslatedCompilerGeneratedCall
|
||||
private import internal.TranslatedCompilerGeneratedDeclaration
|
||||
private import internal.TranslatedCompilerGeneratedCondition
|
||||
private import internal.TranslatedCompilerGeneratedElement
|
||||
private import internal.TranslatedCompilerGeneratedExpr
|
||||
|
||||
/**
|
||||
* Module that exposes the functions needed for the translation of the `lock` stmt.
|
||||
*/
|
||||
module LockElements {
|
||||
TranslatedLockedVarDecl getLockedVarDecl(LockStmt generatedBy) { result.getAst() = generatedBy }
|
||||
|
||||
TranslatedLockTry getTry(LockStmt generatedBy) { result.getAst() = generatedBy }
|
||||
|
||||
TranslatedLockWasTakenDecl getLockWasTakenDecl(LockStmt generatedBy) {
|
||||
result.getAst() = generatedBy
|
||||
}
|
||||
|
||||
int noGeneratedElements() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the `try` stmt.
|
||||
*/
|
||||
private class TranslatedLockTry extends TranslatedCompilerGeneratedTry,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockTry() { this = TTranslatedCompilerGeneratedElement(generatedBy, 0) }
|
||||
|
||||
override TranslatedElement getFinally() {
|
||||
exists(TranslatedLockFinally fin |
|
||||
fin.getAst() = generatedBy and
|
||||
result = fin
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedElement getBody() {
|
||||
exists(TranslatedLockTryBody ltb |
|
||||
ltb.getAst() = generatedBy and
|
||||
result = ltb
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the `lock` stmt's body.
|
||||
*/
|
||||
private class TranslatedLockTryBody extends TranslatedCompilerGeneratedBlock,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockTryBody() { this = TTranslatedCompilerGeneratedElement(generatedBy, 1) }
|
||||
|
||||
override TranslatedElement getStmt(int index) {
|
||||
index = 0 and
|
||||
exists(TranslatedMonitorEnter me |
|
||||
me.getAst() = generatedBy and
|
||||
result = me
|
||||
)
|
||||
or
|
||||
index = 1 and
|
||||
result = getTranslatedStmt(generatedBy.getBlock())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the finally block.
|
||||
*/
|
||||
private class TranslatedLockFinally extends TranslatedCompilerGeneratedBlock,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockFinally() { this = TTranslatedCompilerGeneratedElement(generatedBy, 2) }
|
||||
|
||||
override TranslatedElement getStmt(int index) {
|
||||
index = 0 and
|
||||
exists(TranslatedFinallyIf fif |
|
||||
fif.getAst() = generatedBy and
|
||||
result = fif
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to dispose (inside the finally block)
|
||||
*/
|
||||
private class TranslatedMonitorExit extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedMonitorExit() { this = TTranslatedCompilerGeneratedElement(generatedBy, 3) }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable exit |
|
||||
exit.hasFullyQualifiedName("System.Threading.Monitor", "Exit") and
|
||||
result = exit
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override TranslatedExprBase getArgument(int id) {
|
||||
id = 0 and
|
||||
exists(TranslatedMonitorExitVarAcc var |
|
||||
var.getAst() = generatedBy and
|
||||
result = var
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedExprBase getQualifier() { none() }
|
||||
|
||||
override Instruction getQualifierResult() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to dispose (inside the finally block)
|
||||
*/
|
||||
private class TranslatedMonitorEnter extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedMonitorEnter() { this = TTranslatedCompilerGeneratedElement(generatedBy, 4) }
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable dispose |
|
||||
dispose.hasFullyQualifiedName("System.Threading.Monitor", "Enter") and
|
||||
result = dispose
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override TranslatedExprBase getArgument(int id) {
|
||||
id = 0 and
|
||||
exists(TranslatedMonitorEnterVarAcc var |
|
||||
var.getAst() = generatedBy and
|
||||
result = var
|
||||
)
|
||||
or
|
||||
id = 1 and
|
||||
exists(TranslatedLockWasTakenRefArg refArg |
|
||||
refArg.getAst() = generatedBy and
|
||||
result = refArg
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedExprBase getQualifier() { none() }
|
||||
|
||||
override Instruction getQualifierResult() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the condition of the `if` present in the `finally` clause.
|
||||
*/
|
||||
private class TranslatedIfCondition extends TranslatedCompilerGeneratedValueCondition,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedIfCondition() { this = TTranslatedCompilerGeneratedElement(generatedBy, 5) }
|
||||
|
||||
override TranslatedCompilerGeneratedExpr getValueExpr() {
|
||||
exists(TranslatedLockWasTakenCondVarAcc condVar |
|
||||
condVar.getAst() = generatedBy and
|
||||
result = condVar
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction valueExprResult() { result = this.getValueExpr().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the `if` stmt present in the `finally` clause.
|
||||
*/
|
||||
private class TranslatedFinallyIf extends TranslatedCompilerGeneratedIfStmt,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedFinallyIf() { this = TTranslatedCompilerGeneratedElement(generatedBy, 6) }
|
||||
|
||||
override TranslatedCompilerGeneratedValueCondition getCondition() {
|
||||
exists(TranslatedIfCondition cond |
|
||||
cond.getAst() = generatedBy and
|
||||
result = cond
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getThen() {
|
||||
exists(TranslatedMonitorExit me |
|
||||
me.getAst() = generatedBy and
|
||||
result = me
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getElse() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the translation of the constant that is part of the initialization for the
|
||||
* bool temp variable.
|
||||
*/
|
||||
private class TranslatedWasTakenConst extends TranslatedCompilerGeneratedConstant,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedWasTakenConst() { this = TTranslatedCompilerGeneratedElement(generatedBy, 7) }
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = "false"
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the translation of the `lockWasTaken` temp variable declaration.
|
||||
*/
|
||||
private class TranslatedLockWasTakenDecl extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockWasTakenDecl() { this = TTranslatedCompilerGeneratedElement(generatedBy, 8) }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockWasTakenTemp() and
|
||||
type = getBoolType()
|
||||
}
|
||||
|
||||
override IRTempVariable getIRVariable() {
|
||||
result = getIRTempVariable(generatedBy, LockWasTakenTemp())
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedExpr getInitialization() {
|
||||
exists(TranslatedWasTakenConst const |
|
||||
const.getAst() = generatedBy and
|
||||
result = const
|
||||
)
|
||||
}
|
||||
|
||||
override Type getVarType() { result = this.getInitialization().getResultType() }
|
||||
|
||||
override Instruction getInitializationResult() { result = this.getInitialization().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the translation of the declaration of the temp variable that is initialized to the
|
||||
* expression being locked.
|
||||
*/
|
||||
private class TranslatedLockedVarDecl extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockedVarDecl() { this = TTranslatedCompilerGeneratedElement(generatedBy, 9) }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockedVarTemp() and
|
||||
type = getTypeForPRValue(generatedBy.getExpr().getType())
|
||||
}
|
||||
|
||||
override IRTempVariable getIRVariable() {
|
||||
result = getIRTempVariable(generatedBy, LockedVarTemp())
|
||||
}
|
||||
|
||||
override TranslatedExprBase getInitialization() {
|
||||
result = getTranslatedExpr(generatedBy.getExpr())
|
||||
}
|
||||
|
||||
override Type getVarType() { result = generatedBy.getExpr().getType() }
|
||||
|
||||
override Instruction getInitializationResult() { result = this.getInitialization().getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the translation of access to the temp variable that is initialized to the
|
||||
* expression being locked.
|
||||
* Used as an argument for the `MonitorEnter` call.
|
||||
*/
|
||||
private class TranslatedMonitorEnterVarAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedMonitorEnterVarAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 10) }
|
||||
|
||||
override Type getResultType() { result = generatedBy.getExpr().getType() }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockedVarTemp() and
|
||||
type = getTypeForPRValue(this.getResultType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(LockedVarTemp())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the translation of access to the temp variable that is initialized to the
|
||||
* expression being locked.
|
||||
* Used as an argument for the `MonitorExit` call.
|
||||
*/
|
||||
private class TranslatedMonitorExitVarAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedMonitorExitVarAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 11) }
|
||||
|
||||
override Type getResultType() { result = generatedBy.getExpr().getType() }
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(LockedVarTemp())
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockedVarTemp() and
|
||||
type = getTypeForPRValue(this.getResultType())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents that translation of access to the temporary bool variable.
|
||||
* Used as an argument for the `MonitorEnter` call.
|
||||
*/
|
||||
private class TranslatedLockWasTakenCondVarAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockWasTakenCondVarAcc() { this = TTranslatedCompilerGeneratedElement(generatedBy, 12) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockWasTakenTemp() and
|
||||
type = getTypeForPRValue(this.getResultType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(LockWasTakenTemp())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* That represents that translation of access to the temporary bool variable. Its value is used
|
||||
* as the `if` condition in the finally clause.
|
||||
*/
|
||||
private class TranslatedLockWasTakenRefArg extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess
|
||||
{
|
||||
override LockStmt generatedBy;
|
||||
|
||||
TranslatedLockWasTakenRefArg() { this = TTranslatedCompilerGeneratedElement(generatedBy, 13) }
|
||||
|
||||
override Type getResultType() { result instanceof BoolType }
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, CSharpType type) {
|
||||
tag = LockWasTakenTemp() and
|
||||
type = getTypeForPRValue(this.getResultType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = this.getTempVariable(LockWasTakenTemp())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { none() }
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class that is the super class of the classes that deal with compiler generated calls.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedFunction
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedCallBase
|
||||
private import TranslatedCompilerGeneratedElement
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedCall extends TranslatedCallBase,
|
||||
TranslatedCompilerGeneratedElement
|
||||
{
|
||||
final override string toString() {
|
||||
result = "compiler generated call (" + generatedBy.toString() + ")"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class that is the super class of the classes that deal with compiler generated conditions.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedConditionBase
|
||||
private import TranslatedCompilerGeneratedElement
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedValueCondition extends TranslatedCompilerGeneratedElement,
|
||||
ValueConditionBase
|
||||
{
|
||||
final override string toString() {
|
||||
result = "compiler generated condition (" + generatedBy.toString() + ")"
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class, which is the super class of all the classes that represent compiler
|
||||
* generated declarations. It extends the Base for declarations by incorporating a `Store` instruction, since
|
||||
* we treat the initialization as part of the declaration for compiler generated declarations.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import experimental.ir.implementation.Opcode
|
||||
private import experimental.ir.implementation.internal.OperandTag
|
||||
private import experimental.ir.implementation.raw.internal.InstructionTag
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedFunction
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedDeclarationBase
|
||||
private import TranslatedCompilerGeneratedElement
|
||||
private import experimental.ir.internal.CSharpType
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedDeclaration extends LocalVariableDeclarationBase,
|
||||
TranslatedCompilerGeneratedElement
|
||||
{
|
||||
final override string toString() {
|
||||
result = "compiler generated declaration (" + generatedBy.toString() + ")"
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInitialization() and result = this.getInstruction(InitializerStoreTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) {
|
||||
LocalVariableDeclarationBase.super.hasInstruction(opcode, tag, resultType)
|
||||
or
|
||||
// we can reuse the initializer store tag
|
||||
// since compiler generated declarations
|
||||
// do not have the `Uninitialized` instruction
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(this.getVarType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = LocalVariableDeclarationBase.super.getInstructionSuccessor(tag, kind)
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
result = this.getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = LocalVariableDeclarationBase.super.getInstructionOperand(tag, operandTag)
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and
|
||||
result = this.getInitializationResult()
|
||||
)
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = this.getIRVariable()
|
||||
}
|
||||
|
||||
// A compiler generated declaration does not have an associated `LocalVariable`
|
||||
// element
|
||||
override LocalVariable getDeclVar() { none() }
|
||||
|
||||
override Type getVarType() { result = this.getIRVariable().getType() }
|
||||
|
||||
/**
|
||||
* Gets the IR variable that corresponds to the declaration.
|
||||
*/
|
||||
abstract IRVariable getIRVariable();
|
||||
|
||||
/**
|
||||
* Gets result (instruction) of the initialization expression.
|
||||
*/
|
||||
abstract Instruction getInitializationResult();
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* The abstract super class of every `TranslatedCompilerX` class. It has one member field, `generatedBy`,
|
||||
* which represents the element that generated the compiler generated element.
|
||||
*/
|
||||
|
||||
private import experimental.ir.implementation.raw.internal.TranslatedElement
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedElement extends TranslatedElement,
|
||||
TTranslatedCompilerGeneratedElement
|
||||
{
|
||||
// The element that generates generated the compiler element can
|
||||
// only be a stmt or an expr
|
||||
ControlFlowElement generatedBy;
|
||||
|
||||
override string toString() {
|
||||
result = "compiler generated element (" + generatedBy.toString() + ")"
|
||||
}
|
||||
|
||||
final override Callable getFunction() { result = generatedBy.getEnclosingCallable() }
|
||||
|
||||
final override Language::AST getAst() { result = generatedBy }
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class, which is the super class of all the classes that represent compiler
|
||||
* generated expressions.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import TranslatedCompilerGeneratedElement
|
||||
private import experimental.ir.implementation.raw.Instruction
|
||||
private import experimental.ir.implementation.raw.internal.common.TranslatedExprBase
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedExpr extends TranslatedCompilerGeneratedElement,
|
||||
TranslatedExprBase
|
||||
{
|
||||
override string toString() { result = "compiler generated expr (" + generatedBy.toString() + ")" }
|
||||
|
||||
abstract Type getResultType();
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Contains an abstract class, which is the super class of all the classes that represent compiler
|
||||
* generated statements.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import TranslatedCompilerGeneratedElement
|
||||
private import experimental.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class TranslatedCompilerGeneratedStmt extends TranslatedCompilerGeneratedElement {
|
||||
final override string toString() {
|
||||
result = "compiler generated stmt (" + generatedBy.toString() + ")"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
private import DominanceInternal
|
||||
|
||||
predicate blockImmediatelyDominates(Graph::Block dominator, Graph::Block block) =
|
||||
idominance(Graph::isEntryBlock/1, Graph::blockSuccessor/2)(_, dominator, block)
|
||||
|
||||
predicate blockStrictlyDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockImmediatelyDominates+(dominator, block)
|
||||
}
|
||||
|
||||
predicate blockDominates(Graph::Block dominator, Graph::Block block) {
|
||||
blockStrictlyDominates(dominator, block) or dominator = block
|
||||
}
|
||||
|
||||
Graph::Block getDominanceFrontier(Graph::Block dominator) {
|
||||
Graph::blockSuccessor(dominator, result) and
|
||||
not blockImmediatelyDominates(dominator, result)
|
||||
or
|
||||
exists(Graph::Block prev | result = getDominanceFrontier(prev) |
|
||||
blockImmediatelyDominates(dominator, prev) and
|
||||
not blockImmediatelyDominates(dominator, result)
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
private import ReachableBlock as Reachability
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
private import DominanceInternal
|
||||
private import ReachableBlockInternal
|
||||
private import Dominance
|
||||
import IR
|
||||
|
||||
private class DominancePropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
exists(IRBlock dominator |
|
||||
blockImmediatelyDominates(dominator, block) and
|
||||
key = "ImmediateDominator" and
|
||||
result = "Block " + dominator.getDisplayIndex().toString()
|
||||
)
|
||||
or
|
||||
key = "DominanceFrontier" and
|
||||
result =
|
||||
strictconcat(IRBlock frontierBlock |
|
||||
frontierBlock = getDominanceFrontier(block)
|
||||
|
|
||||
frontierBlock.getDisplayIndex().toString(), ", " order by frontierBlock.getDisplayIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
private import ReachableBlockInternal
|
||||
private import ReachableBlock
|
||||
import IR
|
||||
|
||||
private class ReachableBlockPropertyProvider extends IRPropertyProvider {
|
||||
override string getBlockProperty(IRBlock block, string key) {
|
||||
not block instanceof ReachableBlock and
|
||||
key = "Unreachable" and
|
||||
result = "true"
|
||||
or
|
||||
exists(EdgeKind kind |
|
||||
isInfeasibleEdge(block, kind) and
|
||||
key = "Infeasible(" + kind.toString() + ")" and
|
||||
result = "true"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
private import ReachableBlockInternal
|
||||
private import IR
|
||||
private import ConstantAnalysis
|
||||
|
||||
predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
exists(int conditionValue |
|
||||
conditionValue = getConstantValue(instr.(ConditionalBranchInstruction).getCondition()) and
|
||||
if conditionValue = 0 then kind instanceof TrueEdge else kind instanceof FalseEdge
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isInfeasibleEdge(IRBlockBase block, EdgeKind kind) {
|
||||
isInfeasibleInstructionSuccessor(block.getLastInstruction(), kind)
|
||||
}
|
||||
|
||||
private IRBlock getAFeasiblePredecessorBlock(IRBlock successor) {
|
||||
exists(EdgeKind kind |
|
||||
result.getSuccessor(kind) = successor and
|
||||
not isInfeasibleEdge(result, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isBlockReachable(IRBlock block) {
|
||||
exists(IRFunction f | getAFeasiblePredecessorBlock*(block) = f.getEntryBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR block that is reachable from the entry block of the function, considering only feasible
|
||||
* edges.
|
||||
*/
|
||||
class ReachableBlock extends IRBlockBase {
|
||||
ReachableBlock() { isBlockReachable(this) }
|
||||
|
||||
final ReachableBlock getAFeasiblePredecessor() { result = getAFeasiblePredecessorBlock(this) }
|
||||
|
||||
final ReachableBlock getAFeasibleSuccessor() { this = getAFeasiblePredecessorBlock(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that is contained in a reachable block.
|
||||
*/
|
||||
class ReachableInstruction extends Instruction {
|
||||
ReachableInstruction() { this.getBlock() instanceof ReachableBlock }
|
||||
}
|
||||
|
||||
module Graph {
|
||||
predicate isEntryBlock(ReachableBlock block) { exists(IRFunction f | block = f.getEntryBlock()) }
|
||||
|
||||
predicate blockSuccessor(ReachableBlock pred, ReachableBlock succ) {
|
||||
succ = pred.getAFeasibleSuccessor()
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import experimental.ir.implementation.raw.IR as IR
|
||||
import experimental.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Provides classes that describe the Intermediate Representation (IR) of the program.
|
||||
*
|
||||
* The IR is a representation of the semantics of the program, with very little dependence on the
|
||||
* syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
|
||||
* and `++i` all have the same semantic effect, but appear in the AST as three different types of
|
||||
* `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
|
||||
* operations similar to:
|
||||
*
|
||||
* ```
|
||||
* r1(int*) = VariableAddress[i] // Compute the address of variable `i`
|
||||
* r2(int) = Load &:r1, m0 // Load the value of `i`
|
||||
* r3(int) = Constant[1] // An integer constant with the value `1`
|
||||
* r4(int) = Add r2, r3 // Add `1` to the value of `i`
|
||||
* r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
|
||||
* ```
|
||||
*
|
||||
* This allows IR-based analysis to focus on the fundamental operations, rather than having to be
|
||||
* concerned with the various ways of expressing those operations in source code.
|
||||
*
|
||||
* The key classes in the IR are:
|
||||
*
|
||||
* - `IRFunction` - Contains the IR for an entire function definition, including all of that
|
||||
* function's `Instruction`s, `IRBlock`s, and `IRVariables`.
|
||||
* - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
|
||||
* performed, the operands that produce the inputs to that operation, and the type of the result
|
||||
* of the operation. Control flows from an `Instruction` to one of a set of successor
|
||||
* `Instruction`s.
|
||||
* - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
|
||||
* represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
|
||||
* a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
|
||||
* that produces its value (its "definition").
|
||||
* - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
|
||||
* created for each variable directly accessed by the function. In addition, `IRVariable`s are
|
||||
* created to represent certain temporary storage locations that do not have explicitly declared
|
||||
* variables in the source code, such as the return value of the function.
|
||||
* - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
|
||||
* sequence of instructions such that control flow can only enter the block at the first
|
||||
* instruction, and can only leave the block from the last instruction.
|
||||
* - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
|
||||
* is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
|
||||
* be represented as the `IRType` `uint4`, a four-byte unsigned integer.
|
||||
*/
|
||||
|
||||
import IRFunction
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* A class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "IRPropertyProvider" }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified operand.
|
||||
*/
|
||||
string getOperandProperty(Operand operand, string key) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the instruction `instr` should be included when printing
|
||||
* the IR instructions.
|
||||
*/
|
||||
predicate shouldPrintInstruction(Instruction instr) { any() }
|
||||
|
||||
/**
|
||||
* Holds if the operand `operand` should be included when printing the an
|
||||
* instruction's operand list.
|
||||
*/
|
||||
predicate shouldPrintOperand(Operand operand) { any() }
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
/**
|
||||
* Provides classes describing basic blocks in the IR of a function.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `block` is a block in `func` and `sortOverride`, `sortKey1`, and `sortKey2` are the
|
||||
* sort keys of the block (derived from its first instruction)
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate blockSortKeys(
|
||||
IRFunction func, IRBlockBase block, int sortOverride, int sortKey1, int sortKey2
|
||||
) {
|
||||
block.getEnclosingIRFunction() = func and
|
||||
block.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if block.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block in the IR. A basic block consists of a sequence of `Instructions` with the only
|
||||
* incoming edges at the beginning of the sequence and the only outgoing edges at the end of the
|
||||
* sequence.
|
||||
*
|
||||
* This class does not contain any members that query the predecessor or successor edges of the
|
||||
* block. This allows different classes that extend `IRBlockBase` to expose different subsets of
|
||||
* edges (e.g. ignoring unreachable edges).
|
||||
*
|
||||
* Most consumers should use the class `IRBlock`.
|
||||
*/
|
||||
class IRBlockBase extends TIRBlock {
|
||||
/** Gets a textual representation of this block. */
|
||||
final string toString() { result = getFirstInstruction(this).toString() }
|
||||
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the zero-based index of the block within its function.
|
||||
*
|
||||
* This predicate is used by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
exists(IRFunction func |
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
blockSortKeys(func, funcBlock, sortOverride, sortKey1, sortKey2)
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `index`th non-`Phi` instruction in this block.
|
||||
*/
|
||||
final Instruction getInstruction(int index) { result = getInstruction(this, index) }
|
||||
|
||||
/**
|
||||
* Get the `Phi` instructions that appear at the start of this block.
|
||||
*/
|
||||
final PhiInstruction getAPhiInstruction() {
|
||||
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction in this block. This includes `Phi` instructions.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
result = this.getInstruction(_) or
|
||||
result = this.getAPhiInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first non-`Phi` instruction in this block.
|
||||
*/
|
||||
final Instruction getFirstInstruction() { result = getFirstInstruction(this) }
|
||||
|
||||
/**
|
||||
* Gets the last instruction in this block.
|
||||
*/
|
||||
final Instruction getLastInstruction() {
|
||||
result = this.getInstruction(this.getInstructionCount() - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of non-`Phi` instructions in this block.
|
||||
*/
|
||||
final int getInstructionCount() { result = getInstructionCount(this) }
|
||||
|
||||
/**
|
||||
* Gets the `IRFunction` that contains this block.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() {
|
||||
result = getFirstInstruction(this).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Function` that contains this block.
|
||||
*/
|
||||
final Language::Declaration getEnclosingFunction() {
|
||||
result = getFirstInstruction(this).getEnclosingFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block with additional information about its predecessor and successor edges. Each edge
|
||||
* corresponds to the control flow between the last instruction of one block and the first
|
||||
* instruction of another block.
|
||||
*/
|
||||
class IRBlock extends IRBlockBase {
|
||||
/**
|
||||
* Gets a block to which control flows directly from this block.
|
||||
*/
|
||||
final IRBlock getASuccessor() { blockSuccessor(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a block from which control flows directly to this block.
|
||||
*/
|
||||
final IRBlock getAPredecessor() { blockSuccessor(result, this) }
|
||||
|
||||
/**
|
||||
* Gets the block to which control flows directly from this block along an edge of kind `kind`.
|
||||
*/
|
||||
final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) }
|
||||
|
||||
/**
|
||||
* Gets the block to which control flows directly from this block along a back edge of kind
|
||||
* `kind`.
|
||||
*/
|
||||
final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) }
|
||||
|
||||
/**
|
||||
* Holds if this block immediately dominates `block`.
|
||||
*
|
||||
* Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B`
|
||||
* is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) }
|
||||
|
||||
/**
|
||||
* Holds if this block strictly dominates `block`.
|
||||
*
|
||||
* Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B`
|
||||
* are not the same block.
|
||||
*/
|
||||
final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) }
|
||||
|
||||
/**
|
||||
* Holds if this block dominates `block`.
|
||||
*
|
||||
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
|
||||
* block `B` must pass through block `A`. A block always dominates itself.
|
||||
*/
|
||||
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the dominance frontier of this block.
|
||||
*
|
||||
* The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock dominanceFrontier() {
|
||||
this.getASuccessor() = result and
|
||||
not this.immediatelyDominates(result)
|
||||
or
|
||||
exists(IRBlock prev | result = prev.dominanceFrontier() |
|
||||
this.immediatelyDominates(prev) and
|
||||
not this.immediatelyDominates(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postDominanceFrontier() {
|
||||
this.getAPredecessor() = result and
|
||||
not this.immediatelyPostDominates(result)
|
||||
or
|
||||
exists(IRBlock prev | result = prev.postDominanceFrontier() |
|
||||
this.immediatelyPostDominates(prev) and
|
||||
not this.immediatelyPostDominates(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
final predicate isReachableFromFunctionEntry() {
|
||||
this = this.getEnclosingIRFunction().getEntryBlock() or
|
||||
this.getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
not adjacentInBlock(_, instr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
// - i2 must be the only successor of i1
|
||||
i2 = unique(Instruction i | i = i1.getASuccessor()) and
|
||||
// - i1 must be the only predecessor of i2
|
||||
i1 = unique(Instruction i | i.getASuccessor() = i2) and
|
||||
// - The edge between the two must be a GotoEdge. We just check that one
|
||||
// exists since we've already checked that it's unique.
|
||||
exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
|
||||
// - The edge must not be a back edge. This means we get the same back edges
|
||||
// in the basic-block graph as we do in the raw CFG.
|
||||
not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
|
||||
// This predicate could be simplified to remove one of the `unique`s if we
|
||||
// were willing to rely on the CFG being well-formed and thus never having
|
||||
// more than one successor to an instruction that has a `GotoEdge` out of it.
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) }
|
||||
|
||||
/** Holds if `i` is the `index`th instruction the block starting with `first`. */
|
||||
private Instruction getInstructionFromFirst(Instruction first, int index) =
|
||||
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)
|
||||
|
||||
/** Holds if `i` is the `index`th instruction in `block`. */
|
||||
cached
|
||||
Instruction getInstruction(TIRBlock block, int index) {
|
||||
result = getInstructionFromFirst(getFirstInstruction(block), index)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionCount(TIRBlock block) { result = strictcount(getInstruction(block, _)) }
|
||||
|
||||
cached
|
||||
predicate blockSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = predLast.getSuccessor(kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate blockIdentity(TIRBlock b1, TIRBlock b2) { b1 = b2 }
|
||||
|
||||
pragma[noopt]
|
||||
cached
|
||||
predicate backEdgeSuccessor(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
backEdgeSuccessorRaw(pred, succ, kind)
|
||||
or
|
||||
// See the QLDoc on `backEdgeSuccessorRaw`.
|
||||
exists(TIRBlock pred2 |
|
||||
// Joining with `blockIdentity` is a performance trick to get
|
||||
// `forwardEdgeRaw` on the RHS of a join, where it's fast.
|
||||
blockIdentity(pred, pred2) and
|
||||
forwardEdgeRaw+(pred, pred2)
|
||||
) and
|
||||
blockSuccessor(pred, succ, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an edge from `pred` to `succ` that is not a back edge.
|
||||
*/
|
||||
private predicate forwardEdgeRaw(TIRBlock pred, TIRBlock succ) {
|
||||
exists(EdgeKind kind |
|
||||
blockSuccessor(pred, succ, kind) and
|
||||
not backEdgeSuccessorRaw(pred, succ, kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `kind`-edge from `pred` to `succ` is a back edge according to
|
||||
* `Construction`.
|
||||
*
|
||||
* There could be loops of non-back-edges if there is a flaw in the IR
|
||||
* construction or back-edge detection, and this could cause non-termination
|
||||
* of subsequent analysis. To prevent that, a subsequent predicate further
|
||||
* classifies all edges as back edges if they are involved in a loop of
|
||||
* non-back-edges.
|
||||
*/
|
||||
private predicate backEdgeSuccessorRaw(TIRBlock pred, TIRBlock succ, EdgeKind kind) {
|
||||
exists(Instruction predLast, Instruction succFirst |
|
||||
predLast = getInstruction(pred, getInstructionCount(pred) - 1) and
|
||||
succFirst = Construction::getInstructionBackEdgeSuccessor(predLast, kind) and
|
||||
succ = MkIRBlock(succFirst)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate blockSuccessor(TIRBlock pred, TIRBlock succ) { blockSuccessor(pred, succ, _) }
|
||||
|
||||
cached
|
||||
predicate blockImmediatelyDominates(TIRBlock dominator, TIRBlock block) =
|
||||
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name SSA IR Consistency Check
|
||||
* @description Performs consistency checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind table
|
||||
* @id cpp/ssa-ir-consistency-check
|
||||
*/
|
||||
|
||||
import IRConsistency
|
||||
@@ -1,549 +0,0 @@
|
||||
private import IR
|
||||
import InstructionConsistency // module is below
|
||||
import IRTypeConsistency // module is in IRType.qll
|
||||
import internal.IRConsistencyImports
|
||||
|
||||
module InstructionConsistency {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
private newtype TOptionalIRFunction =
|
||||
TPresentIRFunction(IRFunction irFunc) or
|
||||
TMissingIRFunction()
|
||||
|
||||
/**
|
||||
* An `IRFunction` that might not exist. This is used so that we can produce consistency failures
|
||||
* for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
|
||||
*/
|
||||
abstract private class OptionalIRFunction extends TOptionalIRFunction {
|
||||
abstract string toString();
|
||||
|
||||
abstract Language::Location getLocation();
|
||||
}
|
||||
|
||||
class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
|
||||
private IRFunction irFunc;
|
||||
|
||||
PresentIRFunction() { this = TPresentIRFunction(irFunc) }
|
||||
|
||||
override string toString() {
|
||||
result = concat(LanguageDebug::getIdentityString(irFunc.getFunction()), "; ")
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
// To avoid an overwhelming number of results when the extractor merges functions with the
|
||||
// same name, just pick a single location.
|
||||
result =
|
||||
min(Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
|
||||
}
|
||||
|
||||
IRFunction getIRFunction() { result = irFunc }
|
||||
}
|
||||
|
||||
private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
|
||||
override string toString() { result = "<Missing IRFunction>" }
|
||||
|
||||
override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
|
||||
}
|
||||
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
|
||||
result = TPresentIRFunction(instr.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
|
||||
result = getInstructionIRFunction(instr) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand) {
|
||||
result = TPresentIRFunction(operand.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
|
||||
result = getOperandIRFunction(operand) and
|
||||
irFuncText = result.toString()
|
||||
}
|
||||
|
||||
private OptionalIRFunction getBlockIRFunction(IRBlock block) {
|
||||
result = TPresentIRFunction(block.getEnclosingIRFunction())
|
||||
or
|
||||
not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag |
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
|
||||
"' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock pred |
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
|
||||
pred.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(
|
||||
Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' is missing a type in function '$@'." and
|
||||
irFunc = getOperandIRFunction(operand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(chi, irFuncText)
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message =
|
||||
"Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction and
|
||||
message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple edges of the same kind from `source`.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(
|
||||
Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(EdgeKind kind, int n |
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
message =
|
||||
"Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
|
||||
kind.toString() + "' in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(source, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Language::Function f |
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(
|
||||
PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int n |
|
||||
n = count(instr.getBlock().getAPredecessor()) and
|
||||
n < 2 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
|
||||
" predecessors in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a memory operand is connected to a definition with an unmodeled result.
|
||||
*/
|
||||
query predicate memoryOperandDefinitionIsUnmodeled(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(MemoryOperand operand, Instruction def |
|
||||
operand = instr.getAnOperand() and
|
||||
def = operand.getAnyDef() and
|
||||
not def.isResultModeled() and
|
||||
message =
|
||||
"Memory operand definition on instruction '" + instr.toString() +
|
||||
"' has unmodeled result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
|
||||
OptionalIRFunction defIRFunc, string defIRFuncText
|
||||
) {
|
||||
exists(Instruction useInstr, Instruction defInstr |
|
||||
operand.getUse() = useInstr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
|
||||
defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
|
||||
useIRFunc != defIRFunc and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
|
||||
"' in function '$@', but is defined on instruction '" + defInstr.toString() +
|
||||
"' in function '$@'."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int blockCount |
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
|
||||
" blocks in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f and
|
||||
message = "Function contains a loop consisting of only forward edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(
|
||||
IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction()) and
|
||||
message =
|
||||
"Block '" + block.toString() +
|
||||
"' is not reachable by traversing only forward edges in function '$@'." and
|
||||
irFunc = TPresentIRFunction(f) and
|
||||
irFuncText = irFunc.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
|
||||
exists(int fromInstr, int fromBlock |
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock and
|
||||
message =
|
||||
"The instruction graph for function '" + irFunc.toString() + "' contains " +
|
||||
fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
|
||||
+ " back edges."
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(switchInstr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated() and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr) and
|
||||
message =
|
||||
"Instruction '" + instr.toString() +
|
||||
"' should not be marked as having a conflated result in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
irFunc = getOperandIRFunction(useOperand, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate nonUniqueEnclosingIRFunction(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(int irFuncCount |
|
||||
irFuncCount = count(instr.getEnclosingIRFunction()) and
|
||||
irFuncCount != 1 and
|
||||
message =
|
||||
"Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
|
||||
" results for `getEnclosingIRFunction()` in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
query predicate nonUniqueIRVariable(
|
||||
Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(VariableInstruction vi, IRVariable v1, IRVariable v2 |
|
||||
instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2
|
||||
) and
|
||||
message =
|
||||
"Variable instruction '" + instr.toString() +
|
||||
"' has multiple associated variables, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::VariableAddress and
|
||||
not instr instanceof VariableInstruction and
|
||||
message =
|
||||
"Variable address instruction '" + instr.toString() +
|
||||
"' has no associated variable, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Provides the class `IRFunction`, which represents the Intermediate Representation for the
|
||||
* definition of a function.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import internal.IRFunctionImports as Imports
|
||||
import Imports::IRFunctionBase
|
||||
import Instruction
|
||||
|
||||
/**
|
||||
* The IR for a function.
|
||||
*/
|
||||
class IRFunction extends IRFunctionBase {
|
||||
/**
|
||||
* Gets the entry point for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final EnterFunctionInstruction getEnterFunctionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exit point for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ExitFunctionInstruction getExitFunctionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single return instruction for this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ReturnInstruction getReturnInstruction() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets the variable used to hold the return value of this function. If this
|
||||
* function does not return a value, this predicate does not hold.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRReturnVariable getReturnVariable() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets the block containing the entry point of this function.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock getEntryBlock() {
|
||||
result.getFirstInstruction() = this.getEnterFunctionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all instructions in this function.
|
||||
*/
|
||||
final Instruction getAnInstruction() { result.getEnclosingIRFunction() = this }
|
||||
|
||||
/**
|
||||
* Gets all blocks in this function.
|
||||
*/
|
||||
final IRBlock getABlock() { result.getEnclosingIRFunction() = this }
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
/**
|
||||
* Provides classes that represent variables accessed by the IR.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
import IRFunction
|
||||
private import internal.IRVariableImports as Imports
|
||||
import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
/**
|
||||
* A variable referenced by the IR for a function.
|
||||
*
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
class IRVariable extends TIRVariable {
|
||||
Language::Declaration func;
|
||||
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
* literals, but could also apply to `const` global and static variables.
|
||||
*/
|
||||
predicate isReadOnly() { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
final Language::Type getType() { this.getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = this.getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { none() }
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
Language::AST getAst() { none() }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
/**
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
string getUniqueId() { none() }
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
*/
|
||||
final Language::Location getLocation() { result = this.getAst().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the IR for the function that references this variable.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
||||
|
||||
/**
|
||||
* Gets the function that references this variable.
|
||||
*/
|
||||
final Language::Declaration getEnclosingFunction() { result = func }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
final override string toString() { result = this.getVariable().toString() }
|
||||
|
||||
final override Language::AST getAst() { result = var }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated override Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = this.getVariable().toString() + " " + this.getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
*/
|
||||
Language::Variable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
class IRAutomaticVariable extends IRVariable {
|
||||
IRAutomaticVariable() {
|
||||
exists(Language::Variable var |
|
||||
this = TIRUserVariable(var, _, func) and
|
||||
Language::isVariableAutomatic(var)
|
||||
)
|
||||
or
|
||||
this = TIRTempVariable(func, _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||
* namespace-scope variables, static fields, and static local variables.
|
||||
*/
|
||||
class IRStaticUserVariable extends IRUserVariable {
|
||||
override Language::StaticVariable var;
|
||||
|
||||
IRStaticUserVariable() { not Language::isVariableAutomatic(var) }
|
||||
|
||||
final override Language::StaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
class IRGeneratedVariable extends IRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAst() { result = ast }
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated override Language::AST getAST() { result = this.getAst() }
|
||||
|
||||
override string toString() { result = this.getBaseString() + this.getLocationString() }
|
||||
|
||||
override string getUniqueId() { none() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets a string containing the source code location of the AST that generated this variable.
|
||||
*
|
||||
* This is used by debugging and printing code only.
|
||||
*/
|
||||
final string getLocationString() {
|
||||
result =
|
||||
ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the string that is combined with the location of the variable to generate the string
|
||||
* representation of this variable.
|
||||
*
|
||||
* This is used by debugging and printing code only.
|
||||
*/
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
TempVariableTag tag;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "tag" object that differentiates this temporary variable from other temporary
|
||||
* variables generated for the same AST.
|
||||
*/
|
||||
final TempVariableTag getTag() { result = tag }
|
||||
|
||||
override string getBaseString() { result = "#temp" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the return value of a function.
|
||||
*/
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
IRReturnVariable() { tag = ReturnValueTempVar() }
|
||||
|
||||
final override string toString() { result = "#return" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() { tag = ThrowTempVar() }
|
||||
|
||||
final override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
|
||||
final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
|
||||
final override int getIndex() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||
* a read-only global variable.
|
||||
*/
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
|
||||
final override predicate isReadOnly() { any() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "String: " + this.getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#string" }
|
||||
|
||||
/**
|
||||
* Gets the AST of the string literal represented by this `IRStringLiteral`.
|
||||
*/
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||
}
|
||||
|
||||
final override string toString() { result = var.toString() + "#init" }
|
||||
|
||||
/**
|
||||
* Gets variable whose initialization is guarded by this flag.
|
||||
*/
|
||||
final Language::Variable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result =
|
||||
"Init: " + this.getVariable().toString() + " " + this.getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,499 +0,0 @@
|
||||
/**
|
||||
* Provides classes that represent the input values of IR instructions.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
private import Imports::TOperand
|
||||
private import internal.OperandInternal
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
|
||||
* of `TOperand` that are used in this stage.
|
||||
*/
|
||||
private class TStageOperand =
|
||||
TRegisterOperand or TNonSsaMemoryOperand or TPhiOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* A known location. Testing `loc instanceof KnownLocation` will account for non existing locations, as
|
||||
* opposed to testing `not loc isntanceof UnknownLocation`
|
||||
*/
|
||||
private class KnownLocation extends Language::Location {
|
||||
KnownLocation() { not this instanceof Language::UnknownLocation }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
|
||||
* (the defining instruction) in another instruction (the use instruction)
|
||||
*/
|
||||
class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def))
|
||||
or
|
||||
exists(Instruction use | this = nonSsaMemoryOperand(use, _))
|
||||
or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _) or
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
this = chiOperand(_, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Operand" }
|
||||
|
||||
/**
|
||||
* Gets the location of the source code for this operand.
|
||||
* By default this is where the operand is used, but some subclasses may override this
|
||||
* using `getAnyDef()` if it makes more sense.
|
||||
*/
|
||||
Language::Location getLocation() { result = this.getUse().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the function that contains this operand.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result = this.getUse().getEnclosingIRFunction() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
Instruction getUse() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand. Unlike
|
||||
* `getDef`, this also has a result when `isDefinitionInexact` holds, which
|
||||
* means that the resulting instruction may only _partially_ or _potentially_
|
||||
* be the value of this operand.
|
||||
*/
|
||||
Instruction getAnyDef() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand. Unlike
|
||||
* `getAnyDef`, this also has no result when `isDefinitionInexact` holds,
|
||||
* which means that the resulting instruction must always be exactly the be
|
||||
* the value of this operand.
|
||||
*/
|
||||
final Instruction getDef() {
|
||||
result = this.getAnyDef() and
|
||||
this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the overlap relationship between the operand's definition and its use.
|
||||
*/
|
||||
Overlap getDefinitionOverlap() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the result of the definition instruction does not exactly overlap this use.
|
||||
*/
|
||||
final predicate isDefinitionInexact() {
|
||||
not this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this operand on its use instruction.
|
||||
*/
|
||||
string getDumpId() { result = "" }
|
||||
|
||||
/**
|
||||
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
|
||||
* result ID of the instruction consumed by the operand, plus a label identifying the operand
|
||||
* kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
final string getDumpString() {
|
||||
result = this.getDumpLabel() + this.getInexactSpecifier() + this.getDefinitionId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string containing the identifier of the definition of this use, or `m?` if the
|
||||
* definition is not modeled in SSA.
|
||||
*/
|
||||
private string getDefinitionId() {
|
||||
result = this.getAnyDef().getResultId()
|
||||
or
|
||||
not exists(this.getAnyDef()) and result = "m?"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string prefix to prepend to the operand's definition ID in an IR dump, specifying whether the operand is
|
||||
* an exact or inexact use of its definition. For an inexact use, the prefix is "~". For an exact use, the prefix is
|
||||
* the empty string.
|
||||
*/
|
||||
private string getInexactSpecifier() {
|
||||
if this.isDefinitionInexact() then result = "~" else result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order in which the operand should be sorted in the operand list.
|
||||
*/
|
||||
int getDumpSortOrder() { result = -1 }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { result = this.getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = this.getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { this.getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getType()`.
|
||||
*/
|
||||
final predicate isGLValue() { this.getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
final int getSize() { result = this.getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
cached
|
||||
MemoryOperand() {
|
||||
this instanceof TNonSsaMemoryOperand or
|
||||
this instanceof TPhiOperand or
|
||||
this instanceof TChiOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() { result = this.getUse().getOpcode().getReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Holds if the memory access performed by this operand will not always read from every bit in the
|
||||
* memory location. This is most commonly used for memory accesses that may or may not actually
|
||||
* occur depending on runtime state (for example, the write side effect of an output parameter
|
||||
* that is not written to on all paths), or for accesses where the memory location is a
|
||||
* conservative estimate of the memory that might actually be accessed at runtime (for example,
|
||||
* the global side effects of a function call).
|
||||
*/
|
||||
predicate hasMayReadMemoryAccess() { this.getUse().getOpcode().hasMayReadMemoryAccess() }
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
|
||||
* is `r1`.
|
||||
*/
|
||||
final AddressOperand getAddressOperand() {
|
||||
this.getMemoryAccess().usesAddressOperand() and
|
||||
result.getUse() = this.getUse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand {
|
||||
Instruction useInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = registerOperand(useInstr, tag, _) or
|
||||
this = nonSsaMemoryOperand(useInstr, tag) or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override Instruction getUse() { result = useInstr }
|
||||
|
||||
final override string getDumpLabel() { result = tag.getLabel() }
|
||||
|
||||
final override string getDumpId() { result = tag.getId() }
|
||||
|
||||
final override int getDumpSortOrder() { result = tag.getSortOrder() }
|
||||
|
||||
/**
|
||||
* Gets the `OperandTag` that specifies how this operand is used by its `Instruction`.
|
||||
*/
|
||||
final OperandTag getOperandTag() { result = tag }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
Instruction defInstr;
|
||||
|
||||
cached
|
||||
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
// most `RegisterOperands` have a more meaningful location at the definition
|
||||
// the only exception are specific cases of `ThisArgumentOperand`
|
||||
override Language::Location getLocation() { result = this.getAnyDef().getLocation() }
|
||||
|
||||
final override Instruction getAnyDef() { result = defInstr }
|
||||
|
||||
final override Overlap getDefinitionOverlap() {
|
||||
// All register results overlap exactly with their uses.
|
||||
result instanceof MustExactlyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory operand other than the operand of a `Phi` instruction.
|
||||
*/
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
|
||||
cached
|
||||
NonPhiMemoryOperand() {
|
||||
this = nonSsaMemoryOperand(useInstr, tag)
|
||||
or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
final override Instruction getAnyDef() {
|
||||
result = unique(Instruction defInstr | this.hasDefinition(defInstr, _))
|
||||
}
|
||||
|
||||
final override Overlap getDefinitionOverlap() { this.hasDefinition(_, result) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
|
||||
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
|
||||
not Construction::isInCycle(useInstr) and
|
||||
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the operand totally overlaps with its definition and consumes the
|
||||
* bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
|
||||
*/
|
||||
predicate getUsedInterval(int startBitOffset, int endBitOffset) {
|
||||
Construction::getUsedInterval(this, startBitOffset, endBitOffset)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A memory operand whose type may be different from the type of the result of its definition.
|
||||
*/
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class AddressOperand extends RegisterOperand {
|
||||
override AddressOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperand {
|
||||
override BufferSizeOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
*/
|
||||
class LoadOperand extends TypedOperand {
|
||||
override LoadOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of a `Store` instruction.
|
||||
*/
|
||||
class StoreValueOperand extends RegisterOperand {
|
||||
override StoreValueOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
|
||||
*/
|
||||
class UnaryOperand extends RegisterOperand {
|
||||
override UnaryOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends RegisterOperand {
|
||||
override LeftOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends RegisterOperand {
|
||||
override RightOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends RegisterOperand {
|
||||
override ConditionOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends RegisterOperand {
|
||||
override CallTargetOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit `this` argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
override ThisArgumentOperandTag tag;
|
||||
|
||||
// in most cases the def location makes more sense, but in some corner cases it
|
||||
// has an unknown location: in those cases we fall back to the use location
|
||||
override Language::Location getLocation() {
|
||||
if this.getAnyDef().getLocation() instanceof KnownLocation
|
||||
then result = this.getAnyDef().getLocation()
|
||||
else result = this.getUse().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override PositionalArgumentOperandTag tag;
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() { result = tag.getArgIndex() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing memory read as a side effect of evaluating another instruction.
|
||||
*/
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() {
|
||||
this = phiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
or
|
||||
this = reusedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
final override PhiInstruction getUse() { result = useInstr }
|
||||
|
||||
final override Instruction getAnyDef() { result = defInstr }
|
||||
|
||||
final override Overlap getDefinitionOverlap() { result = overlap }
|
||||
|
||||
final override int getDumpSortOrder() {
|
||||
result = 11 + this.getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
final override string getDumpLabel() {
|
||||
result = "from " + this.getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
final override string getDumpId() {
|
||||
result = this.getPredecessorBlock().getDisplayIndex().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() { result = predecessorBlock }
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof PhiMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* The total operand of a Chi node, representing the previous value of the memory.
|
||||
*/
|
||||
class ChiTotalOperand extends NonPhiMemoryOperand {
|
||||
override ChiTotalOperandTag tag;
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* The partial operand of a Chi node, representing the value being written to part of the memory.
|
||||
*/
|
||||
class ChiPartialOperand extends NonPhiMemoryOperand {
|
||||
override ChiPartialOperandTag tag;
|
||||
|
||||
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @name Print SSA IR
|
||||
* @description Outputs a representation of the SSA IR graph
|
||||
* @id cpp/print-ssa-ir
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import PrintIR
|
||||
@@ -1,342 +0,0 @@
|
||||
/**
|
||||
* Outputs a representation of the IR as a control flow graph.
|
||||
*
|
||||
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
|
||||
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
|
||||
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
|
||||
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
|
||||
* to dump.
|
||||
*/
|
||||
|
||||
private import internal.IRInternal
|
||||
private import IR
|
||||
private import internal.PrintIRImports as Imports
|
||||
import Imports::IRConfiguration
|
||||
|
||||
private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which declarations are printed.
|
||||
*/
|
||||
class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
/** Gets a textual representation of this configuration. */
|
||||
string toString() { result = "PrintIRConfiguration" }
|
||||
|
||||
/**
|
||||
* Holds if the IR for `func` should be printed. By default, holds for all
|
||||
* functions, global and namespace variables, and static local variables.
|
||||
*/
|
||||
predicate shouldPrintDeclaration(Language::Declaration decl) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
|
||||
shouldPrintDeclaration(func)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintDeclaration(Language::Declaration decl) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl))
|
||||
}
|
||||
|
||||
private predicate shouldPrintInstruction(Instruction i) {
|
||||
exists(IRPropertyProvider provider | provider.shouldPrintInstruction(i))
|
||||
}
|
||||
|
||||
private predicate shouldPrintOperand(Operand operand) {
|
||||
exists(IRPropertyProvider provider | provider.shouldPrintOperand(operand))
|
||||
}
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties of an operand from any active property providers.
|
||||
*/
|
||||
private string getAdditionalOperandProperty(Operand operand, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string listing the properties of the operand and their corresponding values. If the
|
||||
* operand has no properties, this predicate has no result.
|
||||
*/
|
||||
private string getOperandPropertyListString(Operand operand) {
|
||||
result =
|
||||
strictconcat(string key, string value |
|
||||
value = getAdditionalOperandProperty(operand, key)
|
||||
|
|
||||
key + ":" + value, ", "
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string listing the properties of the operand and their corresponding values. The list is
|
||||
* surrounded by curly braces. If the operand has no properties, this predicate returns an empty
|
||||
* string.
|
||||
*/
|
||||
private string getOperandPropertyString(Operand operand) {
|
||||
result = "{" + getOperandPropertyListString(operand) + "}"
|
||||
or
|
||||
not exists(getOperandPropertyListString(operand)) and result = ""
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or
|
||||
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or
|
||||
TPrintableInstruction(Instruction instr) {
|
||||
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* A node to be emitted in the IR graph.
|
||||
*/
|
||||
abstract private class PrintableIRNode extends TPrintableIRNode {
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the location to be emitted for the node.
|
||||
*/
|
||||
abstract Language::Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets the label to be emitted for the node.
|
||||
*/
|
||||
abstract string getLabel();
|
||||
|
||||
/**
|
||||
* Gets the order in which the node appears in its parent node.
|
||||
*/
|
||||
abstract int getOrder();
|
||||
|
||||
/**
|
||||
* Gets the parent of this node.
|
||||
*/
|
||||
abstract PrintableIRNode getParent();
|
||||
|
||||
/**
|
||||
* Gets the kind of graph represented by this node ("graph" or "tree").
|
||||
*/
|
||||
string getGraphKind() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this node should always be rendered as text, even in a graphical
|
||||
* viewer.
|
||||
*/
|
||||
predicate forceText() { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the node property with the specified key.
|
||||
*/
|
||||
string getProperty(string key) {
|
||||
key = "semmle.label" and result = this.getLabel()
|
||||
or
|
||||
key = "semmle.order" and result = this.getOrder().toString()
|
||||
or
|
||||
key = "semmle.graphKind" and result = this.getGraphKind()
|
||||
or
|
||||
key = "semmle.forceText" and this.forceText() and result = "true"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing a `IRFunction` object.
|
||||
*/
|
||||
private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
|
||||
IRFunction irFunc;
|
||||
|
||||
PrintableIRFunction() { this = TPrintableIRFunction(irFunc) }
|
||||
|
||||
override string toString() { result = irFunc.toString() }
|
||||
|
||||
override Language::Location getLocation() { result = irFunc.getLocation() }
|
||||
|
||||
override string getLabel() {
|
||||
result = Imports::LanguageDebug::getIdentityString(irFunc.getFunction())
|
||||
}
|
||||
|
||||
override int getOrder() {
|
||||
this =
|
||||
rank[result + 1](PrintableIRFunction orderedFunc, Language::Location location |
|
||||
location = orderedFunc.getIRFunction().getLocation()
|
||||
|
|
||||
orderedFunc
|
||||
order by
|
||||
location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(),
|
||||
orderedFunc.getLabel()
|
||||
)
|
||||
}
|
||||
|
||||
final override PrintableIRNode getParent() { none() }
|
||||
|
||||
final IRFunction getIRFunction() { result = irFunc }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing an `IRBlock` object.
|
||||
*/
|
||||
private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
IRBlock block;
|
||||
|
||||
PrintableIRBlock() { this = TPrintableIRBlock(block) }
|
||||
|
||||
override string toString() { result = this.getLabel() }
|
||||
|
||||
override Language::Location getLocation() { result = block.getLocation() }
|
||||
|
||||
override string getLabel() { result = "Block " + block.getDisplayIndex().toString() }
|
||||
|
||||
override int getOrder() { result = block.getDisplayIndex() }
|
||||
|
||||
final override string getGraphKind() { result = "tree" }
|
||||
|
||||
final override predicate forceText() { any() }
|
||||
|
||||
final override PrintableIRFunction getParent() {
|
||||
result.getIRFunction() = block.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() { result = block }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing an `Instruction`.
|
||||
*/
|
||||
private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
Instruction instr;
|
||||
|
||||
PrintableInstruction() { this = TPrintableInstruction(instr) }
|
||||
|
||||
override string toString() { result = instr.toString() }
|
||||
|
||||
override Language::Location getLocation() { result = instr.getLocation() }
|
||||
|
||||
override string getLabel() {
|
||||
exists(IRBlock block |
|
||||
instr = block.getAnInstruction() and
|
||||
exists(
|
||||
string resultString, string operationString, string operandsString, int resultWidth,
|
||||
int operationWidth
|
||||
|
|
||||
resultString = instr.getResultString() and
|
||||
operationString = instr.getOperationString() and
|
||||
operandsString = this.getOperandsString() and
|
||||
columnWidths(block, resultWidth, operationWidth) and
|
||||
result =
|
||||
resultString + getPaddingString(resultWidth - resultString.length()) + " = " +
|
||||
operationString + getPaddingString(operationWidth - operationString.length()) + " : " +
|
||||
operandsString
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getOrder() { result = instr.getDisplayIndexInBlock() }
|
||||
|
||||
final override PrintableIRBlock getParent() { result.getBlock() = instr.getBlock() }
|
||||
|
||||
final Instruction getInstruction() { result = instr }
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the operand list. This is the same as
|
||||
* `Instruction::getOperandsString()`, except that each operand is annotated with any properties
|
||||
* provided by active `IRPropertyProvider` instances.
|
||||
*/
|
||||
private string getOperandsString() {
|
||||
result =
|
||||
concat(Operand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
shouldPrintOperand(operand)
|
||||
|
|
||||
operand.getDumpString() + getOperandPropertyString(operand), ", "
|
||||
order by
|
||||
operand.getDumpSortOrder()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
resultWidth = max(Instruction instr | instr.getBlock() = block | instr.getResultString().length()) and
|
||||
operationWidth =
|
||||
max(Instruction instr | instr.getBlock() = block | instr.getOperationString().length())
|
||||
}
|
||||
|
||||
private int maxColumnWidth() {
|
||||
result =
|
||||
max(Instruction instr, int width |
|
||||
width = instr.getResultString().length() or
|
||||
width = instr.getOperationString().length() or
|
||||
width = instr.getOperandsString().length()
|
||||
|
|
||||
width
|
||||
)
|
||||
}
|
||||
|
||||
private string getPaddingString(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " "
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` belongs to the output graph, and its property `key` has the given `value`.
|
||||
*/
|
||||
query predicate nodes(PrintableIRNode node, string key, string value) {
|
||||
value = node.getProperty(key)
|
||||
}
|
||||
|
||||
private int getSuccessorIndex(IRBlock pred, IRBlock succ) {
|
||||
succ =
|
||||
rank[result + 1](IRBlock aSucc, EdgeKind kind |
|
||||
aSucc = pred.getSuccessor(kind)
|
||||
|
|
||||
aSucc order by kind.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key`
|
||||
* has the given `value`.
|
||||
*/
|
||||
query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) {
|
||||
exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock |
|
||||
predBlock = pred.getBlock() and
|
||||
succBlock = succ.getBlock() and
|
||||
predBlock.getSuccessor(kind) = succBlock and
|
||||
(
|
||||
(
|
||||
key = "semmle.label" and
|
||||
if predBlock.getBackEdgeSuccessor(kind) = succBlock
|
||||
then value = kind.toString() + " (back edge)"
|
||||
else value = kind.toString()
|
||||
)
|
||||
or
|
||||
key = "semmle.order" and
|
||||
value = getSuccessorIndex(predBlock, succBlock).toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `parent` is the parent node of `child` in the output graph.
|
||||
*/
|
||||
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
|
||||
parent = child.getParent()
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import experimental.ir.internal.IntegerPartial
|
||||
private import IR
|
||||
|
||||
language[monotonicAggregates]
|
||||
int getConstantValue(Instruction instr) {
|
||||
result = instr.(IntegerConstantInstruction).getValue().toInt()
|
||||
or
|
||||
result = getBinaryInstructionValue(instr)
|
||||
or
|
||||
result = neg(getConstantValue(instr.(NegateInstruction).getUnary()))
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and
|
||||
result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate binaryInstructionOperands(BinaryInstruction instr, int left, int right) {
|
||||
left = getConstantValue(instr.getLeft()) and
|
||||
right = getConstantValue(instr.getRight())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||
exists(int left, int right |
|
||||
binaryInstructionOperands(instr, left, right) and
|
||||
(
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user