2026 question paper
Compiler Design
25 questions
Q1a. Consider the statement: int total = 25; Which of the following represents a lexeme? (i) int (ii) 25 (iii) total (iv) All of the Above20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Consider the statement: int total = 25; Which of the following represents a lexeme?
(i) int
(ii) 25
(iii) total
(iv) All of the AboveWorked SolutionAnswer: (iv) All of the Above
Explanation
A lexeme is the actual sequence of characters in the source program that matches the pattern of a token. The scanner groups the characters of
int total = 25;into these lexemes:Lexeme Token class intkeyword totalidentifier =assignment operator 25integer constant ;separator So
int,totaland25are all lexemes.Q1b. The regular expression (a \mid b)^* represents: (i) Strings containing only a (ii) Strings containing only b (iii) All strings over \{a, b\}, including \varepsilon (iv) Strings beginning with ab20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →The regular expression represents:
(i) Strings containing only
(ii) Strings containing only
(iii) All strings over , including
(iv) Strings beginning withWorked SolutionAnswer: (iii) All strings over , including
Explanation
- matches exactly one symbol: either or .
- The Kleene star allows zero or more repetitions of that choice.
So the language is , i.e. every string over the alphabet , with (zero repetitions) included.
Why the other options fail:
- (i), (ii): they allow only one of the two symbols.
- (iv): it forces the string to start with , but also accepts , , etc.
Q1c. Which of the following is a problem for a top-down parser? (i) Left recursion (ii) Right recursion (iii) Ambiguity only (iv) Bottom-up derivation20262m
Module 2: Syntax Analysis (Parser)
View this question on its own page →Which of the following is a problem for a top-down parser?
(i) Left recursion
(ii) Right recursion
(iii) Ambiguity only
(iv) Bottom-up derivationWorked SolutionAnswer: (i) Left recursion
Explanation
A grammar is left recursive if it has a rule of the form .
A top-down (recursive-descent / LL) parser expands the leftmost non-terminal first. For it would call
A()which immediately callsA()again without consuming any input, so it falls into an infinite loop.Fix: eliminate left recursion
- Right recursion () is harmless for top-down parsers.
- Ambiguity affects all parsers, but it is not the characteristic problem of top-down parsing, and option (iii) says "only".
Q1d. In bottom-up parsing, the parser constructs: (i) Leftmost derivation (ii) Rightmost derivation (iii) Reverse of a rightmost derivation (iv) Leftmost derivation in reverse20262m
Module 2: Syntax Analysis (Parser)
View this question on its own page →In bottom-up parsing, the parser constructs:
(i) Leftmost derivation
(ii) Rightmost derivation
(iii) Reverse of a rightmost derivation
(iv) Leftmost derivation in reverseWorked SolutionAnswer: (iii) Reverse of a rightmost derivation
Explanation
A bottom-up (shift-reduce) parser starts from the input string and repeatedly reduces a handle to a non-terminal until the start symbol is reached.
Each reduction undoes one step of a rightmost derivation, so the sequence of reductions is a rightmost derivation traced backwards.
Example for on :
- Rightmost derivation:
- Reductions performed by the parser: (exactly the reverse).
(Top-down parsers build a leftmost derivation; options (i), (ii), (iv) are wrong.)
Q1e. Consider a symbol table implemented using hashing with table size 10. For key 27, using h(k) = k \bmod 10, the initial position is: (i) 5 (ii) 7 (iii) 6 (iv) 820262m
Module 3: Semantic Analysis, Symbol Table, Run-time environment
View this question on its own page →Consider a symbol table implemented using hashing with table size . For key , using , the initial position is:
(i)
(ii)
(iii)
(iv)Worked SolutionAnswer: (ii) 7
Calculation
because . The key is initially placed at index of the hash table.
Note: if slot is already occupied (a collision, e.g. key ), it is resolved by chaining (linked list at slot 7) or open addressing (linear probing to slot 8, 9, ...).
Q1f. Under static scoping, the meaning of a variable reference is determined primarily by: (i) Runtime call sequence (ii) CPU register contents (iii) Heap location (iv) Lexical program structure20262m
Module 3: Semantic Analysis, Symbol Table, Run-time environment
View this question on its own page →Under static scoping, the meaning of a variable reference is determined primarily by:
(i) Runtime call sequence
(ii) CPU register contents
(iii) Heap location
(iv) Lexical program structureWorked SolutionAnswer: (iv) Lexical program structure
Explanation
In static (lexical) scoping, a variable reference is bound to the declaration found by looking at the textual nesting of blocks/procedures in the source code: first the innermost enclosing block, then the next one outward, and so on.
- The binding can be decided at compile time, without running the program.
- The call sequence at run time (option i) decides the binding only in dynamic scoping.
- Registers (ii) and heap location (iii) have nothing to do with name resolution.
int x = 10; void f() { printf("%d", x); } // x refers to the global x void g() { int x = 20; f(); } // still prints 10 under static scopingQ1g. Which optimization eliminates repeated computation of the same expression? (i) Loop interchange (ii) Dead-code elimination only (iii) Common subexpression elimination (iv) Register allocation20262m
Module 4: Intermediate Code Generation, Code Improvement (optimization)
View this question on its own page →Which optimization eliminates repeated computation of the same expression?
(i) Loop interchange
(ii) Dead-code elimination only
(iii) Common subexpression elimination
(iv) Register allocationWorked SolutionAnswer: (iii) Common subexpression elimination
Explanation
Common subexpression elimination (CSE) detects an expression that was already computed and whose operands have not changed since, and reuses the earlier result instead of recomputing it.
Before:
x = a * b + c y = a * b + dAfter CSE:
t = a * b x = t + c y = t + dThe other options: loop interchange changes loop order (cache behaviour); dead-code elimination removes useless code; register allocation maps variables to registers.
Q1h. Which pipeline hazard occurs when an instruction depends on the result of a previous instruction? (i) Structural hazard (ii) Data hazard (iii) Syntax hazard (iv) Memory hazard20262m
Module 5: Architecture Dependent Code Improvement
View this question on its own page →Which pipeline hazard occurs when an instruction depends on the result of a previous instruction?
(i) Structural hazard
(ii) Data hazard
(iii) Syntax hazard
(iv) Memory hazardWorked SolutionAnswer: (ii) Data hazard
Explanation
A data hazard occurs when an instruction needs a value that a previous, still-in-flight instruction has not yet written back. The most common form is RAW (read-after-write):
ADD R1, R2, R3 ; writes R1 SUB R4, R1, R5 ; reads R1 -> must wait for the ADD resultRemedies: pipeline stalls, forwarding (bypassing), and compiler instruction scheduling (placing independent instructions in between).
- Structural hazard: two instructions need the same hardware resource at the same time.
- Control hazard: caused by branches.
- "Syntax hazard" and "memory hazard" are not standard pipeline hazard classes.
Q1i. Dynamic method dispatch is primarily used to implement: (i) Lexical analysis (ii) Constant folding (iii) Runtime polymorphism (iv) Register allocation20262m
Module 6: Advanced Topics
View this question on its own page →Dynamic method dispatch is primarily used to implement:
(i) Lexical analysis
(ii) Constant folding
(iii) Runtime polymorphism
(iv) Register allocationWorked SolutionAnswer: (iii) Runtime polymorphism
Explanation
Dynamic method dispatch selects which overridden method to call based on the actual (run-time) type of the object, not the declared type of the reference.
Compilers implement it with a virtual method table (vtable): each object carries a hidden pointer to its class's vtable, and a virtual call becomes an indirect call through a fixed slot index.
Animal a = new Dog(); a.speak(); // Dog.speak() is chosen at run timeThis is exactly runtime (dynamic) polymorphism. It has nothing to do with lexical analysis, constant folding or register allocation.
Q1j. Which statement is true regarding DFA and NFA? (i) NFA is strictly more powerful than DFA (ii) DFA and NFA recognize the same class of languages (iii) DFA is strictly more powerful than NFA (iv) Every NFA requires more states than every equivalent DFA20262m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Which statement is true regarding DFA and NFA?
(i) NFA is strictly more powerful than DFA
(ii) DFA and NFA recognize the same class of languages
(iii) DFA is strictly more powerful than NFA
(iv) Every NFA requires more states than every equivalent DFAWorked SolutionAnswer: (ii) DFA and NFA recognize the same class of languages
Explanation
Both DFAs and NFAs recognize exactly the regular languages.
- Every DFA is already an NFA (a special case).
- Every NFA can be converted to an equivalent DFA by the subset construction.
So neither is more powerful, which rules out (i) and (iii). An NFA is often smaller than the equivalent DFA: an NFA with states may need up to DFA states. That is a size difference only, and option (iv) is false because many NFAs need no more states than their DFAs (a DFA is itself an NFA).
- Install PocketlyBudget App•POCKETLY
Pocketly on Android
Log daily food, travel, and hostel expenses instantly. Set monthly spending limits and see where your money goes without complex spreadsheets.
Q2a. Explain the different phases of compilation with a suitable example. Show the input and output produced at each phase.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Explain the different phases of compilation with a suitable example. Show the input and output produced at each phase.
Worked SolutionPhases of a compiler
A compiler translates a source program into target code in a sequence of phases. Two components work with every phase: the symbol table (stores names and attributes) and the error handler (reports errors with line numbers).
MermaidDiagramRendering diagram…Running example:
position = initial + rate * 60whereposition,initial,ratearefloat.Phase Task Output for the example 1. Lexical analysis Scans characters, groups them into lexemes and produces tokens; enters identifiers into the symbol table; removes whitespace and comments <id,1> <=> <id,2> <+> <id,3> <*> <num,60>2. Syntax analysis Checks token order against the grammar and builds a parse/syntax tree Syntax tree (diagram below) 3. Semantic analysis Type checking, declaration checks, type conversions (coercion) 60is int,rateis float, so insertinttofloat(60)4. Intermediate code generation Produces machine-independent code, usually three-address code t1 = inttofloat(60)t2 = id3 * t1t3 = id2 + t2id1 = t35. Code optimization Improves speed/size: constant folding, copy propagation, dead-code removal t1 = id3 * 60.0id1 = id2 + t16. Code generation Selects instructions, allocates registers, emits target code LDF R2, id3MULF R2, R2, #60.0LDF R1, id2ADDF R1, R1, R2STF id1, R1Input and output summary
- Lexical analyzer: character stream to token stream.
- Syntax analyzer: tokens to syntax tree.
- Semantic analyzer: syntax tree to type-checked (annotated) tree.
- Intermediate code generator: annotated tree to three-address code.
- Optimizer: intermediate code to better intermediate code.
- Code generator: optimized intermediate code to target code.
The first three phases form the analysis (front end); the last three form the synthesis (back end).
Syntax tree (phase 2)
MermaidDiagramRendering diagram…Tree after semantic analysis (phase 3): the integer constant is converted to float.
MermaidDiagramRendering diagram…Q2b. Construct a DFA over \{0, 1\} that accepts all strings containing an even number of 0s and an odd number of 1s. Verify the constructed DFA using suitable accepted and rejected input strings.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →Construct a DFA over that accepts all strings containing an even number of s and an odd number of s. Verify the constructed DFA using suitable accepted and rejected input strings.
Worked SolutionDFA for: even number of 0s and odd number of 1s over
Idea: track two parities, the parity of the number of 0s and the parity of the number of 1s seen so far. That gives states.
State #0s #1s (start) even even odd even (final) even odd odd odd Reading a
0flips the parity of 0s; reading a1flips the parity of 1s. Accepting state: (even 0s, odd 1s).Transition table
State on 0 on 1 Transition diagram
MermaidDiagramRendering diagram…Formally .
Verification
Input Path Final state 0s / 1s Result 10 / 1 Accepted 0012 / 1 Accepted 100112 / 3 Accepted 011 / 1 Rejected 01102 / 2 Rejected 110 / 2 Rejected 0 / 0 Rejected Every accepted string has an even count of 0s and an odd count of 1s, and every rejected one violates at least one condition, so the DFA is correct.
Q3a. For a lexical analyzer, design regular expressions to recognize the following token classes: (i) Identifiers beginning with a letter and containing letters and digits thereafter. (ii) Unsigned integer constants. (iii) Relational operators <, <=, >, >=, ==, and !=.20267m
Module 1: Introduction, Lexical Analysis
View this question on its own page →For a lexical analyzer, design regular expressions to recognize the following token classes:
(i) Identifiers beginning with a letter and containing letters and digits thereafter.
(ii) Unsigned integer constants.
(iii) Relational operators and .Worked SolutionRegular expressions for the token classes
Let
(i) Identifiers (start with a letter, then letters or digits)
Accepted:
total,x1,rate2023. Rejected:2abc(starts with a digit).(ii) Unsigned integer constants
Accepted:
0,25,1000. Rejected:-5(sign),3.5(not an integer).(iii) Relational operators
Factored form:
The scanner always takes the longest match, so
<=is read as one token, not<followed by=. A single=is the assignment operator, not a relational operator.Recognizer for relop (transition diagram)
MermaidDiagramRendering diagram…Final states:
LT(<),GT(>),LE(<=),GE(>=),EQEQ(==),NE(!=). The statesEQ1andNOTare not final because=alone (assignment) and!alone are not relational operators here.Q3b. Consider the grammar: S \to ABC A \to aA \mid \varepsilon B \to bB \mid \varepsilon C \to cC \mid d Compute the \text{FIRST} and \text{FOLLOW} sets for all non-terminals. Using these, construct the \text{LL}(1) parsing table. Use the parsing table to show the parsing steps for the input string "aabd".20267m
Module 2: Syntax Analysis (Parser)
View this question on its own page →Consider the grammar:
Compute the and sets for all non-terminals. Using these, construct the parsing table. Use the parsing table to show the parsing steps for the input string "aabd".Worked SolutionGrammar
, , ,
FIRST sets
- (since and can both vanish, is reached)
FOLLOW sets
- $\mathrm{FOLLOW}(S) = {$}$
- (from , as is nullable)
- $\mathrm{FOLLOW}(C) = \mathrm{FOLLOW}(S) = {$}$
Non-terminal FIRST FOLLOW ${$}$ ${$}$ LL(1) parsing table
- : , so fill .
- on ; on .
- on ; on .
- on ; on .
| | | | | | \0 | `aabd
| | | 2 | ABC\1 | `aabd| matcha|
| 4 | ABC\2 | `abd| matcha| | 6 | ABC\3 | `bd| |
| 8 | bBC\4 | `d| | | 10 | C\5 | `d| matchd|
| 12 | $$` | accept |The string
aabdis accepted.Q4a. Explain ambiguity in context-free grammars. Illustrate how ambiguity can lead to shift-reduce or reduce-reduce conflicts during LR parsing.20267m
Module 2: Syntax Analysis (Parser)
View this question on its own page →Explain ambiguity in context-free grammars. Illustrate how ambiguity can lead to shift-reduce or reduce-reduce conflicts during LR parsing.
Worked SolutionAmbiguity in a context-free grammar
A CFG is ambiguous if some string in its language has more than one parse tree (equivalently, more than one leftmost derivation or more than one rightmost derivation).
Example: on the string has two parse trees.
MermaidDiagramRendering diagram…The two trees give different meanings (different evaluation order), so the compiler cannot decide which one is intended.
How ambiguity causes conflicts in LR parsing
An LR parser must decide in every state whether to shift or reduce. An ambiguous grammar can never be LR(k) for any , so its LR table always has at least one multiply-defined entry.
1. Shift-reduce conflict. For the grammar above, after reading
E + Ethe parser reaches a state containing both itemsOn the next input symbol
*it could reduce by (giving tree 2) or shift*(giving tree 1). The table entry for (state,*) holds bothsandr.2. Reduce-reduce conflict. Take , , . The string
ahas two parse trees. After shiftingathe state containsOn lookahead `A \to aB \to a$: two reduce actions in one cell.
Removing the conflicts
- Rewrite the grammar to enforce precedence and associativity: , , .
- Or keep the grammar and resolve conflicts with rules (precedence:
*binds tighter than+; left associativity), as Yacc/Bison do. - Shift is the default choice in a shift-reduce conflict; the earlier rule is chosen in a reduce-reduce conflict.
Q4b. Construct a transition diagram for the Pushdown Automaton for the language: L = \{ wcw^R \mid w \in \{a, b\}^* \}20267m
Module 2: Syntax Analysis (Parser)
View this question on its own page →Construct a transition diagram for the Pushdown Automaton for the language:
Worked SolutionPDA for
Idea
- In state , push every symbol of on the stack.
- On reading the middle marker
c, move to without touching the stack. - In , each input symbol must match the stack top; pop it. This checks the second half is (the stack reverses ).
- When the input is finished and only remains, move to the accepting state .
Formal definition: (acceptance by final state).
Transition function (notation: input, stack top / new stack top)
# Move 1 2 3 4 5 6 7 for 8 9 10 Transition diagram
MermaidDiagramRendering diagram…Trace for
abcbaState Remaining input Stack (top left) Move abcbapush abcbapush bcbaread c, go tobabmatches, popaamatches, pop-move to accept A string such as
abcabis rejected: aftercthe stack top isbbut the input isa, so no move exists. The PDA is deterministic becausecmarks the middle.Q5a. Explain the structure of an activation record and its role during procedure execution.20267m
Module 3: Semantic Analysis, Symbol Table, Run-time environment
View this question on its own page →Explain the structure of an activation record and its role during procedure execution.
Worked SolutionActivation record (stack frame)
An activation record is the block of memory pushed on the run-time stack each time a procedure is called. It holds everything one activation of the procedure needs. Records are pushed on a call and popped on return, so the stack mirrors the chain of active calls.
Structure of an activation record
SVGVector Architecture Diagram (SVG)Field Purpose Returned value Space where the callee stores its result for the caller Actual parameters Arguments supplied by the caller Control (dynamic) link Points to the caller's activation record, so the stack can be unwound on return Access (static) link Points to the record of the lexically enclosing procedure, used to reach non-local variables Saved machine status Return address and the register contents of the caller, restored on return Local variables Data declared inside the procedure Temporaries Intermediate values of expression evaluation Role during procedure execution
- Call sequence. The caller evaluates and stores the actual parameters and the return address; the callee saves registers, sets the frame pointer
fp, and allocates space for locals and temporaries. - Execution. Local variables are accessed at a fixed offset from
fp. Non-local variables are reached by following access links (static scope) the required number of levels, then adding the offset. Global variables live in static memory with a fixed address. - Return sequence. The callee stores the return value, restores the saved registers and
fp, pops its record, and jumps to the return address. - Recursion. Every call gets its own record, so each recursive activation has its own copy of locals and parameters.
Accessing variables: for a local
xuse[fp + offset_x]. For a non-localydeclared nesting levels up, follow the access link times to that procedure's record, then use[record + offset_y].- Call sequence. The caller evaluates and stores the actual parameters and the return address; the callee saves registers, sets the frame pointer
Q5b. Discuss the structure of a symbol table and the information stored for each identifier.20267m
Module 3: Semantic Analysis, Symbol Table, Run-time environment
View this question on its own page →Discuss the structure of a symbol table and the information stored for each identifier.
Worked SolutionSymbol table
A symbol table is a data structure used by the compiler to store information about every identifier (variables, constants, functions, labels, types, parameters) found in the source program. It is created during lexical/syntax analysis and used by every later phase.
Structure
Each entry is a record, and the table is organized for fast lookup and insertion.
MermaidDiagramRendering diagram…Common organizations:
Implementation Lookup Remarks Unordered linear list Simple, only for tiny programs Ordered list / binary search tree Slower insertion Hash table average Most common; collisions resolved by chaining Handling scope: one table per scope, chained to its enclosing scope (or a single stack-like table). Entering a block creates a new table; leaving it discards that table. Lookup searches the innermost scope first, then outwards.
Operations:
insert(name, attributes),lookup(name),delete/ exit scope,modify attributes.Information stored for each identifier
Attribute Meaning Name The lexeme (or an index into a string table) Kind / category Variable, constant, function, procedure, parameter, label, type name Data type int, float, char, array, struct, pointer, ... Size Bytes needed (for arrays: element type and dimensions/bounds) Scope / nesting level Block or procedure where the name is visible Storage location Address or offset in the activation record (or static address) For functions Number, order and types of parameters, return type, parameter-passing mode For records/classes Fields, their types and offsets Line numbers Declaration and usage lines, for error messages and cross-reference Other flags Initialized or not, constant value, access mode Example: for
float rate;declared inmain, the entry is<rate, variable, float, 4 bytes, level 1, offset 8>.Use by the phases: the scanner inserts names, the parser and semantic analyzer add type and scope information and check for undeclared or redeclared names, type checking reads types, and the code generator reads sizes and offsets.
Q6a. Generate the three-address code for the following expression. Then represent the generated code using quadruple, triple, and indirect-triple representations. x = a * (b + c) - d / e20267m
Module 4: Intermediate Code Generation, Code Improvement (optimization)
View this question on its own page →Generate the three-address code for the following expression. Then represent the generated code using quadruple, triple, and indirect-triple representations.
Worked SolutionExpression:
Operator precedence and parentheses give the evaluation order: first, then , then , then the subtraction, then the assignment.
Three-address code
t1 = b + c t2 = a * t1 t3 = d / e t4 = t2 - t3 x = t4Syntax tree of the expression (each internal node becomes one three-address instruction, bottom-up)
MermaidDiagramRendering diagram…(i) Quadruples (op, arg1, arg2, result)
# op arg1 arg2 result 0 +b c t1 1 *a t1 t2 2 /d e t3 3 -t2 t3 t4 4 =t4 x (ii) Triples (op, arg1, arg2): there is no result field; a result is referred to by the number of the triple that computed it.
# op arg1 arg2 (0) +b c (1) *a (0) (2) /d e (3) -(1) (2) (4) =x (3) (iii) Indirect triples: the triples are the same as above, but a separate instruction list holds pointers to them. The order of execution can then be changed by reordering the pointers without touching the triples.
Instruction list Points to 100 (0) 101 (1) 102 (2) 103 (3) 104 (4) Triple # op arg1 arg2 (0) +b c (1) *a (0) (2) /d e (3) -(1) (2) (4) =x (3) Comparison
Form Temporaries in symbol table Easy to reorder for optimization Quadruples Yes (explicit result names) Yes Triples No No (numbers refer to positions) Indirect triples No Yes (reorder the pointer list) Q6b. Explain the construction of basic blocks and control-flow graphs (CFGs) in compiler design using a suitable three-address code example.20267m
Module 4: Intermediate Code Generation, Code Improvement (optimization)
View this question on its own page →Explain the construction of basic blocks and control-flow graphs (CFGs) in compiler design using a suitable three-address code example.
Worked SolutionBasic block
A basic block is a maximal sequence of consecutive three-address instructions with one entry point (only the first instruction can be reached from outside) and one exit point (control leaves only at the last instruction, with no jumps into or out of the middle).
Algorithm to find basic blocks
- Find the leaders (first instructions of blocks):
- the first instruction of the program;
- the target of any conditional or unconditional jump;
- the instruction immediately after a jump (conditional or unconditional).
- For each leader, the basic block consists of the leader and all instructions up to (but not including) the next leader or the end of the program.
Example three-address code
1 sum = 0 2 i = 1 3 if i > 10 goto 8 4 t1 = i * 2 5 sum = sum + t1 6 i = i + 1 7 goto 3 8 print sumLeaders: instruction 1 (first), 3 (target of the jump at 7), 4 (follows the conditional jump at 3), 8 (target of the jump at 3 and follows the jump at 7). So the leaders are .
Block Instructions 1-2 3 4-7 8 Control-flow graph (CFG)
A CFG has the basic blocks as nodes. There is an edge if control can pass from the end of to the start of : either by a jump from the last instruction of to the leader of , or because follows in program order and does not end in an unconditional jump.
MermaidDiagramRendering diagram…Uses: the CFG exposes loops (here ), and is the basis for data-flow analysis, local optimizations inside a block, and global optimizations such as code motion and dead-code elimination.
- Find the leaders (first instructions of blocks):
- Install PocketlyBudget App•POCKETLY
Pocketly on Android
Log daily food, travel, and hostel expenses instantly. Set monthly spending limits and see where your money goes without complex spreadsheets.
Q7a. Explain cache locality and its importance in loop optimization.20267m
Module 5: Architecture Dependent Code Improvement
View this question on its own page →Explain cache locality and its importance in loop optimization.
Worked SolutionCache locality
A cache is a small, fast memory between the CPU and main memory. Data moves in blocks called cache lines (typically 64 bytes). A program runs faster when most of its memory accesses are hits. Cache locality is the tendency of a program to access data that is already in, or close to, the data in the cache.
Two kinds of locality
Type Meaning Example Temporal locality A location that was just used is likely to be used again soon A loop variable or a running sum accessed in every iteration Spatial locality Locations near a recently used one are likely to be used soon Walking through consecutive array elements When one element is fetched, the whole line comes with it (for example 16 consecutive 4-byte ints), so sequential access gives about 1 miss per 16 accesses, while a large stride can give a miss on every access.
Why it matters in loop optimization
Loops execute most of a program's instructions and memory accesses. A cache miss costs on the order of 100 cycles against 1-4 for a hit, so the order in which a loop nest touches memory often matters more than the arithmetic it does.
Example (C arrays are stored row by row)
// Poor spatial locality: column-wise walk, stride = N elements for (j = 0; j < N; j++) for (i = 0; i < N; i++) sum += a[i][j]; // After loop interchange: row-wise walk, stride = 1 for (i = 0; i < N; i++) for (j = 0; j < N; j++) sum += a[i][j];Both loops compute the same result, but the second uses every element of each fetched cache line before it is evicted.
SVGVector Architecture Diagram (SVG)Loop transformations that improve locality
Transformation Effect Loop interchange Reorders nested loops so the innermost loop has stride 1 Loop tiling (blocking) Splits the iteration space into small blocks that fit in the cache so data is reused (key for matrix multiplication) Loop fusion (jamming) Merges loops that use the same data so it is used while still in cache Loop fission (distribution) Splits a loop whose body touches too much data for the cache Array padding / data layout change Avoids conflict misses; structure-of-arrays vs array-of-structures The compiler applies these only after a dependence analysis shows that the new order gives the same results.
Q7b. Explain the major stages involved in target code generation in a compiler. Discuss instruction selection, register allocation, and instruction scheduling, and explain how each stage contributes to generating efficient target machine code.20267m
Module 5: Architecture Dependent Code Improvement
View this question on its own page →Explain the major stages involved in target code generation in a compiler. Discuss instruction selection, register allocation, and instruction scheduling, and explain how each stage contributes to generating efficient target machine code.
Worked SolutionTarget code generation
The code generator takes the (optimized) intermediate representation and the symbol-table information and produces target machine code that is correct and efficient.
MermaidDiagramRendering diagram…Major stages
- Instruction selection - choose target instructions for each IR operation.
- Register allocation and assignment - decide which values live in registers.
- Instruction scheduling (ordering) - choose the order of instructions.
- Final clean-up such as peephole optimization, then emit the code.
1. Instruction selection
Maps IR operations to machine instructions, choosing the cheapest sequence that implements them. The choice depends on the instruction set and addressing modes. Techniques: tree-pattern matching, dynamic programming over expression trees.
For
a = b + ca naive translation isLD R0, b LD R1, c ADD R0, R0, R1 ST a, R0and for
x = x + 1a good selector uses a singleINC x(or an add with an immediate) instead of load, add and store.Contribution: decides the number and cost of instructions, so the code is small and fast, and uses special instructions where available.
2. Register allocation
Registers are far faster than memory but very few. Allocation decides which variables/temporaries to keep in registers; assignment picks the specific register. Methods: usage counts, graph colouring (variables that are live at the same time interfere and get different registers). If there are not enough registers, a value is spilled to memory.
Contribution: reduces loads and stores, which dominate execution time.
3. Instruction scheduling
Reorders independent instructions to avoid pipeline stalls and to use all functional units, without changing the program's result (data dependences must be respected).
Assume a load takes one extra cycle before its result can be used:
Unscheduled Scheduled LD R1, aLD R1, aADD R2, R1, R3(stall)LD R4, bLD R4, bADD R2, R1, R3ADD R5, R4, R6(stall)ADD R5, R4, R6The scheduled version has no stalls.
Contribution: hides latency and increases instruction-level parallelism, so the program runs faster on the pipeline.
Interaction: the stages influence each other. Scheduling more instructions in parallel needs more registers, and heavy register reuse creates dependences that limit scheduling. Compilers therefore balance the two.
Summary
Stage Goal Result Instruction selection Correct, low-cost instruction sequence Fewer, cheaper instructions Register allocation Keep hot values in registers Fewer memory accesses Instruction scheduling Avoid stalls, fill pipeline Fewer cycles Q8a. Discuss the major challenges involved in compiling object-oriented features such as classes, inheritance, and polymorphism.20267m
Module 6: Advanced Topics
View this question on its own page →Discuss the major challenges involved in compiling object-oriented features such as classes, inheritance, and polymorphism.
Worked SolutionCompiling object-oriented features: main challenges
Object-oriented languages add classes, objects, inheritance and polymorphism on top of ordinary procedural features. The compiler must map these to plain memory layouts and jumps.
MermaidDiagramRendering diagram…1. Object layout (classes)
An object is stored as a record of its fields, plus a hidden pointer to its class's method table. The compiler must compute field offsets and the total size.
SVGVector Architecture Diagram (SVG)2. Inheritance
- A subclass object must be usable wherever a superclass object is expected, so the inherited fields keep the same offsets as in the parent (the subclass fields are appended).
- Multiple inheritance breaks this: an object has several parent parts, so the compiler needs several vptrs and must adjust the
thispointer when converting to a base type; shared (virtual) base classes need extra indirection. - Constructors and destructors must run in the right order (base first on construction, derived first on destruction).
3. Polymorphism and dynamic dispatch
The method that runs depends on the object's run-time type, not on the declared type. The compiler builds a virtual method table (vtable) for each class and compiles a virtual call as an indirect call through a fixed slot:
Slot Animal vtable Dog vtable 0 Animal::speak Dog::speak (overrides) A call
a.speak()is compiled to an indirect call through the vptr and the fixed slot ofspeak:MermaidDiagramRendering diagram…This costs an extra memory load and an indirect jump, and prevents simple inlining; optimizers use devirtualization (when the exact type is known) to recover speed.
4. Other challenges
Challenge Why it is hard Type checking with subtyping Assignments are legal from subclass to superclass; overriding rules (parameter and return types) must be checked Overloading and name mangling Several methods share a name; the compiler selects one by argument types and encodes the signature in the symbol name Access control private,protected,publicchecks at compile timethispointerPassed as a hidden first argument to every instance method Run-time type information Needed for casts, instanceof, exceptionsInterfaces and abstract classes Dispatch through interface tables; abstract methods have no body Memory management Allocation on heap, garbage collection or destructors Separate compilation Changing a base class's layout forces recompilation of all subclasses Q8b. Consider the following grammar: E \to E + T \mid T T \to T * F \mid F F \to \text{digit} Define suitable synthesized attributes to evaluate an arithmetic expression. Construct the annotated syntax tree and evaluate the following expression: 3 + 5 * 220267m
Module 3: Semantic Analysis, Symbol Table, Run-time environment
View this question on its own page →Consider the following grammar:
Define suitable synthesized attributes to evaluate an arithmetic expression. Construct the annotated syntax tree and evaluate the following expression:Worked SolutionGrammar: , ,
Synthesized attribute: give every non-terminal , , an attribute
val(its numeric value). The terminaldigithas the attributelexvalsupplied by the lexical analyzer.Syntax-directed definition
Production Semantic rule Every attribute is computed from the attributes of the children, so
valis synthesized and the definition is S-attributed (it can be evaluated during bottom-up parsing).Annotated parse tree for
Because of the grammar's structure,
*sits lower in the tree than+, so5 * 2is evaluated first.MermaidDiagramRendering diagram…Evaluation (bottom-up, post-order)
Step Node Rule used Value 1 (digit 3) 3 2 over that 3 3 over that 3 4 (digit 5) 5 5 over that 5 6 (digit 2) 2 7 10 8 13 Q9. Write a short note on any two of the following: (a) \text{SLR}(1) parser (b) Syntax Directed Definition (c) Peephole optimization (d) Attribute grammar202614m
Module 2: Syntax Analysis (Parser)
View this question on its own page →Write a short note on any two of the following:
(a) parser
(b) Syntax Directed Definition
(c) Peephole optimization
(d) Attribute grammarWorked SolutionOnly two notes are required; all four are given here (7 marks each).
(a) SLR(1) parser
SLR(1) (Simple LR with 1 lookahead symbol) is a bottom-up LR parser whose states are the sets of LR(0) items, and which uses the FOLLOW sets to decide when to reduce.
Construction steps
- Augment the grammar with .
- Build the canonical collection of LR(0) item sets using
closureandgoto. - Fill the ACTION/GOTO table:
- in and gives shift on terminal .
- in gives reduce for every terminal in (this is the SLR rule).
- gives accept on
$. - gives GOTO entry for non-terminal .
The grammar is SLR(1) if the table has no conflicts.
Power: SLR(1) is stronger than LR(0) but weaker than LALR(1) and canonical LR(1), because FOLLOW(A) is a coarse lookahead that ignores the context of the item and can cause spurious shift-reduce conflicts. It has the same small number of states as LR(0) and LALR(1).
(b) Syntax Directed Definition (SDD)
An SDD is a context-free grammar in which each grammar symbol has attributes and each production has semantic rules that compute the attributes. It specifies the translation (evaluation of expressions, type checking, code generation) tied to the syntax.
Production Semantic rule - Synthesized attribute: computed from the attributes of the node's children (bottom-up).
- Inherited attribute: computed from the parent and/or siblings (top-down or sideways).
- S-attributed SDD: uses only synthesized attributes (suits bottom-up parsing). L-attributed SDD: each inherited attribute depends only on the parent and the siblings to its left (suits one left-to-right pass).
- The rules are evaluated on the annotated parse tree; the order must respect the dependency graph. An SDD may contain rules with side effects (for example printing or updating the symbol table); an SDD whose rules have no side effects is an attribute grammar. Unlike a syntax-directed translation scheme, an SDD does not fix where actions run; it only states the rules.
(c) Peephole optimization
A peephole optimizer examines a small sliding window of target (or intermediate) instructions, the peephole (typically 2-5 instructions), and replaces patterns by shorter or faster equivalents. It may be applied repeatedly because one change can create new opportunities.
Technique Before After Redundant load/store elimination ST a, R1<br>LD R1, aST a, R1Constant folding x = 3 + 4x = 7Algebraic simplification x = x + 0,x = x * 1removed Strength reduction x = x * 2x = x << 1Use of machine idioms ADD R1, R1, #1INC R1Unreachable code removal goto L2followed by instructions with no labelinstructions removed Control-flow optimization goto L1 ... L1: goto L2goto L2Advantages: simple, local, machine-specific gains; limitation: it cannot see beyond the window, so it complements global optimization.
(d) Attribute grammar
An attribute grammar is a context-free grammar extended with attributes attached to grammar symbols and semantic (attribute evaluation) rules attached to productions. Formally, it is a CFG together with a set of attributes (partitioned into synthesized and inherited) and rules that define each attribute purely as a function of other attributes, with no side effects. An attribute grammar is thus an SDD without side effects, used to define the static semantics of a language.
Example (type declaration, uses an inherited attribute):
Production Semantic rule Here is synthesized and is inherited: the type flows from to every identifier in the list.
Evaluation: build the dependency graph of attribute instances on the parse tree; if it has no cycle, evaluate the attributes in a topological order. Attribute grammars are used for type checking, symbol-table construction and intermediate code generation.
Coursify Android App
Generate AI courses, take quick quizzes between classes, and master complex engineering concepts on the go.