Solution
Correct option: 4)
Given TAC
100: t1 = y + 2
101: initial = x / t1
102: limit = 10
103: if i > j goto 105
104: goto 111
105: if num > b goto 107
106: goto 111
107: num = limit - 1
108: b = y + 2
109: i = i - num
110: goto 103
111: j = num
112: k = j + b
What “optimized version” means here
- Apply standard local optimizations that do not change the program’s semantics:
- Common-Subexpression Elimination (CSE)
- Constant Folding/Propagation
- Copy Propagation & Dead-Code Elimination
- Control-Flow simplification (remove useless gotos / merge branches)
Step-by-step optimization on the TAC
- CSE for
y + 2
y + 2 is computed in 100 (t1 = y + 2) and later recomputed at 108 (b = y + 2). Since y is not modified anywhere in the code, the value of y + 2 is loop-invariant. Replace the recomputation by the already available temporary:
108: b = t1
- Control-flow simplification around 105–107–106
The pattern
105: if num > b goto 107
106: goto 111
107: …
is equivalent to a single conditional jump to the “else” target:
105: if num <= b goto 111
followed by the original “then” part (the statements from 107 onward). This removes an unconditional goto and one label without changing behaviour.
- Constant folding (optional)
Since limit = 10, the assignment num = limit - 1 can be folded to num = 9. (Keeping it as num = limit - 1 is also acceptable; both are semantically identical.)
- Copy propagation near the exit
At the exit we have:
111: j = num
112: k = j + b
Since j is just a temporary copy of num used immediately, we can propagate the copy and remove the assignment to j:
111: k = num + b
(This is a standard local copy-propagation + dead-assignment elimination.)
The optimized TAC that results from these safe transformations (laid out in the same structure as the original) is exactly what is shown in Option 4:
100: t1 = y + 2
101: initial = x / t1
102: limit = 10
103: if i > j goto 105
104: goto 111
105: if num <= b goto 111 // merged (105,106)
106: num = limit - 1 // (could be folded to 9)
107: b = t1 // CSE: reuse y+2
108: i = i - num
109: goto 103
110: k = num + b // copy propagation of j (j removed)
Why the other options are not correct
- Option 1 keeps the redundant recomputation of
y+2, alters the update to i in a way that changes the use of num, and discards the needed num/j flow (semantics change).
- Option 2 tests
num against b before updating b in that iteration and drops labels in a way that changes the control flow; it also replaces the final computation by k = i + b, which is not equivalent.
- Option 3 rewrites the program by inserting a fresh assignment to
num and changing the label structure, thereby altering the original loop’s behaviour.
Conclusion
Only Option 4 applies the standard, semantics-preserving optimizations (CSE, branch merging, and copy propagation) without changing the program’s meaning, so it is the correct optimized version.