Merge pull request #1600 from AndreiDiaconu1/ircsharp

C#: Initial port of IR for C#
This commit is contained in:
Calum Grant
2019-08-29 09:26:34 +01:00
committed by GitHub
94 changed files with 12801 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -52,6 +52,10 @@ predicate hasAsmOperandIndex(int operandIndex) {
)
}
int getTypeSize(Type type) {
result = type.getSize()
}
int getPointerSize() {
exists(Cpp::NullPointerType nullptr |
result = nullptr.getSize()

View File

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

View 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

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

View 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

View 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

View File

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

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

View File

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

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

View File

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

View File

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

View 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" } }
}

View File

@@ -0,0 +1,8 @@
private import internal.TempVariableTagInternal
private import Imports::TempVariableTag
class TempVariableTag extends TTempVariableTag {
string toString() {
result = getTempVariableTagId(this)
}
}

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
private import IR
import InstructionSanity

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.implementation.raw.IR as IR

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.implementation.raw.IR as IR

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind

View File

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

View File

@@ -0,0 +1,2 @@
import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind
import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind

View File

@@ -0,0 +1,2 @@
import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
import IRConstruction as Construction

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.csharp.ir.IRConfiguration as IRConfiguration

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

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

View 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"
}
}

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

View File

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

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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--;
}
}

View 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);
}
}

View File

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

View 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)
{
}
}
}

View 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;
}
}

View 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;
}
}

View 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 :

View File

@@ -0,0 +1 @@
semmle/code/csharp/ir/implementation/raw/PrintIR.ql

View File

@@ -0,0 +1,15 @@
missingOperand
unexpectedOperand
duplicateOperand
missingPhiOperand
missingOperandType
instructionWithoutSuccessor
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction
operandAcrossFunctions
instructionWithoutUniqueBlock
containsLoopOfForwardEdges
lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition

View File

@@ -0,0 +1 @@
semmle/code/csharp/ir/implementation/raw/IRSanity.ql

View File

@@ -0,0 +1,14 @@
using System;
public class test_simple_call
{
public static int f()
{
return 0;
}
public int g()
{
return f();
}
}

View File

@@ -0,0 +1,9 @@
using System;
public class test_simple_function
{
public static int f()
{
return 0;
}
}

View 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);
}
}

View 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;
}
}