Merge pull request #15838 from michaelnebel/csharp/deleteirqueries

C#: Remove IR queries.
This commit is contained in:
Michael Nebel
2024-03-08 11:29:23 +01:00
committed by GitHub
194 changed files with 16 additions and 30036 deletions

View File

@@ -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"
]
}
}

View File

@@ -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

View File

@@ -1 +0,0 @@
import implementation.IRConfiguration

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
import implementation.unaliased_ssa.PrintIR

View File

@@ -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();
}

View File

@@ -1 +0,0 @@
import implementation.unaliased_ssa.gvn.ValueNumbering

View File

@@ -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) }
}

View File

@@ -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() }
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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) }
}

View File

@@ -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() }
}

View File

@@ -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() }
}

View File

@@ -1 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language

View File

@@ -1 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language

View File

@@ -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() }
}

View File

@@ -1,2 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language
import experimental.ir.implementation.raw.internal.IRConstruction as IRConstruction

View File

@@ -1 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language

View File

@@ -1 +0,0 @@
import experimental.ir.implementation.MemoryAccessKind as MemoryAccessKind

View File

@@ -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) }

View File

@@ -1 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language

View File

@@ -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)
}

View File

@@ -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_;
}

View File

@@ -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)
}
}

View File

@@ -1,2 +0,0 @@
import experimental.ir.implementation.IRType as IRType
import experimental.ir.implementation.Opcode as Opcode

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -1,6 +0,0 @@
import experimental.ir.internal.IRCSharpLanguage as Language
private import experimental.ir.internal.TempVariableTag as TempVariableTag_
module Imports {
module TempVariableTag = TempVariableTag_;
}

View File

@@ -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() }
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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 }
}

View File

@@ -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() }
}

View File

@@ -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 }
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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)
)
)
}

View File

@@ -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()
}
}

View File

@@ -1 +0,0 @@
import experimental.ir.implementation.raw.IR as IR

View File

@@ -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"
)
}
}

View File

@@ -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) }

View File

@@ -1,3 +0,0 @@
import experimental.ir.internal.Overlap
import experimental.ir.internal.IRCSharpLanguage as Language
import experimental.ir.implementation.unaliased_ssa.IR

View File

@@ -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())
)
)
}

View File

@@ -1 +0,0 @@
import experimental.ir.implementation.EdgeKind as EdgeKind

View File

@@ -1 +0,0 @@
import experimental.ir.internal.IRCSharpLanguageDebug as LanguageDebug

View File

@@ -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))
}
}

View File

@@ -1 +0,0 @@
import experimental.ir.implementation.internal.IRFunctionBase as IRFunctionBase

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 + ")"
)
}

View File

@@ -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

View File

@@ -1,2 +0,0 @@
private import experimental.ir.implementation.internal.TOperand
import RawOperands

View File

@@ -1,2 +0,0 @@
import experimental.ir.IRConfiguration as IRConfiguration
import experimental.ir.internal.IRCSharpLanguageDebug as LanguageDebug

View File

@@ -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()
)
}
}

View File

@@ -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() }
}

View File

@@ -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())
}
}

View File

@@ -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 }
}

View File

@@ -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())
)
}
}

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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))
}
}

View File

@@ -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() }
}

View File

@@ -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() }
}

View File

@@ -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() + ")"
}
}

View File

@@ -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() + ")"
}
}

View File

@@ -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();
}

View File

@@ -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 }
}

View File

@@ -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();
}

View File

@@ -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() + ")"
}
}

View File

@@ -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)
)
}

View File

@@ -1,7 +0,0 @@
private import ReachableBlock as Reachability
module Graph {
import Reachability::Graph
class Block = Reachability::ReachableBlock;
}

View File

@@ -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()
)
}
}

View File

@@ -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"
)
}
}

View File

@@ -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()
}
}

View File

@@ -1,2 +0,0 @@
import experimental.ir.implementation.raw.IR as IR
import experimental.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis

View File

@@ -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() }
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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 }
}

View File

@@ -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() }
}

View File

@@ -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 }
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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