Compiler
Compiler
The small set of instructions or small part of code on which peephole optimization is performed is
known as peephole or window.
It basically works on the theory of replacement in which a part of code is replaced by shorter and faster
code without a change in output. The peephole is machine-dependent optimization.
On other hand an attribute is said to be Inherited attribute if its parse tree node value is determined by
the attribute value at parent and/or sibling nodes. In case of S → ABC if A can get values from S, B and C.
B can take values from S, A, and C. Likewise, C can take values from S, A, and B then S is said to be
Inherited Attribute.
3.a. What are the main contributions of syntax directed translation in the
compiler?
The syntax-directed translation scheme is beneficial because it allows the compiler designer to define
the generation of intermediate code directly in terms of the syntactic structure of the source language.
It is division into two subsets known as synthesized and inherited attributes of grammar.
4. What is Type Checking?
Type checking is the process of verifying and enforcing constraints of types in values. A compiler must
check that the source program should follow the syntactic and semantic conventions of the source
language and it should also check the type rules of the language. It allows the programmer to limit what
types may be used in certain circumstances and assigns types to values. The type-checker determines
whether these values are used appropriately or not.
It checks the type of objects and reports a type error in the case of a violation, and incorrect types are
corrected. Whatever the compiler we use, while it is compiling the program, it has to follow the type
rules of the language. Every language has its own set of type rules for the language. We know that the
information about data types is maintained and computed by the compiler.
5.a. What is Augmented Grammar?
An augmented grammar is any grammar whose productions are augmented with conditions expressed
using features. Features may be associated with any nonterminal symbol in a derivation. A feature
associated with a nonterminal symbol is shown following that nonterminal separated from it by a "."
OR
The augmented grammar introduces a new start symbol and a new production rule. The new start
symbol represents the entire program or input to be parsed, and the new production rule defines the
relationship between the start symbol and the original start symbol of the grammar.
The goal of loop optimization is to transform the loop code to reduce unnecessary computations,
minimize memory access, exploit parallelism, and enhance cache utilization. By making the loop more
efficient, loop optimization aims to decrease the number of instructions executed, reduce memory
accesses, and improve the utilization of hardware resources.
In a dependency graph, nodes represent individual instructions or operations, and edges represent
dependencies between them. The dependencies can be classified into two main types:
a. Data Dependency: Data dependencies represent the flow of data between instructions. They
indicate that the output of one instruction is dependent on the input or result of another
instruction. Data dependencies can be further categorized into three types:
Read-after-Write (RAW) dependency: A node reads data that was written by a previous node.
Write-after-Read (WAR) dependency: A node writes data that will be subsequently read by a
later node.
Write-after-Write (WAW) dependency: Two nodes write to the same location, and the order of
writes affects the final value.
b. Control Dependency: Control dependencies represent the flow of control within a program.
They indicate the ordering and conditions for executing instructions. Control dependencies are
typically represented by conditional branches, loops, and jumps.
In an L-Attributed definition, the evaluation of attributes follows a top-down, left-to-right (hence the "L"
in L-Attributed) traversal of the parse tree or the input. It means that the attributes of a grammar
symbol can depend only on the attributes of its immediate left siblings and its own inherited attributes
(attributes passed from its parent).
No Cycles: There should be no cyclic dependencies among attributes. In other words, the dependencies
among attributes should form a directed acyclic graph (DAG). This ensures that attribute evaluation can
proceed without encountering circular dependencies.
Locality: The attributes of a grammar symbol can be computed using only information available at or
above that symbol's position in the parse tree. The attributes should not depend on information from
distant siblings or descendants in the tree.
Synthesized and Inherited Attributes: The attributes can be classified into two types: synthesized
attributes and inherited attributes. Synthesized attributes are computed from the attributes of child
nodes, while inherited attributes are passed from parent nodes to child nodes. In an L-Attributed
definition, synthesized attributes are typically computed before inherited attributes.
Together, LEX and YACC (or Flex and Bison, their modern counterparts) provide a powerful toolset for
building the front-end of a compiler. LEX handles the tokenization phase by generating efficient lexical
analyzers, while YACC handles the parsing phase by generating parsers based on context-free grammars.
By using these tools, developers can focus on defining the language syntax and semantics rather than
implementing the low-level details of tokenizing and parsing.
Input buffering is an important step in the lexical analysis phase, which is the initial stage of the
compiler. The purpose of input buffering is to reduce the overhead of reading characters or tokens one
at a time from the source program, which can be time-consuming and inefficient. Instead, input
buffering allows the lexical analyzer to retrieve a block or a batch of characters or tokens from memory,
improving the overall efficiency of the scanning process.
The buffering mechanism typically involves a buffer or a queue that temporarily stores the input
characters or tokens read from the source program. The size of the buffer can vary depending on the
specific implementation or requirements of the compiler.