The Institute of Management Sciences, Lahore
Quiz 3
Semester Fall-2023
BSCS
Section: A
Course Title: CSC-362 Compiler Construction
Course Instructor: Asma Arshad Total Marks:10
Roll No: 211310 Name: ZUNAIRA SHAHID
Q1): Identify and describe at least five semantic checks that need to be performed during
the semantic analysis phase. Include checks related to type compatibility, scope rules,
variable initialization, function signatures, and control flow.
Criteria for evaluating code optimization effectiveness, along with methods of measuring their
impact:
Criteria for Evaluation
● Execution Time (Speed): One of the primary goals of optimization is to make code run
faster. You want to see a decrease in execution time after applying optimizations.
Code Size (Memory Footprint): Reduced code size is especially important in embedded
systems or for code that needs to be transmitted over a network. Smaller code consumes
less storage and might be more efficient for instruction caching.
● Memory Usage (Dynamic Allocation): Optimizing how code uses memory can prevent
excessive heap allocations or stack overflows. This is crucial in resource-constrained
environments or during real-time tasks.
● Energy Consumption: Reducing the number of computations or memory accesses can
lead to lower energy consumption, which is critical for battery-powered devices.
● Maintainability: While not explicitly performance-related, it's important that optimizing
code doesn't sacrifice its understandability. Overly complex optimizations can make
future changes or bug fixes more difficult.
Methods for Measuring Impact
1. Profiling:
● Steps: Profile the code before and after optimization. Many compilers and
development environments provide profiling tools.
● Output: Profilers give detailed breakdowns of where the code is spending its time
(which functions or blocks) and resource usage statistics (memory allocations or
cache performance).
● Analysis: Comparing the before and after profiles helps identify the specific areas
where optimizations have the most significant impact.
2. Benchmarking:
● Steps: Create benchmark tests with representative workloads that exercise the
code under optimization.
● Measurement: Run the benchmarks before and after optimization, tracking
execution time for these representative tasks.
● Analysis: Compare performance metrics from the benchmarks to quantify the
overall improvement from optimizations.
3. Code Size Analysis:
● Tools: Compilers often provide options to report the generated assembly or
executable file size. There are also dedicated tools for analyzing code size and
symbol usage.
● Analysis: Comparing the generated code size before and after optimization
provides insights into the reduction achieved.
4. Memory Usage Monitoring:
● Tools: Utilize tools for monitoring memory usage (like Valgrind) to track heap
allocations, potential memory leaks, and stack usage during execution.
● Analysis: Compare the memory usage patterns before and after optimization to
identify reductions in dynamic memory usage.
5. Energy Measurement:
● Tools: For systems with supported hardware, use energy profiling tools to
measure power consumption patterns directly.
● Estimation: If direct measurement isn't feasible, estimate energy consumption
improvements based on reductions in execution time and the known power
characteristics of the hardware.
By carefully choosing appropriate evaluation criteria and using a combination of the
measurement techniques above, you can effectively assess the impact of code optimization on
various aspects of performance and resource efficiency.
Q2): Describe how you would handle scope resolution during semantic analysis. Discuss
strategies for managing variable scopes, including global scope, function scope, block
scope, and nested scopes.
Scope resolution is a crucial step in semantic analysis, determining where to find the definition
of a variable used in a program. Here's how I would handle it:
1. Symbol Table:
● would use a symbol table data structure to keep track of all declared variables
within different scopes.
● The symbol table would store information about each variable, including its:
○ Name
○ Data type
○ Scope (global, function, or block)
2. Scope Stack:
● I would maintain a scope stack to keep track of the currently active scope.
● When entering a new scope (e.g., function or block), I would push a new symbol
table onto the stack, representing the new scope.
● When exiting a scope, I would pop the top symbol table from the stack,
effectively "closing" that scope.
3. Variable Lookup:
● When encountering a variable name during analysis:
○ I would first search the symbol table at the top of the scope stack (current
scope).
○ If the variable is found, its information (type, etc.) is retrieved for further
processing.
○ If the variable isn't found in the current scope:
■ I would pop the stack and search the symbol table of the previous
scope (parent scope).
■ This process continues until the variable is found or the global
scope is reached.
○ If the variable is not found in any scope, an error is reported as the
variable is likely undeclared or used before being defined.
4. Nested Scopes:
● For nested scopes, the same principle applies. When entering a nested scope, a
new symbol table is pushed onto the stack, and lookups first search the nested
scope's table before moving upwards through the stack.
5. Global Scope:
● The global scope is always present at the bottom of the stack and acts as the
outermost scope where variables declared outside any function or block reside.
Strategies for Managing Different Scopes:
● Global Scope: Use it sparingly for truly global variables that need to be accessed
throughout the program. Excessive use of global variables can lead to naming
conflicts and make code harder to maintain.
● Function Scope: Variables declared within functions are only accessible within
that function and its nested scopes. This promotes better code organization and
reduces the risk of unintended modifications.
● Block Scope: Some languages (like Python) have block scope, where variables
declared within a code block (e.g., if statement or for loop) are only accessible
within that block. This further enhances code modularity and prevents naming
conflicts across different code sections.
Benefits of Proper Scope Management:
● Prevents naming conflicts: Ensures different variables with the same name don't
interfere with each other.
● Improves code clarity: Makes it clear where variables are accessible and avoids
confusion.
● Enables modularity: Allows functions and blocks to have their own independent
variable space, promoting better code organization and maintainability.
By implementing these strategies, I can effectively handle scope resolution during
semantic analysis, ensuring accurate variable usage and promoting well-structured and
maintainable code.