mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Merge pull request #82 from dave-bartolomeo/dave/NewDelete2
C++: IR generation for `new` and `new[]`
This commit is contained in:
@@ -581,24 +581,13 @@ class ReferenceDereferenceExpr extends Conversion, @ref_indirect {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `new` (non-array) expression.
|
||||
* A C++ `new` or `new[]` expression.
|
||||
*/
|
||||
class NewExpr extends Expr, @new_expr {
|
||||
override string toString() { result = "new" }
|
||||
|
||||
class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
override int getPrecedence() { result = 15 }
|
||||
|
||||
/**
|
||||
* Gets the type that is being allocated.
|
||||
*
|
||||
* For example, for `new int` the result is `int`.
|
||||
*/
|
||||
Type getAllocatedType() {
|
||||
new_allocated_type(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `operator new` that allocates storage.
|
||||
* Gets the `operator new` or `operator new[]` that allocates storage.
|
||||
*/
|
||||
Function getAllocator() {
|
||||
expr_allocator(underlyingElement(this), unresolveElement(result), _)
|
||||
@@ -612,6 +601,21 @@ class NewExpr extends Expr, @new_expr {
|
||||
expr_allocator(underlyingElement(this), _, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alignment argument passed to the allocation function, if any.
|
||||
*/
|
||||
Expr getAlignmentArgument() {
|
||||
hasAlignedAllocation() and
|
||||
(
|
||||
// If we have an allocator call, the alignment is the second argument to
|
||||
// that call.
|
||||
result = getAllocatorCall().getArgument(1) or
|
||||
// Otherwise, the alignment winds up as child number 3 of the `new`
|
||||
// itself.
|
||||
result = getChild(3)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call to a non-default `operator new` that allocates storage, if any.
|
||||
*
|
||||
@@ -652,6 +656,30 @@ class NewExpr extends Expr, @new_expr {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type that is being allocated.
|
||||
*
|
||||
* For example, for `new int` the result is `int`.
|
||||
* For `new int[5]` the result is `int[5]`.
|
||||
*/
|
||||
abstract Type getAllocatedType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `new` (non-array) expression.
|
||||
*/
|
||||
class NewExpr extends NewOrNewArrayExpr, @new_expr {
|
||||
override string toString() { result = "new" }
|
||||
|
||||
/**
|
||||
* Gets the type that is being allocated.
|
||||
*
|
||||
* For example, for `new int` the result is `int`.
|
||||
*/
|
||||
override Type getAllocatedType() {
|
||||
new_allocated_type(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call or expression that initializes the allocated object, if any.
|
||||
*
|
||||
@@ -664,17 +692,15 @@ class NewExpr extends Expr, @new_expr {
|
||||
/**
|
||||
* A C++ `new[]` (array) expression.
|
||||
*/
|
||||
class NewArrayExpr extends Expr, @new_array_expr {
|
||||
class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
|
||||
override string toString() { result = "new[]" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
|
||||
/**
|
||||
* Gets the type that is being allocated.
|
||||
*
|
||||
* For example, for `new int[5]` the result is `int[5]`.
|
||||
*/
|
||||
Type getAllocatedType() {
|
||||
override Type getAllocatedType() {
|
||||
new_array_allocated_type(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
@@ -685,56 +711,6 @@ class NewArrayExpr extends Expr, @new_array_expr {
|
||||
result = getType().getUnderlyingType().(PointerType).getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `operator new[]` that allocates storage.
|
||||
*/
|
||||
Function getAllocator() {
|
||||
expr_allocator(underlyingElement(this), unresolveElement(result), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the allocation function is the version that expects an alignment
|
||||
* argument of type `std::align_val_t`.
|
||||
*/
|
||||
predicate hasAlignedAllocation() {
|
||||
expr_allocator(underlyingElement(this), _, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call to a non-default `operator new[]` that allocates storage for the array, if any.
|
||||
*
|
||||
* If the default `operator new[]` is used, then there will be no call.
|
||||
*/
|
||||
FunctionCall getAllocatorCall() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the `operator delete` that deallocates storage if the initialization
|
||||
* throws an exception, if any.
|
||||
*/
|
||||
Function getDeallocator() {
|
||||
expr_deallocator(underlyingElement(this), unresolveElement(result), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the deallocation function expects a size argument.
|
||||
*/
|
||||
predicate hasSizedDeallocation() {
|
||||
exists(int form |
|
||||
expr_deallocator(underlyingElement(this), _, form) and
|
||||
form.bitAnd(1) != 0 // Bit zero is the "size" bit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the deallocation function expects an alignment argument.
|
||||
*/
|
||||
predicate hasAlignedDeallocation() {
|
||||
exists(int form |
|
||||
expr_deallocator(underlyingElement(this), _, form) and
|
||||
form.bitAnd(2) != 0 // Bit one is the "alignment" bit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call or expression that initializes the first element of the array, if any.
|
||||
*
|
||||
|
||||
@@ -195,8 +195,11 @@ class Instruction extends Construction::TInstruction {
|
||||
private string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(resultType.toString()) and
|
||||
if resultType instanceof UnknownType and exists(getResultSize()) then
|
||||
if (resultType instanceof UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())) then (
|
||||
result = valcat + "[" + getResultSize().toString() + "]"
|
||||
)
|
||||
else
|
||||
result = valcat
|
||||
)
|
||||
|
||||
@@ -43,6 +43,9 @@ newtype TInstructionTag =
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
AllocationSizeTag() or
|
||||
AllocationElementSizeTag() or
|
||||
AllocationExtentConvertTag() or
|
||||
ValueConditionConditionalBranchTag() or
|
||||
ConditionValueTrueTempAddressTag() or
|
||||
ConditionValueTrueConstantTag() or
|
||||
@@ -88,11 +91,15 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load)
|
||||
tag = InitializerVariableAddressTag() and result = "InitVarAddr" or
|
||||
tag = InitializerStoreTag() and result = "InitStore" 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 = AssignOperationOpTag() and result = "AssignOpOp" or
|
||||
tag = AssignOperationConvertResultTag() and result = "AssignOpConvRes" or
|
||||
tag = AssignmentStoreTag() and result = "AssigStore" 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
|
||||
@@ -106,6 +113,9 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch" or
|
||||
tag = CallTargetTag() and result = "CallTarget" or
|
||||
tag = CallTag() and result = "Call" 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
|
||||
|
||||
@@ -47,12 +47,12 @@ private Element getRealParent(Expr expr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` should be ignored for the purposes of code generation due to
|
||||
* some property of `expr` itself. Unlike `ignoreExpr()`, this predicate does
|
||||
* not ignore an expression solely because it is a descendant of an ignored
|
||||
* element.
|
||||
* Holds if `expr` and all of its descendants should be ignored for the purposes
|
||||
* of IR generation due to some property of `expr` itself. Unlike
|
||||
* `ignoreExpr()`, this predicate does not ignore an expression solely because
|
||||
* it is a descendant of an ignored element.
|
||||
*/
|
||||
private predicate ignoreExprLocal(Expr expr) {
|
||||
private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
// Ignore parentless expressions
|
||||
not exists(getRealParent(expr)) or
|
||||
// Ignore the constants in SwitchCase, since their values are embedded in the
|
||||
@@ -65,23 +65,32 @@ private predicate ignoreExprLocal(Expr expr) {
|
||||
// node as its qualifier, but that `FieldAccess` does not have a child of its own.
|
||||
// We'll ignore that `FieldAccess`, and supply the receiver as part of the calling
|
||||
// context, much like we do with constructor calls.
|
||||
expr.getParent().(DestructorCall).getParent() instanceof DestructorFieldDestruction
|
||||
expr.getParent().(DestructorCall).getParent() instanceof DestructorFieldDestruction or
|
||||
exists(NewArrayExpr newExpr |
|
||||
// REVIEW: Ignore initializers for `NewArrayExpr` until we determine how to
|
||||
// represent them.
|
||||
newExpr.getInitializer().getFullyConverted() = expr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` (not including its descendants) should be ignored for the
|
||||
* purposes of IR generation.
|
||||
*/
|
||||
private predicate ignoreExprOnly(Expr expr) {
|
||||
exists(NewOrNewArrayExpr newExpr |
|
||||
// Ignore the allocator call, because we always synthesize it. Don't ignore
|
||||
// its arguments, though, because we use them as part of the synthesis.
|
||||
newExpr.getAllocatorCall() = expr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` should be ignored for the purposes of IR generation.
|
||||
*/
|
||||
private predicate ignoreExpr(Expr expr) {
|
||||
ignoreExprLocal(expr) or
|
||||
// Ignore all descendants of ignored elements as well.
|
||||
ignoreElement(getRealParent(expr))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `element` should be ignored for the purposes of IR generation.
|
||||
*/
|
||||
private predicate ignoreElement(Element element) {
|
||||
ignoreExpr(element.(Expr))
|
||||
ignoreExprOnly(expr) or
|
||||
ignoreExprAndDescendants(getRealParent*(expr))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +164,7 @@ predicate ignoreLoad(Expr expr) {
|
||||
|
||||
newtype TTranslatedElement =
|
||||
// An expression that is not being consumed as a condition
|
||||
TTranslatedNonLoadExpr(Expr expr) {
|
||||
TTranslatedValueExpr(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
not isNativeCondition(expr) and
|
||||
not isFlexibleCondition(expr)
|
||||
@@ -216,6 +225,9 @@ newtype TTranslatedElement =
|
||||
exists(ConstructorFieldInit fieldInit |
|
||||
fieldInit.getExpr().getFullyConverted() = expr
|
||||
) or
|
||||
exists(NewExpr newExpr |
|
||||
newExpr.getInitializer().getFullyConverted() = expr
|
||||
) or
|
||||
exists(ThrowExpr throw |
|
||||
throw.getExpr().getFullyConverted() = expr
|
||||
)
|
||||
@@ -298,6 +310,14 @@ newtype TTranslatedElement =
|
||||
exists(DeclStmt declStmt |
|
||||
declStmt.getADeclarationEntry() = entry
|
||||
)
|
||||
} or
|
||||
// An allocator call in a `new` or `new[]` expression
|
||||
TTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
|
||||
not ignoreExpr(newExpr)
|
||||
} or
|
||||
// An allocation size for a `new` or `new[]` expression
|
||||
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) {
|
||||
not ignoreExpr(newExpr)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,33 +16,23 @@ TranslatedExpr getTranslatedExpr(Expr expr) {
|
||||
result.producesExprResult()
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of some part of an expression.
|
||||
* A single `Expr` may consist of multiple `TranslatedExpr` objects. Every
|
||||
* `Expr` has a single `TranslatedCoreExpr`, which produces the result of the
|
||||
* expression before any implicit lvalue-to-rvalue conversion. Any expression
|
||||
* with an lvalue-to-rvalue conversion will also have a `TranslatedLoad` to
|
||||
* perform that conversion on the original result. A few expressions have
|
||||
* additional `TranslatedExpr` objects that compute intermediate values, such
|
||||
* as the `TranslatedAllocatorCall` and `TranslatedAllocationSize` within the
|
||||
* translation of a `NewExpr`.
|
||||
*/
|
||||
abstract class TranslatedExpr extends TranslatedElement {
|
||||
Expr expr;
|
||||
|
||||
override final string toString() {
|
||||
result = expr.toString()
|
||||
}
|
||||
|
||||
override final Locatable getAST() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
final Expr getExpr() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
override final Function getFunction() {
|
||||
result = expr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
final Type getResultType() {
|
||||
result = expr.getType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that produces the result of the expression.
|
||||
*/
|
||||
abstract Instruction getResult();
|
||||
|
||||
/**
|
||||
@@ -54,9 +44,50 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
* TranslatedVariableAccess for `x` does not. The TranslatedVariableAccess
|
||||
* for `y` does produce its result, however, because there is no load on `y`.
|
||||
*/
|
||||
final predicate producesExprResult() {
|
||||
// A load always produces the result of the expression.
|
||||
this instanceof TranslatedLoad or
|
||||
abstract predicate producesExprResult();
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this expression.
|
||||
*/
|
||||
final Type getResultType() {
|
||||
result = expr.getType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
override final Locatable getAST() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
override final Function getFunction() {
|
||||
result = expr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression from which this `TranslatedExpr` is generated.
|
||||
*/
|
||||
final Expr getExpr() {
|
||||
result = expr
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingFunction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the "core" part of an expression. This is the part of
|
||||
* the expression that produces the result value of the expression, before any
|
||||
* lvalue-to-rvalue conversion on the result. Every expression has a single
|
||||
* `TranslatedCoreExpr`.
|
||||
*/
|
||||
abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
override final string toString() {
|
||||
result = expr.toString()
|
||||
}
|
||||
|
||||
override final predicate producesExprResult() {
|
||||
// If there's no load, then this is the only TranslatedExpr for this
|
||||
// expression.
|
||||
not expr.hasLValueToRValueConversion() or
|
||||
@@ -86,7 +117,7 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedConditionValue extends TranslatedExpr, ConditionContext,
|
||||
class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
TTranslatedConditionValue {
|
||||
TranslatedConditionValue() {
|
||||
this = TTranslatedConditionValue(expr)
|
||||
@@ -262,11 +293,19 @@ class TranslatedConditionValue extends TranslatedExpr, ConditionContext,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
|
||||
* an expression.
|
||||
*/
|
||||
class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
TranslatedLoad() {
|
||||
this = TTranslatedLoad(expr)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Load of " + expr.toString()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = getOperand().getFirstInstruction()
|
||||
}
|
||||
@@ -279,8 +318,11 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = expr.getType().getUnspecifiedType() and
|
||||
if expr.isGLValueCategory() then
|
||||
isGLValue = true
|
||||
else
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
@@ -313,8 +355,13 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
)
|
||||
}
|
||||
|
||||
private TranslatedExpr getOperand() {
|
||||
result.getExpr() = expr and not result instanceof TranslatedLoad
|
||||
override final predicate producesExprResult() {
|
||||
// A load always produces the result of the expression.
|
||||
any()
|
||||
}
|
||||
|
||||
private TranslatedCoreExpr getOperand() {
|
||||
result.getExpr() = expr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,21 +953,24 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedNonLoadExpr extends TranslatedExpr,
|
||||
TTranslatedNonLoadExpr {
|
||||
TranslatedNonLoadExpr() {
|
||||
this = TTranslatedNonLoadExpr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedNonConstantExpr extends TranslatedNonLoadExpr {
|
||||
/**
|
||||
* IR translation of an expression whose value is not known at compile time.
|
||||
*/
|
||||
abstract class TranslatedNonConstantExpr extends TranslatedCoreExpr {
|
||||
TranslatedNonConstantExpr() {
|
||||
this = TTranslatedValueExpr(expr) and
|
||||
not expr.isConstant()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedConstantExpr extends TranslatedNonLoadExpr {
|
||||
/**
|
||||
* IR translation of an expression with a compile-time constant value. This
|
||||
* includes not only literals, but also "integral constant expressions" (e.g.
|
||||
* `1 + 2`).
|
||||
*/
|
||||
abstract class TranslatedConstantExpr extends TranslatedCoreExpr {
|
||||
TranslatedConstantExpr() {
|
||||
this = TTranslatedValueExpr(expr) and
|
||||
expr.isConstant()
|
||||
}
|
||||
|
||||
@@ -998,7 +1048,15 @@ class TranslatedStringLiteral extends TranslatedConstantExpr {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedValueExpr extends TranslatedNonConstantExpr {
|
||||
/**
|
||||
* IR translation of an expression that performs a single operation on its
|
||||
* operands and returns the result.
|
||||
*/
|
||||
abstract class TranslatedSingleInstructionExpr extends
|
||||
TranslatedNonConstantExpr {
|
||||
/**
|
||||
* Gets the `Opcode` of the operation to be performed.
|
||||
*/
|
||||
abstract Opcode getOpcode();
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
@@ -1014,7 +1072,7 @@ abstract class TranslatedValueExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedUnaryExpr extends TranslatedValueExpr {
|
||||
class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
|
||||
TranslatedUnaryExpr() {
|
||||
expr instanceof NotExpr or
|
||||
expr instanceof ComplementExpr or
|
||||
@@ -1299,7 +1357,10 @@ private Opcode comparisonOpcode(ComparisonOperation expr) {
|
||||
expr instanceof GEExpr and result instanceof Opcode::CompareGE
|
||||
}
|
||||
|
||||
class TranslatedBinaryOperation extends TranslatedValueExpr {
|
||||
/**
|
||||
* IR translation of a simple binary operation.
|
||||
*/
|
||||
class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
|
||||
TranslatedBinaryOperation() {
|
||||
expr instanceof BinaryArithmeticOperation or
|
||||
expr instanceof BinaryBitwiseOperation or
|
||||
@@ -1707,26 +1768,11 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call to a function.
|
||||
* 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 TranslatedNonConstantExpr {
|
||||
Call call;
|
||||
|
||||
TranslatedCall() {
|
||||
expr = call
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(getQualifier()) then
|
||||
result = getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
abstract class TranslatedCall extends TranslatedExpr {
|
||||
override final 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
|
||||
@@ -1737,6 +1783,21 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
result = getArgument(id)
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
if exists(getQualifier()) then
|
||||
result = getQualifier().getFirstInstruction()
|
||||
else
|
||||
result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Invoke and
|
||||
resultType = getCallResultType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getQualifier() and
|
||||
@@ -1755,14 +1816,6 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Invoke and
|
||||
resultType = call.getType().getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
@@ -1789,13 +1842,15 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
final predicate hasArguments() {
|
||||
exists(call.getArgument(0))
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result type of the call.
|
||||
*/
|
||||
abstract Type getCallResultType();
|
||||
|
||||
/**
|
||||
* Holds if the call has a `this` argument.
|
||||
*/
|
||||
@@ -1834,9 +1889,7 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
* Gets the `TranslatedExpr` for the qualifier of the call (i.e. the value
|
||||
* that is passed as the `this` argument.
|
||||
*/
|
||||
final TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
|
||||
}
|
||||
abstract TranslatedExpr getQualifier();
|
||||
|
||||
/**
|
||||
* Gets the instruction whose result value is the `this` argument of the call.
|
||||
@@ -1852,9 +1905,7 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
* Gets the argument with the specified `index`. Does not include the `this`
|
||||
* argument.
|
||||
*/
|
||||
final TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
|
||||
}
|
||||
abstract TranslatedExpr getArgument(int index);
|
||||
|
||||
/**
|
||||
* If there are any arguments, gets the first instruction of the first
|
||||
@@ -1866,12 +1917,305 @@ abstract class TranslatedCall extends TranslatedNonConstantExpr {
|
||||
else
|
||||
result = getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call has any arguments, not counting the `this` argument.
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the allocation size argument passed to `operator new`
|
||||
* in a `new` expression.
|
||||
*
|
||||
* We have to synthesize this because not all `NewExpr` nodes have an allocator
|
||||
* call, and even the ones that do pass an `ErrorExpr` as the argument.
|
||||
*/
|
||||
abstract class TranslatedAllocationSize extends TranslatedExpr,
|
||||
TTranslatedAllocationSize {
|
||||
NewOrNewArrayExpr newExpr;
|
||||
|
||||
TranslatedAllocationSize() {
|
||||
this = TTranslatedAllocationSize(newExpr) and
|
||||
expr = newExpr
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Allocation size for " + newExpr.toString()
|
||||
}
|
||||
|
||||
override final predicate producesExprResult() {
|
||||
none()
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(AllocationSizeTag())
|
||||
}
|
||||
}
|
||||
|
||||
TranslatedAllocationSize getTranslatedAllocationSize(NewOrNewArrayExpr newExpr) {
|
||||
result.getAST() = newExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a constant allocation size.
|
||||
*
|
||||
* The allocation size for a `new` expression is always a constant. The
|
||||
* allocation size for a `new[]` expression is a constant if the array extent
|
||||
* is a compile-time constant.
|
||||
*/
|
||||
class TranslatedConstantAllocationSize extends TranslatedAllocationSize {
|
||||
TranslatedConstantAllocationSize() {
|
||||
not exists(newExpr.(NewArrayExpr).getExtent())
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = getInstruction(AllocationSizeTag())
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = AllocationSizeTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = newExpr.getAllocator().getParameter(0).getType().getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override final Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
tag = AllocationSizeTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
none()
|
||||
}
|
||||
|
||||
override final string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = AllocationSizeTag() and
|
||||
result = newExpr.getAllocatedType().getSize().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a non-constant allocation size.
|
||||
*
|
||||
* This class is used for the allocation size of a `new[]` expression where the
|
||||
* array extent is not known at compile time. It performs the multiplication of
|
||||
* the extent by the element size.
|
||||
*/
|
||||
class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
NewArrayExpr newArrayExpr;
|
||||
|
||||
TranslatedNonConstantAllocationSize() {
|
||||
newArrayExpr = newExpr and
|
||||
exists(newArrayExpr.getExtent())
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = getExtent().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
isGLValue = false and
|
||||
resultType = newExpr.getAllocator().getParameter(0).getType().getUnspecifiedType() and
|
||||
(
|
||||
// Convert the extent to `size_t`, because the AST doesn't do this already.
|
||||
tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert or
|
||||
tag = AllocationElementSizeTag() and opcode instanceof Opcode::Constant or
|
||||
tag = AllocationSizeTag() and opcode instanceof Opcode::Mul // REVIEW: Overflow?
|
||||
)
|
||||
}
|
||||
|
||||
override final Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
tag = AllocationExtentConvertTag() and
|
||||
result = getInstruction(AllocationElementSizeTag())
|
||||
) or
|
||||
(
|
||||
tag = AllocationElementSizeTag() and
|
||||
result = getInstruction(AllocationSizeTag())
|
||||
) or
|
||||
(
|
||||
tag = AllocationSizeTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getExtent()
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getExtent() and
|
||||
result = getInstruction(AllocationExtentConvertTag())
|
||||
}
|
||||
|
||||
override final string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = AllocationElementSizeTag() and
|
||||
result = newArrayExpr.getAllocatedElementType().getSize().toString()
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = AllocationSizeTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperand and result = getInstruction(AllocationExtentConvertTag()) or
|
||||
operandTag instanceof RightOperand and result = getInstruction(AllocationElementSizeTag())
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = AllocationExtentConvertTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
result = getExtent().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
private TranslatedExpr getExtent() {
|
||||
result = getTranslatedExpr(newArrayExpr.getExtent().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a direct call to a specific function. Used for both
|
||||
* explicit calls (`TranslatedFunctionCall`) and implicit calls
|
||||
* (`TranslatedAllocatorCall`).
|
||||
*/
|
||||
abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
override final Instruction getFirstCallTargetInstruction() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override final Instruction getCallTargetResult() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// The database does not contain a `FunctionType` for a function unless
|
||||
// its address was taken, so we'll just use glval<Unknown> instead of
|
||||
// glval<FunctionType>.
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
result = TranslatedCall.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to `operator new` as part of a `new` or `new[]`
|
||||
* expression.
|
||||
*/
|
||||
class TranslatedAllocatorCall extends TTranslatedAllocatorCall,
|
||||
TranslatedDirectCall {
|
||||
NewOrNewArrayExpr newExpr;
|
||||
|
||||
TranslatedAllocatorCall() {
|
||||
this = TTranslatedAllocatorCall(newExpr) and
|
||||
expr = newExpr
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Allocator call for " + newExpr.toString()
|
||||
}
|
||||
|
||||
override final predicate producesExprResult() {
|
||||
none()
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = newExpr.getAllocator()
|
||||
}
|
||||
|
||||
override final Type getCallResultType() {
|
||||
result = newExpr.getAllocator().getType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
override final TranslatedExpr getQualifier() {
|
||||
none()
|
||||
}
|
||||
|
||||
override final predicate hasArguments() {
|
||||
// All allocator calls have at least one argument.
|
||||
any()
|
||||
}
|
||||
|
||||
override final TranslatedExpr getArgument(int index) {
|
||||
// If the allocator is the default operator new(void*), there will be no
|
||||
// allocator call in the AST. Otherwise, there will be an allocator call
|
||||
// that includes all arguments to the allocator, including the size,
|
||||
// alignment (if any), and placement args. However, the size argument is
|
||||
// an error node, so we need to provide the correct size argument in any
|
||||
// case.
|
||||
if index = 0 then
|
||||
result = getTranslatedAllocationSize(newExpr)
|
||||
else if(index = 1 and newExpr.hasAlignedAllocation()) then
|
||||
result = getTranslatedExpr(newExpr.getAlignmentArgument())
|
||||
else
|
||||
result = getTranslatedExpr(newExpr.getAllocatorCall().getArgument(index))
|
||||
}
|
||||
}
|
||||
|
||||
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
|
||||
result.getAST() = newExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function.
|
||||
*/
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr,
|
||||
TranslatedCall {
|
||||
Call call;
|
||||
|
||||
TranslatedCallExpr() {
|
||||
expr = call
|
||||
}
|
||||
|
||||
override final Type getCallResultType() {
|
||||
result = getResultType()
|
||||
}
|
||||
|
||||
override final predicate hasArguments() {
|
||||
exists(call.getArgument(0))
|
||||
}
|
||||
|
||||
override final TranslatedExpr getQualifier() {
|
||||
result = getTranslatedExpr(call.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
override final TranslatedExpr getArgument(int index) {
|
||||
result = getTranslatedExpr(call.getArgument(index).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a call through a function pointer.
|
||||
*/
|
||||
class TranslatedExprCall extends TranslatedCall {
|
||||
class TranslatedExprCall extends TranslatedCallExpr {
|
||||
ExprCall exprCall;
|
||||
|
||||
TranslatedExprCall() {
|
||||
@@ -1886,42 +2230,13 @@ class TranslatedExprCall extends TranslatedCall {
|
||||
/**
|
||||
* Represents the IR translation of a direct function call.
|
||||
*/
|
||||
class TranslatedFunctionCall extends TranslatedCall {
|
||||
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
FunctionCall funcCall;
|
||||
|
||||
TranslatedFunctionCall() {
|
||||
expr = funcCall
|
||||
}
|
||||
|
||||
override final Instruction getFirstCallTargetInstruction() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override final Instruction getCallTargetResult() {
|
||||
result = getInstruction(CallTargetTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
resultType instanceof BoolType and //HACK
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
result = super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = CallTargetTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getFirstArgumentOrCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and result = funcCall.getTarget()
|
||||
}
|
||||
@@ -2498,3 +2813,103 @@ class TranslatedVarArgCopy extends TranslatedBuiltInOperation {
|
||||
result instanceof Opcode::VarArgCopy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `new` or `new[]` expression.
|
||||
*/
|
||||
abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr,
|
||||
InitializationContext {
|
||||
NewOrNewArrayExpr newExpr;
|
||||
|
||||
TranslatedNewOrNewArrayExpr() {
|
||||
expr = newExpr
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getAllocatorCall() or
|
||||
id = 1 and result = getInitialization()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = getAllocatorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final Instruction getResult() {
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override final Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
tag = OnlyInstructionTag() and
|
||||
if exists(getInitialization()) then
|
||||
result = getInitialization().getFirstInstruction()
|
||||
else
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override final Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getAllocatorCall() and result = getInstruction(OnlyInstructionTag()) or
|
||||
child = getInitialization() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
result = getAllocatorCall().getResult()
|
||||
}
|
||||
|
||||
override final Instruction getTargetAddress() {
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
private TranslatedAllocatorCall getAllocatorCall() {
|
||||
result = getTranslatedAllocatorCall(newExpr)
|
||||
}
|
||||
|
||||
abstract TranslatedInitialization getInitialization();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `new` expression.
|
||||
*/
|
||||
class TranslatedNewExpr extends TranslatedNewOrNewArrayExpr {
|
||||
TranslatedNewExpr() {
|
||||
newExpr instanceof NewExpr
|
||||
}
|
||||
|
||||
override final Type getTargetType() {
|
||||
result = newExpr.getAllocatedType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
override final TranslatedInitialization getInitialization() {
|
||||
result = getTranslatedInitialization(newExpr.(NewExpr).getInitializer())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `new[]` expression.
|
||||
*/
|
||||
class TranslatedNewArrayExpr extends TranslatedNewOrNewArrayExpr {
|
||||
TranslatedNewArrayExpr() {
|
||||
newExpr instanceof NewArrayExpr
|
||||
}
|
||||
|
||||
override final Type getTargetType() {
|
||||
result = newExpr.getAllocatedType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
override final TranslatedInitialization getInitialization() {
|
||||
// REVIEW: Figure out how we want to model array initialization in the IR.
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +195,11 @@ class Instruction extends Construction::TInstruction {
|
||||
private string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(resultType.toString()) and
|
||||
if resultType instanceof UnknownType and exists(getResultSize()) then
|
||||
if (resultType instanceof UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())) then (
|
||||
result = valcat + "[" + getResultSize().toString() + "]"
|
||||
)
|
||||
else
|
||||
result = valcat
|
||||
)
|
||||
|
||||
@@ -195,8 +195,11 @@ class Instruction extends Construction::TInstruction {
|
||||
private string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(resultType.toString()) and
|
||||
if resultType instanceof UnknownType and exists(getResultSize()) then
|
||||
if (resultType instanceof UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())) then (
|
||||
result = valcat + "[" + getResultSize().toString() + "]"
|
||||
)
|
||||
else
|
||||
result = valcat
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user