download 2021 site
This commit is contained in:
commit
dd6e4afb13
138 changed files with 37730 additions and 0 deletions
644
lab/11.html
Normal file
644
lab/11.html
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Eelco Visser">
|
||||
<meta name="generator" content="Jekyll v3.8.5">
|
||||
<title>Lab 11: Implementing Register Allocation and Control Flow</title>
|
||||
<!-- <base href="/2021"> -->
|
||||
|
||||
<!--link rel="canonical" href="https://getbootstrap.com/docs/4.3/examples/starter-template/"-->
|
||||
|
||||
<link rel="icon" href="../img/logo/pl_ico2_2B3_icon.ico" type="image/x-icon">
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<!--link href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"-->
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
text-anchor: middle;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bd-placeholder-img-lg {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="../css/main.css" rel="stylesheet">
|
||||
<link href="../css/borders-responsive.css" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../css/pl.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
TU Delft | CS4200
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../lectures/index.html" tabindex="-1" aria-disabled="true">Lectures</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../homework/index.html" tabindex="-1" aria-disabled="true">Homework</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../project/index.html" tabindex="-1" aria-disabled="true">Project</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../news/index.html" tabindex="-1" aria-disabled="true">News</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="../blog/index.html" tabindex="-1" aria-disabled="true">Blog</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-12">
|
||||
|
||||
|
||||
<div class="mt-3 mb-3 pt-3 pb-3 text-dark border-top border-bottom border-grey">
|
||||
<div class="text-dark font-weight-bold">
|
||||
<h1>
|
||||
|
||||
Lab 11: Implementing Register Allocation and Control Flow
|
||||
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
Project
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
December 03, 2021
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 border-lg-right border-grey">
|
||||
<h2 id="register-allocation">Register Allocation</h2>
|
||||
|
||||
<p>In this lab you implement register allocation taking the implementation from the slides as basis.</p>
|
||||
|
||||
<h3 id="liveness-analysis">Liveness Analysis</h3>
|
||||
|
||||
<p>Implement liveness analysis to determine when (at what instructions) variables are live.
|
||||
Extend the analysis to deal with control flow and loops.
|
||||
This requires a fixed point analysis.</p>
|
||||
|
||||
<h3 id="graph-coloring">Graph Coloring</h3>
|
||||
|
||||
<p>Next implement register allocation bases on the outcome of the liveness analysis.
|
||||
Use the priority queue data structure to represent the interference graph with the nodes representing variables with the most inference listed first.
|
||||
Coloring the graph with a limited number of registers.
|
||||
How do you treat spilling variables to memory?</p>
|
||||
|
||||
<h2 id="control-flow">Control Flow</h2>
|
||||
|
||||
<p>Extend the fragment of the language that is supported by your compiler with control-flow constructs such as if-then-else and booleans.</p>
|
||||
|
||||
<h3 id="desugaring">Desugaring</h3>
|
||||
|
||||
<p>Define desugaring rules to reduce the number of constructs that the downstream compiler has to deal with.
|
||||
Consider whether or for which language constructs this is worthwhile; the target machine may have instructions that correspond directly to the source language operators.</p>
|
||||
|
||||
<h3 id="explicating-control-translation-to-c-ir">Explicating Control: Translation to C-IR</h3>
|
||||
|
||||
<p>Translate the recursive expressions for control flow in ChocoPy to jumps in the control flow graph.</p>
|
||||
|
||||
<h3 id="instruction-selection">Instruction Selection</h3>
|
||||
|
||||
<p>Translate to RISC-V instructions.</p>
|
||||
|
||||
<!-- In this lab you extend your ChocoPy code generator with translation of function definitions and function calls.
|
||||
|
||||
See the slides of [Lecture 13](/2021/lecture/13) on code generation mechanics and the [ChocoPy v2.2: RISC-V Implementation Guide](https://chocopy.org/chocopy_implementation_guide.pdf) for an explanation of the calling convention for ChocoPy.
|
||||
|
||||
### Objectives
|
||||
|
||||
1. Compiling functions
|
||||
- Compiling function definitions according to calling convention
|
||||
- Compiling variables referring to formal parameters and local variables
|
||||
- Generate code for function calls according to calling convention
|
||||
- Define a transformation that lifts nested function calls to top-level assignment statements.
|
||||
2. Integrate the execution environment
|
||||
3. Challenges
|
||||
- Optimize generated code
|
||||
- Alternative calling conventions
|
||||
|
||||
### Context-Sensitive Transformation
|
||||
|
||||
In the previous lab you used rewrite rules to transform source ASTs to target ASTs.
|
||||
The transformations were context-free. That is, an addition operation can be translated without any information about the context in which it appears.
|
||||
This is not the case for transformations in a compiler.
|
||||
For example, compiling a variable in an expression requires knowing where it is stored on the stack, i.e. its offset from the frame pointer.
|
||||
|
||||
The slides of [Lecture 13](/2021/lecture/13) introduce the use of dynamic rewrite rules in Stratego to implement such context-sensitive transformations. See the paper by Bravenboer et al. (2006) referenced on the lecture page for an overview, including applications of dynamic rules.
|
||||
We discuss some examples here that may be useful for your compiler.
|
||||
|
||||
#### Dynamic Rewrite Rules
|
||||
|
||||
A dynamic rewrite rule in Stratego is like a normal rewrite rule, but it is generated at run-time (dynamically) and some of its variables are bound in the context of its definition.
|
||||
For example, consider the following definition of a constant propagation transformation strategy that propagates assignments of constants to variables to uses of those variables (in straight-line code):
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
prop-const =
|
||||
PropConst
|
||||
<+ prop-const-assign
|
||||
<+ (all(prop-const); try(eval))
|
||||
|
||||
prop-const-assign =
|
||||
Assign([Target(?x)], prop-const => e)
|
||||
; if <is-value> e
|
||||
then rules( PropConst : Var(x) -> e )
|
||||
else rules( PropConst :- Var(x) )
|
||||
end
|
||||
|
||||
eval : Add(Int(i), Int(j)) -> Int(<addS>(i, j))
|
||||
|
||||
is-value = ?Int(_)
|
||||
```
|
||||
|
||||
The construct `rules( PropConst : Var(x) -> e )` generates a new dynamic rule for the specific values of `x` and `e` found in the context.
|
||||
Thus, if `PropConst` is later applied to a concrete variable for which a rule has been defined, it is replaced with the corresponding right-hand side.
|
||||
If a different rule had been defined previously for `Var(x)`, the new rule overwrites the old one.
|
||||
|
||||
The construct `rules( PropConst :- Var(x) )` undefines all dynamic `PropConst` rules for `Var(x)`.
|
||||
|
||||
We discuss some examples of applications of dynamic rules that can be useful in your compiler.
|
||||
|
||||
#### Keeping Track of the Stack
|
||||
|
||||
Dynamic rules can be used to implement a counter. For example, the following rules keep track of stack offsets:
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
stack-set(|n) =
|
||||
rules(Stack : () -> n); !n
|
||||
|
||||
stack-get =
|
||||
<Stack>() <+ !0
|
||||
|
||||
stack-inc(|n) =
|
||||
stack-set(|<add>(<stack-get>, n))
|
||||
```
|
||||
|
||||
Dynamic rules can be scoped using the construct `{| L : s}`, where `L` is the label of the dynamic rule. For example, the stack rules above can be used in a transformation to generate subsequent stack offset and at end retrieve the total offset needed:
|
||||
|
||||
```
|
||||
{| Stack
|
||||
: stack-set(|0)
|
||||
; <some-transformation> t => instrs
|
||||
; size := <stack-get>
|
||||
|}
|
||||
```
|
||||
|
||||
After leaving the scope, all definitions for `Stack` in the scope are eliminated and the value before the scope is available again. For example,
|
||||
the code
|
||||
|
||||
```
|
||||
stack-set(|10)
|
||||
; debug(!"stack: ")
|
||||
; {| Stack
|
||||
: stack-set(|20)
|
||||
; debug(!"stack: ")
|
||||
|}
|
||||
; stack-get
|
||||
; debug(!"stack: ")
|
||||
```
|
||||
|
||||
will print
|
||||
|
||||
```
|
||||
stack: 10
|
||||
stack: 20
|
||||
stack: 10
|
||||
```
|
||||
|
||||
#### Binding Variable Offsets
|
||||
|
||||
Another application of dynamic rules is to distribute contex-sensitive information about bindings. For example, the following rules bind an offset with respect to the framepointer to a variable name:
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
var-offset-set(|x, n) =
|
||||
rules(VarOffset : x -> n)
|
||||
|
||||
var-offset-get :
|
||||
x -> n
|
||||
with <VarOffset> x => n
|
||||
```
|
||||
|
||||
This can be used in handling the formal parameters of a function to associate with parameter the next offset in the stack:
|
||||
|
||||
```
|
||||
rules
|
||||
fun-arg :
|
||||
TypedVar(x, t) -> offset
|
||||
with var-offset-set(|x, <stack-get => offset>)
|
||||
with stack-inc(|4)
|
||||
```
|
||||
|
||||
Then, when translating a variable, the dynamic rule can be used to lookup its offset:
|
||||
|
||||
```
|
||||
rules
|
||||
exp-to-instrs-(|r, regs) :
|
||||
Var(x) -> [Lw(r, <int-to-string>offset, "fp")]
|
||||
with <var-offset-get>x => offset
|
||||
``` -->
|
||||
|
||||
<!-- #### Collecting Information
|
||||
|
||||
```
|
||||
rules
|
||||
|
||||
transform :
|
||||
Foo(x, y) -> Bar(x)
|
||||
where rules( Collect :+ )
|
||||
|
||||
``` -->
|
||||
|
||||
<!--
|
||||
### Compiling Functions
|
||||
|
||||
The main objective of this lab is to implement the compilation of function definitions and function calls.
|
||||
We illustrate the process with the following example ChocoPy program:
|
||||
|
||||
```
|
||||
def callee(x : int, y : int, z: int) -> int:
|
||||
a : int = 1
|
||||
b : int = 2
|
||||
return x + y + z + a + b
|
||||
|
||||
def caller():
|
||||
d : int = 0
|
||||
d = callee(345, 4357, 235)
|
||||
```
|
||||
|
||||
#### Compiling Function Definitions
|
||||
|
||||
Compiling a function definition consists of generating code for setting up and breaking down an activation record (aka call frame).
|
||||
This process follows a calling convention such that caller and callee agree about how to pass arguments to and return results from function calls.
|
||||
The process starts with advancing the stack pointer register `sp` so that the call frame has enough space for the local variables and tempories in the call.
|
||||
Then, the return address (`ra`) and frame pointer (`fp`) registers are saved in the call frame so that they are not overwritten on subsequent calls.
|
||||
Next, the local variables are initialized.
|
||||
This is followed by the computation of the body of the function, leaving the return value in the `a0` register.
|
||||
Finally, the return address and frame pointer registers are restored, and the function jumps to the return address.
|
||||
|
||||
The function `callee` above is translated as follows:
|
||||
|
||||
```
|
||||
.globl $callee
|
||||
$callee:
|
||||
addi sp, sp, -@callee.size # reserve space for stack frame
|
||||
sw ra, @callee.size-4(sp) # save return address
|
||||
sw fp, @callee.size-8(sp) # save control link (fp)
|
||||
sw fp, @callee.size(sp) # new fp is at old SP
|
||||
li a0, 1 # initialize local variable a
|
||||
sw a0, -16(fp)
|
||||
li a0, 2 # initialize local variable b
|
||||
sw a0, -12(fp)
|
||||
lw a0, 8(fp) # load argument x
|
||||
lw t0, 4(fp) # load argument y
|
||||
add a0, a0, t0 # x + y
|
||||
lw t0, 0(fp) # load argument z
|
||||
add a0, a0, t0 # (x + y) + z
|
||||
lw t0, -16(fp) # load local variable a
|
||||
add a0, a0, t0 # (x + y + z) + a
|
||||
lw t0, -12(fp) # load local variable b
|
||||
add a0, a0, t0 # (x + y + z + a) + b
|
||||
j label_47
|
||||
mv a0, zero
|
||||
j label_47
|
||||
label_47:
|
||||
.equiv @callee.size, 16
|
||||
lw ra, -4(fp) # restore return address
|
||||
lw fp, -8(fp) # restore frame pointer
|
||||
addi sp, sp, @callee.size # restore stack pointer
|
||||
jr ra # return to caller
|
||||
```
|
||||
|
||||
In this translation you should compute the size of the stack frame needed to hold all local variables, temporaries, return address, and frame pointer
|
||||
|
||||
#### Compiling Variables
|
||||
|
||||
To compile variables bind the stack offsets of formal parameters and local variables and transfer these offsets from definition site to use site. (See the discussion about dynamic rewrite rules above.)
|
||||
Given these offsets using a variable and updating a variable is a matter of loading the variable from the stack into a register (see the `lw` instructions in the code above), and storing the value a register into the stack (see the `sw` instructions in the code above).
|
||||
|
||||
#### Compiling Function Calls
|
||||
|
||||
Executing a function call requires computing the values of the actual parameters and transferring these to the code of the function following the calling convention.
|
||||
The default ChocoPy calling convention is to pass arguments on the stack _in the same order as the function expects them_.
|
||||
|
||||
For example, the following ChocoPy function call
|
||||
|
||||
```
|
||||
def caller():
|
||||
d : int = 0
|
||||
d = callee(345, 4357, 235)
|
||||
```
|
||||
|
||||
is translated to the following RISC-V code:
|
||||
|
||||
```
|
||||
.globl $caller
|
||||
$caller:
|
||||
addi sp, sp, -@caller.size
|
||||
sw ra, @caller.size-4(sp)
|
||||
sw fp, @caller.size-8(sp)
|
||||
sw fp, @caller.size(sp)
|
||||
li a0, 0 # initialize local variable d
|
||||
sw a0, -12(fp)
|
||||
sw sp, -12(sp) # reserve space for arguments
|
||||
li a0, 345 # evaluate first argument
|
||||
sw a0, 12(sp) # push on stack
|
||||
li a0, 4357 # evaluate second argument
|
||||
sw a0, 8(sp) # push on stack
|
||||
li a0, 235 # evaluate third argument
|
||||
sw a0, 4(sp) # push on stack
|
||||
jal $callee # call function
|
||||
sw sp, 12(sp) # clean up stack
|
||||
sw a0, -12(fp) # return value in a0
|
||||
j label_48
|
||||
mv a0, zero
|
||||
j label_48
|
||||
label_48:
|
||||
.equiv @caller.size, 12
|
||||
lw ra, -4(fp)
|
||||
lw fp, -8(fp)
|
||||
addi sp, sp, @caller.size
|
||||
jr ra
|
||||
```
|
||||
|
||||
#### Lifting Nested Function Calls
|
||||
|
||||
When using registers to store the intermediate values of an expression, a function call in the middle of such an expression may overwrite such registers.
|
||||
One approach for avoiding this is to lift function calls to the top-level statements in the context.
|
||||
For example, the nested call to `inc` in the following function
|
||||
|
||||
```
|
||||
def caller():
|
||||
d : int = 0
|
||||
d = callee(345 + 81 + inc(13), 4357, 235)
|
||||
```
|
||||
|
||||
can be lifted to top-level using an extra local variable:
|
||||
|
||||
```
|
||||
def caller( ) :
|
||||
d : int = 0
|
||||
temp_2 : int = 0
|
||||
temp_2 = inc(13)
|
||||
d = callee(345 + 81 + temp_2, 4357, 235)
|
||||
```
|
||||
|
||||
Define a transformation that lifts nested functions to top-level.
|
||||
You can use `<newname> "temp_"` to generate a unique new name.
|
||||
|
||||
#### Execution Environment
|
||||
|
||||
The ChocoPy language includes an execution envionrment consisting of a suite of standard functions such as `print`.
|
||||
We provide implementations of these functions as a set of Stratego strategies in the project template.
|
||||
Integrate this execution environment in the code your compiler generates.
|
||||
|
||||
### Challenges
|
||||
|
||||
#### Challenge: Optimizing Generated Code
|
||||
|
||||
Generating code in a systematic way will generate combinations of instructions that are not very efficient.
|
||||
For example, storing a register on the stack only to retrieve it from the stack in the next instruction.
|
||||
As a challenge, consider avoiding to generate such instructions or to transform the generated code
|
||||
|
||||
#### Challenge: Alternative Calling Convention
|
||||
|
||||
The ChocoPy calling convention uses the stack to pass all arguments to functions. Design an alternative calling convention that passes the first `n` arguments via registers (for some small value of `n`). For functions with few arguments or that doesn't call other functions, this may be quite efficient. (But if a function calls another function, it may end saving the registers on the stack after all.)
|
||||
|
||||
|
||||
### Challenges
|
||||
|
||||
#### Running out of registers
|
||||
|
||||
The approach sketched above does not generalize to arbitrary expressions.
|
||||
Create test cases that require more temporary registers than are available.
|
||||
|
||||
Start thinking about solutions for this limitation. Two standard solutions are using the stack for temporaries (which is slow since it involves writing to and reading from memory), and register allocation (which requires a program analysis on the generated code).
|
||||
|
||||
#### <a name="shortcircuit"></a>Short-circuit Boolean operations.
|
||||
|
||||
When encountering a Boolean operation of the form `False and ...`, do we really care about the right side of the operator? Similarly, when we encounter `True or ...`?
|
||||
|
||||
Think about how we can use conditional jumps in RISC-V to short-circuit (any) Boolean operators. Have a look at the instruction set and online compiler.
|
||||
|
||||
A conditional jump in RISC-V takes a label, which represents an address in memory. In Stratego, labels can be generated using `<newname> "L" => label`. (Note that `"L"` can be anything you want, as long as it is a string.) -->
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-3 col-xl-3 " >
|
||||
<div class="sticky-top top70 d-none d-lg-block d-xl-block">
|
||||
<div class="pb4 mb3 border-bottom border-grey ">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-left">
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="10.html">
|
||||
«
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../project/index.html">
|
||||
^
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="12.html">»</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<ul id="my_toc" class="toc list-group list-group-flush d-none d-lg-block d-xl-block p-0 ml-0 mt-3">
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#register-allocation">Register Allocation</a>
|
||||
<ul class="toc-sub pl-0">
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#liveness-analysis">Liveness Analysis</a></li>
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#graph-coloring">Graph Coloring</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#control-flow">Control Flow</a>
|
||||
<ul class="toc-sub pl-0">
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#desugaring">Desugaring</a></li>
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#explicating-control-translation-to-c-ir">Explicating Control: Translation to C-IR</a></li>
|
||||
<li class="list-group-item pl-0 ml-0 border-0 pl-0 pt-0 pb-1 pr-0 m-0 mr-3"><a href="11.html#instruction-selection">Instruction Selection</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="border-top border-bottom border-grey mt-3 pt-3">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-center">
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="10.html">
|
||||
Previous
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="12.html">Next</a>
|
||||
</li>
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="../project/index.html">
|
||||
Index
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue