Skip to content

Commit 3a2b0a2

Browse files
authored
Merge pull request #16366 from MathiasVP/fix-ir-for-destructors-in-switch
C++: Fix IR destructor calls on `JumpStmt`s
2 parents b86aeb6 + 07dd6d5 commit 3a2b0a2

File tree

5 files changed

+506
-2
lines changed

5 files changed

+506
-2
lines changed

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,11 @@ class TranslatedJumpStmt extends TranslatedStmt {
12761276
override JumpStmt stmt;
12771277

12781278
override Instruction getFirstInstruction(EdgeKind kind) {
1279+
// The first instruction is a destructor call, if any.
1280+
result = this.getChildInternal(0).getFirstInstruction(kind)
1281+
or
1282+
// Otherwise, the first (and only) instruction is a `NoOp`
1283+
not exists(this.getChildInternal(0)) and
12791284
result = this.getInstruction(OnlyInstructionTag()) and
12801285
kind instanceof GotoEdge
12811286
}
@@ -1284,7 +1289,20 @@ class TranslatedJumpStmt extends TranslatedStmt {
12841289
result = this.getInstruction(OnlyInstructionTag())
12851290
}
12861291

1287-
override TranslatedElement getChildInternal(int id) { none() }
1292+
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
1293+
result.getExpr() = stmt.getImplicitDestructorCall(id)
1294+
}
1295+
1296+
override TranslatedElement getLastChild() {
1297+
result =
1298+
this.getTranslatedImplicitDestructorCall(max(int id |
1299+
exists(stmt.getImplicitDestructorCall(id))
1300+
))
1301+
}
1302+
1303+
override TranslatedElement getChildInternal(int id) {
1304+
result = this.getTranslatedImplicitDestructorCall(id)
1305+
}
12881306

12891307
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
12901308
tag = OnlyInstructionTag() and
@@ -1297,7 +1315,19 @@ class TranslatedJumpStmt extends TranslatedStmt {
12971315
result = getTranslatedStmt(stmt.getTarget()).getFirstInstruction(kind)
12981316
}
12991317

1300-
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
1318+
final override predicate handlesDestructorsExplicitly() { any() }
1319+
1320+
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
1321+
exists(int id | child = this.getChildInternal(id) |
1322+
// Transition to the next destructor call, if any.
1323+
result = this.getChildInternal(id + 1).getFirstInstruction(kind)
1324+
or
1325+
// And otherwise, exit this element by flowing to the target of the jump.
1326+
not exists(this.getChildInternal(id + 1)) and
1327+
kind instanceof GotoEdge and
1328+
result = this.getInstruction(OnlyInstructionTag())
1329+
)
1330+
}
13011331
}
13021332

13031333
private EdgeKind getCaseEdge(SwitchCase switchCase) {

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

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22442,6 +22442,139 @@ ir.cpp:
2244222442
#-----| Type = [Class] ClassWithDestructor
2244322443
#-----| ValueCategory = lvalue
2244422444
# 2499| getStmt(4): [ReturnStmt] return ...
22445+
# 2501| [TopLevelFunction] void destruction_in_switch_1(int)
22446+
# 2501| <params>:
22447+
# 2501| getParameter(0): [Parameter] c
22448+
# 2501| Type = [IntType] int
22449+
# 2501| getEntryPoint(): [BlockStmt] { ... }
22450+
# 2502| getStmt(0): [SwitchStmt] switch (...) ...
22451+
# 2502| getExpr(): [VariableAccess] c
22452+
# 2502| Type = [IntType] int
22453+
# 2502| ValueCategory = prvalue(load)
22454+
# 2502| getStmt(): [BlockStmt] { ... }
22455+
# 2503| getStmt(0): [SwitchCase] case ...:
22456+
# 2503| getExpr(): [Literal] 0
22457+
# 2503| Type = [IntType] int
22458+
# 2503| Value = [Literal] 0
22459+
# 2503| ValueCategory = prvalue
22460+
# 2503| getStmt(1): [BlockStmt] { ... }
22461+
# 2504| getStmt(0): [DeclStmt] declaration
22462+
# 2504| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
22463+
# 2504| Type = [Class] ClassWithDestructor
22464+
# 2504| getVariable().getInitializer(): [Initializer] initializer for x
22465+
# 2504| getExpr(): [ConstructorCall] call to ClassWithDestructor
22466+
# 2504| Type = [VoidType] void
22467+
# 2504| ValueCategory = prvalue
22468+
# 2505| getStmt(1): [BreakStmt] break;
22469+
# 2506| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22470+
# 2506| Type = [VoidType] void
22471+
# 2506| ValueCategory = prvalue
22472+
# 2506| getQualifier(): [VariableAccess] x
22473+
# 2506| Type = [Class] ClassWithDestructor
22474+
# 2506| ValueCategory = lvalue
22475+
# 2506| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22476+
# 2506| Type = [VoidType] void
22477+
# 2506| ValueCategory = prvalue
22478+
# 2506| getQualifier(): [VariableAccess] x
22479+
# 2506| Type = [Class] ClassWithDestructor
22480+
# 2506| ValueCategory = lvalue
22481+
# 2507| getStmt(1): [LabelStmt] label ...:
22482+
# 2508| getStmt(2): [ReturnStmt] return ...
22483+
# 2510| [TopLevelFunction] void destruction_in_switch_2(int)
22484+
# 2510| <params>:
22485+
# 2510| getParameter(0): [Parameter] c
22486+
# 2510| Type = [IntType] int
22487+
# 2510| getEntryPoint(): [BlockStmt] { ... }
22488+
# 2511| getStmt(0): [SwitchStmt] switch (...) ...
22489+
# 2511| getInitialization(): [DeclStmt] declaration
22490+
# 2511| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
22491+
# 2511| Type = [Class] ClassWithDestructor
22492+
# 2511| getVariable().getInitializer(): [Initializer] initializer for y
22493+
# 2511| getExpr(): [ConstructorCall] call to ClassWithDestructor
22494+
# 2511| Type = [VoidType] void
22495+
# 2511| ValueCategory = prvalue
22496+
# 2511| getExpr(): [VariableAccess] c
22497+
# 2511| Type = [IntType] int
22498+
# 2511| ValueCategory = prvalue(load)
22499+
# 2511| getStmt(): [BlockStmt] { ... }
22500+
# 2512| getStmt(0): [SwitchCase] case ...:
22501+
# 2512| getExpr(): [Literal] 0
22502+
# 2512| Type = [IntType] int
22503+
# 2512| Value = [Literal] 0
22504+
# 2512| ValueCategory = prvalue
22505+
# 2512| getStmt(1): [BlockStmt] { ... }
22506+
# 2513| getStmt(0): [BreakStmt] break;
22507+
# 2515| getStmt(2): [SwitchCase] default:
22508+
# 2515| getStmt(3): [BlockStmt] { ... }
22509+
# 2516| getStmt(0): [BreakStmt] break;
22510+
# 2518| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22511+
# 2518| Type = [VoidType] void
22512+
# 2518| ValueCategory = prvalue
22513+
# 2518| getQualifier(): [VariableAccess] y
22514+
# 2518| Type = [Class] ClassWithDestructor
22515+
# 2518| ValueCategory = lvalue
22516+
# 2518| getStmt(1): [LabelStmt] label ...:
22517+
# 2519| getStmt(2): [ReturnStmt] return ...
22518+
# 2521| [TopLevelFunction] void destruction_in_switch_3(int)
22519+
# 2521| <params>:
22520+
# 2521| getParameter(0): [Parameter] c
22521+
# 2521| Type = [IntType] int
22522+
# 2521| getEntryPoint(): [BlockStmt] { ... }
22523+
# 2522| getStmt(0): [SwitchStmt] switch (...) ...
22524+
# 2522| getInitialization(): [DeclStmt] declaration
22525+
# 2522| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
22526+
# 2522| Type = [Class] ClassWithDestructor
22527+
# 2522| getVariable().getInitializer(): [Initializer] initializer for y
22528+
# 2522| getExpr(): [ConstructorCall] call to ClassWithDestructor
22529+
# 2522| Type = [VoidType] void
22530+
# 2522| ValueCategory = prvalue
22531+
# 2522| getExpr(): [VariableAccess] c
22532+
# 2522| Type = [IntType] int
22533+
# 2522| ValueCategory = prvalue(load)
22534+
# 2522| getStmt(): [BlockStmt] { ... }
22535+
# 2523| getStmt(0): [SwitchCase] case ...:
22536+
# 2523| getExpr(): [Literal] 0
22537+
# 2523| Type = [IntType] int
22538+
# 2523| Value = [Literal] 0
22539+
# 2523| ValueCategory = prvalue
22540+
# 2523| getStmt(1): [BlockStmt] { ... }
22541+
# 2524| getStmt(0): [DeclStmt] declaration
22542+
# 2524| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
22543+
# 2524| Type = [Class] ClassWithDestructor
22544+
# 2524| getVariable().getInitializer(): [Initializer] initializer for x
22545+
# 2524| getExpr(): [ConstructorCall] call to ClassWithDestructor
22546+
# 2524| Type = [VoidType] void
22547+
# 2524| ValueCategory = prvalue
22548+
# 2525| getStmt(1): [BreakStmt] break;
22549+
# 2526| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22550+
# 2526| Type = [VoidType] void
22551+
# 2526| ValueCategory = prvalue
22552+
# 2526| getQualifier(): [VariableAccess] x
22553+
# 2526| Type = [Class] ClassWithDestructor
22554+
# 2526| ValueCategory = lvalue
22555+
# 2530| getImplicitDestructorCall(1): [DestructorCall] call to ~ClassWithDestructor
22556+
# 2530| Type = [VoidType] void
22557+
# 2530| ValueCategory = prvalue
22558+
# 2530| getQualifier(): [VariableAccess] y
22559+
# 2530| Type = [Class] ClassWithDestructor
22560+
# 2530| ValueCategory = lvalue
22561+
# 2526| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22562+
# 2526| Type = [VoidType] void
22563+
# 2526| ValueCategory = prvalue
22564+
# 2526| getQualifier(): [VariableAccess] x
22565+
# 2526| Type = [Class] ClassWithDestructor
22566+
# 2526| ValueCategory = lvalue
22567+
# 2527| getStmt(2): [SwitchCase] default:
22568+
# 2527| getStmt(3): [BlockStmt] { ... }
22569+
# 2528| getStmt(0): [BreakStmt] break;
22570+
# 2530| getImplicitDestructorCall(0): [DestructorCall] call to ~ClassWithDestructor
22571+
# 2530| Type = [VoidType] void
22572+
# 2530| ValueCategory = prvalue
22573+
# 2530| getQualifier(): [VariableAccess] y
22574+
# 2530| Type = [Class] ClassWithDestructor
22575+
# 2530| ValueCategory = lvalue
22576+
# 2530| getStmt(1): [LabelStmt] label ...:
22577+
# 2531| getStmt(2): [ReturnStmt] return ...
2244522578
perf-regression.cpp:
2244622579
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
2244722580
# 4| <params>:

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

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17958,6 +17958,152 @@ ir.cpp:
1795817958
# 2484| v2484_8(void) = AliasedUse : ~m2497_4
1795917959
# 2484| v2484_9(void) = ExitFunction :
1796017960

17961+
# 2501| void destruction_in_switch_1(int)
17962+
# 2501| Block 0
17963+
# 2501| v2501_1(void) = EnterFunction :
17964+
# 2501| m2501_2(unknown) = AliasedDefinition :
17965+
# 2501| m2501_3(unknown) = InitializeNonLocal :
17966+
# 2501| m2501_4(unknown) = Chi : total:m2501_2, partial:m2501_3
17967+
# 2501| r2501_5(glval<int>) = VariableAddress[c] :
17968+
# 2501| m2501_6(int) = InitializeParameter[c] : &:r2501_5
17969+
# 2502| r2502_1(glval<int>) = VariableAddress[c] :
17970+
# 2502| r2502_2(int) = Load[c] : &:r2502_1, m2501_6
17971+
# 2502| v2502_3(void) = Switch : r2502_2
17972+
#-----| Case[0] -> Block 1
17973+
#-----| Default -> Block 2
17974+
17975+
# 2503| Block 1
17976+
# 2503| v2503_1(void) = NoOp :
17977+
# 2504| r2504_1(glval<ClassWithDestructor>) = VariableAddress[x] :
17978+
# 2504| m2504_2(ClassWithDestructor) = Uninitialized[x] : &:r2504_1
17979+
# 2504| r2504_3(glval<unknown>) = FunctionAddress[ClassWithDestructor] :
17980+
# 2504| v2504_4(void) = Call[ClassWithDestructor] : func:r2504_3, this:r2504_1
17981+
# 2504| m2504_5(unknown) = ^CallSideEffect : ~m2501_4
17982+
# 2504| m2504_6(unknown) = Chi : total:m2501_4, partial:m2504_5
17983+
# 2504| m2504_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2504_1
17984+
# 2504| m2504_8(ClassWithDestructor) = Chi : total:m2504_2, partial:m2504_7
17985+
# 2506| r2506_1(glval<ClassWithDestructor>) = VariableAddress[x] :
17986+
# 2506| r2506_2(glval<unknown>) = FunctionAddress[~ClassWithDestructor] :
17987+
# 2506| v2506_3(void) = Call[~ClassWithDestructor] : func:r2506_2, this:r2506_1
17988+
# 2506| m2506_4(unknown) = ^CallSideEffect : ~m2504_6
17989+
# 2506| m2506_5(unknown) = Chi : total:m2504_6, partial:m2506_4
17990+
# 2506| v2506_6(void) = ^IndirectReadSideEffect[-1] : &:r2506_1, m2504_8
17991+
# 2506| m2506_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2506_1
17992+
# 2506| m2506_8(ClassWithDestructor) = Chi : total:m2504_8, partial:m2506_7
17993+
# 2505| v2505_1(void) = NoOp :
17994+
#-----| Goto -> Block 2
17995+
17996+
# 2507| Block 2
17997+
# 2507| m2507_1(unknown) = Phi : from 0:~m2501_4, from 1:~m2506_5
17998+
# 2507| v2507_2(void) = NoOp :
17999+
# 2508| v2508_1(void) = NoOp :
18000+
# 2501| v2501_7(void) = ReturnVoid :
18001+
# 2501| v2501_8(void) = AliasedUse : ~m2507_1
18002+
# 2501| v2501_9(void) = ExitFunction :
18003+
18004+
# 2510| void destruction_in_switch_2(int)
18005+
# 2510| Block 0
18006+
# 2510| v2510_1(void) = EnterFunction :
18007+
# 2510| m2510_2(unknown) = AliasedDefinition :
18008+
# 2510| m2510_3(unknown) = InitializeNonLocal :
18009+
# 2510| m2510_4(unknown) = Chi : total:m2510_2, partial:m2510_3
18010+
# 2510| r2510_5(glval<int>) = VariableAddress[c] :
18011+
# 2510| m2510_6(int) = InitializeParameter[c] : &:r2510_5
18012+
# 2511| r2511_1(glval<ClassWithDestructor>) = VariableAddress[y] :
18013+
# 2511| m2511_2(ClassWithDestructor) = Uninitialized[y] : &:r2511_1
18014+
# 2511| r2511_3(glval<unknown>) = FunctionAddress[ClassWithDestructor] :
18015+
# 2511| v2511_4(void) = Call[ClassWithDestructor] : func:r2511_3, this:r2511_1
18016+
# 2511| m2511_5(unknown) = ^CallSideEffect : ~m2510_4
18017+
# 2511| m2511_6(unknown) = Chi : total:m2510_4, partial:m2511_5
18018+
# 2511| m2511_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2511_1
18019+
# 2511| m2511_8(ClassWithDestructor) = Chi : total:m2511_2, partial:m2511_7
18020+
# 2511| r2511_9(glval<int>) = VariableAddress[c] :
18021+
# 2511| r2511_10(int) = Load[c] : &:r2511_9, m2510_6
18022+
# 2511| v2511_11(void) = Switch : r2511_10
18023+
#-----| Case[0] -> Block 1
18024+
#-----| Default -> Block 2
18025+
18026+
# 2512| Block 1
18027+
# 2512| v2512_1(void) = NoOp :
18028+
# 2513| v2513_1(void) = NoOp :
18029+
#-----| Goto -> Block 3
18030+
18031+
# 2515| Block 2
18032+
# 2515| v2515_1(void) = NoOp :
18033+
# 2516| v2516_1(void) = NoOp :
18034+
#-----| Goto -> Block 3
18035+
18036+
# 2518| Block 3
18037+
# 2518| v2518_1(void) = NoOp :
18038+
# 2519| v2519_1(void) = NoOp :
18039+
# 2510| v2510_7(void) = ReturnVoid :
18040+
# 2510| v2510_8(void) = AliasedUse : ~m2511_6
18041+
# 2510| v2510_9(void) = ExitFunction :
18042+
18043+
# 2521| void destruction_in_switch_3(int)
18044+
# 2521| Block 0
18045+
# 2521| v2521_1(void) = EnterFunction :
18046+
# 2521| m2521_2(unknown) = AliasedDefinition :
18047+
# 2521| m2521_3(unknown) = InitializeNonLocal :
18048+
# 2521| m2521_4(unknown) = Chi : total:m2521_2, partial:m2521_3
18049+
# 2521| r2521_5(glval<int>) = VariableAddress[c] :
18050+
# 2521| m2521_6(int) = InitializeParameter[c] : &:r2521_5
18051+
# 2522| r2522_1(glval<ClassWithDestructor>) = VariableAddress[y] :
18052+
# 2522| m2522_2(ClassWithDestructor) = Uninitialized[y] : &:r2522_1
18053+
# 2522| r2522_3(glval<unknown>) = FunctionAddress[ClassWithDestructor] :
18054+
# 2522| v2522_4(void) = Call[ClassWithDestructor] : func:r2522_3, this:r2522_1
18055+
# 2522| m2522_5(unknown) = ^CallSideEffect : ~m2521_4
18056+
# 2522| m2522_6(unknown) = Chi : total:m2521_4, partial:m2522_5
18057+
# 2522| m2522_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2522_1
18058+
# 2522| m2522_8(ClassWithDestructor) = Chi : total:m2522_2, partial:m2522_7
18059+
# 2522| r2522_9(glval<int>) = VariableAddress[c] :
18060+
# 2522| r2522_10(int) = Load[c] : &:r2522_9, m2521_6
18061+
# 2522| v2522_11(void) = Switch : r2522_10
18062+
#-----| Case[0] -> Block 1
18063+
#-----| Default -> Block 2
18064+
18065+
# 2523| Block 1
18066+
# 2523| v2523_1(void) = NoOp :
18067+
# 2524| r2524_1(glval<ClassWithDestructor>) = VariableAddress[x] :
18068+
# 2524| m2524_2(ClassWithDestructor) = Uninitialized[x] : &:r2524_1
18069+
# 2524| r2524_3(glval<unknown>) = FunctionAddress[ClassWithDestructor] :
18070+
# 2524| v2524_4(void) = Call[ClassWithDestructor] : func:r2524_3, this:r2524_1
18071+
# 2524| m2524_5(unknown) = ^CallSideEffect : ~m2522_6
18072+
# 2524| m2524_6(unknown) = Chi : total:m2522_6, partial:m2524_5
18073+
# 2524| m2524_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2524_1
18074+
# 2524| m2524_8(ClassWithDestructor) = Chi : total:m2524_2, partial:m2524_7
18075+
# 2526| r2526_1(glval<ClassWithDestructor>) = VariableAddress[x] :
18076+
# 2526| r2526_2(glval<unknown>) = FunctionAddress[~ClassWithDestructor] :
18077+
# 2526| v2526_3(void) = Call[~ClassWithDestructor] : func:r2526_2, this:r2526_1
18078+
# 2526| m2526_4(unknown) = ^CallSideEffect : ~m2524_6
18079+
# 2526| m2526_5(unknown) = Chi : total:m2524_6, partial:m2526_4
18080+
# 2526| v2526_6(void) = ^IndirectReadSideEffect[-1] : &:r2526_1, m2524_8
18081+
# 2526| m2526_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2526_1
18082+
# 2526| m2526_8(ClassWithDestructor) = Chi : total:m2524_8, partial:m2526_7
18083+
# 2530| r2530_1(glval<ClassWithDestructor>) = VariableAddress[y] :
18084+
# 2530| r2530_2(glval<unknown>) = FunctionAddress[~ClassWithDestructor] :
18085+
# 2530| v2530_3(void) = Call[~ClassWithDestructor] : func:r2530_2, this:r2530_1
18086+
# 2530| m2530_4(unknown) = ^CallSideEffect : ~m2526_5
18087+
# 2530| m2530_5(unknown) = Chi : total:m2526_5, partial:m2530_4
18088+
# 2530| v2530_6(void) = ^IndirectReadSideEffect[-1] : &:r2530_1, m2522_8
18089+
# 2530| m2530_7(ClassWithDestructor) = ^IndirectMayWriteSideEffect[-1] : &:r2530_1
18090+
# 2530| m2530_8(ClassWithDestructor) = Chi : total:m2522_8, partial:m2530_7
18091+
# 2525| v2525_1(void) = NoOp :
18092+
#-----| Goto -> Block 3
18093+
18094+
# 2527| Block 2
18095+
# 2527| v2527_1(void) = NoOp :
18096+
# 2528| v2528_1(void) = NoOp :
18097+
#-----| Goto -> Block 3
18098+
18099+
# 2530| Block 3
18100+
# 2530| m2530_9(unknown) = Phi : from 1:~m2530_5, from 2:~m2522_6
18101+
# 2530| v2530_10(void) = NoOp :
18102+
# 2531| v2531_1(void) = NoOp :
18103+
# 2521| v2521_7(void) = ReturnVoid :
18104+
# 2521| v2521_8(void) = AliasedUse : ~m2530_9
18105+
# 2521| v2521_9(void) = ExitFunction :
18106+
1796118107
perf-regression.cpp:
1796218108
# 6| void Big::Big()
1796318109
# 6| Block 0

0 commit comments

Comments
 (0)