mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge pull request #1600 from AndreiDiaconu1/ircsharp
C#: Initial port of IR for C#
This commit is contained in:
@@ -39,42 +39,90 @@
|
||||
"IR Instruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TIRVariable":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll"
|
||||
],
|
||||
"IR IRSanity": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -148,5 +196,23 @@
|
||||
"C++ IR PrintDominance": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ private newtype TOpcode =
|
||||
TVariableAddress() or
|
||||
TFieldAddress() or
|
||||
TFunctionAddress() or
|
||||
TElementsAddress() or
|
||||
TConstant() or
|
||||
TStringConstant() or
|
||||
TConditionalBranch() or
|
||||
@@ -72,7 +73,8 @@ private newtype TOpcode =
|
||||
TBufferMayWriteSideEffect() or
|
||||
TChi() or
|
||||
TInlineAsm() or
|
||||
TUnreached()
|
||||
TUnreached() or
|
||||
TNewObj()
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() {
|
||||
@@ -190,6 +192,7 @@ module Opcode {
|
||||
class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { override final string toString() { result = "DynamicCastToVoid" } }
|
||||
class VariableAddress extends Opcode, TVariableAddress { override final string toString() { result = "VariableAddress" } }
|
||||
class FieldAddress extends UnaryOpcode, TFieldAddress { override final string toString() { result = "FieldAddress" } }
|
||||
class ElementsAddress extends UnaryOpcode, TElementsAddress { override final string toString() { result = "ElementsAddress" } }
|
||||
class FunctionAddress extends Opcode, TFunctionAddress { override final string toString() { result = "FunctionAddress" } }
|
||||
class Constant extends Opcode, TConstant { override final string toString() { result = "Constant" } }
|
||||
class StringConstant extends Opcode, TStringConstant { override final string toString() { result = "StringConstant" } }
|
||||
@@ -221,4 +224,5 @@ module Opcode {
|
||||
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
|
||||
class InlineAsm extends Opcode, TInlineAsm { override final string toString() { result = "InlineAsm" } }
|
||||
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
|
||||
class NewObj extends Opcode, TNewObj { override final string toString() { result = "NewObj" } }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import internal.TempVariableTagInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
class TempVariableTag extends TTempVariableTag {
|
||||
string toString() {
|
||||
|
||||
@@ -512,7 +512,7 @@ class Instruction extends Construction::TInstruction {
|
||||
else if getResultType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionResultSize(this)
|
||||
else (
|
||||
result = getResultType().getSize()
|
||||
result = Language::getTypeSize(getResultType())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ class Operand extends TOperand {
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() {
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class SideEffectOperand extends TypedOperand {
|
||||
if getType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
module TempVariableTag = TempVariableTag_;
|
||||
}
|
||||
@@ -512,7 +512,7 @@ class Instruction extends Construction::TInstruction {
|
||||
else if getResultType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionResultSize(this)
|
||||
else (
|
||||
result = getResultType().getSize()
|
||||
result = Language::getTypeSize(getResultType())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ class Operand extends TOperand {
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() {
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class SideEffectOperand extends TypedOperand {
|
||||
if getType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
|
||||
@@ -512,7 +512,7 @@ class Instruction extends Construction::TInstruction {
|
||||
else if getResultType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionResultSize(this)
|
||||
else (
|
||||
result = getResultType().getSize()
|
||||
result = Language::getTypeSize(getResultType())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ class Operand extends TOperand {
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() {
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class SideEffectOperand extends TypedOperand {
|
||||
if getType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else
|
||||
result = getType().getSize()
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
|
||||
@@ -52,6 +52,10 @@ predicate hasAsmOperandIndex(int operandIndex) {
|
||||
)
|
||||
}
|
||||
|
||||
int getTypeSize(Type type) {
|
||||
result = type.getSize()
|
||||
}
|
||||
|
||||
int getPointerSize() {
|
||||
exists(Cpp::NullPointerType nullptr |
|
||||
result = nullptr.getSize()
|
||||
|
||||
@@ -422,6 +422,9 @@ class ValueType extends ValueOrRefType, @value_type {
|
||||
* (`FloatingPointType`), or a decimal type (`DecimalType`).
|
||||
*/
|
||||
class SimpleType extends ValueType, @simple_type {
|
||||
/** Gets the size of this type, in bytes. */
|
||||
int getSize() { none() }
|
||||
|
||||
/** Gets the minimum integral value of this type, if any. */
|
||||
int minValue() { none() }
|
||||
|
||||
@@ -436,6 +439,8 @@ class SimpleType extends ValueType, @simple_type {
|
||||
*/
|
||||
class BoolType extends SimpleType, @bool_type {
|
||||
override string toStringWithTypes() { result = "bool" }
|
||||
|
||||
override int getSize() { result = 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,6 +449,8 @@ class BoolType extends SimpleType, @bool_type {
|
||||
class CharType extends SimpleType, @char_type {
|
||||
override string toStringWithTypes() { result = "char" }
|
||||
|
||||
override int getSize() { result = 2 }
|
||||
|
||||
override int minValue() { result = 0 }
|
||||
|
||||
override int maxValue() { result = 65535 }
|
||||
@@ -481,6 +488,8 @@ class SignedIntegralType extends IntegralType, @signed_integral_type { }
|
||||
class SByteType extends SignedIntegralType, @sbyte_type {
|
||||
override string toStringWithTypes() { result = "sbyte" }
|
||||
|
||||
override int getSize() { result = 1 }
|
||||
|
||||
override int minValue() { result = -128 }
|
||||
|
||||
override int maxValue() { result = 127 }
|
||||
@@ -492,6 +501,8 @@ class SByteType extends SignedIntegralType, @sbyte_type {
|
||||
class ShortType extends SignedIntegralType, @short_type {
|
||||
override string toStringWithTypes() { result = "short" }
|
||||
|
||||
override int getSize() { result = 2 }
|
||||
|
||||
override int minValue() { result = -32768 }
|
||||
|
||||
override int maxValue() { result = 32767 }
|
||||
@@ -503,6 +514,8 @@ class ShortType extends SignedIntegralType, @short_type {
|
||||
class IntType extends SignedIntegralType, @int_type {
|
||||
override string toStringWithTypes() { result = "int" }
|
||||
|
||||
override int getSize() { result = 4 }
|
||||
|
||||
override int minValue() { result = -2147483647 - 1 }
|
||||
|
||||
override int maxValue() { result = 2147483647 }
|
||||
@@ -513,6 +526,8 @@ class IntType extends SignedIntegralType, @int_type {
|
||||
*/
|
||||
class LongType extends SignedIntegralType, @long_type {
|
||||
override string toStringWithTypes() { result = "long" }
|
||||
|
||||
override int getSize() { result = 8 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,6 +536,8 @@ class LongType extends SignedIntegralType, @long_type {
|
||||
class ByteType extends UnsignedIntegralType, @byte_type {
|
||||
override string toStringWithTypes() { result = "byte" }
|
||||
|
||||
override int getSize() { result = 1 }
|
||||
|
||||
override int maxValue() { result = 255 }
|
||||
}
|
||||
|
||||
@@ -530,6 +547,8 @@ class ByteType extends UnsignedIntegralType, @byte_type {
|
||||
class UShortType extends UnsignedIntegralType, @ushort_type {
|
||||
override string toStringWithTypes() { result = "ushort" }
|
||||
|
||||
override int getSize() { result = 2 }
|
||||
|
||||
override int maxValue() { result = 65535 }
|
||||
}
|
||||
|
||||
@@ -538,6 +557,8 @@ class UShortType extends UnsignedIntegralType, @ushort_type {
|
||||
*/
|
||||
class UIntType extends UnsignedIntegralType, @uint_type {
|
||||
override string toStringWithTypes() { result = "uint" }
|
||||
|
||||
override int getSize() { result = 4 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,6 +566,8 @@ class UIntType extends UnsignedIntegralType, @uint_type {
|
||||
*/
|
||||
class ULongType extends UnsignedIntegralType, @ulong_type {
|
||||
override string toStringWithTypes() { result = "ulong" }
|
||||
|
||||
override int getSize() { result = 8 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,6 +582,8 @@ class FloatingPointType extends SimpleType, @floating_point_type { }
|
||||
*/
|
||||
class FloatType extends FloatingPointType, @float_type {
|
||||
override string toStringWithTypes() { result = "float" }
|
||||
|
||||
override int getSize() { result = 4 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,6 +591,8 @@ class FloatType extends FloatingPointType, @float_type {
|
||||
*/
|
||||
class DoubleType extends FloatingPointType, @double_type {
|
||||
override string toStringWithTypes() { result = "double" }
|
||||
|
||||
override int getSize() { result = 8 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -573,6 +600,8 @@ class DoubleType extends FloatingPointType, @double_type {
|
||||
*/
|
||||
class DecimalType extends SimpleType, @decimal_type {
|
||||
override string toStringWithTypes() { result = "decimal" }
|
||||
|
||||
override int getSize() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
5
csharp/ql/src/semmle/code/csharp/ir/IR.qll
Normal file
5
csharp/ql/src/semmle/code/csharp/ir/IR.qll
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Most queries should operate on the aliased SSA IR, so that's what we expose
|
||||
* publicly as the "IR".
|
||||
*/
|
||||
import implementation.raw.IR
|
||||
23
csharp/ql/src/semmle/code/csharp/ir/IRConfiguration.qll
Normal file
23
csharp/ql/src/semmle/code/csharp/ir/IRConfiguration.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Module used to configure the IR generation process.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
private newtype TIRConfiguration = MkIRConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which functions have IR generated for them.
|
||||
*/
|
||||
class IRConfiguration extends TIRConfiguration {
|
||||
string toString() {
|
||||
result = "IRConfiguration"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if IR should be created for callable `callable`. By default, holds for all callables.
|
||||
*/
|
||||
predicate shouldCreateIRForFunction(Callable callable) {
|
||||
any()
|
||||
}
|
||||
}
|
||||
8
csharp/ql/src/semmle/code/csharp/ir/IRSanity.ql
Normal file
8
csharp/ql/src/semmle/code/csharp/ir/IRSanity.ql
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @name IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind table
|
||||
* @id csharp/ir-sanity-check
|
||||
*/
|
||||
|
||||
import implementation.raw.IRSanity
|
||||
8
csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql
Normal file
8
csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @name Print IR
|
||||
* @description Outputs a representation of the IR graph
|
||||
* @id charp/print-ir
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import implementation.raw.PrintIR
|
||||
1
csharp/ql/src/semmle/code/csharp/ir/PrintIR.qll
Normal file
1
csharp/ql/src/semmle/code/csharp/ir/PrintIR.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.raw.PrintIR
|
||||
43
csharp/ql/src/semmle/code/csharp/ir/Util.qll
Normal file
43
csharp/ql/src/semmle/code/csharp/ir/Util.qll
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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) {
|
||||
isInitialized(elementIndex) and
|
||||
not exists(this.getElement(elementIndex))
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectInitializerMod extends ObjectInitializer {
|
||||
private predicate isInitialized(Field field) {
|
||||
not field.isReadOnly() and // TODO: Is this the only instance whena field can not be init?
|
||||
this.getAMemberInitializer().getTargetVariable() = field
|
||||
}
|
||||
|
||||
predicate isValueInitialized(Field field) {
|
||||
this.isInitialized(field) and
|
||||
not field = this.getAMemberInitializer().getInitializedMember()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: See if we need to adapt this for C#
|
||||
abstract class SideEffectFunction extends Callable {
|
||||
/**
|
||||
* Holds if the function never reads from memory that was defined before entry to the function.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverReadsMemory();
|
||||
|
||||
/**
|
||||
* Holds if the function never writes to memory that remains allocated after the function
|
||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||
* from a pointer that was passed into the function.
|
||||
*/
|
||||
abstract predicate neverWritesMemory();
|
||||
}
|
||||
1
csharp/ql/src/semmle/code/csharp/ir/ValueNumbering.qll
Normal file
1
csharp/ql/src/semmle/code/csharp/ir/ValueNumbering.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.raw.gvn.ValueNumbering
|
||||
122
csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll
Normal file
122
csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll
Normal file
@@ -0,0 +1,122 @@
|
||||
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 {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A "goto" edge, representing the unconditional successor of an `Instruction`
|
||||
* or `IRBlock`.
|
||||
*/
|
||||
class GotoEdge extends EdgeKind, TGotoEdge {
|
||||
override final string toString() {
|
||||
result = "Goto"
|
||||
}
|
||||
}
|
||||
|
||||
GotoEdge gotoEdge() {
|
||||
result = TGotoEdge()
|
||||
}
|
||||
|
||||
/**
|
||||
* A "true" edge, representing the successor of a conditional branch when the
|
||||
* condition is non-zero.
|
||||
*/
|
||||
class TrueEdge extends EdgeKind, TTrueEdge {
|
||||
override final string toString() {
|
||||
result = "True"
|
||||
}
|
||||
}
|
||||
|
||||
TrueEdge trueEdge() {
|
||||
result = TTrueEdge()
|
||||
}
|
||||
|
||||
/**
|
||||
* A "false" edge, representing the successor of a conditional branch when the
|
||||
* condition is zero.
|
||||
*/
|
||||
class FalseEdge extends EdgeKind, TFalseEdge {
|
||||
override final string toString() {
|
||||
result = "False"
|
||||
}
|
||||
}
|
||||
|
||||
FalseEdge falseEdge() {
|
||||
result = TFalseEdge()
|
||||
}
|
||||
|
||||
/**
|
||||
* An "exception" edge, representing the successor of an instruction when that
|
||||
* instruction's evaluation throws an exception.
|
||||
*/
|
||||
class ExceptionEdge extends EdgeKind, TExceptionEdge {
|
||||
override final string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionEdge exceptionEdge() {
|
||||
result = TExceptionEdge()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
override final string toString() {
|
||||
result = "Default"
|
||||
}
|
||||
}
|
||||
|
||||
DefaultEdge defaultEdge() {
|
||||
result = TDefaultEdge()
|
||||
}
|
||||
|
||||
/**
|
||||
* A "case" edge, representing the successor of a `Switch` instruction when the
|
||||
* the condition value matches a correponding `case` label.
|
||||
*/
|
||||
class CaseEdge extends EdgeKind, TCaseEdge {
|
||||
string minValue;
|
||||
string maxValue;
|
||||
|
||||
CaseEdge() {
|
||||
this = TCaseEdge(minValue, maxValue)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
if minValue = maxValue then
|
||||
result = "Case[" + minValue + "]"
|
||||
else
|
||||
result = "Case[" + minValue + ".." + maxValue + "]"
|
||||
}
|
||||
|
||||
string getMinValue() {
|
||||
result = minValue
|
||||
}
|
||||
|
||||
string getMaxValue() {
|
||||
result = maxValue
|
||||
}
|
||||
}
|
||||
|
||||
CaseEdge caseEdge(string minValue, string maxValue) {
|
||||
result = TCaseEdge(minValue, maxValue)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 {
|
||||
string toString() {
|
||||
result = "IRConfiguration"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if IR should be created for function `func`. By default, holds for all functions.
|
||||
*/
|
||||
predicate shouldCreateIRForFunction(Language::Function func) {
|
||||
any()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
private newtype TMemoryAccessKind =
|
||||
TIndirectMemoryAccess() or
|
||||
TIndirectMayMemoryAccess() or
|
||||
TBufferMemoryAccess() or
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TEscapedMayMemoryAccess() 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 {
|
||||
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"
|
||||
}
|
||||
|
||||
override final predicate usesAddressOperand() {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory at the address specified by the
|
||||
* `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class IndirectMayMemoryAccess extends MemoryAccessKind, TIndirectMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "indirect(may)"
|
||||
}
|
||||
|
||||
override final 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"
|
||||
}
|
||||
|
||||
override final predicate usesAddressOperand() {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access some, all, or none of the memory starting at the address
|
||||
* specified by the `AddressOperand` on the same instruction, accessing a number of consecutive
|
||||
* elements given by the `BufferSizeOperand`.
|
||||
*/
|
||||
class BufferMayMemoryAccess extends MemoryAccessKind, TBufferMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "buffer(may)"
|
||||
}
|
||||
|
||||
override final 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 may access all memory whose address has escaped.
|
||||
*/
|
||||
class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
|
||||
override string toString() {
|
||||
result = "escaped(may)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand accesses memory not modeled in SSA. Used only on the result of
|
||||
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.
|
||||
*/
|
||||
class UnmodeledMemoryAccess extends MemoryAccessKind, TUnmodeledMemoryAccess {
|
||||
override string toString() {
|
||||
result = "unmodeled"
|
||||
}
|
||||
}
|
||||
228
csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll
Normal file
228
csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll
Normal file
@@ -0,0 +1,228 @@
|
||||
private newtype TOpcode =
|
||||
TNoOp() or
|
||||
TUninitialized() or
|
||||
TError() or
|
||||
TInitializeParameter() or
|
||||
TInitializeThis() or
|
||||
TEnterFunction() or
|
||||
TExitFunction() or
|
||||
TReturnValue() or
|
||||
TReturnVoid() or
|
||||
TCopyValue() or
|
||||
TLoad() or
|
||||
TStore() or
|
||||
TAdd() or
|
||||
TSub() or
|
||||
TMul() or
|
||||
TDiv() or
|
||||
TRem() or
|
||||
TNegate() or
|
||||
TShiftLeft() or
|
||||
TShiftRight() or
|
||||
TBitAnd() or
|
||||
TBitOr() or
|
||||
TBitXor() or
|
||||
TBitComplement() or
|
||||
TLogicalNot() or
|
||||
TCompareEQ() or
|
||||
TCompareNE() or
|
||||
TCompareLT() or
|
||||
TCompareGT() or
|
||||
TCompareLE() or
|
||||
TCompareGE() or
|
||||
TPointerAdd() or
|
||||
TPointerSub() or
|
||||
TPointerDiff() or
|
||||
TConvert() or
|
||||
TConvertToBase() or
|
||||
TConvertToVirtualBase() or
|
||||
TConvertToDerived() or
|
||||
TCheckedConvertOrNull() or
|
||||
TCheckedConvertOrThrow() or
|
||||
TDynamicCastToVoid() or
|
||||
TVariableAddress() or
|
||||
TFieldAddress() or
|
||||
TFunctionAddress() or
|
||||
TElementsAddress() or
|
||||
TConstant() or
|
||||
TStringConstant() or
|
||||
TConditionalBranch() or
|
||||
TSwitch() or
|
||||
TCall() or
|
||||
TCatchByType() or
|
||||
TCatchAny() or
|
||||
TThrowValue() or
|
||||
TReThrow() or
|
||||
TUnwind() or
|
||||
TUnmodeledDefinition() or
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TPhi() or
|
||||
TBuiltIn() or
|
||||
TVarArgsStart() or
|
||||
TVarArgsEnd() or
|
||||
TVarArg() or
|
||||
TVarArgCopy() or
|
||||
TCallSideEffect() or
|
||||
TCallReadSideEffect() or
|
||||
TIndirectReadSideEffect() or
|
||||
TIndirectWriteSideEffect() or
|
||||
TIndirectMayWriteSideEffect() or
|
||||
TBufferReadSideEffect() or
|
||||
TBufferWriteSideEffect() or
|
||||
TBufferMayWriteSideEffect() or
|
||||
TChi() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
TNewObj()
|
||||
|
||||
class Opcode extends TOpcode {
|
||||
string toString() {
|
||||
result = "UnknownOpcode"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class UnaryOpcode extends Opcode {}
|
||||
|
||||
abstract class BinaryOpcode extends Opcode {}
|
||||
|
||||
abstract class PointerArithmeticOpcode extends BinaryOpcode {}
|
||||
|
||||
abstract class PointerOffsetOpcode extends PointerArithmeticOpcode {}
|
||||
|
||||
abstract class ArithmeticOpcode extends Opcode {}
|
||||
|
||||
abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode {}
|
||||
|
||||
abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode {}
|
||||
|
||||
abstract class BitwiseOpcode extends Opcode {}
|
||||
|
||||
abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode {}
|
||||
|
||||
abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode {}
|
||||
|
||||
abstract class CompareOpcode extends BinaryOpcode {}
|
||||
|
||||
abstract class RelationalOpcode extends CompareOpcode {}
|
||||
|
||||
abstract class CopyOpcode extends Opcode {}
|
||||
|
||||
abstract class MemoryAccessOpcode extends Opcode {}
|
||||
|
||||
abstract class ReturnOpcode extends Opcode {}
|
||||
|
||||
abstract class ThrowOpcode extends Opcode {}
|
||||
|
||||
abstract class CatchOpcode extends Opcode {}
|
||||
|
||||
abstract class OpcodeWithCondition extends Opcode {}
|
||||
|
||||
abstract class BuiltInOperationOpcode extends Opcode {}
|
||||
|
||||
abstract class SideEffectOpcode extends Opcode {}
|
||||
|
||||
/**
|
||||
* An opcode that reads a value from memory.
|
||||
*/
|
||||
abstract class OpcodeWithLoad extends MemoryAccessOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class ReadSideEffectOpcode extends SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that writes to a set of memory locations as a side effect.
|
||||
*/
|
||||
abstract class WriteSideEffectOpcode extends SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
|
||||
* as a read of the original contents, plus a "may" write of the new contents.
|
||||
*/
|
||||
abstract class MayWriteSideEffectOpcode extends SideEffectOpcode {}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends MemoryAccessOpcode {}
|
||||
|
||||
module Opcode {
|
||||
class NoOp extends Opcode, TNoOp { override final string toString() { result = "NoOp" } }
|
||||
class Uninitialized extends MemoryAccessOpcode, TUninitialized { override final string toString() { result = "Uninitialized" } }
|
||||
class Error extends Opcode, TError { override final string toString() { result = "Error" } }
|
||||
class InitializeParameter extends MemoryAccessOpcode, TInitializeParameter { override final string toString() { result = "InitializeParameter" } }
|
||||
class InitializeThis extends Opcode, TInitializeThis { override final string toString() { result = "InitializeThis" } }
|
||||
class EnterFunction extends Opcode, TEnterFunction { override final string toString() { result = "EnterFunction" } }
|
||||
class ExitFunction extends Opcode, TExitFunction { override final string toString() { result = "ExitFunction" } }
|
||||
class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue { override final string toString() { result = "ReturnValue" } }
|
||||
class ReturnVoid extends ReturnOpcode, TReturnVoid { override final string toString() { result = "ReturnVoid" } }
|
||||
class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { override final string toString() { result = "CopyValue" } }
|
||||
class Load extends CopyOpcode, OpcodeWithLoad, TLoad { override final string toString() { result = "Load" } }
|
||||
class Store extends CopyOpcode, MemoryAccessOpcode, TStore { override final string toString() { result = "Store" } }
|
||||
class Add extends BinaryArithmeticOpcode, TAdd { override final string toString() { result = "Add" } }
|
||||
class Sub extends BinaryArithmeticOpcode, TSub { override final string toString() { result = "Sub" } }
|
||||
class Mul extends BinaryArithmeticOpcode, TMul { override final string toString() { result = "Mul" } }
|
||||
class Div extends BinaryArithmeticOpcode, TDiv { override final string toString() { result = "Div" } }
|
||||
class Rem extends BinaryArithmeticOpcode, TRem { override final string toString() { result = "Rem" } }
|
||||
class Negate extends UnaryArithmeticOpcode, TNegate { override final string toString() { result = "Negate" } }
|
||||
class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { override final string toString() { result = "ShiftLeft" } }
|
||||
class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { override final string toString() { result = "ShiftRight" } }
|
||||
class BitAnd extends BinaryBitwiseOpcode, TBitAnd { override final string toString() { result = "BitAnd" } }
|
||||
class BitOr extends BinaryBitwiseOpcode, TBitOr { override final string toString() { result = "BitOr" } }
|
||||
class BitXor extends BinaryBitwiseOpcode, TBitXor { override final string toString() { result = "BitXor" } }
|
||||
class BitComplement extends UnaryBitwiseOpcode, TBitComplement { override final string toString() { result = "BitComplement" } }
|
||||
class LogicalNot extends UnaryOpcode, TLogicalNot { override final string toString() { result = "LogicalNot" } }
|
||||
class CompareEQ extends CompareOpcode, TCompareEQ { override final string toString() { result = "CompareEQ" } }
|
||||
class CompareNE extends CompareOpcode, TCompareNE { override final string toString() { result = "CompareNE" } }
|
||||
class CompareLT extends RelationalOpcode, TCompareLT { override final string toString() { result = "CompareLT" } }
|
||||
class CompareGT extends RelationalOpcode, TCompareGT { override final string toString() { result = "CompareGT" } }
|
||||
class CompareLE extends RelationalOpcode, TCompareLE { override final string toString() { result = "CompareLE" } }
|
||||
class CompareGE extends RelationalOpcode, TCompareGE { override final string toString() { result = "CompareGE" } }
|
||||
class PointerAdd extends PointerOffsetOpcode, TPointerAdd { override final string toString() { result = "PointerAdd" } }
|
||||
class PointerSub extends PointerOffsetOpcode, TPointerSub { override final string toString() { result = "PointerSub" } }
|
||||
class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { override final string toString() { result = "PointerDiff" } }
|
||||
class Convert extends UnaryOpcode, TConvert { override final string toString() { result = "Convert" } }
|
||||
class ConvertToBase extends UnaryOpcode, TConvertToBase { override final string toString() { result = "ConvertToBase" } }
|
||||
class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase { override final string toString() { result = "ConvertToVirtualBase" } }
|
||||
class ConvertToDerived extends UnaryOpcode, TConvertToDerived { override final string toString() { result = "ConvertToDerived" } }
|
||||
class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull { override final string toString() { result = "CheckedConvertOrNull" } }
|
||||
class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow { override final string toString() { result = "CheckedConvertOrThrow" } }
|
||||
class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid { override final string toString() { result = "DynamicCastToVoid" } }
|
||||
class VariableAddress extends Opcode, TVariableAddress { override final string toString() { result = "VariableAddress" } }
|
||||
class FieldAddress extends UnaryOpcode, TFieldAddress { override final string toString() { result = "FieldAddress" } }
|
||||
class ElementsAddress extends UnaryOpcode, TElementsAddress { override final string toString() { result = "ElementsAddress" } }
|
||||
class FunctionAddress extends Opcode, TFunctionAddress { override final string toString() { result = "FunctionAddress" } }
|
||||
class Constant extends Opcode, TConstant { override final string toString() { result = "Constant" } }
|
||||
class StringConstant extends Opcode, TStringConstant { override final string toString() { result = "StringConstant" } }
|
||||
class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch { override final string toString() { result = "ConditionalBranch" } }
|
||||
class Switch extends OpcodeWithCondition, TSwitch { override final string toString() { result = "Switch" } }
|
||||
class Call extends Opcode, TCall { override final string toString() { result = "Call" } }
|
||||
class CatchByType extends CatchOpcode, TCatchByType { override final string toString() { result = "CatchByType" } }
|
||||
class CatchAny extends CatchOpcode, TCatchAny { override final string toString() { result = "CatchAny" } }
|
||||
class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue { override final string toString() { result = "ThrowValue" } }
|
||||
class ReThrow extends ThrowOpcode, TReThrow { override final string toString() { result = "ReThrow" } }
|
||||
class Unwind extends Opcode, TUnwind { override final string toString() { result = "Unwind" } }
|
||||
class UnmodeledDefinition extends Opcode, TUnmodeledDefinition { override final string toString() { result = "UnmodeledDefinition" } }
|
||||
class UnmodeledUse extends Opcode, TUnmodeledUse { override final string toString() { result = "UnmodeledUse" } }
|
||||
class AliasedDefinition extends Opcode, TAliasedDefinition { override final string toString() { result = "AliasedDefinition" } }
|
||||
class Phi extends Opcode, TPhi { override final string toString() { result = "Phi" } }
|
||||
class BuiltIn extends BuiltInOperationOpcode, TBuiltIn { override final string toString() { result = "BuiltIn" } }
|
||||
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart { override final string toString() { result = "VarArgsStart" } }
|
||||
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd { override final string toString() { result = "VarArgsEnd" } }
|
||||
class VarArg extends BuiltInOperationOpcode, TVarArg { override final string toString() { result = "VarArg" } }
|
||||
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy { override final string toString() { result = "VarArgCopy" } }
|
||||
class CallSideEffect extends MayWriteSideEffectOpcode, TCallSideEffect { override final string toString() { result = "CallSideEffect" } }
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, TCallReadSideEffect { override final string toString() { result = "CallReadSideEffect" } }
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, MemoryAccessOpcode, TIndirectReadSideEffect { override final string toString() { result = "IndirectReadSideEffect" } }
|
||||
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode, TIndirectWriteSideEffect { override final string toString() { result = "IndirectWriteSideEffect" } }
|
||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode, TIndirectMayWriteSideEffect { override final string toString() { result = "IndirectMayWriteSideEffect" } }
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferAccessOpcode, TBufferReadSideEffect { override final string toString() { result = "BufferReadSideEffect" } }
|
||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode, TBufferWriteSideEffect { override final string toString() { result = "BufferWriteSideEffect" } }
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode, TBufferMayWriteSideEffect { override final string toString() { result = "BufferMayWriteSideEffect" } }
|
||||
class Chi extends Opcode, TChi { override final string toString() { result = "Chi" } }
|
||||
class InlineAsm extends Opcode, TInlineAsm { override final string toString() { result = "InlineAsm" } }
|
||||
class Unreached extends Opcode, TUnreached { override final string toString() { result = "Unreached" } }
|
||||
class NewObj extends Opcode, TNewObj { override final string toString() { result = "NewObj" } }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
private import internal.TempVariableTagInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
class TempVariableTag extends TTempVariableTag {
|
||||
string toString() {
|
||||
result = getTempVariableTagId(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -0,0 +1,380 @@
|
||||
private import OperandTagInternal
|
||||
|
||||
private newtype TOperandTag =
|
||||
TAddressOperand() or
|
||||
TBufferSizeOperand() or
|
||||
TSideEffectOperand() or
|
||||
TLoadOperand() or
|
||||
TStoreValueOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
TRightOperand() or
|
||||
TConditionOperand() or
|
||||
TUnmodeledUseOperand() 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 {
|
||||
abstract string toString();
|
||||
|
||||
abstract int getSortOrder();
|
||||
|
||||
string getLabel() {
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 printing.
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
override final string toString() {
|
||||
result = "Address"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 0
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "&:"
|
||||
}
|
||||
}
|
||||
|
||||
AddressOperandTag addressOperand() {
|
||||
result = TAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperandTag, TBufferSizeOperand {
|
||||
override final string toString() {
|
||||
result = "BufferSize"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||
*/
|
||||
class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand {
|
||||
override final string toString() {
|
||||
result = "SideEffect"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 2
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
override final string toString() {
|
||||
result = "Load"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 3
|
||||
}
|
||||
}
|
||||
|
||||
LoadOperandTag loadOperand() {
|
||||
result = TLoadOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of a `Store` instruction.
|
||||
*/
|
||||
class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand {
|
||||
override final string toString() {
|
||||
result = "StoreValue"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 4
|
||||
}
|
||||
}
|
||||
|
||||
StoreValueOperandTag storeValueOperand() {
|
||||
result = TStoreValueOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
|
||||
*/
|
||||
class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand {
|
||||
override final string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 5
|
||||
}
|
||||
}
|
||||
|
||||
UnaryOperandTag unaryOperand() {
|
||||
result = TUnaryOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperandTag extends RegisterOperandTag, TLeftOperand {
|
||||
override final string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 6
|
||||
}
|
||||
}
|
||||
|
||||
LeftOperandTag leftOperand() {
|
||||
result = TLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperandTag extends RegisterOperandTag, TRightOperand {
|
||||
override final string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 7
|
||||
}
|
||||
}
|
||||
|
||||
RightOperandTag rightOperand() {
|
||||
result = TRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperandTag extends RegisterOperandTag, TConditionOperand {
|
||||
override final string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 8
|
||||
}
|
||||
}
|
||||
|
||||
ConditionOperandTag conditionOperand() {
|
||||
result = TConditionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperandTag extends MemoryOperandTag, TUnmodeledUseOperand {
|
||||
override final string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 9
|
||||
}
|
||||
}
|
||||
|
||||
UnmodeledUseOperandTag unmodeledUseOperand() {
|
||||
result = TUnmodeledUseOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand {
|
||||
override final string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 10
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
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()
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(this)"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
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)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 12 + argIndex
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = argIndex.toString() + ":"
|
||||
}
|
||||
|
||||
final int getArgIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
class ChiTotalOperandTag extends MemoryOperandTag, TChiTotalOperand {
|
||||
override final string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 13
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "total:"
|
||||
}
|
||||
}
|
||||
|
||||
ChiTotalOperandTag chiTotalOperand() {
|
||||
result = TChiTotalOperand()
|
||||
}
|
||||
|
||||
class ChiPartialOperandTag extends MemoryOperandTag, TChiPartialOperand {
|
||||
override final string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 14
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "partial:"
|
||||
}
|
||||
}
|
||||
|
||||
ChiPartialOperandTag chiPartialOperand() {
|
||||
result = TChiPartialOperand()
|
||||
}
|
||||
|
||||
class AsmOperandTag extends RegisterOperandTag, TAsmOperand {
|
||||
int index;
|
||||
|
||||
AsmOperandTag() {
|
||||
this = TAsmOperand(index)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "AsmOperand(" + index + ")"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 15 + index
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = index.toString() + ":"
|
||||
}
|
||||
}
|
||||
|
||||
AsmOperandTag asmOperand(int index) {
|
||||
result = TAsmOperand(index)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
@@ -0,0 +1,12 @@
|
||||
private import TIRVariableInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
newtype TIRVariable =
|
||||
TIRUserVariable(Language::Variable var, Language::Type type,
|
||||
Language::Function func) {
|
||||
Construction::hasUserVariable(func, var, type)
|
||||
} or
|
||||
TIRTempVariable(Language::Function func, Language::AST ast, TempVariableTag tag,
|
||||
Language::Type type) {
|
||||
Construction::hasTempVariable(func, ast, tag, type)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
private import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
module TempVariableTag = TempVariableTag_;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
private import semmle.code.csharp.ir.internal.TempVariableTag as TempVariableTag_
|
||||
|
||||
module Imports {
|
||||
module TempVariableTag = TempVariableTag_;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import IRFunction
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
private import internal.IRBlockImports as Imports
|
||||
import Imports::EdgeKind
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
final string toString() {
|
||||
result = getFirstInstruction(this).toString()
|
||||
}
|
||||
|
||||
final Language::Location getLocation() {
|
||||
result = getFirstInstruction().getLocation()
|
||||
}
|
||||
|
||||
final string getUniqueId() {
|
||||
result = getFirstInstruction(this).getUniqueId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the block within its function. This is used
|
||||
* by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
this = rank[result + 1](IRBlock funcBlock |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction() |
|
||||
funcBlock order by funcBlock.getUniqueId()
|
||||
)
|
||||
}
|
||||
|
||||
final Instruction getInstruction(int index) {
|
||||
result = getInstruction(this, index)
|
||||
}
|
||||
|
||||
final PhiInstruction getAPhiInstruction() {
|
||||
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
|
||||
}
|
||||
|
||||
final Instruction getAnInstruction() {
|
||||
result = getInstruction(_) or
|
||||
result = getAPhiInstruction()
|
||||
}
|
||||
|
||||
final Instruction getFirstInstruction() {
|
||||
result = getFirstInstruction(this)
|
||||
}
|
||||
|
||||
final Instruction getLastInstruction() {
|
||||
result = getInstruction(getInstructionCount() - 1)
|
||||
}
|
||||
|
||||
final int getInstructionCount() {
|
||||
result = getInstructionCount(this)
|
||||
}
|
||||
|
||||
final IRFunction getEnclosingIRFunction() {
|
||||
result = getFirstInstruction(this).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
final Language::Function 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 {
|
||||
final IRBlock getASuccessor() {
|
||||
blockSuccessor(this, result)
|
||||
}
|
||||
|
||||
final IRBlock getAPredecessor() {
|
||||
blockSuccessor(result, this)
|
||||
}
|
||||
|
||||
final IRBlock getSuccessor(EdgeKind kind) {
|
||||
blockSuccessor(this, result, kind)
|
||||
}
|
||||
|
||||
final IRBlock getBackEdgeSuccessor(EdgeKind kind) {
|
||||
backEdgeSuccessor(this, result, kind)
|
||||
}
|
||||
|
||||
final predicate immediatelyDominates(IRBlock block) {
|
||||
blockImmediatelyDominates(this, block)
|
||||
}
|
||||
|
||||
final predicate strictlyDominates(IRBlock block) {
|
||||
blockImmediatelyDominates+(this, block)
|
||||
}
|
||||
|
||||
final predicate dominates(IRBlock block) {
|
||||
strictlyDominates(block) or this = block
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final IRBlock dominanceFrontier() {
|
||||
dominates(result.getAPredecessor()) and
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry point of its function
|
||||
*/
|
||||
final predicate isReachableFromFunctionEntry() {
|
||||
this = getEnclosingIRFunction().getEntryBlock() or
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate startsBasicBlock(Instruction instr) {
|
||||
not instr instanceof PhiInstruction and
|
||||
(
|
||||
count(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor()
|
||||
) != 1 or // Multiple predecessors or no predecessor
|
||||
exists(Instruction predecessor |
|
||||
instr = predecessor.getASuccessor() and
|
||||
strictcount(Instruction other |
|
||||
other = predecessor.getASuccessor()
|
||||
) > 1
|
||||
) or // Predecessor has multiple successors
|
||||
exists(Instruction predecessor, EdgeKind kind |
|
||||
instr = predecessor.getSuccessor(kind) and
|
||||
not kind instanceof GotoEdge
|
||||
) or // Incoming edge is not a GotoEdge
|
||||
exists(Instruction predecessor |
|
||||
instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
|
||||
) // A back edge enters this instruction
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isEntryBlock(TIRBlock block) {
|
||||
block = MkIRBlock(any(EnterFunctionInstruction enter))
|
||||
}
|
||||
|
||||
private cached module Cached {
|
||||
cached newtype TIRBlock =
|
||||
MkIRBlock(Instruction firstInstr) {
|
||||
startsBasicBlock(firstInstr)
|
||||
}
|
||||
|
||||
/** Holds if `i2` follows `i1` in a `IRBlock`. */
|
||||
private predicate adjacentInBlock(Instruction i1, Instruction i2) {
|
||||
exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
|
||||
not startsBasicBlock(i2)
|
||||
}
|
||||
|
||||
/** 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)
|
||||
}
|
||||
|
||||
Instruction getFirstInstruction(TIRBlock block) {
|
||||
block = MkIRBlock(result)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
|
||||
private newtype TIRFunction =
|
||||
MkIRFunction(Language::Function func) {
|
||||
Construction::functionHasIR(func)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR for a function.
|
||||
*/
|
||||
class IRFunction extends TIRFunction {
|
||||
Language::Function func;
|
||||
|
||||
IRFunction() {
|
||||
this = MkIRFunction(func)
|
||||
}
|
||||
|
||||
final string toString() {
|
||||
result = "IR: " + func.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function whose IR is represented.
|
||||
*/
|
||||
final Language::Function getFunction() {
|
||||
result = func
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of the function.
|
||||
*/
|
||||
final Language::Location getLocation() {
|
||||
result = func.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final UnmodeledUseInstruction getUnmodeledUseInstruction() {
|
||||
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() = 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @name Raw IR Sanity Check
|
||||
* @description Performs sanity checks on the Intermediate Representation. This query should have no results.
|
||||
* @kind table
|
||||
* @id csharp/raw-ir-sanity-check
|
||||
*/
|
||||
|
||||
import IRSanity
|
||||
@@ -0,0 +1,3 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
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
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
result.getEnclosingFunction() = func
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents 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`).
|
||||
*/
|
||||
abstract class IRVariable extends TIRVariable {
|
||||
Language::Function func;
|
||||
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::Type getType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
abstract Language::AST getAST();
|
||||
|
||||
/**
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
abstract string getUniqueId();
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
*/
|
||||
final Language::Location getLocation() {
|
||||
result = 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::Function getEnclosingFunction() {
|
||||
result = func
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::Type type;
|
||||
|
||||
IRUserVariable() {
|
||||
this = TIRUserVariable(var, type, func)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = getVariable().toString()
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = var
|
||||
}
|
||||
|
||||
override final string getUniqueId() {
|
||||
result = getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
override final Language::Type getType() {
|
||||
result = type
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
*/
|
||||
Language::Variable getVariable() {
|
||||
result = var
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a variable (user-declared or temporary) that is allocated on the
|
||||
* stack. This includes all parameters, non-static local variables, and
|
||||
* temporary variables.
|
||||
*/
|
||||
abstract class IRAutomaticVariable extends IRVariable {
|
||||
}
|
||||
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
IRAutomaticUserVariable() {
|
||||
Language::isVariableAutomatic(var)
|
||||
}
|
||||
|
||||
final override Language::AutomaticVariable getVariable() {
|
||||
result = var
|
||||
}
|
||||
}
|
||||
|
||||
class IRStaticUserVariable extends IRUserVariable {
|
||||
override Language::StaticVariable var;
|
||||
|
||||
IRStaticUserVariable() {
|
||||
not Language::isVariableAutomatic(var)
|
||||
}
|
||||
|
||||
final override Language::StaticVariable getVariable() {
|
||||
result = var
|
||||
}
|
||||
}
|
||||
|
||||
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
result.getAST() = ast and
|
||||
result.getTag() = tag
|
||||
}
|
||||
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
TempVariableTag tag;
|
||||
Language::Type type;
|
||||
|
||||
IRTempVariable() {
|
||||
this = TIRTempVariable(func, ast, tag, type)
|
||||
}
|
||||
|
||||
override final Language::Type getType() {
|
||||
result = type
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = ast
|
||||
}
|
||||
|
||||
override final string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
final TempVariableTag getTag() {
|
||||
result = tag
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() {
|
||||
result = "#temp"
|
||||
}
|
||||
}
|
||||
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
IRReturnVariable() {
|
||||
tag = ReturnValueTempVar()
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "#return"
|
||||
}
|
||||
}
|
||||
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() {
|
||||
tag = ThrowTempVar()
|
||||
}
|
||||
|
||||
override string getBaseString() {
|
||||
result = "#throw"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,561 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
private newtype TOperand =
|
||||
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not isInCycle(useInstr)
|
||||
} or
|
||||
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap) {
|
||||
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
|
||||
not isInCycle(useInstr)
|
||||
} or
|
||||
TPhiOperand(PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap) {
|
||||
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/** Gets a non-phi instruction that defines an operand of `instr`. */
|
||||
private Instruction getNonPhiOperandDef(Instruction instr) {
|
||||
result = Construction::getRegisterOperandDefinition(instr, _)
|
||||
or
|
||||
result = Construction::getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
*
|
||||
* If such cycles are present, either due to a programming error in the IR
|
||||
* generation or due to a malformed database, it can cause infinite loops in
|
||||
* analyses that assume a cycle-free graph of non-phi operands. Therefore it's
|
||||
* better to remove these operands than to leave cycles in the operand graph.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDef+(instr) = instr
|
||||
}
|
||||
|
||||
/**
|
||||
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
string toString() {
|
||||
result = "Operand"
|
||||
}
|
||||
|
||||
final Language::Location getLocation() {
|
||||
result = getUse().getLocation()
|
||||
}
|
||||
|
||||
final IRFunction getEnclosingIRFunction() {
|
||||
result = 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
|
||||
getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: renamed to `getUse`.
|
||||
*
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
deprecated
|
||||
final Instruction getUseInstruction() {
|
||||
result = getUse()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
|
||||
* predicate is `getAnyDef`, but most uses of this predicate should probably
|
||||
* be replaced with `getDef`.
|
||||
*
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
deprecated
|
||||
final Instruction getDefinitionInstruction() {
|
||||
result = getAnyDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() {
|
||||
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 = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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::Type getType() {
|
||||
result = getAnyDef().getResultType()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 `getResultType()`.
|
||||
*/
|
||||
predicate isGLValue() {
|
||||
getAnyDef().isGLValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int getSize() {
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
MemoryOperand() {
|
||||
this = TNonPhiMemoryOperand(_, _, _, _) or
|
||||
this = TPhiOperand(_, _, _, _)
|
||||
}
|
||||
|
||||
override predicate isGLValue() {
|
||||
// A `MemoryOperand` can never be a glvalue
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
getMemoryAccess().usesAddressOperand() and
|
||||
result.getUse() = getUse()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand {
|
||||
Instruction useInstr;
|
||||
Instruction defInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = TRegisterOperand(useInstr, tag, defInstr) or
|
||||
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
|
||||
}
|
||||
|
||||
override final Instruction getUse() {
|
||||
result = useInstr
|
||||
}
|
||||
|
||||
override final Instruction getAnyDef() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = tag.getLabel()
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = tag.getSortOrder()
|
||||
}
|
||||
|
||||
final OperandTag getOperandTag() {
|
||||
result = tag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
|
||||
override final Overlap getDefinitionOverlap() {
|
||||
// All register results overlap exactly with their uses.
|
||||
result instanceof MustExactlyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
Overlap overlap;
|
||||
|
||||
NonPhiMemoryOperand() {
|
||||
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap)
|
||||
}
|
||||
|
||||
override final Overlap getDefinitionOverlap() {
|
||||
result = overlap
|
||||
}
|
||||
}
|
||||
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
override final Language::Type getType() {
|
||||
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;
|
||||
|
||||
override string toString() {
|
||||
result = "Address"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
override string toString() {
|
||||
result = "Load"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of a `Store` instruction.
|
||||
*/
|
||||
class StoreValueOperand extends RegisterOperand {
|
||||
override StoreValueOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "StoreValue"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`, `Copy`).
|
||||
*/
|
||||
class UnaryOperand extends RegisterOperand {
|
||||
override UnaryOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends RegisterOperand {
|
||||
override LeftOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends RegisterOperand {
|
||||
override RightOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends RegisterOperand {
|
||||
override ConditionOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends NonPhiMemoryOperand {
|
||||
override UnmodeledUseOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends RegisterOperand {
|
||||
override CallTargetOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
override ThisArgumentOperandTag tag;
|
||||
|
||||
override string toString() {
|
||||
result = "ThisArgument"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
override PositionalArgumentOperandTag tag;
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
argIndex = tag.getArgIndex()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based index of the argument.
|
||||
*/
|
||||
final int getIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override final int getSize() {
|
||||
if getType() instanceof Language::UnknownType then
|
||||
result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else
|
||||
result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallReadSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectReadSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
result instanceof IndirectMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferMayWriteSideEffectInstruction and
|
||||
result instanceof BufferMayMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
PhiInputOperand() {
|
||||
this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final PhiInstruction getUse() {
|
||||
result = useInstr
|
||||
}
|
||||
|
||||
override final Instruction getAnyDef() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final Overlap getDefinitionOverlap() {
|
||||
result = overlap
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
|
||||
override final 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;
|
||||
|
||||
override string toString() {
|
||||
result = "ChiTotal"
|
||||
}
|
||||
|
||||
override final 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;
|
||||
|
||||
override string toString() {
|
||||
result = "ChiPartial"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof ChiPartialMemoryAccess
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @name Print Raw IR
|
||||
* @description Outputs a representation of the Raw IR graph
|
||||
* @id csharp/print-raw-ir
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import PrintIR
|
||||
@@ -0,0 +1,305 @@
|
||||
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 functions are printed.
|
||||
*/
|
||||
class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
string toString() {
|
||||
result = "PrintIRConfiguration"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the IR for `func` should be printed. By default, holds for all
|
||||
* functions.
|
||||
*/
|
||||
predicate shouldPrintFunction(Language::Function func) {
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config |
|
||||
config.shouldPrintFunction(func)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldCreateIRForFunction(Language::Function func) {
|
||||
shouldPrintFunction(func)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableIRFunction(IRFunction irFunc) {
|
||||
shouldPrintFunction(irFunc.getFunction())
|
||||
} or
|
||||
TPrintableIRBlock(IRBlock block) {
|
||||
shouldPrintFunction(block.getEnclosingFunction())
|
||||
} or
|
||||
TPrintableInstruction(Instruction instr) {
|
||||
shouldPrintFunction(instr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* A node to be emitted in the IR graph.
|
||||
*/
|
||||
abstract 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 = getLabel() or
|
||||
key = "semmle.order" and result = getOrder().toString() or
|
||||
key = "semmle.graphKind" and result = getGraphKind() or
|
||||
key = "semmle.forceText" and forceText() and result = "true"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing a `IRFunction` object.
|
||||
*/
|
||||
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 = Language::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()
|
||||
)
|
||||
}
|
||||
|
||||
override final PrintableIRNode getParent() {
|
||||
none()
|
||||
}
|
||||
|
||||
final IRFunction getIRFunction() {
|
||||
result = irFunc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR graph node representing an `IRBlock` object.
|
||||
*/
|
||||
class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
IRBlock block;
|
||||
|
||||
PrintableIRBlock() {
|
||||
this = TPrintableIRBlock(block)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getLabel()
|
||||
}
|
||||
|
||||
override Language::Location getLocation() {
|
||||
result = block.getLocation()
|
||||
}
|
||||
|
||||
override string getLabel() {
|
||||
result = "Block " + block.getDisplayIndex().toString()
|
||||
}
|
||||
|
||||
override int getOrder() {
|
||||
result = block.getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getGraphKind() {
|
||||
result = "tree"
|
||||
}
|
||||
|
||||
override final predicate forceText() {
|
||||
any()
|
||||
}
|
||||
|
||||
override final 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`.
|
||||
*/
|
||||
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 = instr.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()
|
||||
}
|
||||
|
||||
override final 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)
|
||||
}
|
||||
}
|
||||
|
||||
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) + " "
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
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()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
|
||||
parent = child.getParent()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.csharp.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)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
private import internal.ConstantAnalysisInternal
|
||||
private import semmle.code.csharp.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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.implementation.raw.IR as IR
|
||||
@@ -0,0 +1,292 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
import csharp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* 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.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
|
||||
variableAddressValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(IRFunction irFunc) {
|
||||
initializeThisValueNumber(_, irFunc)
|
||||
} or
|
||||
TConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(IRFunction irFunc, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(IRFunction irFunc, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
TUniqueValueNumber(IRFunction irFunc, Instruction instr) {
|
||||
uniqueValueNumber(instr, irFunc)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
|
||||
final Operand getAUse() {
|
||||
this = valueNumber(result.getDefinitionInstruction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof StringConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, IRFunction irFunc,
|
||||
IRVariable var) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
IRFunction irFunc, IRVariable var) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, IRFunction irFunc, Type type,
|
||||
string value) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(StringConstantInstruction instr, IRFunction irFunc, Type type,
|
||||
string value) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, IRFunction irFunc,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr,
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
cached ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(IRFunction irFunc |
|
||||
uniqueValueNumber(instr, irFunc) and
|
||||
result = TUniqueValueNumber(irFunc, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumberOfOperand(Operand op) {
|
||||
result = valueNumber(op.getDefinitionInstruction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(IRFunction irFunc |
|
||||
irFunc = instr.getEnclosingIRFunction() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, irFunc, var) and
|
||||
result = TVariableAddressValueNumber(irFunc, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, irFunc, var) and
|
||||
result = TInitializeParameterValueNumber(irFunc, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass,
|
||||
operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.implementation.raw.IR as IR
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind
|
||||
@@ -0,0 +1,299 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.ir.implementation.raw.IR
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.csharp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
private import semmle.code.csharp.ir.Util
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = MkInstruction(result, _)
|
||||
}
|
||||
|
||||
InstructionTag getInstructionTag(Instruction instruction) {
|
||||
instruction = MkInstruction(_, result)
|
||||
}
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
cached predicate functionHasIR(Callable callable) {
|
||||
exists(getTranslatedFunction(callable)) and
|
||||
callable.fromSource()
|
||||
}
|
||||
|
||||
cached newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _, _)
|
||||
}
|
||||
|
||||
cached predicate hasUserVariable(Callable callable, Variable var, Type type) {
|
||||
getTranslatedFunction(callable).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached predicate hasTempVariable(Callable callable, Language::AST ast, TempVariableTag tag,
|
||||
Type type) {
|
||||
exists(TranslatedElement element |
|
||||
element.getAST() = ast and
|
||||
callable = element.getFunction() and
|
||||
element.hasTempVariable(tag, type)
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate hasModeledMemoryResult(Instruction instruction) {
|
||||
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 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)
|
||||
}
|
||||
|
||||
cached Type 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).getResultType()
|
||||
else
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionOperandType(
|
||||
getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached int getInstructionOperandSize(Instruction instruction, SideEffectOperandTag tag) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionOperandSize(
|
||||
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
|
||||
// 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 predicate instructionHasType(Instruction instruction, Type type, boolean isLValue) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), type, isLValue)
|
||||
}
|
||||
|
||||
cached Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _, _)
|
||||
}
|
||||
|
||||
cached IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
|
||||
result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
|
||||
}
|
||||
|
||||
cached IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionVariable(
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached Field getInstructionField(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionField(tag)
|
||||
)
|
||||
}
|
||||
|
||||
cached ArrayAccess getInstructionArrayAccess(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionArrayAccess(
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached Callable getInstructionFunction(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionFunction(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached string getInstructionConstantValue(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction).getInstructionConstantValue(
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result =
|
||||
getInstructionTranslatedElement(instruction).getInstructionStringLiteral(
|
||||
getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached Type 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)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionOrigin(Instruction instruction,
|
||||
TranslatedElement element, InstructionTag tag) {
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction)
|
||||
}
|
||||
|
||||
cached int getInstructionElementSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
instructionOrigin(instruction, element, tag) and
|
||||
result = element.getInstructionElementSize(tag)
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
cached Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instr) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
import CachedForDebugging
|
||||
cached private module CachedForDebugging {
|
||||
cached string getTempVariableUniqueId(IRTempVariable var) {
|
||||
exists(TranslatedElement element |
|
||||
var = element.getTempVariable(_) and
|
||||
result = element.getId() + ":" + getTempVariableTagId(var.getTag())
|
||||
)
|
||||
}
|
||||
|
||||
cached string getInstructionUniqueId(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction).getId() + ":" +
|
||||
getInstructionTagId(getInstructionTag(instruction))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import semmle.code.csharp.ir.internal.IRUtilities as IRUtilities
|
||||
import semmle.code.csharp.ir.internal.TempVariableTag as TTempVariableTag
|
||||
import semmle.code.csharp.ir.implementation.internal.TIRVariable as TIRVariable
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.csharp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag
|
||||
@@ -0,0 +1,180 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.ir.Util
|
||||
|
||||
|
||||
//private predicate fieldIsInitialized(Field field) {
|
||||
// exists(field.getInitializer())
|
||||
//}
|
||||
|
||||
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
|
||||
UnmodeledDefinitionTag() or
|
||||
UnmodeledUseTag() or
|
||||
AliasedDefinitionTag() 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
|
||||
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" or // Single instruction (not including implicit Load)
|
||||
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 = UnmodeledDefinitionTag() and result = "UnmodeledDef" or
|
||||
tag = UnmodeledUseTag() and result = "UnmodeledUse" or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef" 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" or // Implicit load due to lvalue-to-rvalue conversion
|
||||
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
|
||||
|
||||
// TODO: Reread
|
||||
// exists(Field field, Class cls, int index, string tagName |
|
||||
// field = cls.getCanonicalMember(index) and
|
||||
// (
|
||||
// tag = InitializerFieldAddressTag(field) and tagName = "InitFieldAddr" or
|
||||
// tag = InitializerFieldDefaultValueTag(field) and tagName = "InitFieldDefVal" or
|
||||
// tag = InitializerFieldDefaultValueStoreTag(field) and tagName = "InitFieldDefValStore"
|
||||
// ) and
|
||||
// result = tagName + "(" + index + ")"
|
||||
// ) 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 + ")"
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.csharp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.csharp.ir.IRConfiguration as IRConfiguration
|
||||
@@ -0,0 +1,321 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import semmle.code.csharp.ir.Util
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
* call in the source code, or could be a call that is part of the translation
|
||||
* of a higher-level constructor (e.g. the allocator call in a `NewExpr`).
|
||||
*/
|
||||
abstract class TranslatedCall extends TranslatedExpr {
|
||||
final override TranslatedElement getChild(int id) {
|
||||
// We choose the child's id in the order of evaluation.
|
||||
// The qualifier is evaluated before the call target, because the value of
|
||||
// the call target may depend on the value of the qualifier for virtual
|
||||
// calls.
|
||||
id = -2 and result = this.getQualifier() or
|
||||
id = -1 and result = this.getCallTarget() or
|
||||
result = this.getArgument(id)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(this.getQualifier()) then
|
||||
result = this.getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = this.getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
|
||||
) {
|
||||
(
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getCallResultType() and
|
||||
isLValue = false
|
||||
)
|
||||
or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
(
|
||||
if hasWriteSideEffect() then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof Language::UnknownType
|
||||
)
|
||||
else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType instanceof Language::UnknownType
|
||||
)
|
||||
) and
|
||||
isLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = this.getQualifier() and
|
||||
result = this.getFirstCallTargetInstruction()
|
||||
) or
|
||||
(
|
||||
child = this.getCallTarget() and
|
||||
result = this.getFirstArgumentOrCallInstruction()
|
||||
) 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = CallTag() and
|
||||
(
|
||||
tag = CallTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = this.getCallTargetResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = this.getQualifierResult()
|
||||
) or
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = this.getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = CallSideEffectTag() and
|
||||
this.hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = this.getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
or
|
||||
tag = CallSideEffectTag() and
|
||||
hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
}
|
||||
|
||||
final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = CallSideEffectTag() and
|
||||
this.hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result instanceof Language::UnknownType
|
||||
}
|
||||
|
||||
override final 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 `TranslatedExpr` for the indirect target of the call, if any.
|
||||
*/
|
||||
TranslatedExpr getCallTarget() { none() }
|
||||
|
||||
/**
|
||||
* Gets the first instruction of the sequence to evaluate the call target.
|
||||
* By default, this is just the first instruction of `getCallTarget()`, but
|
||||
* it can be overridden by a subclass for cases where there is a call target
|
||||
* that is not computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getFirstCallTargetInstruction() {
|
||||
result = this.getCallTarget().getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the target of the call. By
|
||||
* default, this is just the result of `getCallTarget()`, but it can be
|
||||
* overridden by a subclass for cases where there is a call target that is not
|
||||
* computed from an expression (e.g. a direct call).
|
||||
*/
|
||||
Instruction getCallTargetResult() {
|
||||
result = this.getCallTarget().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
|
||||
* that is passed as the `this` argument.
|
||||
*/
|
||||
abstract TranslatedExpr getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
* By default, 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).
|
||||
*/
|
||||
Instruction getQualifierResult() {
|
||||
result = this.getQualifier().getResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument.
|
||||
*/
|
||||
abstract TranslatedExpr 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.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
|
||||
// TODO: Fix side effects
|
||||
predicate hasReadSideEffect() { any() }
|
||||
|
||||
predicate hasWriteSideEffect() { any() }
|
||||
|
||||
private predicate hasSideEffect() { hasReadSideEffect() or hasWriteSideEffect() }
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
this.hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = this.getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a direct call to a specific function. Used for both
|
||||
* explicit calls and implicit calls.
|
||||
*/
|
||||
abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
final override Instruction getFirstCallTargetInstruction() {
|
||||
result = this.getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
final override Instruction getCallTargetResult() { result = this.getInstruction(CallTargetTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
|
||||
) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isLValue)
|
||||
or
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
resultType = expr.getType() and
|
||||
isLValue = true
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = TranslatedCall.super.getInstructionSuccessor(tag, kind)
|
||||
or
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getFirstArgumentOrCallInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function.
|
||||
*/
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedCall {
|
||||
override Call expr;
|
||||
|
||||
override Type getCallResultType() { result = this.getResultType() }
|
||||
|
||||
final override predicate hasArguments() { exists(expr.getArgument(0)) }
|
||||
|
||||
final override TranslatedExpr getQualifier() {
|
||||
expr instanceof QualifiableExpr and
|
||||
result = getTranslatedExpr(expr.(QualifiableExpr).getQualifier())
|
||||
}
|
||||
|
||||
final override TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(expr.getArgument(index))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a direct function call.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
override Call expr;
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = expr.getTarget()
|
||||
}
|
||||
|
||||
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.
|
||||
* The target of the call is a newly allocated object whose address, after
|
||||
* the constructor call, address will be passed to a variable declaration.
|
||||
*/
|
||||
class TranslatedConstructorCall extends TranslatedFunctionCall {
|
||||
TranslatedConstructorCall() {
|
||||
expr instanceof ObjectCreation or
|
||||
expr instanceof ConstructorInitializer
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
// We must retrieve the qualifier from the context the
|
||||
// constructor call happened
|
||||
exists(StructorCallContext context |
|
||||
context = this.getParent() and
|
||||
result = context.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override Type getCallResultType() { result instanceof VoidType }
|
||||
|
||||
override predicate hasQualifier() { any() }
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
abstract class ConditionContext extends TranslatedElement {
|
||||
abstract Instruction getChildTrueSuccessor(TranslatedCondition child);
|
||||
|
||||
abstract Instruction getChildFalseSuccessor(TranslatedCondition child);
|
||||
}
|
||||
|
||||
TranslatedCondition getTranslatedCondition(Expr expr) {
|
||||
result.getExpr() = expr
|
||||
}
|
||||
|
||||
abstract class TranslatedCondition extends TranslatedElement {
|
||||
Expr expr;
|
||||
|
||||
override final string toString() {
|
||||
result = expr.toString()
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
final ConditionContext getConditionContext() {
|
||||
result = this.getParent()
|
||||
}
|
||||
|
||||
final Expr getExpr() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
override final Callable getFunction() {
|
||||
result = expr.getEnclosingCallable()
|
||||
}
|
||||
|
||||
final Type getResultType() {
|
||||
result = expr.getType()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedFlexibleCondition extends TranslatedCondition,
|
||||
ConditionContext, TTranslatedFlexibleCondition {
|
||||
TranslatedFlexibleCondition() {
|
||||
this = TTranslatedFlexibleCondition(expr)
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getOperand()
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = this.getOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
none()
|
||||
}
|
||||
|
||||
abstract TranslatedCondition getOperand();
|
||||
}
|
||||
|
||||
class TranslatedParenthesisCondition extends TranslatedFlexibleCondition {
|
||||
override ParenthesizedExpr expr;
|
||||
|
||||
final override Instruction getChildTrueSuccessor(TranslatedCondition child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildFalseSuccessor(TranslatedCondition 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(TranslatedCondition child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition 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)
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedBinaryLogicalOperation extends
|
||||
TranslatedNativeCondition, ConditionContext {
|
||||
override BinaryLogicalOperation expr;
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getLeftOperand() or
|
||||
id = 1 and result = this.getRightOperand()
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = this.getLeftOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final 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(TranslatedCondition child) {
|
||||
(
|
||||
child = this.getLeftOperand() and
|
||||
result = this.getRightOperand().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = this.getRightOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
|
||||
child = this.getAnOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedLogicalOrExpr extends TranslatedBinaryLogicalOperation {
|
||||
override LogicalOrExpr expr;
|
||||
|
||||
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
|
||||
child = getAnOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
|
||||
(
|
||||
child = this.getLeftOperand() and
|
||||
result = getRightOperand().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = this.getRightOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedValueCondition extends TranslatedCondition,
|
||||
TTranslatedValueCondition {
|
||||
TranslatedValueCondition() {
|
||||
this = TTranslatedValueCondition(expr)
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getValueExpr()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = this.getValueExpr().getFirstInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType instanceof VoidType and
|
||||
isLValue = false
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = 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.getValueExpr().getResult()
|
||||
}
|
||||
|
||||
private TranslatedExpr getValueExpr() {
|
||||
result = getTranslatedExpr(expr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import csharp
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.internal.IRUtilities
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
override final Callable getFunction() {
|
||||
result = expr.getEnclosingCallable()
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = expr.toString()
|
||||
}
|
||||
|
||||
override final 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,
|
||||
InitializationContext {
|
||||
LocalVariable var;
|
||||
|
||||
TranslatedLocalVariableDeclaration() {
|
||||
var = expr.getVariable()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getInitialization()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getVariableType(var) and
|
||||
isLValue = true
|
||||
) or
|
||||
(
|
||||
this.hasUninitializedInstruction() and
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Uninitialized and
|
||||
resultType = getVariableType(var) and
|
||||
isLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
if this.hasUninitializedInstruction() then
|
||||
result = this.getInstruction(InitializerStoreTag())
|
||||
else
|
||||
if this.isInitializedByExpr() then
|
||||
// initialization is done by the expression
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
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 IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() or
|
||||
this.hasUninitializedInstruction() and tag = InitializerStoreTag()
|
||||
) and
|
||||
result = getIRUserVariable(getFunction(), var)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
this.hasUninitializedInstruction() and
|
||||
tag = InitializerStoreTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override Instruction getTargetAddress() {
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override Type getTargetType() {
|
||||
result = getVariableType(var)
|
||||
}
|
||||
|
||||
private 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())
|
||||
}
|
||||
|
||||
private predicate hasUninitializedInstruction() {
|
||||
(
|
||||
not exists(this.getInitialization()) or
|
||||
this.getInitialization() instanceof TranslatedListInitialization
|
||||
) and
|
||||
not this.isInitializedByExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that holds if a declaration is not explicitly initialized,
|
||||
* but will be initialized as part of an expression.
|
||||
*/
|
||||
private predicate isInitializedByExpr() {
|
||||
expr.getParent() instanceof IsExpr
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.ir.implementation.raw.IR
|
||||
private import semmle.code.csharp.ir.IRConfiguration
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.csharp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedFunction
|
||||
private import TranslatedStmt
|
||||
private import IRConstruction
|
||||
private import semmle.code.csharp.ir.Util
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
|
||||
/**
|
||||
* Gets the built-in `int` type.
|
||||
*/
|
||||
IntType getIntType() { any() }
|
||||
|
||||
ArrayType getArrayOfDim(int dim, Type type) {
|
||||
result.getRank() = dim and
|
||||
result.getElementType() = type
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 to work around QL-796
|
||||
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
|
||||
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
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 separate element to handle the lvalue-to-rvalue conversion step of an
|
||||
// expression.
|
||||
TTranslatedLoad(Expr expr) {
|
||||
// TODO: Revisit and make sure Loads are only used when needed
|
||||
expr instanceof AssignableRead and
|
||||
not expr.getParent() instanceof ArrayAccess and
|
||||
not (
|
||||
expr.getParent() instanceof Assignment and
|
||||
expr.getType() instanceof RefType
|
||||
) and
|
||||
// Ignore loads for reads in `++` and `--` since their
|
||||
// translated elements handle them
|
||||
not expr.getParent() instanceof MutatorOperation
|
||||
} 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 and
|
||||
not expr instanceof ArrayCreation and
|
||||
not expr instanceof ObjectCreation
|
||||
)
|
||||
or
|
||||
// Then treat more complex ones
|
||||
expr instanceof ObjectCreation
|
||||
or
|
||||
expr instanceof ArrayInitializer
|
||||
or
|
||||
expr instanceof ObjectInitializer
|
||||
or
|
||||
expr = any(ThrowExpr throw).getExpr()
|
||||
or
|
||||
expr = any(CollectionInitializer colInit).getAnElementInitializer()
|
||||
or
|
||||
expr = any(ReturnStmt returnStmt).getExpr()
|
||||
or
|
||||
expr = any(ArrayInitializer arrInit).getAnElement()
|
||||
or
|
||||
expr = any(LambdaExpr lambda).getSourceDeclaration()
|
||||
or
|
||||
expr = any(AnonymousMethodExpr anonMethExpr).getSourceDeclaration()
|
||||
)
|
||||
} 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 value initialization of a range of array elements that were omitted
|
||||
// from an initializer list.
|
||||
TTranslatedElementValueInitialization(
|
||||
ArrayInitializer initList, int elementIndex, int elementCount
|
||||
) {
|
||||
not ignoreExpr(initList) and
|
||||
isFirstValueInitializedElementInRange(initList, elementIndex) and
|
||||
elementCount = getEndOfValueInitializedRange(initList, elementIndex) - 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)
|
||||
|
||||
/**
|
||||
* Gets the index of the first explicitly initialized element in `initList`
|
||||
* whose index is greater than `afterElementIndex`, where `afterElementIndex`
|
||||
* is a first value-initialized element in a value-initialized range in
|
||||
* `initList`. If there are no remaining explicitly initialized elements in
|
||||
* `initList`, the result is the total number of elements in the array being
|
||||
* initialized.
|
||||
*/
|
||||
private int getEndOfValueInitializedRange(ArrayInitializer initList, int afterElementIndex) {
|
||||
result = getNextExplicitlyInitializedElementAfter(initList, afterElementIndex)
|
||||
or
|
||||
isFirstValueInitializedElementInRange(initList, afterElementIndex) and
|
||||
not exists(getNextExplicitlyInitializedElementAfter(initList, afterElementIndex)) and
|
||||
result = initList.getNumberOfElements()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the first explicitly initialized element in `initList`
|
||||
* whose index is greater than `afterElementIndex`, where `afterElementIndex`
|
||||
* is a first value-initialized element in a value-initialized range in
|
||||
* `initList`.
|
||||
*/
|
||||
private int getNextExplicitlyInitializedElementAfter(
|
||||
ArrayInitializer initList, int afterElementIndex
|
||||
) {
|
||||
isFirstValueInitializedElementInRange(initList, afterElementIndex) and
|
||||
result = min(int i | exists(initList.getElement(i)) and i > afterElementIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if element `elementIndex` is the first value-initialized element in a
|
||||
* range of one or more consecutive value-initialized elements in `initList`.
|
||||
*/
|
||||
private predicate isFirstValueInitializedElementInRange(ArrayInitWithMod initList, int elementIndex) {
|
||||
initList.isValueInitialized(elementIndex) and
|
||||
(
|
||||
elementIndex = 0 or
|
||||
not initList.isValueInitialized(elementIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string is unique within
|
||||
* the scope of the element's function.
|
||||
*/
|
||||
final string getId() { result = this.getUniqueId().toString() }
|
||||
|
||||
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, Type resultType, boolean isLValue
|
||||
);
|
||||
|
||||
/**
|
||||
* 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 = 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, Type 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 knonwn
|
||||
* 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.
|
||||
*/
|
||||
Type 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`.
|
||||
*/
|
||||
Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
|
||||
/**
|
||||
* Gets the size of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
|
||||
* Only holds for operands whose type is `UnknownType`.
|
||||
*/
|
||||
int getInstructionOperandSize(InstructionTag tag, SideEffectOperandTag 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() = getAST() and
|
||||
result.getTag() = tag and
|
||||
this.hasTempVariable(tag, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent element of this element.
|
||||
*/
|
||||
final TranslatedElement getParent() { result.getAChild() = this }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,435 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.ir.implementation.raw.IR
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.internal.IRUtilities
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.csharp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import TranslatedStmt
|
||||
private import semmle.code.csharp.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)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = callable.toString()
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = callable
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function being translated.
|
||||
*/
|
||||
override final Callable getFunction() {
|
||||
result = callable
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = -2 and result = this.getConstructorInitializer() or
|
||||
id = -1 and result = this.getBody() or
|
||||
result = this.getParameter(id)
|
||||
}
|
||||
|
||||
private final TranslatedConstructorInitializer getConstructorInitializer() {
|
||||
exists(ConstructorInitializer ci |
|
||||
ci = callable.getAChild() and
|
||||
result = getTranslatedConstructorInitializer(ci))
|
||||
}
|
||||
|
||||
private final TranslatedStmt getBody() {
|
||||
result = getTranslatedStmt(callable.getBody())
|
||||
}
|
||||
|
||||
private final TranslatedParameter getParameter(int index) {
|
||||
result = getTranslatedParameter(callable.getParameter(index))
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(EnterFunctionTag())
|
||||
}
|
||||
|
||||
override final Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
result = this.getInstruction(AliasedDefinitionTag())
|
||||
) or (
|
||||
tag = AliasedDefinitionTag() and
|
||||
result = this.getInstruction(UnmodeledDefinitionTag())
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledDefinitionTag() and
|
||||
if exists(getThisType()) then
|
||||
result = this.getInstruction(InitializeThisTag())
|
||||
else if exists(getParameter(0)) then
|
||||
result = this.getParameter(0).getFirstInstruction()
|
||||
else
|
||||
result = this.getBody().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
if exists(getParameter(0)) then
|
||||
result = this.getParameter(0).getFirstInstruction()
|
||||
else
|
||||
if (exists(getConstructorInitializer())) then
|
||||
result = this.getConstructorInitializer().getFirstInstruction()
|
||||
else
|
||||
result = this.getBody().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
tag = ReturnValueAddressTag() and
|
||||
result = this.getInstruction(ReturnTag())
|
||||
) or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
result = this.getInstruction(UnmodeledUseTag())
|
||||
) or
|
||||
(
|
||||
tag = UnwindTag() and
|
||||
result = this.getInstruction(UnmodeledUseTag())
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
result = this.getInstruction(ExitFunctionTag())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final 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(getConstructorInitializer())) then
|
||||
result = this.getConstructorInitializer().getFirstInstruction()
|
||||
else
|
||||
result = this.getBody().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = this.getConstructorInitializer() and
|
||||
result = this.getBody().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = this.getBody() and
|
||||
result = this.getReturnSuccessorInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
(
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
opcode instanceof Opcode::EnterFunction and
|
||||
resultType instanceof VoidType and
|
||||
isLValue = false
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledDefinitionTag() and
|
||||
opcode instanceof Opcode::UnmodeledDefinition and
|
||||
resultType instanceof Language::UnknownType and
|
||||
isLValue = false
|
||||
) or
|
||||
(
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType instanceof Language::UnknownType and
|
||||
isLValue = false
|
||||
) or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
resultType = getThisType() and
|
||||
isLValue = true
|
||||
) or
|
||||
(
|
||||
tag = ReturnValueAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = this.getReturnType() and
|
||||
not resultType instanceof VoidType and
|
||||
isLValue = true
|
||||
) or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
resultType instanceof VoidType and
|
||||
isLValue = false 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 instanceof VoidType and
|
||||
isLValue = false and
|
||||
(
|
||||
// Only generate the `Unwind` instruction if there is any exception
|
||||
// handling present in the function.
|
||||
exists(TryStmt try |
|
||||
try.getEnclosingCallable() = callable
|
||||
) or
|
||||
exists(ThrowStmt throw |
|
||||
throw.getEnclosingCallable() = callable
|
||||
)
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
opcode instanceof Opcode::UnmodeledUse and
|
||||
resultType instanceof VoidType and
|
||||
isLValue = false
|
||||
) or
|
||||
(
|
||||
tag = ExitFunctionTag() and
|
||||
opcode instanceof Opcode::ExitFunction and
|
||||
resultType instanceof VoidType and
|
||||
isLValue = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getExceptionSuccessorInstruction() {
|
||||
result = this.getInstruction(UnwindTag())
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result.getEnclosingFunction() = callable and
|
||||
result.hasMemoryResult()
|
||||
) or
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
) or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
not this.getReturnType() instanceof VoidType and
|
||||
(
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(ReturnValueAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final Type getInstructionOperandType(InstructionTag tag,
|
||||
TypedOperandTag operandTag) {
|
||||
tag = ReturnTag() and
|
||||
not this.getReturnType() instanceof VoidType and
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = this.getReturnType()
|
||||
}
|
||||
|
||||
override final IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = ReturnValueAddressTag() and
|
||||
result = this.getReturnVariable()
|
||||
}
|
||||
|
||||
override final predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = ReturnValueTempVar() and
|
||||
type = this.getReturnType() and
|
||||
not type 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 `UnmodeledDefinition` instruction for this function.
|
||||
*/
|
||||
final Instruction getUnmodeledDefinitionInstruction() {
|
||||
result = this.getInstruction(UnmodeledDefinitionTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, Type type) {
|
||||
(
|
||||
(
|
||||
var.(Member).isStatic() and
|
||||
exists(VariableAccess access |
|
||||
access.getTarget() = var and
|
||||
access.getEnclosingCallable() = callable
|
||||
)
|
||||
) or
|
||||
var.(LocalScopeVariable).getCallable() = callable
|
||||
) and
|
||||
type = getVariableType(var)
|
||||
}
|
||||
|
||||
private final 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)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = param.toString()
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = param
|
||||
}
|
||||
|
||||
override final Callable getFunction() {
|
||||
result = param.getCallable()
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final 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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getVariableType(param) and
|
||||
isLValue = true
|
||||
) or
|
||||
(
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::InitializeParameter and
|
||||
resultType = getVariableType(param) and
|
||||
isLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override final IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
(
|
||||
tag = InitializerStoreTag() or
|
||||
tag = InitializerVariableAddressTag()
|
||||
) and
|
||||
result = getIRUserVariable(getFunction(), param)
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(InitializerVariableAddressTag())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
/**
|
||||
* Class that deals with variable initializations.
|
||||
* Separated from `TranslatedExpr` for clarity.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
private import semmle.code.csharp.ir.Util
|
||||
private import IRInternal
|
||||
|
||||
/**
|
||||
* 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() }
|
||||
|
||||
override final 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, Type resultType, boolean isLValue
|
||||
) {
|
||||
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() {
|
||||
// TODO: Make sure this is complete and correct
|
||||
not expr instanceof ArrayInitializer and
|
||||
not expr instanceof ObjectInitializer and
|
||||
not expr instanceof CollectionInitializer and
|
||||
not expr instanceof ObjectCreation and
|
||||
not expr instanceof StringLiteral
|
||||
}
|
||||
|
||||
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, Type resultType, boolean isLValue
|
||||
) {
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = this.getContext().getTargetType() and
|
||||
isLValue = false
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = InitializerStoreTag() and
|
||||
result = this.getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getInitializer() and result = this.getInstruction(InitializerStoreTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getContext().getTargetAddress()
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and
|
||||
result = this.getInitializer().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
TranslatedExpr getInitializer() { result = getTranslatedExpr(expr) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of an initialization from a constructor.
|
||||
* The `NewObj` instruction denotes the fact that during initialization a new
|
||||
* object of type `expr.getType()` is allocated, which is then initialized by the
|
||||
* constructor.
|
||||
*/
|
||||
class TranslatedObjectInitialization extends TranslatedInitialization,
|
||||
StructorCallContext {
|
||||
override ObjectCreation expr;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = this.getConstructorCall() or
|
||||
id = 1 and result = this.getInitializerExpr()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue) {
|
||||
(
|
||||
// Instruction that allocated space for a new object,
|
||||
// and returns its address
|
||||
tag = NewObjTag() and
|
||||
opcode instanceof Opcode::NewObj and
|
||||
resultType = expr.getType() and
|
||||
isLValue = false
|
||||
)
|
||||
or
|
||||
(
|
||||
// Store op used to assign the variable that
|
||||
// is initialized the address of the newly allocated
|
||||
// object
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = expr.getType() and
|
||||
isLValue = false
|
||||
)
|
||||
or
|
||||
(
|
||||
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 = this.getContext().getTargetType() and
|
||||
isLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = this.getInstruction(NewObjTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
tag = NewObjTag() and
|
||||
result = this.getConstructorCall().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
(
|
||||
tag = InitializerStoreTag() and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
)
|
||||
or
|
||||
(
|
||||
tag = AssignmentConvertRightTag() and
|
||||
result = this.getInstruction(InitializerStoreTag())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = this.getConstructorCall() and
|
||||
if (exists(this.getInitializerExpr())) then
|
||||
result = this.getInitializerExpr().getFirstInstruction()
|
||||
else
|
||||
if this.needsConversion() then
|
||||
result = this.getInstruction(AssignmentConvertRightTag())
|
||||
else
|
||||
result = this.getInstruction(InitializerStoreTag())
|
||||
) or
|
||||
(
|
||||
child = this.getInitializerExpr() 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 StoreValueOperandTag and
|
||||
if (needsConversion()) then
|
||||
result = this.getInstruction(AssignmentConvertRightTag())
|
||||
else
|
||||
result = this.getInstruction(NewObjTag())
|
||||
)
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = AssignmentConvertRightTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getInstruction(NewObjTag())
|
||||
)
|
||||
}
|
||||
|
||||
TranslatedExpr getConstructorCall() {
|
||||
result = getTranslatedExpr(expr)
|
||||
}
|
||||
|
||||
TranslatedExpr getInitializerExpr() {
|
||||
result = getTranslatedExpr(expr.getInitializer())
|
||||
}
|
||||
|
||||
override Instruction getReceiver() {
|
||||
// The newly allocated object will be the target of the constructor call
|
||||
result = this.getInstruction(NewObjTag())
|
||||
}
|
||||
|
||||
private predicate needsConversion() {
|
||||
expr.getType() != this.getContext().getTargetType()
|
||||
}
|
||||
}
|
||||
|
||||
//private string getZeroValue(Type type) {
|
||||
// if type instanceof FloatingPointType then result = "0.0" else result = "0"
|
||||
//}
|
||||
|
||||
/**
|
||||
* 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() + "[" + getElementIndex().toString() + "]"
|
||||
}
|
||||
|
||||
override final Language::AST getAST() {
|
||||
result = initList
|
||||
}
|
||||
|
||||
final override Callable getFunction() { result = initList.getEnclosingCallable() }
|
||||
|
||||
final override Instruction getFirstInstruction() { result = this.getInstruction(getElementIndexTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
|
||||
) {
|
||||
tag = getElementIndexTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType() and
|
||||
isLValue = false
|
||||
or
|
||||
tag = getElementAddressTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getElementType() and
|
||||
isLValue = true
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = getElementIndexTag() and
|
||||
result = this.getInstruction(getElementAddressTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = getElementAddressTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getParent().(InitializationContext).getTargetAddress()
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(getElementIndexTag())
|
||||
)
|
||||
}
|
||||
|
||||
override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = getElementIndexTag() and
|
||||
result = getElementIndex().toString()
|
||||
}
|
||||
|
||||
abstract int getElementIndex();
|
||||
|
||||
final InstructionTag getElementAddressTag() {
|
||||
result = InitializerElementAddressTag(getElementIndex())
|
||||
}
|
||||
|
||||
final InstructionTag getElementIndexTag() {
|
||||
result = InitializerElementIndexTag(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(getElementAddressTag()) }
|
||||
|
||||
override Type getTargetType() { result = getElementType() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind)
|
||||
or
|
||||
tag = 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, StructorCallContext {
|
||||
Call call;
|
||||
|
||||
override final 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 = "constuructor init: " + call.toString()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if (needsConversion()) then
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
else
|
||||
result = this.getConstructorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
|
||||
) {
|
||||
this.needsConversion() and
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
resultType = call.getTarget().getDeclaringType() and
|
||||
isLValue = true
|
||||
}
|
||||
|
||||
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(getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
predicate needsConversion() {
|
||||
call.getTarget().getDeclaringType() != this.getFunction().getDeclaringType()
|
||||
}
|
||||
|
||||
override predicate getInstructionInheritance(
|
||||
InstructionTag tag, Class baseClass, Class derivedClass
|
||||
) {
|
||||
tag = OnlyInstructionTag() and
|
||||
baseClass = call.getTarget().getDeclaringType() and
|
||||
derivedClass = this.getFunction().getDeclaringType()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
private import ReachableBlock as Reachability
|
||||
private module ReachabilityGraph = Reachability::Graph;
|
||||
|
||||
module Graph {
|
||||
import Reachability::Graph
|
||||
class Block = Reachability::ReachableBlock;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.csharp.ir.implementation.raw.IR as IR
|
||||
import semmle.code.csharp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
@@ -0,0 +1,123 @@
|
||||
private import csharp as CSharp
|
||||
private import IRUtilities
|
||||
|
||||
class Function = CSharp::Callable;
|
||||
class Location = CSharp::Location;
|
||||
class AST = CSharp::Element;
|
||||
|
||||
class Type = CSharp::Type;
|
||||
class UnknownType = CSharp::NullType;
|
||||
class VoidType = CSharp::VoidType;
|
||||
class IntegralType = CSharp::IntegralType;
|
||||
class FloatingPointType = CSharp::FloatingPointType;
|
||||
|
||||
private newtype TClassDerivation =
|
||||
// Note that this is the `Class` type exported from this module, not CSharp::Class.
|
||||
MkClassDerivation(Class base, Class derived) {
|
||||
derived.getABaseType() = base
|
||||
}
|
||||
|
||||
private newtype TBuiltInOperation =
|
||||
NoOp()
|
||||
|
||||
class BuiltInOperation extends TBuiltInOperation {
|
||||
string toString() {
|
||||
result = "BuiltInOp"
|
||||
}
|
||||
}
|
||||
|
||||
class ClassDerivation extends MkClassDerivation {
|
||||
Class baseClass;
|
||||
Class derivedClass;
|
||||
|
||||
ClassDerivation() {
|
||||
this = MkClassDerivation(baseClass, derivedClass)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = "ClassDerivation"
|
||||
}
|
||||
|
||||
final Class getBaseClass() {
|
||||
result = baseClass
|
||||
}
|
||||
|
||||
final Class getDerivedClass() {
|
||||
result = derivedClass
|
||||
}
|
||||
|
||||
final int getByteOffset() {
|
||||
// Inheritance never requires adjusting the `this` pointer in C#.
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
|
||||
class StringLiteral = CSharp::StringLiteral;
|
||||
|
||||
class Variable = CSharp::Variable;
|
||||
class AutomaticVariable = CSharp::LocalScopeVariable;
|
||||
class StaticVariable = CSharp::Variable;
|
||||
class Parameter = CSharp::Parameter;
|
||||
class Field = CSharp::Field;
|
||||
|
||||
// TODO: Remove necessity for these.
|
||||
class Expr = CSharp::Expr;
|
||||
class Class = CSharp::ValueOrRefType; // Used for inheritance conversions
|
||||
|
||||
string getIdentityString(Function func) {
|
||||
result = func.getLabel()
|
||||
}
|
||||
|
||||
predicate hasCaseEdge(string minValue, string maxValue) {
|
||||
// TODO: Need to handle pattern matching
|
||||
exists(CSharp :: CaseStmt cst |
|
||||
hasCaseEdge(cst, minValue, maxValue)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasPositionalArgIndex(int argIndex) {
|
||||
exists(CSharp::MethodCall call |
|
||||
exists(call.getArgument(argIndex))
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasAsmOperandIndex(int operandIndex) {
|
||||
none()
|
||||
}
|
||||
|
||||
int getTypeSize(Type type) {
|
||||
// REVIEW: Is this complete?
|
||||
result = type.(CSharp::SimpleType).getSize() or
|
||||
result = getTypeSize(type.(CSharp::Enum).getUnderlyingType()) or
|
||||
// TODO: Generate a reasonable size
|
||||
type instanceof CSharp::Struct and result = 16 or
|
||||
type instanceof CSharp::RefType and result = getPointerSize() or
|
||||
type instanceof CSharp::PointerType and result = getPointerSize() or
|
||||
result = getTypeSize(type.(CSharp::TupleType).getUnderlyingType()) or
|
||||
// TODO: Add room for extra field
|
||||
result = getTypeSize(type.(CSharp::NullableType).getUnderlyingType()) or
|
||||
type instanceof CSharp::VoidType and result = 0
|
||||
}
|
||||
|
||||
int getPointerSize() {
|
||||
// TODO: Deal with sizes in general
|
||||
result = 8
|
||||
}
|
||||
|
||||
predicate isVariableAutomatic(Variable var) {
|
||||
var instanceof CSharp::LocalScopeVariable
|
||||
}
|
||||
|
||||
string getStringLiteralText(StringLiteral s) {
|
||||
// REVIEW: Is this the right escaping?
|
||||
result = s.toString()
|
||||
}
|
||||
|
||||
predicate hasPotentialLoop(Function f) {
|
||||
exists(CSharp::LoopStmt l | l.getEnclosingCallable() = f) or
|
||||
exists(CSharp::GotoStmt s | s.getEnclosingCallable() = f)
|
||||
}
|
||||
|
||||
predicate hasGoto(Function f) {
|
||||
exists(CSharp::GotoStmt s | s.getEnclosingCallable() = f)
|
||||
}
|
||||
41
csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll
Normal file
41
csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
private import csharp
|
||||
|
||||
/**
|
||||
* Given a type, get the type that would result by applying "pointer decay".
|
||||
* A function type becomes a pointer to that function type, and an array type
|
||||
* becomes a pointer to the element type of the array. If the specified type
|
||||
* is not subject to pointer decay, this predicate does not hold.
|
||||
*/
|
||||
// TODO: Only pointer to array decay in C#?
|
||||
private Type getDecayedType(Type type) {
|
||||
result.(PointerType).getReferentType() = type.(ArrayType).getElementType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual type of the specified variable, as opposed to the declared type.
|
||||
* This returns the type of the variable after any pointer decay is applied, and
|
||||
* after any unsized array type has its size inferred from the initializer.
|
||||
*/
|
||||
Type getVariableType(Variable v) {
|
||||
exists(Type declaredType |
|
||||
declaredType = v.getType() and
|
||||
if v instanceof Parameter then (
|
||||
result = getDecayedType(declaredType) or
|
||||
not exists(getDecayedType(declaredType)) and result = declaredType
|
||||
)
|
||||
else if declaredType instanceof ArrayType then (
|
||||
// TODO: Arrays have a declared dimension in C#, so this should not be needed
|
||||
// and not declaredType.(ArrayType).hasArraySize()
|
||||
result = v.getInitializer().getType() or
|
||||
not exists(v.getInitializer()) and result = declaredType
|
||||
)
|
||||
else (
|
||||
result = declaredType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasCaseEdge(CaseStmt caseStmt, string minValue, string maxValue) {
|
||||
minValue = caseStmt.getPattern().getValue() and
|
||||
maxValue = minValue
|
||||
}
|
||||
255
csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll
Normal file
255
csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll
Normal file
@@ -0,0 +1,255 @@
|
||||
class IntValue = int;
|
||||
|
||||
/**
|
||||
* Returns the value of the maximum representable integer.
|
||||
*/
|
||||
int maxValue() {
|
||||
result = 2147483647
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the minimum representable integer.
|
||||
*/
|
||||
int minValue() {
|
||||
result = -2147483647
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value representing an unknown integer.
|
||||
*/
|
||||
IntValue unknown() {
|
||||
result = -2147483648
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` has a known value.
|
||||
*/
|
||||
bindingset[n]
|
||||
predicate hasValue(IntValue n) {
|
||||
n != unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of `n`. If `n` does not have a known value, the result is "??".
|
||||
*/
|
||||
bindingset[n]
|
||||
string intValueToString(IntValue n) {
|
||||
if hasValue(n) then result = n.toString() else result = "??"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value `f` is within the range of representable integers.
|
||||
*/
|
||||
pragma[inline]
|
||||
bindingset[f]
|
||||
private predicate isRepresentable(float f) {
|
||||
(f >= minValue()) and (f <= maxValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of `n`. Holds only if `n` has a known value.
|
||||
*/
|
||||
bindingset[n]
|
||||
int getValue(IntValue n) {
|
||||
hasValue(n) and result = n
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a + b`. If either input is unknown, or if the addition overflows,
|
||||
* the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue add(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) and isRepresentable((float)a + (float)b) then
|
||||
result = a + b
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a - b`. If either input is unknown, or if the subtraction overflows,
|
||||
* the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue sub(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) and isRepresentable((float)a - (float)b) then
|
||||
result = a - b
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a * b`. If the multiplication overflows, the result is unknown. If
|
||||
* either input is unknown and the other input is non-zero, the result is
|
||||
* unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue mul(IntValue a, IntValue b) {
|
||||
if (a = 0) or (b = 0) then
|
||||
result = 0
|
||||
else if hasValue(a) and hasValue(b) and isRepresentable((float)a * (float)b) then
|
||||
result = a * b
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a / b`. If either input is unknown, or if `b` is zero, the result is
|
||||
* unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue div(IntValue a, IntValue b) {
|
||||
// Normally, integer division has to worry about overflow for INT_MIN/-1.
|
||||
// However, since we use INT_MIN to represent an unknown value anyway, we only
|
||||
// have to worry about division by zero.
|
||||
if hasValue(a) and hasValue(b) and (b != 0) then
|
||||
result = a / b
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a == b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareEQ(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a = b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a != b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareNE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a != b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a < b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a < b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a > b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGT(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a > b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a <= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareLE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a <= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a >= b`. If either input is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
IntValue compareGE(IntValue a, IntValue b) {
|
||||
if hasValue(a) and hasValue(b) then (
|
||||
if a >= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
)
|
||||
else
|
||||
result = unknown()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `-a`. If `a` is unknown, the result is unknown.
|
||||
*/
|
||||
bindingset[a]
|
||||
IntValue neg(IntValue a) {
|
||||
result = -a // -INT_MIN = INT_MIN, so this preserves unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is equal to `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isEQ(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a = b
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is not equal to `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isNE(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a != b
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is less than `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isLT(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a < b
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is less than or equal to `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isLE(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a <= b
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is greater than `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isGT(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a > b
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `a` is greater than or equal to `b`. Does not hold if either `a` or `b` is unknown.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
predicate isGE(IntValue a, IntValue b) {
|
||||
hasValue(a) and hasValue(b) and a >= b
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Support for integer intervals.
|
||||
* An interval is represented as by its inclusive lower bound, `start`, and its exclusive upper bound, `end`.
|
||||
* Either or both of `start` and `end` may have an unknown value.
|
||||
*/
|
||||
|
||||
import Overlap
|
||||
private import IntegerConstant
|
||||
|
||||
/**
|
||||
* Gets the overlap relationship between the definition interval [`defStart`, `defEnd`) and the use interval
|
||||
* [`useStart`, `useEnd`).
|
||||
*/
|
||||
bindingset[defStart, defEnd, useStart, useEnd]
|
||||
Overlap getOverlap(IntValue defStart, IntValue defEnd, IntValue useStart, IntValue useEnd) {
|
||||
if isEQ(defStart, useStart) and isEQ(defEnd, useEnd) then
|
||||
result instanceof MustExactlyOverlap
|
||||
else if isLE(defStart, useStart) and isGE(defEnd, useEnd) then
|
||||
result instanceof MustTotallyOverlap
|
||||
else if isLE(defEnd, useStart) or isGE(defStart, useEnd) then
|
||||
none()
|
||||
else
|
||||
result instanceof MayPartiallyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representation of the interval [`start`, `end`).
|
||||
*/
|
||||
bindingset[start, end]
|
||||
string getIntervalString(IntValue start, IntValue end) {
|
||||
// We represent an interval has half-open, so print it as "[start..end)".
|
||||
result = "[" + intValueToString(start) + ".." + intValueToString(end) + ")"
|
||||
}
|
||||
134
csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll
Normal file
134
csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Provides basic arithmetic operations that have no result if their result
|
||||
* would overflow a 32-bit two's complement integer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the value of the maximum representable integer.
|
||||
*/
|
||||
int maxValue() {
|
||||
result = 2147483647
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the minimum representable integer.
|
||||
*/
|
||||
int minValue() {
|
||||
result = -2147483648
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value `f` is within the range of representable integers.
|
||||
*/
|
||||
pragma[inline]
|
||||
bindingset[f]
|
||||
private predicate isRepresentable(float f) {
|
||||
(f >= minValue()) and (f <= maxValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a + b`. If the addition overflows, there is no result.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
int add(int a, int b) {
|
||||
isRepresentable((float)a + (float)b) and
|
||||
result = a + b
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a - b`. If the subtraction overflows, there is no result.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
int sub(int a, int b) {
|
||||
isRepresentable((float)a - (float)b) and
|
||||
result = a - b
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a * b`. If the multiplication overflows, there is no result. If
|
||||
* either input is not given, and the other input is non-zero, there is no
|
||||
* result.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
int mul(int a, int b) {
|
||||
a = 0 and
|
||||
result = 0
|
||||
or
|
||||
b = 0 and
|
||||
result = 0
|
||||
or
|
||||
isRepresentable((float)a * (float)b) and
|
||||
result = a * b
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `a / b`. If the division overflows, there is no result.
|
||||
*/
|
||||
bindingset[a, b]
|
||||
int div(int a, int b) {
|
||||
b != 0 and (a != minValue() or b != -1) and
|
||||
result = a / b
|
||||
}
|
||||
|
||||
/** Returns `a == b`. */
|
||||
bindingset[a, b]
|
||||
int compareEQ(int a, int b) {
|
||||
if a = b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Returns `a != b`. */
|
||||
bindingset[a, b]
|
||||
int compareNE(int a, int b) {
|
||||
if a != b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Returns `a < b`. */
|
||||
bindingset[a, b]
|
||||
int compareLT(int a, int b) {
|
||||
if a < b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Returns `a > b`. */
|
||||
bindingset[a, b]
|
||||
int compareGT(int a, int b) {
|
||||
if a > b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Returns `a <= b`. */
|
||||
bindingset[a, b]
|
||||
int compareLE(int a, int b) {
|
||||
if a <= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/** Returns `a >= b`. */
|
||||
bindingset[a, b]
|
||||
int compareGE(int a, int b) {
|
||||
if a >= b then
|
||||
result = 1
|
||||
else
|
||||
result = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `-a`. If the negation would overflow, there is no result.
|
||||
*/
|
||||
bindingset[a]
|
||||
int neg(int a) {
|
||||
a != minValue() and
|
||||
result = -a
|
||||
}
|
||||
26
csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll
Normal file
26
csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll
Normal file
@@ -0,0 +1,26 @@
|
||||
private newtype TOverlap =
|
||||
TMayPartiallyOverlap() or
|
||||
TMustTotallyOverlap() or
|
||||
TMustExactlyOverlap()
|
||||
|
||||
abstract class Overlap extends TOverlap {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap {
|
||||
override final string toString() {
|
||||
result = "MayPartiallyOverlap"
|
||||
}
|
||||
}
|
||||
|
||||
class MustTotallyOverlap extends Overlap, TMustTotallyOverlap {
|
||||
override final string toString() {
|
||||
result = "MustTotallyOverlap"
|
||||
}
|
||||
}
|
||||
|
||||
class MustExactlyOverlap extends Overlap, TMustExactlyOverlap {
|
||||
override final string toString() {
|
||||
result = "MustExactlyOverlap"
|
||||
}
|
||||
}
|
||||
15
csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll
Normal file
15
csharp/ql/src/semmle/code/csharp/ir/internal/TIRVariable.qll
Normal file
@@ -0,0 +1,15 @@
|
||||
private import csharp
|
||||
private import semmle.code.csharp.ir.implementation.TempVariableTag
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.IRConstruction as Construction
|
||||
private import semmle.code.csharp.ir.Util
|
||||
private import IRCSharpLanguage as Language
|
||||
|
||||
newtype TIRVariable =
|
||||
TIRAutomaticUserVariable(LocalScopeVariable var, Callable callable) {
|
||||
Construction::functionHasIR(callable) and
|
||||
var.getCallable() = callable
|
||||
} or
|
||||
TIRTempVariable(Callable callable, Language::AST ast, TempVariableTag tag, Type type) {
|
||||
Construction::hasTempVariable(callable, ast, tag, type)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import csharp
|
||||
|
||||
// TODO: ARE THOSE TAGS ENOUGH?
|
||||
newtype TTempVariableTag =
|
||||
ConditionValueTempVar() or
|
||||
ReturnValueTempVar() or
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar()
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal" or
|
||||
tag = ReturnValueTempVar() and result = "Ret" or
|
||||
tag = ThrowTempVar() and result = "Throw" or
|
||||
tag = LambdaTempVar() and result = "Lambda"
|
||||
}
|
||||
20
csharp/ql/test/library-tests/ir/ir/array.cs
Normal file
20
csharp/ql/test/library-tests/ir/ir/array.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
public class ArrayTest {
|
||||
public void one_dim_init_acc()
|
||||
{
|
||||
int[] one_dim = {100, 101, 102};
|
||||
one_dim[0] = 1000;
|
||||
one_dim[1] = one_dim[0];
|
||||
one_dim[1] = 1003;
|
||||
}
|
||||
|
||||
public void twod_and_init_acc()
|
||||
{
|
||||
int[,] a = { {100, 101}, {102, 103} };
|
||||
int[,] b = new int[5, 5];
|
||||
int[,] c = new int[2, 2] { {100, 101}, {102, 103} };
|
||||
int[,] d = new int[,] { {100, 101}, {102, 103} };
|
||||
int[,] e = a;
|
||||
e[1, 1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
17
csharp/ql/test/library-tests/ir/ir/casts.cs
Normal file
17
csharp/ql/test/library-tests/ir/ir/casts.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
public class Casts_A
|
||||
{
|
||||
}
|
||||
|
||||
public class Casts_B : Casts_A
|
||||
{
|
||||
}
|
||||
|
||||
public class Casts
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
Casts_A Aobj = new Casts_A();
|
||||
Casts_B bobjCE = (Casts_B) Aobj;
|
||||
Casts_B bobjAS = Aobj as Casts_B;
|
||||
}
|
||||
}
|
||||
35
csharp/ql/test/library-tests/ir/ir/constructor_init.cs
Normal file
35
csharp/ql/test/library-tests/ir/ir/constructor_init.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
public class BaseClass
|
||||
{
|
||||
int num;
|
||||
|
||||
public BaseClass()
|
||||
{
|
||||
}
|
||||
|
||||
public BaseClass(int i)
|
||||
{
|
||||
num = i;
|
||||
}
|
||||
}
|
||||
|
||||
public class DerivedClass : BaseClass
|
||||
{
|
||||
public DerivedClass() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public DerivedClass(int i) : base(i)
|
||||
{
|
||||
}
|
||||
|
||||
public DerivedClass(int i, int j): this(i)
|
||||
{
|
||||
}
|
||||
|
||||
static void Main()
|
||||
{
|
||||
DerivedClass obj1 = new DerivedClass();
|
||||
DerivedClass obj2 = new DerivedClass(1);
|
||||
DerivedClass obj3 = new DerivedClass(1, 2);
|
||||
}
|
||||
}
|
||||
11
csharp/ql/test/library-tests/ir/ir/crement.cs
Normal file
11
csharp/ql/test/library-tests/ir/ir/crement.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
class CrementOpsTest
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
int x = 10;
|
||||
int a = x++;
|
||||
int b = --x;
|
||||
int c = ++x;
|
||||
x = x--;
|
||||
}
|
||||
}
|
||||
14
csharp/ql/test/library-tests/ir/ir/func_with_param_call.cs
Normal file
14
csharp/ql/test/library-tests/ir/ir/func_with_param_call.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
public class test_call_with_param
|
||||
{
|
||||
public static int f(int x, int y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
public static int g()
|
||||
{
|
||||
return f(2, 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
public class A
|
||||
{
|
||||
public virtual int function()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class B : A
|
||||
{
|
||||
}
|
||||
|
||||
class C : B
|
||||
{
|
||||
public override int function()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
B objB = new B();
|
||||
objB.function();
|
||||
|
||||
// Check conversion works
|
||||
A objA;
|
||||
objA = objB;
|
||||
objA.function();
|
||||
|
||||
A objC = new C();
|
||||
objC.function();
|
||||
}
|
||||
|
||||
}
|
||||
22
csharp/ql/test/library-tests/ir/ir/isexpr.cs
Normal file
22
csharp/ql/test/library-tests/ir/ir/isexpr.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
public class Is_A
|
||||
{
|
||||
public int x;
|
||||
}
|
||||
|
||||
public class IsExpr
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
Is_A obj = null;
|
||||
|
||||
object o = obj;
|
||||
if (o is Is_A tmp)
|
||||
{
|
||||
int res = tmp.x;
|
||||
}
|
||||
if (o is Is_A)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
23
csharp/ql/test/library-tests/ir/ir/obj_creation.cs
Normal file
23
csharp/ql/test/library-tests/ir/ir/obj_creation.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
public class ObjCreation
|
||||
{
|
||||
public class MyClass
|
||||
{
|
||||
public int x;
|
||||
|
||||
public MyClass()
|
||||
{
|
||||
}
|
||||
|
||||
public MyClass(int _x)
|
||||
{
|
||||
x = _x;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
MyClass obj = new MyClass(100);
|
||||
MyClass obj_initlist = new MyClass { x = 101 };
|
||||
int a = obj.x;
|
||||
}
|
||||
}
|
||||
32
csharp/ql/test/library-tests/ir/ir/prop.cs
Normal file
32
csharp/ql/test/library-tests/ir/ir/prop.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
class PropClass
|
||||
{
|
||||
private int prop;
|
||||
|
||||
public int Prop
|
||||
{
|
||||
get
|
||||
{
|
||||
return func();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
prop = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int func()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Prog
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
PropClass obj = new PropClass();
|
||||
obj.Prop = 5;
|
||||
int x = obj.Prop;
|
||||
}
|
||||
}
|
||||
965
csharp/ql/test/library-tests/ir/ir/raw_ir.expected
Normal file
965
csharp/ql/test/library-tests/ir/ir/raw_ir.expected
Normal file
@@ -0,0 +1,965 @@
|
||||
array.cs:
|
||||
# 2| System.Void ArrayTest.one_dim_init_acc()
|
||||
# 2| Block 0
|
||||
# 2| v0_0(Void) = EnterFunction :
|
||||
# 2| mu0_1(null) = AliasedDefinition :
|
||||
# 2| mu0_2(null) = UnmodeledDefinition :
|
||||
# 2| r0_3(glval<ArrayTest>) = InitializeThis :
|
||||
# 4| r0_4(glval<Int32[]>) = VariableAddress[one_dim] :
|
||||
# 4| mu0_5(Int32[]) = Uninitialized[one_dim] : &:r0_4
|
||||
# 4| r0_6(Int32) = Constant[0] :
|
||||
# 4| r0_7(glval<Int32>) = PointerAdd : r0_4, r0_6
|
||||
# 4| r0_8(Int32) = Constant[100] :
|
||||
# 4| mu0_9(Int32) = Store : &:r0_7, r0_8
|
||||
# 4| r0_10(Int32) = Constant[1] :
|
||||
# 4| r0_11(glval<Int32>) = PointerAdd : r0_4, r0_10
|
||||
# 4| r0_12(Int32) = Constant[101] :
|
||||
# 4| mu0_13(Int32) = Store : &:r0_11, r0_12
|
||||
# 4| r0_14(Int32) = Constant[2] :
|
||||
# 4| r0_15(glval<Int32>) = PointerAdd : r0_4, r0_14
|
||||
# 4| r0_16(Int32) = Constant[102] :
|
||||
# 4| mu0_17(Int32) = Store : &:r0_15, r0_16
|
||||
# 5| r0_18(Int32) = Constant[1000] :
|
||||
# 5| r0_19(glval<Int32[]>) = VariableAddress[one_dim] :
|
||||
# 5| r0_20(Int32[]) = ElementsAddress : r0_19
|
||||
# 5| r0_21(Int32) = Constant[0] :
|
||||
# 5| r0_22(Int32[]) = PointerAdd[4] : r0_20, r0_21
|
||||
# 5| mu0_23(Int32) = Store : &:r0_22, r0_18
|
||||
# 6| r0_24(glval<Int32[]>) = VariableAddress[one_dim] :
|
||||
# 6| r0_25(Int32[]) = ElementsAddress : r0_24
|
||||
# 6| r0_26(Int32) = Constant[0] :
|
||||
# 6| r0_27(Int32[]) = PointerAdd[4] : r0_25, r0_26
|
||||
# 6| r0_28(Int32) = Load : &:r0_27, ~mu0_2
|
||||
# 6| r0_29(glval<Int32[]>) = VariableAddress[one_dim] :
|
||||
# 6| r0_30(Int32[]) = ElementsAddress : r0_29
|
||||
# 6| r0_31(Int32) = Constant[1] :
|
||||
# 6| r0_32(Int32[]) = PointerAdd[4] : r0_30, r0_31
|
||||
# 6| mu0_33(Int32) = Store : &:r0_32, r0_28
|
||||
# 7| r0_34(Int32) = Constant[1003] :
|
||||
# 7| r0_35(glval<Int32[]>) = VariableAddress[one_dim] :
|
||||
# 7| r0_36(Int32[]) = ElementsAddress : r0_35
|
||||
# 7| r0_37(Int32) = Constant[1] :
|
||||
# 7| r0_38(Int32[]) = PointerAdd[4] : r0_36, r0_37
|
||||
# 7| mu0_39(Int32) = Store : &:r0_38, r0_34
|
||||
# 2| v0_40(Void) = ReturnVoid :
|
||||
# 2| v0_41(Void) = UnmodeledUse : mu*
|
||||
# 2| v0_42(Void) = ExitFunction :
|
||||
|
||||
# 10| System.Void ArrayTest.twod_and_init_acc()
|
||||
# 10| Block 0
|
||||
# 10| v0_0(Void) = EnterFunction :
|
||||
# 10| mu0_1(null) = AliasedDefinition :
|
||||
# 10| mu0_2(null) = UnmodeledDefinition :
|
||||
# 10| r0_3(glval<ArrayTest>) = InitializeThis :
|
||||
# 12| r0_4(glval<Int32[,]>) = VariableAddress[a] :
|
||||
# 12| mu0_5(Int32[,]) = Uninitialized[a] : &:r0_4
|
||||
# 12| r0_6(Int32) = Constant[0] :
|
||||
# 12| r0_7(glval<null>) = PointerAdd : r0_4, r0_6
|
||||
# 12| r0_8(Int32) = Constant[0] :
|
||||
# 12| r0_9(glval<Int32>) = PointerAdd : r0_7, r0_8
|
||||
# 12| r0_10(Int32) = Constant[100] :
|
||||
# 12| mu0_11(Int32) = Store : &:r0_9, r0_10
|
||||
# 12| r0_12(Int32) = Constant[1] :
|
||||
# 12| r0_13(glval<Int32>) = PointerAdd : r0_7, r0_12
|
||||
# 12| r0_14(Int32) = Constant[101] :
|
||||
# 12| mu0_15(Int32) = Store : &:r0_13, r0_14
|
||||
# 12| r0_16(Int32) = Constant[1] :
|
||||
# 12| r0_17(glval<null>) = PointerAdd : r0_4, r0_16
|
||||
# 12| r0_18(Int32) = Constant[0] :
|
||||
# 12| r0_19(glval<Int32>) = PointerAdd : r0_17, r0_18
|
||||
# 12| r0_20(Int32) = Constant[102] :
|
||||
# 12| mu0_21(Int32) = Store : &:r0_19, r0_20
|
||||
# 12| r0_22(Int32) = Constant[1] :
|
||||
# 12| r0_23(glval<Int32>) = PointerAdd : r0_17, r0_22
|
||||
# 12| r0_24(Int32) = Constant[103] :
|
||||
# 12| mu0_25(Int32) = Store : &:r0_23, r0_24
|
||||
# 13| r0_26(glval<Int32[,]>) = VariableAddress[b] :
|
||||
# 13| mu0_27(Int32[,]) = Uninitialized[b] : &:r0_26
|
||||
# 14| r0_28(glval<Int32[,]>) = VariableAddress[c] :
|
||||
# 14| mu0_29(Int32[,]) = Uninitialized[c] : &:r0_28
|
||||
# 14| r0_30(Int32) = Constant[0] :
|
||||
# 14| r0_31(glval<null>) = PointerAdd : r0_28, r0_30
|
||||
# 14| r0_32(Int32) = Constant[0] :
|
||||
# 14| r0_33(glval<Int32>) = PointerAdd : r0_31, r0_32
|
||||
# 14| r0_34(Int32) = Constant[100] :
|
||||
# 14| mu0_35(Int32) = Store : &:r0_33, r0_34
|
||||
# 14| r0_36(Int32) = Constant[1] :
|
||||
# 14| r0_37(glval<Int32>) = PointerAdd : r0_31, r0_36
|
||||
# 14| r0_38(Int32) = Constant[101] :
|
||||
# 14| mu0_39(Int32) = Store : &:r0_37, r0_38
|
||||
# 14| r0_40(Int32) = Constant[1] :
|
||||
# 14| r0_41(glval<null>) = PointerAdd : r0_28, r0_40
|
||||
# 14| r0_42(Int32) = Constant[0] :
|
||||
# 14| r0_43(glval<Int32>) = PointerAdd : r0_41, r0_42
|
||||
# 14| r0_44(Int32) = Constant[102] :
|
||||
# 14| mu0_45(Int32) = Store : &:r0_43, r0_44
|
||||
# 14| r0_46(Int32) = Constant[1] :
|
||||
# 14| r0_47(glval<Int32>) = PointerAdd : r0_41, r0_46
|
||||
# 14| r0_48(Int32) = Constant[103] :
|
||||
# 14| mu0_49(Int32) = Store : &:r0_47, r0_48
|
||||
# 15| r0_50(glval<Int32[,]>) = VariableAddress[d] :
|
||||
# 15| mu0_51(Int32[,]) = Uninitialized[d] : &:r0_50
|
||||
# 15| r0_52(Int32) = Constant[0] :
|
||||
# 15| r0_53(glval<null>) = PointerAdd : r0_50, r0_52
|
||||
# 15| r0_54(Int32) = Constant[0] :
|
||||
# 15| r0_55(glval<Int32>) = PointerAdd : r0_53, r0_54
|
||||
# 15| r0_56(Int32) = Constant[100] :
|
||||
# 15| mu0_57(Int32) = Store : &:r0_55, r0_56
|
||||
# 15| r0_58(Int32) = Constant[1] :
|
||||
# 15| r0_59(glval<Int32>) = PointerAdd : r0_53, r0_58
|
||||
# 15| r0_60(Int32) = Constant[101] :
|
||||
# 15| mu0_61(Int32) = Store : &:r0_59, r0_60
|
||||
# 15| r0_62(Int32) = Constant[1] :
|
||||
# 15| r0_63(glval<null>) = PointerAdd : r0_50, r0_62
|
||||
# 15| r0_64(Int32) = Constant[0] :
|
||||
# 15| r0_65(glval<Int32>) = PointerAdd : r0_63, r0_64
|
||||
# 15| r0_66(Int32) = Constant[102] :
|
||||
# 15| mu0_67(Int32) = Store : &:r0_65, r0_66
|
||||
# 15| r0_68(Int32) = Constant[1] :
|
||||
# 15| r0_69(glval<Int32>) = PointerAdd : r0_63, r0_68
|
||||
# 15| r0_70(Int32) = Constant[103] :
|
||||
# 15| mu0_71(Int32) = Store : &:r0_69, r0_70
|
||||
# 16| r0_72(glval<Int32[,]>) = VariableAddress[e] :
|
||||
# 16| r0_73(glval<Int32[,]>) = VariableAddress[a] :
|
||||
# 16| mu0_74(Int32[,]) = Store : &:r0_72, r0_73
|
||||
# 17| r0_75(Int32) = Constant[-1] :
|
||||
# 17| r0_76(glval<Int32[,]>) = VariableAddress[e] :
|
||||
# 17| r0_77(Int32[,]) = ElementsAddress : r0_76
|
||||
# 17| r0_78(Int32) = Constant[1] :
|
||||
# 17| r0_79(Int32[,]) = PointerAdd[4] : r0_77, r0_78
|
||||
# 17| r0_80(Int32[]) = ElementsAddress : r0_79
|
||||
# 17| r0_81(Int32) = Constant[1] :
|
||||
# 17| r0_82(Int32[]) = PointerAdd[4] : r0_80, r0_81
|
||||
# 17| mu0_83(Int32) = Store : &:r0_82, r0_75
|
||||
# 10| v0_84(Void) = ReturnVoid :
|
||||
# 10| v0_85(Void) = UnmodeledUse : mu*
|
||||
# 10| v0_86(Void) = ExitFunction :
|
||||
|
||||
casts.cs:
|
||||
# 11| System.Void Casts.Main()
|
||||
# 11| Block 0
|
||||
# 11| v0_0(Void) = EnterFunction :
|
||||
# 11| mu0_1(null) = AliasedDefinition :
|
||||
# 11| mu0_2(null) = UnmodeledDefinition :
|
||||
# 13| r0_3(glval<Casts_A>) = VariableAddress[Aobj] :
|
||||
# 13| r0_4(Casts_A) = NewObj :
|
||||
# 13| r0_5(glval<Casts_A>) = FunctionAddress[Casts_A] :
|
||||
# 13| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 13| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 13| mu0_8(Casts_A) = Store : &:r0_3, r0_4
|
||||
# 14| r0_9(glval<Casts_B>) = VariableAddress[bobjCE] :
|
||||
# 14| r0_10(glval<Casts_A>) = VariableAddress[Aobj] :
|
||||
# 14| r0_11(Casts_A) = Load : &:r0_10, ~mu0_2
|
||||
# 14| r0_12(Casts_B) = CheckedConvertOrThrow : r0_11
|
||||
# 14| mu0_13(Casts_B) = Store : &:r0_9, r0_12
|
||||
# 15| r0_14(glval<Casts_B>) = VariableAddress[bobjAS] :
|
||||
# 15| r0_15(glval<Casts_A>) = VariableAddress[Aobj] :
|
||||
# 15| r0_16(Casts_A) = Load : &:r0_15, ~mu0_2
|
||||
# 15| r0_17(Casts_B) = CheckedConvertOrNull : r0_16
|
||||
# 15| mu0_18(Casts_B) = Store : &:r0_14, r0_17
|
||||
# 11| v0_19(Void) = ReturnVoid :
|
||||
# 11| v0_20(Void) = UnmodeledUse : mu*
|
||||
# 11| v0_21(Void) = ExitFunction :
|
||||
|
||||
constructor_init.cs:
|
||||
# 5| System.Void BaseClass..ctor()
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 5| r0_3(glval<BaseClass>) = InitializeThis :
|
||||
# 6| v0_4(Void) = NoOp :
|
||||
# 5| v0_5(Void) = ReturnVoid :
|
||||
# 5| v0_6(Void) = UnmodeledUse : mu*
|
||||
# 5| v0_7(Void) = ExitFunction :
|
||||
|
||||
# 9| System.Void BaseClass..ctor(System.Int32)
|
||||
# 9| Block 0
|
||||
# 9| v0_0(Void) = EnterFunction :
|
||||
# 9| mu0_1(null) = AliasedDefinition :
|
||||
# 9| mu0_2(null) = UnmodeledDefinition :
|
||||
# 9| r0_3(glval<BaseClass>) = InitializeThis :
|
||||
# 9| r0_4(glval<Int32>) = VariableAddress[i] :
|
||||
# 9| mu0_5(Int32) = InitializeParameter[i] : &:r0_4
|
||||
# 11| r0_6(glval<Int32>) = VariableAddress[i] :
|
||||
# 11| r0_7(Int32) = Load : &:r0_6, ~mu0_2
|
||||
# 11| r0_8(BaseClass) = CopyValue : r0_3
|
||||
# 11| r0_9(glval<Int32>) = FieldAddress[num] : r0_8
|
||||
# 11| mu0_10(Int32) = Store : &:r0_9, r0_7
|
||||
# 9| v0_11(Void) = ReturnVoid :
|
||||
# 9| v0_12(Void) = UnmodeledUse : mu*
|
||||
# 9| v0_13(Void) = ExitFunction :
|
||||
|
||||
# 17| System.Void DerivedClass..ctor()
|
||||
# 17| Block 0
|
||||
# 17| v0_0(Void) = EnterFunction :
|
||||
# 17| mu0_1(null) = AliasedDefinition :
|
||||
# 17| mu0_2(null) = UnmodeledDefinition :
|
||||
# 17| r0_3(glval<DerivedClass>) = InitializeThis :
|
||||
# 17| r0_4(glval<BaseClass>) = Convert[DerivedClass : BaseClass] : r0_3
|
||||
# 17| r0_5(glval<BaseClass>) = FunctionAddress[BaseClass] :
|
||||
# 17| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 17| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 18| v0_8(Void) = NoOp :
|
||||
# 17| v0_9(Void) = ReturnVoid :
|
||||
# 17| v0_10(Void) = UnmodeledUse : mu*
|
||||
# 17| v0_11(Void) = ExitFunction :
|
||||
|
||||
# 21| System.Void DerivedClass..ctor(System.Int32)
|
||||
# 21| Block 0
|
||||
# 21| v0_0(Void) = EnterFunction :
|
||||
# 21| mu0_1(null) = AliasedDefinition :
|
||||
# 21| mu0_2(null) = UnmodeledDefinition :
|
||||
# 21| r0_3(glval<DerivedClass>) = InitializeThis :
|
||||
# 21| r0_4(glval<Int32>) = VariableAddress[i] :
|
||||
# 21| mu0_5(Int32) = InitializeParameter[i] : &:r0_4
|
||||
# 21| r0_6(glval<BaseClass>) = Convert[DerivedClass : BaseClass] : r0_3
|
||||
# 21| r0_7(glval<BaseClass>) = FunctionAddress[BaseClass] :
|
||||
# 21| r0_8(glval<Int32>) = VariableAddress[i] :
|
||||
# 21| r0_9(Int32) = Load : &:r0_8, ~mu0_2
|
||||
# 21| v0_10(Void) = Call : func:r0_7, this:r0_6, 0:r0_9
|
||||
# 21| mu0_11(null) = ^CallSideEffect : ~mu0_2
|
||||
# 22| v0_12(Void) = NoOp :
|
||||
# 21| v0_13(Void) = ReturnVoid :
|
||||
# 21| v0_14(Void) = UnmodeledUse : mu*
|
||||
# 21| v0_15(Void) = ExitFunction :
|
||||
|
||||
# 25| System.Void DerivedClass..ctor(System.Int32,System.Int32)
|
||||
# 25| Block 0
|
||||
# 25| v0_0(Void) = EnterFunction :
|
||||
# 25| mu0_1(null) = AliasedDefinition :
|
||||
# 25| mu0_2(null) = UnmodeledDefinition :
|
||||
# 25| r0_3(glval<DerivedClass>) = InitializeThis :
|
||||
# 25| r0_4(glval<Int32>) = VariableAddress[i] :
|
||||
# 25| mu0_5(Int32) = InitializeParameter[i] : &:r0_4
|
||||
# 25| r0_6(glval<Int32>) = VariableAddress[j] :
|
||||
# 25| mu0_7(Int32) = InitializeParameter[j] : &:r0_6
|
||||
# 25| r0_8(glval<DerivedClass>) = FunctionAddress[DerivedClass] :
|
||||
# 25| r0_9(glval<Int32>) = VariableAddress[i] :
|
||||
# 25| r0_10(Int32) = Load : &:r0_9, ~mu0_2
|
||||
# 25| v0_11(Void) = Call : func:r0_8, this:r0_3, 0:r0_10
|
||||
# 25| mu0_12(null) = ^CallSideEffect : ~mu0_2
|
||||
# 26| v0_13(Void) = NoOp :
|
||||
# 25| v0_14(Void) = ReturnVoid :
|
||||
# 25| v0_15(Void) = UnmodeledUse : mu*
|
||||
# 25| v0_16(Void) = ExitFunction :
|
||||
|
||||
# 29| System.Void DerivedClass.Main()
|
||||
# 29| Block 0
|
||||
# 29| v0_0(Void) = EnterFunction :
|
||||
# 29| mu0_1(null) = AliasedDefinition :
|
||||
# 29| mu0_2(null) = UnmodeledDefinition :
|
||||
# 31| r0_3(glval<DerivedClass>) = VariableAddress[obj1] :
|
||||
# 31| r0_4(DerivedClass) = NewObj :
|
||||
# 31| r0_5(glval<DerivedClass>) = FunctionAddress[DerivedClass] :
|
||||
# 31| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 31| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 31| mu0_8(DerivedClass) = Store : &:r0_3, r0_4
|
||||
# 32| r0_9(glval<DerivedClass>) = VariableAddress[obj2] :
|
||||
# 32| r0_10(DerivedClass) = NewObj :
|
||||
# 32| r0_11(glval<DerivedClass>) = FunctionAddress[DerivedClass] :
|
||||
# 32| r0_12(Int32) = Constant[1] :
|
||||
# 32| v0_13(Void) = Call : func:r0_11, this:r0_10, 0:r0_12
|
||||
# 32| mu0_14(null) = ^CallSideEffect : ~mu0_2
|
||||
# 32| mu0_15(DerivedClass) = Store : &:r0_9, r0_10
|
||||
# 33| r0_16(glval<DerivedClass>) = VariableAddress[obj3] :
|
||||
# 33| r0_17(DerivedClass) = NewObj :
|
||||
# 33| r0_18(glval<DerivedClass>) = FunctionAddress[DerivedClass] :
|
||||
# 33| r0_19(Int32) = Constant[1] :
|
||||
# 33| r0_20(Int32) = Constant[2] :
|
||||
# 33| v0_21(Void) = Call : func:r0_18, this:r0_17, 0:r0_19, 1:r0_20
|
||||
# 33| mu0_22(null) = ^CallSideEffect : ~mu0_2
|
||||
# 33| mu0_23(DerivedClass) = Store : &:r0_16, r0_17
|
||||
# 29| v0_24(Void) = ReturnVoid :
|
||||
# 29| v0_25(Void) = UnmodeledUse : mu*
|
||||
# 29| v0_26(Void) = ExitFunction :
|
||||
|
||||
crement.cs:
|
||||
# 3| System.Void CrementOpsTest.Main()
|
||||
# 3| Block 0
|
||||
# 3| v0_0(Void) = EnterFunction :
|
||||
# 3| mu0_1(null) = AliasedDefinition :
|
||||
# 3| mu0_2(null) = UnmodeledDefinition :
|
||||
# 5| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 5| r0_4(Int32) = Constant[10] :
|
||||
# 5| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
# 6| r0_6(glval<Int32>) = VariableAddress[a] :
|
||||
# 6| r0_7(glval<Int32>) = VariableAddress[x] :
|
||||
# 6| r0_8(Int32) = Load : &:r0_7, ~mu0_2
|
||||
# 6| r0_9(Int32) = Constant[1] :
|
||||
# 6| r0_10(Int32) = Add : r0_8, r0_9
|
||||
# 6| mu0_11(Int32) = Store : &:r0_7, r0_10
|
||||
# 6| mu0_12(Int32) = Store : &:r0_6, r0_8
|
||||
# 7| r0_13(glval<Int32>) = VariableAddress[b] :
|
||||
# 7| r0_14(glval<Int32>) = VariableAddress[x] :
|
||||
# 7| r0_15(Int32) = Load : &:r0_14, ~mu0_2
|
||||
# 7| r0_16(Int32) = Constant[1] :
|
||||
# 7| r0_17(Int32) = Sub : r0_15, r0_16
|
||||
# 7| mu0_18(Int32) = Store : &:r0_14, r0_17
|
||||
# 7| mu0_19(Int32) = Store : &:r0_13, r0_17
|
||||
# 8| r0_20(glval<Int32>) = VariableAddress[c] :
|
||||
# 8| r0_21(glval<Int32>) = VariableAddress[x] :
|
||||
# 8| r0_22(Int32) = Load : &:r0_21, ~mu0_2
|
||||
# 8| r0_23(Int32) = Constant[1] :
|
||||
# 8| r0_24(Int32) = Add : r0_22, r0_23
|
||||
# 8| mu0_25(Int32) = Store : &:r0_21, r0_24
|
||||
# 8| mu0_26(Int32) = Store : &:r0_20, r0_24
|
||||
# 9| r0_27(glval<Int32>) = VariableAddress[x] :
|
||||
# 9| r0_28(Int32) = Load : &:r0_27, ~mu0_2
|
||||
# 9| r0_29(Int32) = Constant[1] :
|
||||
# 9| r0_30(Int32) = Sub : r0_28, r0_29
|
||||
# 9| mu0_31(Int32) = Store : &:r0_27, r0_30
|
||||
# 9| r0_32(glval<Int32>) = VariableAddress[x] :
|
||||
# 9| mu0_33(Int32) = Store : &:r0_32, r0_28
|
||||
# 3| v0_34(Void) = ReturnVoid :
|
||||
# 3| v0_35(Void) = UnmodeledUse : mu*
|
||||
# 3| v0_36(Void) = ExitFunction :
|
||||
|
||||
func_with_param_call.cs:
|
||||
# 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32)
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 5| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 5| mu0_4(Int32) = InitializeParameter[x] : &:r0_3
|
||||
# 5| r0_5(glval<Int32>) = VariableAddress[y] :
|
||||
# 5| mu0_6(Int32) = InitializeParameter[y] : &:r0_5
|
||||
# 7| r0_7(glval<Int32>) = VariableAddress[#return] :
|
||||
# 7| r0_8(glval<Int32>) = VariableAddress[x] :
|
||||
# 7| r0_9(Int32) = Load : &:r0_8, ~mu0_2
|
||||
# 7| r0_10(glval<Int32>) = VariableAddress[y] :
|
||||
# 7| r0_11(Int32) = Load : &:r0_10, ~mu0_2
|
||||
# 7| r0_12(Int32) = Add : r0_9, r0_11
|
||||
# 7| mu0_13(Int32) = Store : &:r0_7, r0_12
|
||||
# 5| r0_14(glval<Int32>) = VariableAddress[#return] :
|
||||
# 5| v0_15(Void) = ReturnValue : &:r0_14, ~mu0_2
|
||||
# 5| v0_16(Void) = UnmodeledUse : mu*
|
||||
# 5| v0_17(Void) = ExitFunction :
|
||||
|
||||
# 10| System.Int32 test_call_with_param.g()
|
||||
# 10| Block 0
|
||||
# 10| v0_0(Void) = EnterFunction :
|
||||
# 10| mu0_1(null) = AliasedDefinition :
|
||||
# 10| mu0_2(null) = UnmodeledDefinition :
|
||||
# 12| r0_3(glval<Int32>) = VariableAddress[#return] :
|
||||
# 12| r0_4(glval<Int32>) = FunctionAddress[f] :
|
||||
# 12| r0_5(Int32) = Constant[2] :
|
||||
# 12| r0_6(Int32) = Constant[3] :
|
||||
# 12| r0_7(Int32) = Call : func:r0_4, 0:r0_5, 1:r0_6
|
||||
# 12| mu0_8(null) = ^CallSideEffect : ~mu0_2
|
||||
# 12| mu0_9(Int32) = Store : &:r0_3, r0_7
|
||||
# 10| r0_10(glval<Int32>) = VariableAddress[#return] :
|
||||
# 10| v0_11(Void) = ReturnValue : &:r0_10, ~mu0_2
|
||||
# 10| v0_12(Void) = UnmodeledUse : mu*
|
||||
# 10| v0_13(Void) = ExitFunction :
|
||||
|
||||
inheritance_polymorphism.cs:
|
||||
# 3| System.Int32 A.function()
|
||||
# 3| Block 0
|
||||
# 3| v0_0(Void) = EnterFunction :
|
||||
# 3| mu0_1(null) = AliasedDefinition :
|
||||
# 3| mu0_2(null) = UnmodeledDefinition :
|
||||
# 3| r0_3(glval<A>) = InitializeThis :
|
||||
# 5| r0_4(glval<Int32>) = VariableAddress[#return] :
|
||||
# 5| r0_5(Int32) = Constant[0] :
|
||||
# 5| mu0_6(Int32) = Store : &:r0_4, r0_5
|
||||
# 3| r0_7(glval<Int32>) = VariableAddress[#return] :
|
||||
# 3| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2
|
||||
# 3| v0_9(Void) = UnmodeledUse : mu*
|
||||
# 3| v0_10(Void) = ExitFunction :
|
||||
|
||||
# 15| System.Int32 C.function()
|
||||
# 15| Block 0
|
||||
# 15| v0_0(Void) = EnterFunction :
|
||||
# 15| mu0_1(null) = AliasedDefinition :
|
||||
# 15| mu0_2(null) = UnmodeledDefinition :
|
||||
# 15| r0_3(glval<C>) = InitializeThis :
|
||||
# 17| r0_4(glval<Int32>) = VariableAddress[#return] :
|
||||
# 17| r0_5(Int32) = Constant[1] :
|
||||
# 17| mu0_6(Int32) = Store : &:r0_4, r0_5
|
||||
# 15| r0_7(glval<Int32>) = VariableAddress[#return] :
|
||||
# 15| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2
|
||||
# 15| v0_9(Void) = UnmodeledUse : mu*
|
||||
# 15| v0_10(Void) = ExitFunction :
|
||||
|
||||
# 23| System.Void Program.Main()
|
||||
# 23| Block 0
|
||||
# 23| v0_0(Void) = EnterFunction :
|
||||
# 23| mu0_1(null) = AliasedDefinition :
|
||||
# 23| mu0_2(null) = UnmodeledDefinition :
|
||||
# 25| r0_3(glval<B>) = VariableAddress[objB] :
|
||||
# 25| r0_4(B) = NewObj :
|
||||
# 25| r0_5(glval<B>) = FunctionAddress[B] :
|
||||
# 25| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 25| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 25| mu0_8(B) = Store : &:r0_3, r0_4
|
||||
# 26| r0_9(glval<B>) = VariableAddress[objB] :
|
||||
# 26| r0_10(B) = Load : &:r0_9, ~mu0_2
|
||||
# 26| r0_11(glval<Int32>) = FunctionAddress[function] :
|
||||
# 26| r0_12(Int32) = Call : func:r0_11, this:r0_10
|
||||
# 26| mu0_13(null) = ^CallSideEffect : ~mu0_2
|
||||
# 29| r0_14(glval<A>) = VariableAddress[objA] :
|
||||
# 29| mu0_15(A) = Uninitialized[objA] : &:r0_14
|
||||
# 30| r0_16(glval<B>) = VariableAddress[objB] :
|
||||
# 30| r0_17(A) = Convert : r0_16
|
||||
# 30| r0_18(glval<A>) = VariableAddress[objA] :
|
||||
# 30| mu0_19(A) = Store : &:r0_18, r0_17
|
||||
# 31| r0_20(glval<A>) = VariableAddress[objA] :
|
||||
# 31| r0_21(A) = Load : &:r0_20, ~mu0_2
|
||||
# 31| r0_22(glval<Int32>) = FunctionAddress[function] :
|
||||
# 31| r0_23(Int32) = Call : func:r0_22, this:r0_21
|
||||
# 31| mu0_24(null) = ^CallSideEffect : ~mu0_2
|
||||
# 33| r0_25(glval<A>) = VariableAddress[objC] :
|
||||
# 33| r0_26(C) = NewObj :
|
||||
# 33| r0_27(glval<C>) = FunctionAddress[C] :
|
||||
# 33| v0_28(Void) = Call : func:r0_27, this:r0_26
|
||||
# 33| mu0_29(null) = ^CallSideEffect : ~mu0_2
|
||||
# 33| r0_30(A) = Convert : r0_26
|
||||
# 33| mu0_31(C) = Store : &:r0_25, r0_30
|
||||
# 34| r0_32(glval<A>) = VariableAddress[objC] :
|
||||
# 34| r0_33(A) = Load : &:r0_32, ~mu0_2
|
||||
# 34| r0_34(glval<Int32>) = FunctionAddress[function] :
|
||||
# 34| r0_35(Int32) = Call : func:r0_34, this:r0_33
|
||||
# 34| mu0_36(null) = ^CallSideEffect : ~mu0_2
|
||||
# 23| v0_37(Void) = ReturnVoid :
|
||||
# 23| v0_38(Void) = UnmodeledUse : mu*
|
||||
# 23| v0_39(Void) = ExitFunction :
|
||||
|
||||
isexpr.cs:
|
||||
# 8| System.Void IsExpr.Main()
|
||||
# 8| Block 0
|
||||
# 8| v0_0(Void) = EnterFunction :
|
||||
# 8| mu0_1(null) = AliasedDefinition :
|
||||
# 8| mu0_2(null) = UnmodeledDefinition :
|
||||
# 10| r0_3(glval<Is_A>) = VariableAddress[obj] :
|
||||
# 10| r0_4(null) = Constant[null] :
|
||||
# 10| mu0_5(Is_A) = Store : &:r0_3, r0_4
|
||||
# 12| r0_6(glval<Object>) = VariableAddress[o] :
|
||||
# 12| r0_7(glval<Is_A>) = VariableAddress[obj] :
|
||||
# 12| mu0_8(Object) = Store : &:r0_6, r0_7
|
||||
# 13| r0_9(glval<Object>) = VariableAddress[o] :
|
||||
# 13| r0_10(Object) = Load : &:r0_9, ~mu0_2
|
||||
# 13| r0_11(Is_A) = CheckedConvertOrNull : r0_10
|
||||
# 13| r0_12(Is_A) = Constant[0] :
|
||||
# 13| r0_13(Boolean) = CompareNE : r0_11, r0_12
|
||||
# 13| r0_14(Boolean) = ConditionalBranch : r0_13
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 8| Block 1
|
||||
# 8| v1_0(Void) = ReturnVoid :
|
||||
# 8| v1_1(Void) = UnmodeledUse : mu*
|
||||
# 8| v1_2(Void) = ExitFunction :
|
||||
|
||||
# 13| Block 2
|
||||
# 13| v2_0(Void) = ConditionalBranch : r0_13
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 4
|
||||
|
||||
# 13| Block 3
|
||||
# 13| r3_0(glval<Is_A>) = VariableAddress[tmp] :
|
||||
# 13| mu3_1(Is_A) = Store : &:r3_0, r0_11
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 15| Block 4
|
||||
# 15| r4_0(glval<Int32>) = VariableAddress[res] :
|
||||
# 15| r4_1(glval<Is_A>) = VariableAddress[tmp] :
|
||||
# 15| r4_2(Is_A) = Load : &:r4_1, ~mu0_2
|
||||
# 15| r4_3(glval<Int32>) = FieldAddress[x] : r4_2
|
||||
# 15| r4_4(Int32) = Load : &:r4_3, ~mu0_2
|
||||
# 15| mu4_5(Int32) = Store : &:r4_0, r4_4
|
||||
#-----| Goto -> Block 5
|
||||
|
||||
# 17| Block 5
|
||||
# 17| r5_0(glval<Object>) = VariableAddress[o] :
|
||||
# 17| r5_1(Object) = Load : &:r5_0, ~mu0_2
|
||||
# 17| r5_2(Is_A) = CheckedConvertOrNull : r5_1
|
||||
# 17| r5_3(Is_A) = Constant[0] :
|
||||
# 17| r5_4(Boolean) = CompareNE : r5_2, r5_3
|
||||
# 17| v5_5(Void) = ConditionalBranch : r5_4
|
||||
#-----| False -> Block 1
|
||||
#-----| True -> Block 6
|
||||
|
||||
# 18| Block 6
|
||||
# 18| v6_0(Void) = NoOp :
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
obj_creation.cs:
|
||||
# 7| System.Void ObjCreation.MyClass..ctor()
|
||||
# 7| Block 0
|
||||
# 7| v0_0(Void) = EnterFunction :
|
||||
# 7| mu0_1(null) = AliasedDefinition :
|
||||
# 7| mu0_2(null) = UnmodeledDefinition :
|
||||
# 7| r0_3(glval<MyClass>) = InitializeThis :
|
||||
# 8| v0_4(Void) = NoOp :
|
||||
# 7| v0_5(Void) = ReturnVoid :
|
||||
# 7| v0_6(Void) = UnmodeledUse : mu*
|
||||
# 7| v0_7(Void) = ExitFunction :
|
||||
|
||||
# 11| System.Void ObjCreation.MyClass..ctor(System.Int32)
|
||||
# 11| Block 0
|
||||
# 11| v0_0(Void) = EnterFunction :
|
||||
# 11| mu0_1(null) = AliasedDefinition :
|
||||
# 11| mu0_2(null) = UnmodeledDefinition :
|
||||
# 11| r0_3(glval<MyClass>) = InitializeThis :
|
||||
# 11| r0_4(glval<Int32>) = VariableAddress[_x] :
|
||||
# 11| mu0_5(Int32) = InitializeParameter[_x] : &:r0_4
|
||||
# 13| r0_6(glval<Int32>) = VariableAddress[_x] :
|
||||
# 13| r0_7(Int32) = Load : &:r0_6, ~mu0_2
|
||||
# 13| r0_8(MyClass) = CopyValue : r0_3
|
||||
# 13| r0_9(glval<Int32>) = FieldAddress[x] : r0_8
|
||||
# 13| mu0_10(Int32) = Store : &:r0_9, r0_7
|
||||
# 11| v0_11(Void) = ReturnVoid :
|
||||
# 11| v0_12(Void) = UnmodeledUse : mu*
|
||||
# 11| v0_13(Void) = ExitFunction :
|
||||
|
||||
# 17| System.Void ObjCreation.Main()
|
||||
# 17| Block 0
|
||||
# 17| v0_0(Void) = EnterFunction :
|
||||
# 17| mu0_1(null) = AliasedDefinition :
|
||||
# 17| mu0_2(null) = UnmodeledDefinition :
|
||||
# 19| r0_3(glval<MyClass>) = VariableAddress[obj] :
|
||||
# 19| r0_4(MyClass) = NewObj :
|
||||
# 19| r0_5(glval<MyClass>) = FunctionAddress[MyClass] :
|
||||
# 19| r0_6(Int32) = Constant[100] :
|
||||
# 19| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6
|
||||
# 19| mu0_8(null) = ^CallSideEffect : ~mu0_2
|
||||
# 19| mu0_9(MyClass) = Store : &:r0_3, r0_4
|
||||
# 20| r0_10(glval<MyClass>) = VariableAddress[obj_initlist] :
|
||||
# 20| r0_11(MyClass) = NewObj :
|
||||
# 20| r0_12(glval<MyClass>) = FunctionAddress[MyClass] :
|
||||
# 20| v0_13(Void) = Call : func:r0_12, this:r0_11
|
||||
# 20| mu0_14(null) = ^CallSideEffect : ~mu0_2
|
||||
# 20| r0_15(Int32) = Constant[101] :
|
||||
# 20| r0_16(glval<Int32>) = FieldAddress[x] : r0_11
|
||||
# 20| mu0_17(Int32) = Store : &:r0_16, r0_15
|
||||
# 20| mu0_18(MyClass) = Store : &:r0_10, r0_11
|
||||
# 21| r0_19(glval<Int32>) = VariableAddress[a] :
|
||||
# 21| r0_20(glval<MyClass>) = VariableAddress[obj] :
|
||||
# 21| r0_21(MyClass) = Load : &:r0_20, ~mu0_2
|
||||
# 21| r0_22(glval<Int32>) = FieldAddress[x] : r0_21
|
||||
# 21| r0_23(Int32) = Load : &:r0_22, ~mu0_2
|
||||
# 21| mu0_24(Int32) = Store : &:r0_19, r0_23
|
||||
# 17| v0_25(Void) = ReturnVoid :
|
||||
# 17| v0_26(Void) = UnmodeledUse : mu*
|
||||
# 17| v0_27(Void) = ExitFunction :
|
||||
|
||||
prop.cs:
|
||||
# 7| System.Int32 PropClass.get_Prop()
|
||||
# 7| Block 0
|
||||
# 7| v0_0(Void) = EnterFunction :
|
||||
# 7| mu0_1(null) = AliasedDefinition :
|
||||
# 7| mu0_2(null) = UnmodeledDefinition :
|
||||
# 7| r0_3(glval<PropClass>) = InitializeThis :
|
||||
# 9| r0_4(glval<Int32>) = VariableAddress[#return] :
|
||||
# 9| r0_5(PropClass) = CopyValue : r0_3
|
||||
# 9| r0_6(glval<Int32>) = FunctionAddress[func] :
|
||||
# 9| r0_7(Int32) = Call : func:r0_6, this:r0_5
|
||||
# 9| mu0_8(null) = ^CallSideEffect : ~mu0_2
|
||||
# 9| mu0_9(Int32) = Store : &:r0_4, r0_7
|
||||
# 7| r0_10(glval<Int32>) = VariableAddress[#return] :
|
||||
# 7| v0_11(Void) = ReturnValue : &:r0_10, ~mu0_2
|
||||
# 7| v0_12(Void) = UnmodeledUse : mu*
|
||||
# 7| v0_13(Void) = ExitFunction :
|
||||
|
||||
# 12| System.Void PropClass.set_Prop(System.Int32)
|
||||
# 12| Block 0
|
||||
# 12| v0_0(Void) = EnterFunction :
|
||||
# 12| mu0_1(null) = AliasedDefinition :
|
||||
# 12| mu0_2(null) = UnmodeledDefinition :
|
||||
# 12| r0_3(glval<PropClass>) = InitializeThis :
|
||||
# 12| r0_4(glval<Int32>) = VariableAddress[value] :
|
||||
# 12| mu0_5(Int32) = InitializeParameter[value] : &:r0_4
|
||||
# 14| r0_6(glval<Int32>) = VariableAddress[value] :
|
||||
# 14| r0_7(Int32) = Load : &:r0_6, ~mu0_2
|
||||
# 14| r0_8(PropClass) = CopyValue : r0_3
|
||||
# 14| r0_9(glval<Int32>) = FieldAddress[prop] : r0_8
|
||||
# 14| mu0_10(Int32) = Store : &:r0_9, r0_7
|
||||
# 12| v0_11(Void) = ReturnVoid :
|
||||
# 12| v0_12(Void) = UnmodeledUse : mu*
|
||||
# 12| v0_13(Void) = ExitFunction :
|
||||
|
||||
# 18| System.Int32 PropClass.func()
|
||||
# 18| Block 0
|
||||
# 18| v0_0(Void) = EnterFunction :
|
||||
# 18| mu0_1(null) = AliasedDefinition :
|
||||
# 18| mu0_2(null) = UnmodeledDefinition :
|
||||
# 18| r0_3(glval<PropClass>) = InitializeThis :
|
||||
# 20| r0_4(glval<Int32>) = VariableAddress[#return] :
|
||||
# 20| r0_5(Int32) = Constant[0] :
|
||||
# 20| mu0_6(Int32) = Store : &:r0_4, r0_5
|
||||
# 18| r0_7(glval<Int32>) = VariableAddress[#return] :
|
||||
# 18| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2
|
||||
# 18| v0_9(Void) = UnmodeledUse : mu*
|
||||
# 18| v0_10(Void) = ExitFunction :
|
||||
|
||||
# 26| System.Void Prog.Main()
|
||||
# 26| Block 0
|
||||
# 26| v0_0(Void) = EnterFunction :
|
||||
# 26| mu0_1(null) = AliasedDefinition :
|
||||
# 26| mu0_2(null) = UnmodeledDefinition :
|
||||
# 28| r0_3(glval<PropClass>) = VariableAddress[obj] :
|
||||
# 28| r0_4(PropClass) = NewObj :
|
||||
# 28| r0_5(glval<PropClass>) = FunctionAddress[PropClass] :
|
||||
# 28| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 28| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 28| mu0_8(PropClass) = Store : &:r0_3, r0_4
|
||||
# 29| r0_9(glval<PropClass>) = VariableAddress[obj] :
|
||||
# 29| r0_10(PropClass) = Load : &:r0_9, ~mu0_2
|
||||
# 29| r0_11(glval<Int32>) = FunctionAddress[set_Prop] :
|
||||
# 29| r0_12(Int32) = Constant[5] :
|
||||
# 29| r0_13(Int32) = Call : func:r0_11, this:r0_10, 0:r0_12
|
||||
# 29| mu0_14(null) = ^CallSideEffect : ~mu0_2
|
||||
# 30| r0_15(glval<Int32>) = VariableAddress[x] :
|
||||
# 30| r0_16(glval<PropClass>) = VariableAddress[obj] :
|
||||
# 30| r0_17(PropClass) = Load : &:r0_16, ~mu0_2
|
||||
# 30| r0_18(glval<Int32>) = FunctionAddress[get_Prop] :
|
||||
# 30| r0_19(Int32) = Call : func:r0_18, this:r0_17
|
||||
# 30| mu0_20(null) = ^CallSideEffect : ~mu0_2
|
||||
# 30| r0_21(Int32) = Load : &:r0_19, ~mu0_2
|
||||
# 30| mu0_22(Int32) = Store : &:r0_15, r0_21
|
||||
# 26| v0_23(Void) = ReturnVoid :
|
||||
# 26| v0_24(Void) = UnmodeledUse : mu*
|
||||
# 26| v0_25(Void) = ExitFunction :
|
||||
|
||||
simple_call.cs:
|
||||
# 5| System.Int32 test_simple_call.f()
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 7| r0_3(glval<Int32>) = VariableAddress[#return] :
|
||||
# 7| r0_4(Int32) = Constant[0] :
|
||||
# 7| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
# 5| r0_6(glval<Int32>) = VariableAddress[#return] :
|
||||
# 5| v0_7(Void) = ReturnValue : &:r0_6, ~mu0_2
|
||||
# 5| v0_8(Void) = UnmodeledUse : mu*
|
||||
# 5| v0_9(Void) = ExitFunction :
|
||||
|
||||
# 10| System.Int32 test_simple_call.g()
|
||||
# 10| Block 0
|
||||
# 10| v0_0(Void) = EnterFunction :
|
||||
# 10| mu0_1(null) = AliasedDefinition :
|
||||
# 10| mu0_2(null) = UnmodeledDefinition :
|
||||
# 10| r0_3(glval<test_simple_call>) = InitializeThis :
|
||||
# 12| r0_4(glval<Int32>) = VariableAddress[#return] :
|
||||
# 12| r0_5(glval<Int32>) = FunctionAddress[f] :
|
||||
# 12| r0_6(Int32) = Call : func:r0_5
|
||||
# 12| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 12| mu0_8(Int32) = Store : &:r0_4, r0_6
|
||||
# 10| r0_9(glval<Int32>) = VariableAddress[#return] :
|
||||
# 10| v0_10(Void) = ReturnValue : &:r0_9, ~mu0_2
|
||||
# 10| v0_11(Void) = UnmodeledUse : mu*
|
||||
# 10| v0_12(Void) = ExitFunction :
|
||||
|
||||
simple_function.cs:
|
||||
# 5| System.Int32 test_simple_function.f()
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 7| r0_3(glval<Int32>) = VariableAddress[#return] :
|
||||
# 7| r0_4(Int32) = Constant[0] :
|
||||
# 7| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
# 5| r0_6(glval<Int32>) = VariableAddress[#return] :
|
||||
# 5| v0_7(Void) = ReturnValue : &:r0_6, ~mu0_2
|
||||
# 5| v0_8(Void) = UnmodeledUse : mu*
|
||||
# 5| v0_9(Void) = ExitFunction :
|
||||
|
||||
stmts.cs:
|
||||
# 5| System.Int32 test_stmts.ifStmt(System.Int32)
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 5| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 5| mu0_4(Int32) = InitializeParameter[x] : &:r0_3
|
||||
# 7| r0_5(glval<Int32>) = VariableAddress[x] :
|
||||
# 7| r0_6(Int32) = Load : &:r0_5, ~mu0_2
|
||||
# 7| r0_7(Int32) = Constant[5] :
|
||||
# 7| r0_8(Boolean) = CompareEQ : r0_6, r0_7
|
||||
# 7| v0_9(Void) = ConditionalBranch : r0_8
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 5| Block 1
|
||||
# 5| r1_0(glval<Int32>) = VariableAddress[#return] :
|
||||
# 5| v1_1(Void) = ReturnValue : &:r1_0, ~mu0_2
|
||||
# 5| v1_2(Void) = UnmodeledUse : mu*
|
||||
# 5| v1_3(Void) = ExitFunction :
|
||||
|
||||
# 10| Block 2
|
||||
# 10| r2_0(glval<Int32>) = VariableAddress[#return] :
|
||||
# 10| r2_1(Int32) = Constant[1] :
|
||||
# 10| mu2_2(Int32) = Store : &:r2_0, r2_1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 8| Block 3
|
||||
# 8| r3_0(glval<Int32>) = VariableAddress[#return] :
|
||||
# 8| r3_1(Int32) = Constant[0] :
|
||||
# 8| mu3_2(Int32) = Store : &:r3_0, r3_1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 13| System.Void test_stmts.whileStmt(System.Int32)
|
||||
# 13| Block 0
|
||||
# 13| v0_0(Void) = EnterFunction :
|
||||
# 13| mu0_1(null) = AliasedDefinition :
|
||||
# 13| mu0_2(null) = UnmodeledDefinition :
|
||||
# 13| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 13| mu0_4(Int32) = InitializeParameter[x] : &:r0_3
|
||||
# 15| r0_5(glval<Int32>) = VariableAddress[i] :
|
||||
# 15| r0_6(Int32) = Constant[0] :
|
||||
# 15| mu0_7(Int32) = Store : &:r0_5, r0_6
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 13| Block 1
|
||||
# 13| v1_0(Void) = ReturnVoid :
|
||||
# 13| v1_1(Void) = UnmodeledUse : mu*
|
||||
# 13| v1_2(Void) = ExitFunction :
|
||||
|
||||
# 16| Block 2
|
||||
# 16| r2_0(glval<Int32>) = VariableAddress[i] :
|
||||
# 16| r2_1(Int32) = Load : &:r2_0, ~mu0_2
|
||||
# 16| r2_2(Int32) = Constant[10] :
|
||||
# 16| r2_3(Boolean) = CompareLT : r2_1, r2_2
|
||||
# 16| v2_4(Void) = ConditionalBranch : r2_3
|
||||
#-----| False -> Block 1
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 18| Block 3
|
||||
# 18| r3_0(glval<Int32>) = VariableAddress[x] :
|
||||
# 18| r3_1(Int32) = Load : &:r3_0, ~mu0_2
|
||||
# 18| r3_2(Int32) = Constant[1] :
|
||||
# 18| r3_3(Int32) = Add : r3_1, r3_2
|
||||
# 18| r3_4(glval<Int32>) = VariableAddress[x] :
|
||||
# 18| mu3_5(Int32) = Store : &:r3_4, r3_3
|
||||
#-----| Goto (back edge) -> Block 2
|
||||
|
||||
# 22| System.Int32 test_stmts.switchStmt()
|
||||
# 22| Block 0
|
||||
# 22| v0_0(Void) = EnterFunction :
|
||||
# 22| mu0_1(null) = AliasedDefinition :
|
||||
# 22| mu0_2(null) = UnmodeledDefinition :
|
||||
# 24| r0_3(glval<Object>) = VariableAddress[caseSwitch] :
|
||||
# 24| r0_4(Object) = NewObj :
|
||||
# 24| r0_5(glval<Object>) = FunctionAddress[Object] :
|
||||
# 24| v0_6(Void) = Call : func:r0_5, this:r0_4
|
||||
# 24| mu0_7(null) = ^CallSideEffect : ~mu0_2
|
||||
# 24| mu0_8(Object) = Store : &:r0_3, r0_4
|
||||
# 25| r0_9(glval<Int32>) = VariableAddress[select] :
|
||||
# 25| r0_10(Int32) = Constant[0] :
|
||||
# 25| mu0_11(Int32) = Store : &:r0_9, r0_10
|
||||
# 27| r0_12(glval<Object>) = VariableAddress[caseSwitch] :
|
||||
# 27| r0_13(Object) = Load : &:r0_12, ~mu0_2
|
||||
# 27| v0_14(Void) = Switch : r0_13
|
||||
#-----| Case[-1] -> Block 2
|
||||
#-----| Case[0] -> Block 3
|
||||
#-----| Case[123] -> Block 4
|
||||
#-----| Case[true] -> Block 5
|
||||
#-----| Default -> Block 6
|
||||
|
||||
# 22| Block 1
|
||||
# 22| r1_0(glval<Int32>) = VariableAddress[#return] :
|
||||
# 22| v1_1(Void) = ReturnValue : &:r1_0, ~mu0_2
|
||||
# 22| v1_2(Void) = UnmodeledUse : mu*
|
||||
# 22| v1_3(Void) = ExitFunction :
|
||||
|
||||
# 29| Block 2
|
||||
# 29| v2_0(Void) = NoOp :
|
||||
# 30| v2_1(Void) = NoOp :
|
||||
#-----| Goto (back edge) -> Block 5
|
||||
|
||||
# 31| Block 3
|
||||
# 31| v3_0(Void) = NoOp :
|
||||
# 32| v3_1(Void) = NoOp :
|
||||
#-----| Goto (back edge) -> Block 4
|
||||
|
||||
# 33| Block 4
|
||||
# 33| v4_0(Void) = NoOp :
|
||||
# 34| r4_1(Int32) = Constant[100] :
|
||||
# 34| r4_2(glval<Int32>) = VariableAddress[select] :
|
||||
# 34| mu4_3(Int32) = Store : &:r4_2, r4_1
|
||||
# 35| v4_4(Void) = NoOp :
|
||||
# 42| r4_5(glval<Int32>) = VariableAddress[#return] :
|
||||
# 42| r4_6(Int32) = Constant[0] :
|
||||
# 42| mu4_7(Int32) = Store : &:r4_5, r4_6
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 36| Block 5
|
||||
# 36| v5_0(Void) = NoOp :
|
||||
# 37| r5_1(Int32) = Constant[101] :
|
||||
# 37| r5_2(glval<Int32>) = VariableAddress[select] :
|
||||
# 37| mu5_3(Int32) = Store : &:r5_2, r5_1
|
||||
# 38| v5_4(Void) = NoOp :
|
||||
#-----| Goto (back edge) -> Block 6
|
||||
|
||||
# 39| Block 6
|
||||
# 39| v6_0(Void) = NoOp :
|
||||
# 40| r6_1(glval<Int32>) = VariableAddress[#return] :
|
||||
# 40| r6_2(glval<Int32>) = VariableAddress[select] :
|
||||
# 40| r6_3(Int32) = Load : &:r6_2, ~mu0_2
|
||||
# 40| mu6_4(Int32) = Store : &:r6_1, r6_3
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 46| System.Void test_stmts.tryCatchFinally()
|
||||
# 46| Block 0
|
||||
# 46| v0_0(Void) = EnterFunction :
|
||||
# 46| mu0_1(null) = AliasedDefinition :
|
||||
# 46| mu0_2(null) = UnmodeledDefinition :
|
||||
# 48| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 48| r0_4(Int32) = Constant[5] :
|
||||
# 48| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
# 51| r0_6(glval<Int32>) = VariableAddress[x] :
|
||||
# 51| r0_7(Int32) = Load : &:r0_6, ~mu0_2
|
||||
# 51| r0_8(Int32) = Constant[0] :
|
||||
# 51| r0_9(Boolean) = CompareNE : r0_7, r0_8
|
||||
# 51| v0_10(Void) = ConditionalBranch : r0_9
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 46| Block 1
|
||||
# 46| v1_0(Void) = UnmodeledUse : mu*
|
||||
# 46| v1_1(Void) = ExitFunction :
|
||||
|
||||
# 46| Block 2
|
||||
# 46| v2_0(Void) = Unwind :
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 52| Block 3
|
||||
# 52| r3_0(glval<Exception>) = VariableAddress[#throw52:17] :
|
||||
# 52| r3_1(Exception) = NewObj :
|
||||
# 52| r3_2(glval<Exception>) = FunctionAddress[Exception] :
|
||||
# 52| v3_3(Void) = Call : func:r3_2, this:r3_1
|
||||
# 52| mu3_4(null) = ^CallSideEffect : ~mu0_2
|
||||
# 52| mu3_5(Exception) = Store : &:r3_0, r3_1
|
||||
# 52| v3_6(Void) = ThrowValue : &:r3_0, ~mu0_2
|
||||
#-----| Exception -> Block 6
|
||||
|
||||
# 53| Block 4
|
||||
# 53| r4_0(Int32) = Constant[0] :
|
||||
# 53| r4_1(glval<Int32>) = VariableAddress[x] :
|
||||
# 53| mu4_2(Int32) = Store : &:r4_1, r4_0
|
||||
#-----| Goto -> Block 5
|
||||
|
||||
# 64| Block 5
|
||||
# 64| r5_0(Int32) = Constant[2] :
|
||||
# 64| r5_1(glval<Int32>) = VariableAddress[x] :
|
||||
# 64| mu5_2(Int32) = Store : &:r5_1, r5_0
|
||||
# 46| v5_3(Void) = ReturnVoid :
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 55| Block 6
|
||||
# 55| v6_0(Void) = CatchByType[Exception] :
|
||||
#-----| Exception -> Block 8
|
||||
#-----| Goto -> Block 7
|
||||
|
||||
# 55| Block 7
|
||||
# 55| r7_0(glval<Exception>) = VariableAddress[ex] :
|
||||
# 55| mu7_1(Exception) = Uninitialized[ex] : &:r7_0
|
||||
# 57| r7_2(Int32) = Constant[1] :
|
||||
# 57| r7_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 57| mu7_4(Int32) = Store : &:r7_3, r7_2
|
||||
#-----| Goto -> Block 5
|
||||
|
||||
# 59| Block 8
|
||||
# 59| v8_0(Void) = CatchAny :
|
||||
# 60| v8_1(Void) = ReThrow :
|
||||
#-----| Exception -> Block 2
|
||||
|
||||
# 68| System.Void test_stmts.forStmt()
|
||||
# 68| Block 0
|
||||
# 68| v0_0(Void) = EnterFunction :
|
||||
# 68| mu0_1(null) = AliasedDefinition :
|
||||
# 68| mu0_2(null) = UnmodeledDefinition :
|
||||
# 70| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 70| r0_4(Int32) = Constant[0] :
|
||||
# 70| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
# 71| r0_6(glval<Int32>) = VariableAddress[i] :
|
||||
# 71| r0_7(Int32) = Constant[0] :
|
||||
# 71| mu0_8(Int32) = Store : &:r0_6, r0_7
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 68| Block 1
|
||||
# 68| v1_0(Void) = ReturnVoid :
|
||||
# 68| v1_1(Void) = UnmodeledUse : mu*
|
||||
# 68| v1_2(Void) = ExitFunction :
|
||||
|
||||
# 71| Block 2
|
||||
# 71| r2_0(glval<Int32>) = VariableAddress[i] :
|
||||
# 71| r2_1(Int32) = Load : &:r2_0, ~mu0_2
|
||||
# 71| r2_2(Int32) = Constant[10] :
|
||||
# 71| r2_3(Boolean) = CompareLE : r2_1, r2_2
|
||||
# 71| v2_4(Void) = ConditionalBranch : r2_3
|
||||
#-----| False -> Block 1
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 73| Block 3
|
||||
# 73| r3_0(glval<Int32>) = VariableAddress[x] :
|
||||
# 73| r3_1(Int32) = Load : &:r3_0, ~mu0_2
|
||||
# 73| r3_2(Int32) = Constant[1] :
|
||||
# 73| r3_3(Int32) = Sub : r3_1, r3_2
|
||||
# 73| r3_4(glval<Int32>) = VariableAddress[x] :
|
||||
# 73| mu3_5(Int32) = Store : &:r3_4, r3_3
|
||||
# 71| r3_6(glval<Int32>) = VariableAddress[i] :
|
||||
# 71| r3_7(Int32) = Load : &:r3_6, ~mu0_2
|
||||
# 71| r3_8(Int32) = Constant[1] :
|
||||
# 71| r3_9(Int32) = Add : r3_7, r3_8
|
||||
# 71| mu3_10(Int32) = Store : &:r3_6, r3_9
|
||||
#-----| Goto (back edge) -> Block 2
|
||||
|
||||
# 77| System.Void test_stmts.doWhile()
|
||||
# 77| Block 0
|
||||
# 77| v0_0(Void) = EnterFunction :
|
||||
# 77| mu0_1(null) = AliasedDefinition :
|
||||
# 77| mu0_2(null) = UnmodeledDefinition :
|
||||
# 79| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 79| r0_4(Int32) = Constant[0] :
|
||||
# 79| mu0_5(Int32) = Store : &:r0_3, r0_4
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 77| Block 1
|
||||
# 77| v1_0(Void) = ReturnVoid :
|
||||
# 77| v1_1(Void) = UnmodeledUse : mu*
|
||||
# 77| v1_2(Void) = ExitFunction :
|
||||
|
||||
# 82| Block 2
|
||||
# 82| r2_0(glval<Int32>) = VariableAddress[x] :
|
||||
# 82| r2_1(Int32) = Load : &:r2_0, ~mu0_2
|
||||
# 82| r2_2(Int32) = Constant[1] :
|
||||
# 82| r2_3(Int32) = Add : r2_1, r2_2
|
||||
# 82| r2_4(glval<Int32>) = VariableAddress[x] :
|
||||
# 82| mu2_5(Int32) = Store : &:r2_4, r2_3
|
||||
# 84| r2_6(glval<Int32>) = VariableAddress[x] :
|
||||
# 84| r2_7(Int32) = Load : &:r2_6, ~mu0_2
|
||||
# 84| r2_8(Int32) = Constant[10] :
|
||||
# 84| r2_9(Boolean) = CompareLT : r2_7, r2_8
|
||||
# 84| v2_10(Void) = ConditionalBranch : r2_9
|
||||
#-----| False -> Block 1
|
||||
#-----| True (back edge) -> Block 2
|
||||
|
||||
variables.cs:
|
||||
# 5| System.Void test_variables.f()
|
||||
# 5| Block 0
|
||||
# 5| v0_0(Void) = EnterFunction :
|
||||
# 5| mu0_1(null) = AliasedDefinition :
|
||||
# 5| mu0_2(null) = UnmodeledDefinition :
|
||||
# 7| r0_3(glval<Int32>) = VariableAddress[x] :
|
||||
# 7| mu0_4(Int32) = Uninitialized[x] : &:r0_3
|
||||
# 7| r0_5(glval<Int32>) = VariableAddress[y] :
|
||||
# 7| r0_6(Int32) = Constant[5] :
|
||||
# 7| mu0_7(Int32) = Store : &:r0_5, r0_6
|
||||
# 8| r0_8(Int32) = Constant[4] :
|
||||
# 8| r0_9(glval<Int32>) = VariableAddress[x] :
|
||||
# 8| mu0_10(Int32) = Store : &:r0_9, r0_8
|
||||
# 9| r0_11(glval<Int32>) = VariableAddress[y] :
|
||||
# 9| r0_12(Int32) = Load : &:r0_11, ~mu0_2
|
||||
# 9| r0_13(glval<Int32>) = VariableAddress[x] :
|
||||
# 9| mu0_14(Int32) = Store : &:r0_13, r0_12
|
||||
# 10| r0_15(glval<Int32>) = VariableAddress[z] :
|
||||
# 10| r0_16(glval<Int32>) = VariableAddress[y] :
|
||||
# 10| r0_17(Int32) = Load : &:r0_16, ~mu0_2
|
||||
# 10| mu0_18(Int32) = Store : &:r0_15, r0_17
|
||||
# 5| v0_19(Void) = ReturnVoid :
|
||||
# 5| v0_20(Void) = UnmodeledUse : mu*
|
||||
# 5| v0_21(Void) = ExitFunction :
|
||||
1
csharp/ql/test/library-tests/ir/ir/raw_ir.qlref
Normal file
1
csharp/ql/test/library-tests/ir/ir/raw_ir.qlref
Normal file
@@ -0,0 +1 @@
|
||||
semmle/code/csharp/ir/implementation/raw/PrintIR.ql
|
||||
15
csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected
Normal file
15
csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected
Normal file
@@ -0,0 +1,15 @@
|
||||
missingOperand
|
||||
unexpectedOperand
|
||||
duplicateOperand
|
||||
missingPhiOperand
|
||||
missingOperandType
|
||||
instructionWithoutSuccessor
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
1
csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.qlref
Normal file
1
csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.qlref
Normal file
@@ -0,0 +1 @@
|
||||
semmle/code/csharp/ir/implementation/raw/IRSanity.ql
|
||||
14
csharp/ql/test/library-tests/ir/ir/simple_call.cs
Normal file
14
csharp/ql/test/library-tests/ir/ir/simple_call.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
public class test_simple_call
|
||||
{
|
||||
public static int f()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int g()
|
||||
{
|
||||
return f();
|
||||
}
|
||||
}
|
||||
9
csharp/ql/test/library-tests/ir/ir/simple_function.cs
Normal file
9
csharp/ql/test/library-tests/ir/ir/simple_function.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
public class test_simple_function
|
||||
{
|
||||
public static int f()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
87
csharp/ql/test/library-tests/ir/ir/stmts.cs
Normal file
87
csharp/ql/test/library-tests/ir/ir/stmts.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
|
||||
public class test_stmts
|
||||
{
|
||||
public static int ifStmt(int x)
|
||||
{
|
||||
if (x == 5)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static void whileStmt(int x)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < 10)
|
||||
{
|
||||
x = x + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int switchStmt()
|
||||
{
|
||||
object caseSwitch = new object();
|
||||
int select = 0;
|
||||
|
||||
switch (caseSwitch)
|
||||
{
|
||||
case -1:
|
||||
goto case true;
|
||||
case 0:
|
||||
goto case "123";
|
||||
case "123":
|
||||
select = 100;
|
||||
break;
|
||||
case true:
|
||||
select = 101;
|
||||
goto default;
|
||||
default:
|
||||
return select;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public static void tryCatchFinally()
|
||||
{
|
||||
int x = 5;
|
||||
try
|
||||
{
|
||||
if (x != 0)
|
||||
throw (new System.Exception());
|
||||
x = 0;
|
||||
}
|
||||
catch(System.Exception ex)
|
||||
{
|
||||
x = 1;
|
||||
}
|
||||
catch {
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public static void forStmt()
|
||||
{
|
||||
int x = 0;
|
||||
for (int i = 0; i <= 10; i++)
|
||||
{
|
||||
x = x - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void doWhile()
|
||||
{
|
||||
int x = 0;
|
||||
do
|
||||
{
|
||||
x = x + 1;
|
||||
}
|
||||
while (x < 10);
|
||||
}
|
||||
|
||||
}
|
||||
12
csharp/ql/test/library-tests/ir/ir/variables.cs
Normal file
12
csharp/ql/test/library-tests/ir/ir/variables.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
public class test_variables
|
||||
{
|
||||
public static void f()
|
||||
{
|
||||
int x, y = 5;
|
||||
x = 4;
|
||||
x = y;
|
||||
int z = y;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user