Skip to content

Commit 66fd566

Browse files
author
Dave Bartolomeo
authored
Merge pull request github#3006 from jbj/ir-no-static-init
C++: IR: Ignore constant static initializers
2 parents 11b5c54 + ef194d3 commit 66fd566

File tree

11 files changed

+435
-19
lines changed

11 files changed

+435
-19
lines changed

cpp/ql/src/semmle/code/cpp/Variable.qll

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,49 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
366366
}
367367
}
368368

369+
/**
370+
* A variable whose contents always have static storage duration. This can be a
371+
* global variable, a namespace variable, a static local variable, or a static
372+
* member variable.
373+
*/
374+
class StaticStorageDurationVariable extends Variable {
375+
StaticStorageDurationVariable() {
376+
this instanceof GlobalOrNamespaceVariable
377+
or
378+
this.(LocalVariable).isStatic()
379+
or
380+
this.(MemberVariable).isStatic()
381+
}
382+
383+
/**
384+
* Holds if the initializer for this variable is evaluated at compile time.
385+
*/
386+
predicate hasConstantInitialization() {
387+
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
388+
}
389+
}
390+
391+
/**
392+
* Holds if `e` is an expression in a static initializer that must be evaluated
393+
* at run time. This predicate computes "is non-const" instead of "is const"
394+
* since computing "is const" for an aggregate literal with many children would
395+
* either involve recursion through `forall` on those children or an iteration
396+
* through the rank numbers of the children, both of which can be slow.
397+
*/
398+
private predicate runtimeExprInStaticInitializer(Expr e) {
399+
inStaticInitializer(e) and
400+
if e instanceof AggregateLiteral
401+
then runtimeExprInStaticInitializer(e.getAChild())
402+
else not e.getFullyConverted().isConstant()
403+
}
404+
405+
/** Holds if `e` is part of the initializer of a `StaticStorageDurationVariable`. */
406+
private predicate inStaticInitializer(Expr e) {
407+
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
408+
or
409+
inStaticInitializer(e.getParent())
410+
}
411+
369412
/**
370413
* A C/C++ variable which has global scope or namespace scope. For example the
371414
* variables `a` and `b` in the following code:

cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,7 @@ private Node getControlOrderChildSparse(Node n, int i) {
443443
private predicate skipInitializer(Initializer init) {
444444
exists(LocalVariable local |
445445
init = local.getInitializer() and
446-
local.isStatic() and
447-
not runtimeExprInStaticInitializer(init.getExpr())
446+
local.(StaticStorageDurationVariable).hasConstantInitialization()
448447
)
449448
}
450449

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ private predicate ignoreExprAndDescendants(Expr expr) {
5050
// constant value.
5151
isIRConstant(getRealParent(expr))
5252
or
53+
// Only translate the initializer of a static local if it uses run-time data.
54+
// Otherwise the initializer does not run in function scope.
55+
exists(Initializer init, StaticStorageDurationVariable var |
56+
init = var.getInitializer() and
57+
var.hasConstantInitialization() and
58+
expr = init.getExpr().getFullyConverted()
59+
)
60+
or
5361
// Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
5462
getRealParent(expr) instanceof AssumeExpr
5563
or

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8349,3 +8349,184 @@ perf-regression.cpp:
83498349
# 12| Type = [IntType] int
83508350
# 12| Value = [Literal] 0
83518351
# 12| ValueCategory = prvalue
8352+
struct_init.cpp:
8353+
# 1| [TopLevelFunction] int handler1(void*)
8354+
# 1| params:
8355+
# 1| 0: [Parameter] p
8356+
# 1| Type = [VoidPointerType] void *
8357+
# 2| [TopLevelFunction] int handler2(void*)
8358+
# 2| params:
8359+
# 2| 0: [Parameter] p
8360+
# 2| Type = [VoidPointerType] void *
8361+
# 4| [CopyAssignmentOperator] Info& Info::operator=(Info const&)
8362+
# 4| params:
8363+
#-----| 0: [Parameter] p#0
8364+
#-----| Type = [LValueReferenceType] const Info &
8365+
# 4| [MoveAssignmentOperator] Info& Info::operator=(Info&&)
8366+
# 4| params:
8367+
#-----| 0: [Parameter] p#0
8368+
#-----| Type = [RValueReferenceType] Info &&
8369+
# 16| [TopLevelFunction] void let_info_escape(Info*)
8370+
# 16| params:
8371+
# 16| 0: [Parameter] info
8372+
# 16| Type = [PointerType] Info *
8373+
# 16| body: [Block] { ... }
8374+
# 17| 0: [ExprStmt] ExprStmt
8375+
# 17| 0: [AssignExpr] ... = ...
8376+
# 17| Type = [PointerType] Info *
8377+
# 17| ValueCategory = lvalue
8378+
# 17| 0: [VariableAccess] global_pointer
8379+
# 17| Type = [PointerType] Info *
8380+
# 17| ValueCategory = lvalue
8381+
# 17| 1: [VariableAccess] info
8382+
# 17| Type = [PointerType] Info *
8383+
# 17| ValueCategory = prvalue(load)
8384+
# 18| 1: [ReturnStmt] return ...
8385+
# 20| [TopLevelFunction] void declare_static_infos()
8386+
# 20| params:
8387+
# 20| body: [Block] { ... }
8388+
# 21| 0: [DeclStmt] declaration
8389+
# 21| 0: [VariableDeclarationEntry] definition of static_infos
8390+
# 21| Type = [ArrayType] Info[]
8391+
# 21| init: [Initializer] initializer for static_infos
8392+
# 21| expr: [ArrayAggregateLiteral] {...}
8393+
# 21| Type = [ArrayType] Info[2]
8394+
# 21| ValueCategory = prvalue
8395+
# 22| [0]: [ClassAggregateLiteral] {...}
8396+
# 22| Type = [Struct] Info
8397+
# 22| ValueCategory = prvalue
8398+
# 22| .name: [ArrayToPointerConversion] array to pointer conversion
8399+
# 22| Type = [PointerType] const char *
8400+
# 22| ValueCategory = prvalue
8401+
# 22| expr: 1
8402+
# 22| Type = [ArrayType] const char[2]
8403+
# 22| Value = [StringLiteral] "1"
8404+
# 22| ValueCategory = lvalue
8405+
# 22| .handler: [FunctionAccess] handler1
8406+
# 22| Type = [FunctionPointerType] ..(*)(..)
8407+
# 22| ValueCategory = prvalue(load)
8408+
# 23| [1]: [ClassAggregateLiteral] {...}
8409+
# 23| Type = [Struct] Info
8410+
# 23| ValueCategory = prvalue
8411+
# 23| .name: [ArrayToPointerConversion] array to pointer conversion
8412+
# 23| Type = [PointerType] const char *
8413+
# 23| ValueCategory = prvalue
8414+
# 23| expr: 2
8415+
# 23| Type = [ArrayType] const char[2]
8416+
# 23| Value = [StringLiteral] "2"
8417+
# 23| ValueCategory = lvalue
8418+
# 23| .handler: [AddressOfExpr] & ...
8419+
# 23| Type = [FunctionPointerType] ..(*)(..)
8420+
# 23| ValueCategory = prvalue
8421+
# 23| 0: [FunctionAccess] handler2
8422+
# 23| Type = [RoutineType] ..()(..)
8423+
# 23| ValueCategory = lvalue
8424+
# 25| 1: [ExprStmt] ExprStmt
8425+
# 25| 0: [FunctionCall] call to let_info_escape
8426+
# 25| Type = [VoidType] void
8427+
# 25| ValueCategory = prvalue
8428+
# 25| 0: [ArrayToPointerConversion] array to pointer conversion
8429+
# 25| Type = [PointerType] Info *
8430+
# 25| ValueCategory = prvalue
8431+
# 25| expr: [VariableAccess] static_infos
8432+
# 25| Type = [ArrayType] Info[2]
8433+
# 25| ValueCategory = lvalue
8434+
# 26| 2: [ReturnStmt] return ...
8435+
# 28| [TopLevelFunction] void declare_local_infos()
8436+
# 28| params:
8437+
# 28| body: [Block] { ... }
8438+
# 29| 0: [DeclStmt] declaration
8439+
# 29| 0: [VariableDeclarationEntry] definition of local_infos
8440+
# 29| Type = [ArrayType] Info[]
8441+
# 29| init: [Initializer] initializer for local_infos
8442+
# 29| expr: [ArrayAggregateLiteral] {...}
8443+
# 29| Type = [ArrayType] Info[2]
8444+
# 29| ValueCategory = prvalue
8445+
# 30| [0]: [ClassAggregateLiteral] {...}
8446+
# 30| Type = [Struct] Info
8447+
# 30| ValueCategory = prvalue
8448+
# 30| .name: [ArrayToPointerConversion] array to pointer conversion
8449+
# 30| Type = [PointerType] const char *
8450+
# 30| ValueCategory = prvalue
8451+
# 30| expr: 1
8452+
# 30| Type = [ArrayType] const char[2]
8453+
# 30| Value = [StringLiteral] "1"
8454+
# 30| ValueCategory = lvalue
8455+
# 30| .handler: [FunctionAccess] handler1
8456+
# 30| Type = [FunctionPointerType] ..(*)(..)
8457+
# 30| ValueCategory = prvalue(load)
8458+
# 31| [1]: [ClassAggregateLiteral] {...}
8459+
# 31| Type = [Struct] Info
8460+
# 31| ValueCategory = prvalue
8461+
# 31| .name: [ArrayToPointerConversion] array to pointer conversion
8462+
# 31| Type = [PointerType] const char *
8463+
# 31| ValueCategory = prvalue
8464+
# 31| expr: 2
8465+
# 31| Type = [ArrayType] const char[2]
8466+
# 31| Value = [StringLiteral] "2"
8467+
# 31| ValueCategory = lvalue
8468+
# 31| .handler: [AddressOfExpr] & ...
8469+
# 31| Type = [FunctionPointerType] ..(*)(..)
8470+
# 31| ValueCategory = prvalue
8471+
# 31| 0: [FunctionAccess] handler2
8472+
# 31| Type = [RoutineType] ..()(..)
8473+
# 31| ValueCategory = lvalue
8474+
# 33| 1: [ExprStmt] ExprStmt
8475+
# 33| 0: [FunctionCall] call to let_info_escape
8476+
# 33| Type = [VoidType] void
8477+
# 33| ValueCategory = prvalue
8478+
# 33| 0: [ArrayToPointerConversion] array to pointer conversion
8479+
# 33| Type = [PointerType] Info *
8480+
# 33| ValueCategory = prvalue
8481+
# 33| expr: [VariableAccess] local_infos
8482+
# 33| Type = [ArrayType] Info[2]
8483+
# 33| ValueCategory = lvalue
8484+
# 34| 2: [ReturnStmt] return ...
8485+
# 36| [TopLevelFunction] void declare_static_runtime_infos(char const*)
8486+
# 36| params:
8487+
# 36| 0: [Parameter] name1
8488+
# 36| Type = [PointerType] const char *
8489+
# 36| body: [Block] { ... }
8490+
# 37| 0: [DeclStmt] declaration
8491+
# 37| 0: [VariableDeclarationEntry] definition of static_infos
8492+
# 37| Type = [ArrayType] Info[]
8493+
# 37| init: [Initializer] initializer for static_infos
8494+
# 37| expr: [ArrayAggregateLiteral] {...}
8495+
# 37| Type = [ArrayType] Info[2]
8496+
# 37| ValueCategory = prvalue
8497+
# 38| [0]: [ClassAggregateLiteral] {...}
8498+
# 38| Type = [Struct] Info
8499+
# 38| ValueCategory = prvalue
8500+
# 38| .name: [VariableAccess] name1
8501+
# 38| Type = [PointerType] const char *
8502+
# 38| ValueCategory = prvalue(load)
8503+
# 38| .handler: [FunctionAccess] handler1
8504+
# 38| Type = [FunctionPointerType] ..(*)(..)
8505+
# 38| ValueCategory = prvalue(load)
8506+
# 39| [1]: [ClassAggregateLiteral] {...}
8507+
# 39| Type = [Struct] Info
8508+
# 39| ValueCategory = prvalue
8509+
# 39| .name: [ArrayToPointerConversion] array to pointer conversion
8510+
# 39| Type = [PointerType] const char *
8511+
# 39| ValueCategory = prvalue
8512+
# 39| expr: 2
8513+
# 39| Type = [ArrayType] const char[2]
8514+
# 39| Value = [StringLiteral] "2"
8515+
# 39| ValueCategory = lvalue
8516+
# 39| .handler: [AddressOfExpr] & ...
8517+
# 39| Type = [FunctionPointerType] ..(*)(..)
8518+
# 39| ValueCategory = prvalue
8519+
# 39| 0: [FunctionAccess] handler2
8520+
# 39| Type = [RoutineType] ..()(..)
8521+
# 39| ValueCategory = lvalue
8522+
# 41| 1: [ExprStmt] ExprStmt
8523+
# 41| 0: [FunctionCall] call to let_info_escape
8524+
# 41| Type = [VoidType] void
8525+
# 41| ValueCategory = prvalue
8526+
# 41| 0: [ArrayToPointerConversion] array to pointer conversion
8527+
# 41| Type = [PointerType] Info *
8528+
# 41| ValueCategory = prvalue
8529+
# 41| expr: [VariableAccess] static_infos
8530+
# 41| Type = [ArrayType] Info[2]
8531+
# 41| ValueCategory = lvalue
8532+
# 42| 2: [ReturnStmt] return ...

0 commit comments

Comments
 (0)