download 2021 site
This commit is contained in:
commit
dd6e4afb13
138 changed files with 37730 additions and 0 deletions
503
lab/10.html
Normal file
503
lab/10.html
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
<!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 10: Instruction Selection</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 10: Instruction Selection
|
||||
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
|
||||
|
||||
Project
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
November 26, 2021
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 border-lg-right border-grey">
|
||||
<p>See the slides of <a href="../lecture/12.html">Lecture 12</a> on code generation for an approach to get started.</p>
|
||||
|
||||
<h3 id="objectives">Objectives</h3>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<p>Develop tests for your compiler that explore the edge cases.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Define a transformation strategy that translates ChocoPy expressions to RISC-V code, including variable definitions, integer and boolean constants and operators.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Develop a simplification transformation that transforms expressions such that more concise code can be generated.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h3 id="basic-compiler-pipeline">Basic Compiler Pipeline</h3>
|
||||
|
||||
<p>Follow the slides of <a href="../lecture/12.html">Lecture 12</a> to set up a basic compiler pipeline:</p>
|
||||
|
||||
<pre><code class="language-stratego:"> compile-to-rv32im-ast :: Program -> RProgram
|
||||
|
||||
compile-to-rv32im-ast =
|
||||
compile-cpy-to-cir
|
||||
; compile-cir-to-rv32im
|
||||
; compile-rv32im
|
||||
|
||||
</code></pre>
|
||||
|
||||
<h4 id="debugging">Debugging</h4>
|
||||
|
||||
<p>Use <code class="language-plaintext highlighter-rouge">debug(!msg)</code> or <code class="language-plaintext highlighter-rouge">dbg(|msg)</code> to print the current term to the console.</p>
|
||||
|
||||
<p>You can also debug your generated code, by creating a <code class="language-plaintext highlighter-rouge">.cpy</code> file, transforming it to an <code class="language-plaintext highlighter-rouge">.rv32im</code> file using the <code class="language-plaintext highlighter-rouge">Spoofax > Generation</code> menu, and copying the generated output to the online Venus editor, where you can use the <code class="language-plaintext highlighter-rouge">Simulator</code> tab to run your code or step through each line, and inspect the memory and registers.</p>
|
||||
|
||||
<h3 id="from-chocopy-to-c-ir">From ChocoPy to C-IR</h3>
|
||||
|
||||
<p>Develop a first compilation phase to transform ChocoPy programs to an intermediate language that makes control flow explicit.</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> compile-cpy-to-cir :: Program -> CProgram
|
||||
|
||||
compile-cpy-to-cir =
|
||||
explicate-types
|
||||
; desugar
|
||||
; uniquify
|
||||
; remove-complex-operands
|
||||
; explicate-control
|
||||
</code></pre></div></div>
|
||||
|
||||
<h4 id="explicate-types">Explicate Types</h4>
|
||||
|
||||
<p>A useful transformation is to type specialize the constructors of the AST, such that the analysis results are no longer needed.
|
||||
That is useful when applying transformations, since preserving those annotations cannot be done for all transformations.
|
||||
<strong>The following <code class="language-plaintext highlighter-rouge">explicate-types</code> transformation should be invoked on the AST before invoking the compiler transformation.</strong></p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>signature
|
||||
constructors
|
||||
AddInt : Exp * Exp -> Exp
|
||||
|
||||
rules
|
||||
|
||||
explicate-types =
|
||||
innermost(type-specialize)
|
||||
|
||||
type-specialize :
|
||||
add@Add(e1, e2) -> AddInt(e1, e2)
|
||||
where <get-type> add => Int()
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Note that the rules for translating integer additions should be adapted to reflect the change in constructors.</p>
|
||||
|
||||
<h4 id="desugar">Desugar</h4>
|
||||
|
||||
<p>To improve the result of code generation, it can be useful to transform the source language expression.
|
||||
For example, left-associative additions may produce a better result.</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> desugar-exp :
|
||||
AddInt(e1, AddInt(e2, e3)) -> AddInt(AddInt(e1, e2), e3)
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Define a transformation to <code class="language-plaintext highlighter-rouge">desugar</code> ChocoPy programs, making the task of downstream compilation easier.</p>
|
||||
|
||||
<p>This simplification can include constant folding, eventually.
|
||||
However, do not yet include constant folding rules, since you want to test the translation of operators.
|
||||
That is, your code generator should be able to translate arbitrary combinations of operators, so be prepared for the general case.</p>
|
||||
|
||||
<p>Think about (and try out in the online compiler!) the differences between adding integers and concatenating strings in RISC-V. In ChocoPy, they initially both use the <code class="language-plaintext highlighter-rouge">Add(...)</code> constructor, so make sure to disambiguate them into separate constructors you will later use for code generation. The same holds for other operations, but you have to think about those yourself.</p>
|
||||
|
||||
<h4 id="uniquify">Uniquify</h4>
|
||||
|
||||
<p>Define a transformation that eliminates shadowing. (See slides)</p>
|
||||
|
||||
<h4 id="remove-complex-operands">Remove Complex Operands</h4>
|
||||
|
||||
<p>To simplify instruction selection, simplify expressions to only use atomic expressions (constants or variables) creating new variables to store intermediate results.</p>
|
||||
|
||||
<h4 id="explicate-control-using-c-ir">Explicate Control using C-IR</h4>
|
||||
|
||||
<p>Use an intermediate language to explicate control.
|
||||
That is, the <code class="language-plaintext highlighter-rouge">explicate-control</code> strategy translates from ChocoPy ASTs</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>signature
|
||||
sorts CID CINT CProgram CBlock CLabel CTail CStmt
|
||||
CType CExp CAtom CVar
|
||||
constructors
|
||||
: string -> CID
|
||||
: string -> CINT
|
||||
CProgram : List(CBlock) -> CProgram
|
||||
CBlock : CLabel * CTail -> CBlock
|
||||
CLabel : CID -> CLabel
|
||||
CReturn : CExp -> CTail
|
||||
CReturnNone : CTail
|
||||
CSeq : CStmt * CTail -> CTail
|
||||
CVarDec : CVar * CType * CExp -> CStmt
|
||||
CAssign : CVar * CExp -> CStmt
|
||||
CIntT : CType
|
||||
: CAtom -> CExp
|
||||
CMin : CAtom -> CExp
|
||||
CAdd : CAtom * CAtom -> CExp
|
||||
CMul : CAtom * CAtom -> CExp
|
||||
CDiv : CAtom * CAtom -> CExp
|
||||
CInt : CINT -> CAtom
|
||||
: CVar -> CAtom
|
||||
CVar : CID -> CVar
|
||||
</code></pre></div></div>
|
||||
|
||||
<h3 id="instruction-selection">Instruction Selection</h3>
|
||||
|
||||
<p>Define a transformation from C-IR to RISC-V instructions:</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> compile-cir-to-rv32im :: CProgram -> RProgram
|
||||
|
||||
compile-cir-to-rv32im =
|
||||
select-instructions-cprogram
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>The essence of this translation consists of instruction selection, i.e. mapping the high-level instructions of the intermediate language to concrete RISC-V instructions.
|
||||
For example, register allocation for integer constants, variables, and addition may be defined as follows:</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> select-instrs-exp(|RArg) :: CExp -> List(RLine)
|
||||
|
||||
select-instrs-exp(|x) :
|
||||
CInt(i) -> [RLi(x, <cint-to-rint>i)]
|
||||
|
||||
select-instrs-exp(|x) :
|
||||
CVar(y) -> [RMv(x, RVar(<cid-to-string>y))]
|
||||
|
||||
select-instrs-exp(|x) :
|
||||
CAdd(y@CVar(_), z@CVar(_)) -> [RAdd(x, <cvar-to-rvar>y, <cvar-to-rvar>z)]
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Note that because expressions have been transformed to applications of operators to atomic expressions, this step does not need to invent names for intermediate results.
|
||||
We use <code class="language-plaintext highlighter-rouge">RVar(x)</code> as symbolic registers in RISC-V code in order to postpone the mapping to concrete registers.</p>
|
||||
|
||||
<p>Define transformation rules for all operators of ChocoPy expressions.</p>
|
||||
|
||||
<h4 id="special-cases">Special Cases</h4>
|
||||
|
||||
<p>RISC-V provides specialized instructions for some operations.
|
||||
For example, the <code class="language-plaintext highlighter-rouge">addi</code> instruction allows directly adding an integer constant (between -2048 and 2047) to a register.
|
||||
A compiler can make use of such instructions, by detecting special patterns in the source language.
|
||||
For example, the following rule, detects additions with an integer constant, and translates those to applications of <code class="language-plaintext highlighter-rouge">addi</code>, avoiding the use of an extra register.</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>select-instrs-exp(|x) :
|
||||
CAdd(z@CVar(_), CInt(i)) -> [RAddi(x, <cvar-to-rvar>z, <cint-to-rint>i)]
|
||||
with debug(!"select-instrs-exp: ")
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Can you detect other specialized instructions and corresponding source language patterns that provide a more concise and/or faster target code?</p>
|
||||
|
||||
<h4 id="booleans">Booleans</h4>
|
||||
|
||||
<p>In ChocoPy we compile the Booleans <code class="language-plaintext highlighter-rouge">True</code> and <code class="language-plaintext highlighter-rouge">False</code> to integers in RISC-V (<code class="language-plaintext highlighter-rouge">1</code> and <code class="language-plaintext highlighter-rouge">0</code>, respectively).
|
||||
Implement the Boolean operators <code class="language-plaintext highlighter-rouge">and</code>, <code class="language-plaintext highlighter-rouge">or</code> and <code class="language-plaintext highlighter-rouge">not</code>, and integer comparison operators <code class="language-plaintext highlighter-rouge">==</code>, <code class="language-plaintext highlighter-rouge">!=</code>, <code class="language-plaintext highlighter-rouge"><</code>, <code class="language-plaintext highlighter-rouge">></code>, <code class="language-plaintext highlighter-rouge"><=</code>, <code class="language-plaintext highlighter-rouge">>=</code> (you can ignore <code class="language-plaintext highlighter-rouge">is</code> for now since it operates on objects).</p>
|
||||
|
||||
<p>You can make use of the online compiler, or the RISC-V instruction set to find the proper instructions in RISC-V.</p>
|
||||
|
||||
<!-- Also implement the ternary operator `... if ... else ...`. (See [Short-circuit Boolean operations](#shortcircuit) to get an idea on how to implement it.) -->
|
||||
|
||||
<h3 id="patching-risc-v-instructions">Patching RISC-V instructions</h3>
|
||||
|
||||
<p>Finally, we define a compiler stage that patches up the RISC-V code:</p>
|
||||
|
||||
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> compile-rv32im :: RProgram -> RProgram
|
||||
|
||||
compile-rv32im =
|
||||
assign-homes
|
||||
; patch-instructions
|
||||
</code></pre></div></div>
|
||||
|
||||
<h4 id="assign-homes">Assign Homes</h4>
|
||||
|
||||
<p>Map symbolic registers to concrete registers.
|
||||
Either by mapping variables to actual registers or by using a stack discipline.
|
||||
We will look at register allocation next, so keep it simple.</p>
|
||||
|
||||
<h4 id="patch-instructions">Patch Instructions</h4>
|
||||
|
||||
<p>This is the place for any mopping up that needs to be done.</p>
|
||||
|
||||
|
||||
</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="9.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="11.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="10.html#objectives">Objectives</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="10.html#basic-compiler-pipeline">Basic Compiler Pipeline</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="10.html#debugging">Debugging</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="10.html#from-chocopy-to-c-ir">From ChocoPy to C-IR</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="10.html#explicate-types">Explicate Types</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="10.html#desugar">Desugar</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="10.html#uniquify">Uniquify</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="10.html#remove-complex-operands">Remove Complex Operands</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="10.html#explicate-control-using-c-ir">Explicate Control using C-IR</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="10.html#instruction-selection">Instruction Selection</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="10.html#special-cases">Special Cases</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="10.html#booleans">Booleans</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="10.html#patching-risc-v-instructions">Patching RISC-V instructions</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="10.html#assign-homes">Assign Homes</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="10.html#patch-instructions">Patch Instructions</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="9.html">
|
||||
Previous
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="11.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