From f4a060099b702c70b46c0ed938348aaea81cd0d3 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Tue, 14 Aug 2018 17:45:37 -0700 Subject: [PATCH] C++: Handle casts to `void` in IR Casts to `void` did not have a semantic conversion type in the AST, so they also weren't getting generated correctly in the IR. I've added a `VoidConversion` class to the AST, along with tests. I've also added IR translation for such conversions, using a new `ConvertToVoid` opcode. I'm not sure if it's really necessary to generate an instruction to represent this, but it may be useful for detecting values that are explicitly unused (e.g. return value from a call). I added two new sanity queries for the IR to detect the following: - IR blocks with no successors, which usually indicates bad IR translation - Phi instruction without an operand for one of the predecessor blocks. These sanity queries found another subtle IR translation bug. If an expression that is normally translated as a condition (e.g. `&&`, `||`, or parens in certain contexts) has a constant value, we were not creating a `TranslatedExpr` for the expression at all. I changed it to always treat a constant condition as a non-condition expression. --- cpp/ql/src/semmle/code/cpp/exprs/Cast.qll | 14 +++++ .../code/cpp/ir/internal/Instruction.qll | 25 ++++++++ .../semmle/code/cpp/ir/internal/Opcode.qll | 2 + .../cpp/ir/internal/TranslatedElement.qll | 6 +- .../code/cpp/ir/internal/TranslatedExpr.qll | 13 +++++ .../ssa/internal/aliased_ssa/Instruction.qll | 25 ++++++++ .../code/cpp/ssa/internal/ssa/Instruction.qll | 25 ++++++++ .../library-tests/conversions/conversions.cpp | 13 +++++ .../conversions/conversions.expected | 6 ++ .../ir/ir/AliasedSSAIRSanity.expected | 2 + .../library-tests/ir/ir/IRSanity.expected | 2 + .../library-tests/ir/ir/PrintAST.expected | 57 +++++++++++++++++++ .../library-tests/ir/ir/SSAIRSanity.expected | 2 + .../ir/ir/aliased_ssa_ir.expected | 54 ++++++++++++++++++ cpp/ql/test/library-tests/ir/ir/ir.cpp | 9 +++ cpp/ql/test/library-tests/ir/ir/ir.expected | 53 +++++++++++++++++ .../ir/ir/ssa_block_count.expected | 2 + .../test/library-tests/ir/ir/ssa_ir.expected | 54 ++++++++++++++++++ 18 files changed, 362 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll index 985cca77814..46a19ae787f 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll @@ -292,6 +292,20 @@ class BoolConversion extends Cast { } } +/** + * A conversion to `void`. + */ +class VoidConversion extends Cast { + VoidConversion() { + conversionkinds(this, 0) and + getType().getUnspecifiedType() instanceof VoidType + } + + override string getSemanticConversionString() { + result = "conversion to void" + } +} + /** * A conversion between two pointers or glvalues related by inheritance. The * base class will always be either a direct base class of the derived class, diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll index ed220bc7024..aa737b3978c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll @@ -67,6 +67,31 @@ module InstructionSanity { not tag instanceof UnmodeledUseOperand } + /** + * Holds if `Phi` instruction `instr` has fewer than two operands. + */ + query predicate missingPhiOperands(PhiInstruction instr, int predIndex, Location predLoc) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + predLoc = pred.getLocation() and + predIndex = pred.getDisplayIndex() and + not exists(PhiOperand operand | + exists(instr.getOperand(operand)) and + operand.getPredecessorBlock() = pred + ) + ) + } + + /** + * Holds if an instruction, other than `ExitFunction`, has no successors. + */ + query predicate instructionWithoutSuccessor(Instruction instr) { + not exists(instr.getASuccessor()) and + not instr instanceof ExitFunctionInstruction and + // Phi instructions aren't linked into the instruction-level flow graph. + not instr instanceof PhiInstruction + } + query predicate operandAcrossFunctions( Instruction op, Instruction operand, OperandTag tag ) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll index 922c2a0d95c..e419f7ad46a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll @@ -33,6 +33,7 @@ private newtype TOpcode = TPointerSub() or TPointerDiff() or TConvert() or + TConvertToVoid() or TConvertToBase() or TConvertToVirtualBase() or TConvertToDerived() or @@ -125,6 +126,7 @@ module Opcode { 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 ConvertToVoid extends UnaryOpcode, TConvertToVoid { override final string toString() { result = "ConvertToVoid" } } 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" } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll index eb233bf6bae..4e2cfc5dfd3 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedElement.qll @@ -89,7 +89,8 @@ private predicate ignoreElement(Element element) { * a value. */ private predicate isNativeCondition(Expr expr) { - expr instanceof BinaryLogicalOperation + expr instanceof BinaryLogicalOperation and + not expr.isConstant() } /** @@ -101,7 +102,8 @@ private predicate isFlexibleCondition(Expr expr) { expr instanceof ParenthesisExpr or expr instanceof NotExpr ) and - usedAsCondition(expr) + usedAsCondition(expr) and + not expr.isConstant() } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll index c259067867c..52ff72b9f3f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TranslatedExpr.qll @@ -1126,6 +1126,19 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio abstract Opcode getOpcode(); } +/** + * The translation of an explicit cast to `void`. + */ +class TranslatedVoidConversion extends TranslatedSingleInstructionConversion { + TranslatedVoidConversion() { + conv instanceof VoidConversion + } + + override Opcode getOpcode() { + result instanceof Opcode::ConvertToVoid + } +} + /** * Represents the translation of a conversion expression that generates a * `Convert` instruction. diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll index ed220bc7024..a3fa91f0a13 100644 --- a/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll @@ -67,6 +67,31 @@ module InstructionSanity { not tag instanceof UnmodeledUseOperand } + /** + * Holds if `Phi` instruction `instr` has fewer than two operands. + */ + query predicate missingPhiOperand(PhiInstruction instr, int predIndex, Location predLoc) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + predLoc = pred.getLocation() and + predIndex = pred.getDisplayIndex() and + not exists(PhiOperand operand | + exists(instr.getOperand(operand)) and + operand.getPredecessorBlock() = pred + ) + ) + } + + /** + * Holds if an instruction, other than `ExitFunction`, has no successors. + */ + query predicate instructionWithoutSuccessor(Instruction instr) { + not exists(instr.getASuccessor()) and + not instr instanceof ExitFunctionInstruction and + // Phi instructions aren't linked into the instruction-level flow graph. + not instr instanceof PhiInstruction + } + query predicate operandAcrossFunctions( Instruction op, Instruction operand, OperandTag tag ) { diff --git a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll index ed220bc7024..aa737b3978c 100644 --- a/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll @@ -67,6 +67,31 @@ module InstructionSanity { not tag instanceof UnmodeledUseOperand } + /** + * Holds if `Phi` instruction `instr` has fewer than two operands. + */ + query predicate missingPhiOperands(PhiInstruction instr, int predIndex, Location predLoc) { + exists(IRBlock pred | + pred = instr.getBlock().getAPredecessor() and + predLoc = pred.getLocation() and + predIndex = pred.getDisplayIndex() and + not exists(PhiOperand operand | + exists(instr.getOperand(operand)) and + operand.getPredecessorBlock() = pred + ) + ) + } + + /** + * Holds if an instruction, other than `ExitFunction`, has no successors. + */ + query predicate instructionWithoutSuccessor(Instruction instr) { + not exists(instr.getASuccessor()) and + not instr instanceof ExitFunctionInstruction and + // Phi instructions aren't linked into the instruction-level flow graph. + not instr instanceof PhiInstruction + } + query predicate operandAcrossFunctions( Instruction op, Instruction operand, OperandTag tag ) { diff --git a/cpp/ql/test/library-tests/conversions/conversions.cpp b/cpp/ql/test/library-tests/conversions/conversions.cpp index 5409a4ad572..5bc614cbf12 100644 --- a/cpp/ql/test/library-tests/conversions/conversions.cpp +++ b/cpp/ql/test/library-tests/conversions/conversions.cpp @@ -235,3 +235,16 @@ void FuncPtrConversions(int(*pfn)(int), void* p) { p = (void*)pfn; pfn = (int(*)(int))p; } + +int Func(); + +void ConversionsToVoid() { + int x; + (void)x; + static_cast(x); + (void)Func(); + static_cast(Func()); + (void)1; + static_cast(1); +} + diff --git a/cpp/ql/test/library-tests/conversions/conversions.expected b/cpp/ql/test/library-tests/conversions/conversions.expected index 348f7636f55..8c4d8af8b62 100644 --- a/cpp/ql/test/library-tests/conversions/conversions.expected +++ b/cpp/ql/test/library-tests/conversions/conversions.expected @@ -143,3 +143,9 @@ | conversions.cpp:231:28:231:63 | dynamic_cast... | dynamic_cast | lval | PolymorphicDerived | PolymorphicBase | | conversions.cpp:235:7:235:16 | (void *)... | pointer conversion | prval | void * | ..(*)(..) | | conversions.cpp:236:9:236:22 | (..(*)(..))... | pointer conversion | prval | ..(*)(..) | void * | +| conversions.cpp:243:3:243:9 | (void)... | conversion to void | prval | void | int | +| conversions.cpp:244:3:244:22 | static_cast... | conversion to void | prval | void | int | +| conversions.cpp:245:3:245:14 | (void)... | conversion to void | prval | void | int | +| conversions.cpp:246:3:246:27 | static_cast... | conversion to void | prval | void | int | +| conversions.cpp:247:3:247:9 | (void)... | conversion to void | prval | void | int | +| conversions.cpp:248:3:248:22 | static_cast... | conversion to void | prval | void | int | diff --git a/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected index f05c1067fb9..eef73161f00 100644 --- a/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/AliasedSSAIRSanity.expected @@ -1,4 +1,6 @@ missingOperand unexpectedOperand duplicateOperand +missingPhiOperand +instructionWithoutSuccessor operandAcrossFunctions diff --git a/cpp/ql/test/library-tests/ir/ir/IRSanity.expected b/cpp/ql/test/library-tests/ir/ir/IRSanity.expected index f05c1067fb9..6ac8b749416 100644 --- a/cpp/ql/test/library-tests/ir/ir/IRSanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/IRSanity.expected @@ -1,4 +1,6 @@ missingOperand unexpectedOperand duplicateOperand +missingPhiOperands +instructionWithoutSuccessor operandAcrossFunctions diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 5353e7dd66a..eefffaf9f62 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -5900,3 +5900,60 @@ ir.cpp: # 897| Type = __va_list_tag[1] # 897| ValueCategory = lvalue # 898| 8: return ... +# 900| CastToVoid(int) -> void +# 900| params: +# 900| 0: x +# 900| Type = int +# 900| body: { ... } +# 901| 0: ExprStmt +# 901| 0: (void)... +# 901| Conversion = conversion to void +# 901| Type = void +# 901| ValueCategory = prvalue +# 901| expr: x +# 901| Type = int +# 901| ValueCategory = lvalue +# 902| 1: return ... +# 904| ConstantConditions(int) -> void +# 904| params: +# 904| 0: x +# 904| Type = int +# 904| body: { ... } +# 905| 0: declaration +# 905| 0: definition of a +# 905| Type = bool +# 905| init: initializer for a +# 905| expr: ... && ... +# 905| Type = bool +# 905| Value = 1 +# 905| ValueCategory = prvalue +# 905| 0: 1 +# 905| Type = bool +# 905| Value = 1 +# 905| ValueCategory = prvalue +# 905| 1: 1 +# 905| Type = bool +# 905| Value = 1 +# 905| ValueCategory = prvalue +# 906| 1: declaration +# 906| 0: definition of b +# 906| Type = int +# 906| init: initializer for b +# 906| expr: ... ? ... : ... +# 906| Type = int +# 906| ValueCategory = prvalue +# 906| 0: (...) +# 906| Type = bool +# 906| Value = 1 +# 906| ValueCategory = prvalue +# 906| expr: 1 +# 906| Type = bool +# 906| Value = 1 +# 906| ValueCategory = prvalue +# 906| 1: x +# 906| Type = int +# 906| ValueCategory = prvalue(load) +# 906| 2: x +# 906| Type = int +# 906| ValueCategory = prvalue(load) +# 907| 2: return ... diff --git a/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected index f05c1067fb9..6ac8b749416 100644 --- a/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/SSAIRSanity.expected @@ -1,4 +1,6 @@ missingOperand unexpectedOperand duplicateOperand +missingPhiOperands +instructionWithoutSuccessor operandAcrossFunctions diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected index 827ad900efe..d055009b527 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_ir.expected @@ -3846,3 +3846,57 @@ ir.cpp: # 888| v0_40(void) = ReturnVoid : # 888| v0_41(void) = UnmodeledUse : mu* # 888| v0_42(void) = ExitFunction : + +# 900| CastToVoid(int) -> void +# 900| Block 0 +# 900| v0_0(void) = EnterFunction : +# 900| mu0_1(unknown) = UnmodeledDefinition : +# 900| r0_2(int) = InitializeParameter[x] : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| mu0_4(int) = Store : r0_3, r0_2 +# 901| r0_5(glval) = VariableAddress[x] : +# 901| v0_6(void) = ConvertToVoid : r0_5 +# 902| v0_7(void) = NoOp : +# 900| v0_8(void) = ReturnVoid : +# 900| v0_9(void) = UnmodeledUse : mu* +# 900| v0_10(void) = ExitFunction : + +# 904| ConstantConditions(int) -> void +# 904| Block 0 +# 904| v0_0(void) = EnterFunction : +# 904| mu0_1(unknown) = UnmodeledDefinition : +# 904| r0_2(int) = InitializeParameter[x] : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| m0_4(int) = Store : r0_3, r0_2 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| m0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 906| Block 1 +# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3 +# 906| r1_1(glval) = VariableAddress[#temp906:11] : +# 906| r1_2(int) = Load : r1_1, m1_0 +# 906| m1_3(int) = Store : r0_8, r1_2 +# 907| v1_4(void) = NoOp : +# 904| v1_5(void) = ReturnVoid : +# 904| v1_6(void) = UnmodeledUse : mu* +# 904| v1_7(void) = ExitFunction : + +# 906| Block 2 +# 906| r2_0(glval) = VariableAddress[x] : +# 906| r2_1(int) = Load : r2_0, m0_4 +# 906| r2_2(glval) = VariableAddress[#temp906:11] : +# 906| m2_3(int) = Store : r2_2, r2_1 +#-----| Goto -> Block 1 + +# 906| Block 3 +# 906| r3_0(glval) = VariableAddress[x] : +# 906| r3_1(int) = Load : r3_0, m0_4 +# 906| r3_2(glval) = VariableAddress[#temp906:11] : +# 906| m3_3(int) = Store : r3_2, r3_1 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 3460b095d49..fe0efe1248b 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -896,3 +896,12 @@ void VarArgUsage(int x, ...) { __builtin_va_end(args); __builtin_va_end(args2); } + +void CastToVoid(int x) { + (void)x; +} + +void ConstantConditions(int x) { + bool a = true && true; + int b = (true) ? x : x; +} diff --git a/cpp/ql/test/library-tests/ir/ir/ir.expected b/cpp/ql/test/library-tests/ir/ir/ir.expected index e1505b59d4b..9fc08cb6e52 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/ir.expected @@ -3826,3 +3826,56 @@ ir.cpp: # 888| v0_40(void) = ReturnVoid : # 888| v0_41(void) = UnmodeledUse : mu* # 888| v0_42(void) = ExitFunction : + +# 900| CastToVoid(int) -> void +# 900| Block 0 +# 900| v0_0(void) = EnterFunction : +# 900| mu0_1(unknown) = UnmodeledDefinition : +# 900| r0_2(int) = InitializeParameter[x] : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| mu0_4(int) = Store : r0_3, r0_2 +# 901| r0_5(glval) = VariableAddress[x] : +# 901| v0_6(void) = ConvertToVoid : r0_5 +# 902| v0_7(void) = NoOp : +# 900| v0_8(void) = ReturnVoid : +# 900| v0_9(void) = UnmodeledUse : mu* +# 900| v0_10(void) = ExitFunction : + +# 904| ConstantConditions(int) -> void +# 904| Block 0 +# 904| v0_0(void) = EnterFunction : +# 904| mu0_1(unknown) = UnmodeledDefinition : +# 904| r0_2(int) = InitializeParameter[x] : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| mu0_4(int) = Store : r0_3, r0_2 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| mu0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 906| Block 1 +# 906| r1_0(glval) = VariableAddress[#temp906:11] : +# 906| r1_1(int) = Load : r1_0, mu0_1 +# 906| mu1_2(int) = Store : r0_8, r1_1 +# 907| v1_3(void) = NoOp : +# 904| v1_4(void) = ReturnVoid : +# 904| v1_5(void) = UnmodeledUse : mu* +# 904| v1_6(void) = ExitFunction : + +# 906| Block 2 +# 906| r2_0(glval) = VariableAddress[x] : +# 906| r2_1(int) = Load : r2_0, mu0_1 +# 906| r2_2(glval) = VariableAddress[#temp906:11] : +# 906| mu2_3(int) = Store : r2_2, r2_1 +#-----| Goto -> Block 1 + +# 906| Block 3 +# 906| r3_0(glval) = VariableAddress[x] : +# 906| r3_1(int) = Load : r3_0, mu0_1 +# 906| r3_2(glval) = VariableAddress[#temp906:11] : +# 906| mu3_3(int) = Store : r3_2, r3_1 +#-----| Goto -> Block 1 diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected index 2fdcb215153..f3b1d0697b6 100644 --- a/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected +++ b/cpp/ql/test/library-tests/ir/ir/ssa_block_count.expected @@ -12,12 +12,14 @@ | IR: CallMin | 1 | | IR: CallNestedTemplateFunc | 1 | | IR: CallViaFuncPtr | 1 | +| IR: CastToVoid | 1 | | IR: Comma | 1 | | IR: CompoundAssignment | 1 | | IR: ConditionValues | 13 | | IR: Conditional | 4 | | IR: Conditional_LValue | 4 | | IR: Conditional_Void | 4 | +| IR: ConstantConditions | 4 | | IR: Constants | 1 | | IR: Continue | 6 | | IR: DeclareObject | 1 | diff --git a/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected b/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected index 6ced5bfd857..7b52191a0a8 100644 --- a/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/ssa_ir.expected @@ -3846,3 +3846,57 @@ ir.cpp: # 888| v0_40(void) = ReturnVoid : # 888| v0_41(void) = UnmodeledUse : mu* # 888| v0_42(void) = ExitFunction : + +# 900| CastToVoid(int) -> void +# 900| Block 0 +# 900| v0_0(void) = EnterFunction : +# 900| mu0_1(unknown) = UnmodeledDefinition : +# 900| r0_2(int) = InitializeParameter[x] : +# 900| r0_3(glval) = VariableAddress[x] : +# 900| mu0_4(int) = Store : r0_3, r0_2 +# 901| r0_5(glval) = VariableAddress[x] : +# 901| v0_6(void) = ConvertToVoid : r0_5 +# 902| v0_7(void) = NoOp : +# 900| v0_8(void) = ReturnVoid : +# 900| v0_9(void) = UnmodeledUse : mu* +# 900| v0_10(void) = ExitFunction : + +# 904| ConstantConditions(int) -> void +# 904| Block 0 +# 904| v0_0(void) = EnterFunction : +# 904| mu0_1(unknown) = UnmodeledDefinition : +# 904| r0_2(int) = InitializeParameter[x] : +# 904| r0_3(glval) = VariableAddress[x] : +# 904| m0_4(int) = Store : r0_3, r0_2 +# 905| r0_5(glval) = VariableAddress[a] : +# 905| r0_6(bool) = Constant[1] : +# 905| m0_7(bool) = Store : r0_5, r0_6 +# 906| r0_8(glval) = VariableAddress[b] : +# 906| r0_9(bool) = Constant[1] : +# 906| v0_10(void) = ConditionalBranch : r0_9 +#-----| False -> Block 3 +#-----| True -> Block 2 + +# 906| Block 1 +# 906| m1_0(int) = Phi : from 2:m2_3, from 3:m3_3 +# 906| r1_1(glval) = VariableAddress[#temp906:11] : +# 906| r1_2(int) = Load : r1_1, m1_0 +# 906| m1_3(int) = Store : r0_8, r1_2 +# 907| v1_4(void) = NoOp : +# 904| v1_5(void) = ReturnVoid : +# 904| v1_6(void) = UnmodeledUse : mu* +# 904| v1_7(void) = ExitFunction : + +# 906| Block 2 +# 906| r2_0(glval) = VariableAddress[x] : +# 906| r2_1(int) = Load : r2_0, m0_4 +# 906| r2_2(glval) = VariableAddress[#temp906:11] : +# 906| m2_3(int) = Store : r2_2, r2_1 +#-----| Goto -> Block 1 + +# 906| Block 3 +# 906| r3_0(glval) = VariableAddress[x] : +# 906| r3_1(int) = Load : r3_0, m0_4 +# 906| r3_2(glval) = VariableAddress[#temp906:11] : +# 906| m3_3(int) = Store : r3_2, r3_1 +#-----| Goto -> Block 1