Three-address code is simple to construct and convert to machine code. It employs just three places and one operator to describe an expression, and the value computed at each instruction is saved in a compiler-generated temporary variable. Three address codes in the compiler determine the sequence of operations.
Three-address code, often known as TAC or 3AC, is an intermediate code in computer science that is used by optimizing compilers to help in the implementation of code-improving transformations. At most, three operands can be used in a TAC instruction, which is normally made up of an assignment and a binary operator. For illustration, t1:= t2 + t3. Despite the possibility of instructions with fewer operands, the name of these statements comes from their use of three operands.
Since three-address code is used as an intermediate language within compilers, the operands will most likely not be physical memory addresses or processor registers but rather symbolic addresses that will be transformed into actual addresses during register allocation. Because three-address code is often generated by the compiler, operand names are frequently numbered sequentially.
General illustration
a = b op c
Where a, b, or c stand for operands such as names, constants, compiler-generated temporaries, and op stands for the operator.
There can only be one operator on the right side of instruction in a three-address code; no built-up arithmetic expressions are allowed. As a result, a source-language statement like x+y*z might be translated into a three-address instruction sequence.
t₁=y*z
t₂=x+t₁
Where t₁ and t₂ are temporary names produced by the compiler. The unwinding of multi-operator arithmetic expressions and nested flow-of-control statements make three-address code ideal for target-code creation and optimization. Using names for the intermediate values computed by a computer makes it simple to rearrange three-address codes.
Examples
1. Convert a = (-c * b) + (-c * d) into three address code.
In the target program, t is utilized as a register.
2. For the following code, write the three address codes.
for(i = 1; i<=10; i++) { a[i]=x * 5; }
Solution
i=1
L:t1=x*5
t2=&a
t3=sizeof(int)
t4=t3*i
t5=t2+t4
*t5=t1
i=i+1
if i<10 goto L
3 Address Code is used in Compiler Applications
Three-address code (3AC) is a crucial concept in compiler design and has several important applications in the compilation process:
Intermediate Representation (IR)
3AC serves as an intermediate representation of the source code. It simplifies the complexity of high-level language constructs into a format that's easier to analyze, optimize, and translate into machine code. It provides a structured representation of program semantics that retains essential information while abstracting away from the specifics of the source language.
Code Optimization
3AC facilitates various optimization techniques such as constant folding, common subexpression elimination, dead code elimination, and loop optimizations. Because 3-address code is relatively simple and uniform, it becomes easier for compilers to apply optimization algorithms to improve the efficiency and performance of generated code.
Register Allocation
Register allocation is a critical optimization phase where the compiler assigns variables and temporary values to processor registers. 3AC can be transformed into a form suitable for register allocation algorithms, helping compilers efficiently utilize available hardware resources.
Code Generation
Once the code has been optimized and registered allocated, 3-address code can be translated into the target machine code. The simplicity and structured nature of 3AC make code generation more manageable and enable compilers to produce efficient and correct machine code for different target architectures.
Target Independence
3-address code abstracts away from the intricacies of specific hardware architectures, allowing compilers to generate code for various target platforms from the same intermediate representation. This level of abstraction makes it easier to port compilers across different architectures and optimize code for multiple platforms.
Implementation of Three Address Code
There are three different ways to express three address codes:
Quadruple
Triples
Indirect Triples
Quadruple
It is a structure that has four fields: op, arg1, arg2, and result. The operator is denoted by op, arg1, and arg2 denote the operands, and the result is used to record the outcome of the expression.
These quadruples play a crucial role in breaking down high-level language statements into more digestible parts, facilitating compilation-stage analysis and optimization procedures.
Benefits of Quadrule
For global optimization, it's simple to restructure code.
Using a symbol table, one may rapidly get the value of temporary variables.
Drawbacks of Quadrule
There are a lot of temporary items.
The establishment of temporary variables adds to the time and space complexity.
Example
Convert a = -b * c + d into three address codes.
The following is the three-address code:
t₁ = -b
t₂ = c + d
t₃ = t₁ * t₂
a = t₃
Quadruples are used to symbolize these statements:
#
Op
Arg1
Arg2
Result
(0)
unimus
b
-
t₁
(1)
+
c
d
t₂
(2)
*
t₁
t₂
t₃
(3)
=
t₃
-
a
Triples
Instead of using an additional temporary variable to represent a single action, a pointer to the triple is utilized when a reference to another triple's value is required. As a result, it only has three fields: op, arg1, and arg2.
Benefits of Triples
Triples make it easier to analyze and optimize code by disassembling difficult high-level language constructs into smaller, more manageable parts
Triples facilitate error, data flow, and control flow analysis of code, facilitating improved debugging and comprehension
Drawbacks of Triples
Temporaries are difficult to rearrange since they are implicit.
It's tough to optimize since it necessitates the relocation of intermediary code. When a triple is relocated, all triples that relate to it must likewise be changed. The symbol table entry can be accessed directly using the pointer.
This approach employs a pointer to a list of all references to computations that are created and kept separately. Its usefulness is comparable to quadruple representation, however, it takes up less space. Temporaries are easy to rearrange since they are implicit.
Benefits of Indirect Triples
For languages that use dynamic memory allocation and pointer manipulation, indirect triples are essential for representing complex pointer operations and memory accesses
They simplify the intricate address calculations needed for nested structures, multi-dimensional arrays, and other memory architectures
Drawbacks of Indirect Triples
Indirect triples can increase the complexity of the intermediate representation and optimization phases of the compiler, complicating the design and implementation of the compiler
Due to the additional pointer dereferencing and memory access operations required by using indirect triples, there may be performance overhead that could slow down execution
Example
Convert a = b * – c + b * – c into three address codes.
The following is the three-address code:
t1 = uminus c
t2 = b * t1
t3 = uminus c
t4 = b * t3
t5 = t2 + t4
a = t5
#
Op
Arg1
Arg2
(14)
unimus
c
-
(15)
*
(14)
b
(16)
unimus
c
-
(17)
*
(16)
b
(18)
+
(15)
(17)
(19)
=
a
(18)
List of pointers to the table
#
Statement
(0)
(14)
(1)
(15)
(2)
(16)
(3)
(17)
(4)
(18)
(5)
(19)
Frequently Asked Questions
What are the three address codes?
Three-address code is a simple, intermediate code to construct and convert to machine code. It employs just three places and one operator to describe an expression, and the value computed at each instruction is saved in a compiler-generated temporary variable.
How do I create a 3 address code for declaration?
For declaration in 3-address code, assign memory addresses to variables: temp = allocate_memory(type, size).
3-address code grammar is a notation used in compiler design to represent intermediate code with three operands. It typically consists of instructions with the format: result = operand1 operator operand2, facilitating code optimization and translation into machine code.
Conclusion
In this article, we have discussed the three address codes. We have discussed how to implement three address codes and their need. We have also discussed three ways to implement three address codes: quadruple, triple, and indirect. So basically, the compiler uses three address codes to determine the sequence of operations.